我如何 return 来自一个函数的字符串,该函数将在 C 中的 printf 中使用?

How do I return a string from a function that's meant to be used in printf in C?

我的函数需要 return 一个字符串并使用索引构建它。理想情况下,我想在 printf() 中使用 return 值。我知道鉴于我的 return 类型,我需要 return 一个 char 字符串指针,但我不能使 reg 成为一个字符串指针,因为我需要访问单个元素。另外,我知道我需要 malloc() 它,但我不知道如果它被用作 printf 的参数我将如何释放该内存。

这是我的代码:

char * printRegister(int num) {
    char reg[] = "$error";
    memset(reg, 0, sizeof(reg));
    switch(num)
    {
        case 0:
            strcpy(reg, "$zero");
            break;
        case 1:
            strcpy(reg, "$at");
            break;
        case 2:
        case 3:
            reg[0] = '$';
            reg[1] = 'v';
            reg[2] = num - 2 + '0';
            reg[3] = '[=11=]';
            break;
        case 4:
        case 5:
        case 6:
        case 7:
            reg[0] = '$';
            reg[1] = 'a';
            reg[2] = num - 4 + '0';
            reg[3] = '[=11=]';
            break;
        case 8:
        case 9:
        case 10:
        case 11:
        case 12:
        case 13:
        case 14:
        case 15:
            reg[0] = '$';
            reg[1] = 't';
            reg[2] = num - 8 + '0';
            reg[3] = '[=11=]';
            break;
        case 16:
        case 17:
        case 18:
        case 19:
        case 20:
        case 21:
        case 22:
        case 23:
            reg[0] = '$';
            reg[1] = 's';
            reg[2] = num - 16 + '0';
            reg[3] = '[=11=]';
            break;
        case 24:
        case 25:
            reg[0] = '$';
            reg[1] = 't';
            reg[2] = num - 16 + '0';
            reg[3] = '[=11=]';
            break;
        case 26:
        case 27:
            reg[0] = '$';
            reg[1] = 'k';
            reg[2] = num - 26 + '0';
            reg[3] = '[=11=]';
            break;
        case 28:
            strcpy(reg, "$gp");
            break;
        case 29:
            strcpy(reg, "$sp");
            break;
        case 30:
            strcpy(reg, "$fp");
            break;
        case 31:
            strcpy(reg, "$ra");
            break;
        default:
            strcpy(reg, "error");
            break;
    }
    return reg;
}

有办法吗?

How do I return a string from a function that's meant to be used in printf in C?

对于 C99 或更高版本,使用 复合文字 来临时存储字符串。

首先,重写 printRegister() 以接受 char * 缓冲区。

#define  printRegister_N 7
// char * printRegister(int num) {
char * printRegister(char *reg, int num) {
  // char reg[] = "$error";
  // memset(reg, 0, sizeof(reg));
  memset(reg, 0, printRegister_N);  // not really needed anymore with compound literal
  ...
  return reg;
}

形成一个调用 printRegister() 复合文字 的宏。

//                                   v---------------------------v  compound literal
#define PRINT_REG(num) printRegister((char [printRegister_N]){ 0 }, (num))

然后调用PRINT_REG(num)。返回的 char*(它是指向 复合文字 的指针)在块结束之前有效。不需要 *alloc()free()

printf("1st:%s  2nd:%s\n", PRINT_REG(0), PRINT_REG(1));

代码仍然使用 "%s" 的常用装饰,例如 "%-7s""%.*s"

试试 strdup(),它会为您分配内存并复制您想要的任何字符串。

任何已经 dynamically allocated 的内存块都可以用 free() 释放。

举个例子:

#include <string.h> //note: don't include "string", instead include string.h 
#include <stdlib.h> //note: this header defines free,

int main()
{
    char *data = strdup("the return value of strdup is will be a string containing this");

    /* use dynamically allocated string... */



    free(data); /* free up the memory. */
}

通过声明一个接受字符串参数的辅助函数

void myprint(char *str) {
    printf("rs: %s\n", str);
}

您可以将 printRegister() 的实现更改为

void printRegister(int num, void (*fn)(char*)) {
    ...
    fn(reg); // instead of return reg;
}

然后调用

printRegister(rs, myprint);

这通过调换调用者和被调用者的角色来避免动态分配内存的需要; printRegister() 的调用者现在提供了一个 callback 可以接受临时堆栈变量并对它做一些事情。

Try it online!

当前代码具有未定义的行为,因为您 return 指向具有自动存储功能的对象的指针,当函数 returns.

时,该对象将超出范围

有几种方法可以解决这个问题:

  • 你可以给 reg static 存储空间 class。返回指向 reg 的指针是可以的,但是内容将被随后对 printRegister 的调用覆盖,因此您不能多次使用 printRegister 作为单个 printf 调用的参数.

  • 您可以 return reg 的已分配副本,但调用者需要释放此内存块以避免内存泄漏。这对您的目的来说很麻烦。

  • 您可以更改 printRegister 的原型以获取指向目标数组的指针,并且 return 在存储寄存器名称的文本表示之后。请注意,在同一 printf 调用中多次调用 printRegister 需要单独的数组。

  • 因为只有 33 种不同的可能字符串,您可以 return 字符串常量。此解决方案简单高效,但建议将 return 类型更改为 const char *.

这是最后一种方法的实现:

const char *printRegister(int num) {
    static const char * const regname[] = {
        "$zero", "$at", "$v0", "$v1", "$a0", "$a1", "$a2", "$a3",
        "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7",
        "$s0", "$s1", "$s2", "$s3", "$s4", "$s5", "$s6", "$s7",
        "$t8", "$t9", "$k0", "$k1", "$gp", "$sp", "$fp", "$ra",
    };
    return (num >= 0 && num < 32) ? regname[num] : "error";
}