如何使函数返回指向C中新字符串的指针?

时间:2013-06-17 03:08:19

标签: c pointers

我正在阅读K& R,我几乎完成了关于指针的章节。我不确定我是否会以正确的方式使用它们。我决定尝试使用指针实现itoa(n)。关于我这样做的方式有什么明显的错误吗?我不是特别喜欢我需要留出一个大型数组来作为字符串缓冲区来做任何事情,但话又说回来,我不确定这是否真的是在C中实现它的正确方法。 / p>

在决定在代码中使用指针时,是否有任何一般指导原则?我可以在下面的代码中改进一下吗?有没有一种方法可以使用没有静态字符串缓冲区的字符串?

/*Source file: String Functions*/
#include <stdio.h>

static char stringBuffer[500];
static char *strPtr = stringBuffer;

/* Algorithm: n % 10^(n+1) / 10^(n) */
char *intToString(int n){
    int p = 1;
    int i = 0;

    while(n/p != 0)
        p*=10, i++;

    for(;p != 1; p/=10)
       *(strPtr++) = ((n % p)/(p/10)) + '0';  
    *strPtr++ = '\0';

    return strPtr - i - 1;
}

int main(){
    char *s[3] = {intToString(123), intToString(456), intToString(78910)};
    printf("%s\n",s[2]);
    int x = stringToInteger(s[2]);

    printf("%d\n", x);

    return 0;
}

最后,有人可以告诉我数组和指针之间的区别是什么? K&amp; R中有一节让我非常困惑; “5.5 - 字符指针和功能。”我在这里引用它:

  

“定义之间存在重要差异:

char amessage[] = "now is the time"; /*an array*/
char *pmessage = "now is the time"; /*a pointer*/
     

amessage是一个数组,大小足以容纳字符序列和'\ 0'   初始化它。数组中的单个字符可能会更改,但是消息将会更改   总是指相同的存储空间。另一方面,pmessage是一个指针,已初始化   指向一个字符串常量;随后可以将指针修改为指向   在其他地方,但如果你试图修改字符串内容,结果是未定义的。“

这甚至意味着什么?

5 个答案:

答案 0 :(得分:1)

关于你的上一个问题:

char amessage[] = "now is the time"; - 是一个数组。无法重新分配数组以指向别的东西(与指针不同),它指向内存中的固定地址。如果数组是在一个块中分配的,那么它将在块的末尾被清除(意味着你不能从函数返回这样的数组)。但是,只要不超过数组的大小,就可以根据需要调整数组中的数据。

E.g。这是合法的amessage[0] = 'N';

char *pmessage = "now is the time"; - 是一个指针。指针指向内存中的块,仅此而已。 "now is the time"是一个字符串文字,意味着它存储在只读位置的可执行文件中。在任何情况下,您都不能修改它指向的数据。但是,您可以将指针重新指定为指向其他内容。

这不合法 - *pmessage = 'N'; - 最有可能发生段错误(请注意,您可以使用带指针的数组语法,*pmessage等同于pmessage[0])。

如果使用-S标志使用gcc编译它,实际上可以看到存储在程序集可执行文件的只读部分中的"now is the time"

另外需要指出的是,当作为参数传递给函数时,数组会衰减为指针。以下两个声明是等效的:

void foo(char arr[]);

void foo(char* arr);

答案 1 :(得分:1)

关于您的代码:

每次调用intToString时都使用一个静态缓冲区:这很糟糕,因为第一次调用它所产生的字符串将被下一个调用。

通常,处理C中字符串的函数应该从malloc返回一个新缓冲区,或者它们应该写入调用者提供的缓冲区。由于缓冲区空间不足,分配新缓冲区不太容易出现问题。

你也正在使用一个静态指针来写入缓冲区,它永远不会倒带,所以这肯定是一个问题:对这个函数的调用足够多,你将在缓冲区的末尾运行并崩溃。 / p>

您已经有一个初始循环来计算函数中的位数。所以你应该使用malloc创建一个新的缓冲区,确保为\0留出空间,写入,然后返回。

此外,由于i不仅仅是一个循环索引,因此将其更改为更明显的length

也就是说:摆脱全局变量,而不是在计算length之后:

char *s, *result;

// compute length
s = result = malloc(length+1);
if (!s) return NULL; // out of memory

for(;p != 1; p/=10)
   *(s++) = ((n % p)/(p/10)) + '0';  
*s++ = '\0';
return result;

调用者负责在完成缓冲后释放缓冲区。

在学习指针时我真正推荐的另外两件事:

  • 编译打开所有警告(-Wall等),如果收到错误,请尝试了解导致错误的原因;他们会有一些东西可以教你如何使用这种语言

  • 在Valgrind或类似的检查程序下运行程序,这会使指针错误更明显,而不是导致静默损坏

答案 2 :(得分:1)

对于itoa,结果字符串的长度不能大于INT_MAX +减号的长度 - 所以使用该长度的缓冲区是安全的。使用log10(number) + 1可以很容易地确定数字字符串的长度,因此您需要缓冲区大小为log10(INT_MAX) + 3,空间为负数并终止\ 0。

此外,通常从函数返回指向“黑盒子”缓冲区的指针并不是一个好习惯。这里你最好的选择是在intToString中提供一个缓冲区作为指针参数,这样你就可以轻松使用你喜欢的任何类型的内存(动态,堆栈分配等)。这是一个例子:

char *intToString(int n, char *buffer) {
    // ...        
    char *bufferStart = buffer;
    for(;p != 1; p/=10)
      *(buffer++) = ((n % p)/(p/10)) + '0';  
    *buffer++ = '\0';
    return bufferStart;
}

然后您可以按如下方式使用它:

  char *buffer1 = malloc(30);
  char buffer2[15];

  intToString(10, buffer1); // providing pointer to heap allocated memory as a buffer
  intToString(20, &buffer2[0]); // providing pointer to statically allocated memory
  

数组与指针的区别是什么?

答案在你的引用中 - 指针可以修改为指向另一个内存地址。比较:

int a[] = {1, 2, 3};
int b[] = {4, 5, 6};
int *ptrA = &a[0]; // the ptrA now contains pointer to a's first element
ptrA = &b[0];      // now it's b's first element
a = b;             // it won't compile

此外,数组通常是静态分配的,而指针适用于任何分配机制。

答案 3 :(得分:0)

关于如何使用指针以及数组和指针之间的区别,我建议你阅读“专家c编程”(http://www.amazon.com/Expert-Programming-Peter-van-Linden/dp/0131774298/ref=sr_1_1?ie=UTF8&qid=1371439251&sr=8-1&keywords=expert+c+programming)。

答案 4 :(得分:0)

从函数返回字符串的更好方法是分配动态内存(使用malloc)并用必需的字符串填充...将此指针返回给调用函数然后释放它。

示例代码:

#include "stdio.h"
#include "stdlib.h"
#include "string.h"

#define MAX_NAME_SIZE 20

char * func1()
{
    char * c1= NULL;
    c1 = (char*)malloc(sizeof(MAX_NAME_SIZE));
    strcpy(c1,"John");
    return c1;
}

main()
{
    char * c2 = NULL;
    c2 = func1();
    printf("%s \n",c2);
    free(c2);
}

这没有静态字符串。