C返回字符串

函数返回字符串时,它返回的实际是字符串的地址。这里应该关注的主要问题是如何返回合法的地址,要做到这一点,可以返回以下三种对象之一的引用:

  • 字面量;
  • 动态分配的内存;
  • 本地字符串变量。

返回字面量的地址

返回字面量的例子如下所示,利用一个整数码从四个处理中心选择一个。这个函数的目的是把处理中心的名字作为字符串返回。在本例中,它只是返回了字面量的地址:

char* returnALiteral(int code) {
    switch(code) {
        case 100:
            return "Boston Processing Center";
        case 200:
            return "Denver Processing Center";
        case 300:
            return "Atlanta Processing Center";
        case 400:
            return "San Jose Processing Center";
    }
}

这段代码会工作得很好。唯一需要记住的一点是我们并非总是将字符串字面量看做常量,字符串字面量池讨论过这一点。也可以像下例这样声明静态字面量,我们增加了subCode字段来选择不同的中心,这么做的好处是无需在不同的地方使用同一个字面量,也就不会因为打错字而引入错误了:

char* returnAStaticLiteral(int code, int subCode) {
    static char* bpCenter = "Boston Processing Center";
    static char* dpCenter = "Denver Processing Center";
    static char* apCenter = "Atlanta Processing Center";
    static char* sjpCenter = "San Jose Processing Center";

    switch(code) {
        case 100:
            return bpCenter;
        case 135:
            if(subCode <35) {
                return dpCenter;
            } else {
                return bpCenter;
            }
        case 200:
            return dpCenter;
        case 300:
            return apCenter;
        case 400:
            return sjpCenter;
    }
}

针对多个不同目的返回同一个静态字符串的指针可能会有问题。考虑下面的函数,这是在字符串传递中开发的format函数的变体。将一个部件的信息传递给函数,然后返回一个表示这个部件的格式化字符串:

char* staticFormat(const char* name, size_t quantity, size_t weight) {
    static char buffer[64]; // 假设缓冲区足够大
    sprintf(buffer, "Item: %s Quantity: %u Weight: %u",
            name, quantity, weight);
    return buffer;
}

为缓冲区分配64字节可能够,也可能不够,就本例的目的而言,我们会忽略这个潜在的问题。这种方法的主要问题用如下代码片段说明:

char* part1 = staticFormat("Axle",25,45);
char* part2 = staticFormat("Piston",55,5);
printf("%s\n",part1);
printf("%s\n",part2);

执行后得到如下输出:

Item: Piston Quantity: 55 Weight: 5
Item: Piston Quantity: 55 Weight: 5

staticFormat两次调用都使用同一个静态缓冲区,后一次调用会覆写前一次调用的结果。

返回动态分配内存的地址

如果需要从函数返回字符串,我们可以在堆上分配字符串的内存然后返回其地址。我们会开发一个blanks函数来说明这种技术,这个函数会返回一个包含一系列代表“制表符”的空白的字符串,如下所示。函数接受一个指定制表符序列长度的整数参数:

char* blanks(int number) {
    char* spaces = (char*) malloc(number + 1);
    int i;
    for (i = 0; i<number; i++) {
        spaces[i] = ' ';
    }
    spaces[number] = '\0';
    return spaces;
}

    ...
    char *tmp = blanks(5);

NUL终结符赋给由number索引的数组的最后一个元素,图5-14说明了本例的内存分配,它显示了blanks函数返回前后应用程序的状态。

enter image description here

图5-14:返回动态分配的字符串

释放返回的内存是函数调用者的责任,如果不再需要内存但没有将其释放会造成内存泄漏。下面是一个内存泄漏的例子,printf函数中使用了字符串,但是接着它的地址就丢失了,因为我们没有保存:

printf("[%s]\n",blanks(5));

一个更安全的方法如下所示:

char *tmp = blanks(5);
printf("[%s]\n",tmp);
free(tmp);

返回局部字符串的地址

返回局部字符串的地址可能会有问题,如果内存被别的栈帧覆写就会损坏,应该避免使用这种方法,这里作解释只是为了说明实际使用这种方法的潜在问题。

我们重写前面的blanks函数,如下所示。在函数内部声明一个数组,而不是动态分配内存,这个数组位于栈帧上。函数返回数组的地址:

#define MAX_TAB_LENGTH 32

char* blanks(int number) {
    char spaces[MAX_TAB_LENGTH];
    int i;
    for (i = 0; i < number && i < MAX_TAB_LENGTH; i++) {
        spaces[i] = ' ';
    }
    spaces[i] = '\0';
    return spaces;
}

执行函数后会返回数组的地址,但是之后下一次函数调用会覆写这块内存区域。解引指针后该内存地址的内容可能已经改变。图5-15说明了程序栈的状态。

enter image description here

图5-15:返回局部字符串的地址

赞(1)

评论 抢沙发

评论前必须登录!

 

C指针