在C中通过引用传递字符串

我无法弄清楚如何通过函数的参数传回字符串。 我是编程新手,所以我想这可能是一个初学者的问题。 你能给予的任何帮助都将非常感激。 这段代码出错了,我不知道为什么,但是我提供的代码是为了展示我到目前为止的内容。

我已将其设为社区维基,因此您可以自由编辑。

PS这不是功课。

这是原始版本

#include  #include  #include  void fn(char *baz, char *foo, char *bar) { char *pch; /* this is the part I'm having trouble with */ pch = strtok (baz, ":"); foo = malloc(strlen(pch)); strcpy(foo, pch); pch = strtok (NULL, ":"); bar = malloc(strlen(pch)); strcpy(bar, pch); return; } int main(void) { char *mybaz, *myfoo, *mybar; mybaz = "hello:world"; fn(mybaz, myfoo, mybar); fprintf(stderr, "%s %s", myfoo, mybar); } 

更新这是一个更新版本,其中包含一些建议:

 #include  #include  #include  #define MAXLINE 1024 void fn(char *baz, char **foo, char **bar) { char line[MAXLINE]; char *pch; strcpy(line, baz); pch = strtok (line, ":"); *foo = (char *)malloc(strlen(pch)+1); (*foo)[strlen(pch)] = '\n'; strcpy(*foo, pch); pch = strtok (NULL, ":"); *bar = (char *)malloc(strlen(pch)+1); (*bar)[strlen(pch)] = '\n'; strcpy(*bar, pch); return; } int main(void) { char *mybaz, *myfoo, *mybar; mybaz = "hello:world"; fn(mybaz, &myfoo, &mybar); fprintf(stderr, "%s %s", myfoo, mybar); free(myfoo); free(mybar); } 

首先,那些mallocs应该是strlen(whatever)+1字节。 C字符串有一个0字符表示结尾,称为NUL终止符,并且它不包含在strlen测量的长度中。

接下来,strtok修改你正在搜索的字符串。 您正在向它传递一个指向您不允许修改的字符串的指针(您无法修改文字字符串)。 这可能是段错误的原因。 因此,您可以将其复制到您自己的可修改缓冲区,而不是使用指向不可修改的字符串文字的指针,如下所示:

 char mybaz[] = "hello:world"; 

这样做是在堆栈上放置一个12字符大小的数组,并将字符串文字的字节复制到该数组中。 它的工作原理是因为编译器在编译时知道字符串的长度,并且可以相应地创建空间。 这节省了使用malloc的特定副本。

你引用的问题是你正在将mybaz,myfoo和mybar的传递给你的函数。 除非传递指向 myfoo和mybar的指针 ,否则无法修改调用者的变量。 由于myfoo是一个char *,指向它的指针是一个char **:

 void fn(char *baz, char **foo, char **bar) // take pointers-to-pointers *foo = malloc(...); // set the value pointed to by foo fn(mybaz, &myfoo, &mybar); // pass pointers to myfoo and mybar 

修改代码中函数的foo对myfoo完全没有影响。 myfoo是未初始化的,所以如果前两个都没有引起它,那么当您使用未初始化的指针进行打印时,很可能会发生段错误。

一旦你基本上工作了,你可能想要添加一些error handling。 如果找不到它正在查找的分隔符, strtok可以返回NULL,并且不能使用NULL调用strlen 。 如果没有足够的内存, malloc可以返回NULL,也不能用NULL调用strcpy

每个人都忽视的一件事是你在存储在const存储器中的数组上调用strtok。 strtok写入您传递的数组,因此请确保在调用strtok之前将其复制到临时数组,或者只是分配原始数组:

 char mybaz[] = "hello:world"; 

在C中,您通常通过传递1)数组的第一个元素的指针,以及2)数组的长度来传递引用。

如果您确定缓冲区大小,有时可以省略数组的长度,并且可以通过查找空终止字符(值为0或'\0'字符)来知道字符串的长度。

从您的代码示例看来,您尝试设置指针所指向的值。 所以你可能想要一个char**指针。 并且您将传入要设置的char*变量的地址。

哦,是的,那里的问题很少。

通常,如果您要从函数内部操作字符串,那么这些字符串的存储最好不在函数内部。 实现这一目标的简单方法是在函数外部声明数组(例如在main() )并将数组(它们自动成为指向它们开头的指针)传递给函数。 只要结果字符串不会溢出数组中分配的空间,这就可以正常工作。

您已经使用了更多function但更困难的路径:使用malloc()为结果创建空间(目前为止很好!)然后尝试将malloc’d空间分配给您传入的指针。唉,唉, 不管用。

进入的指针是一个值; 你无法改变它。 解决方案是将指针传递给指针,并在函数内部使用它来更改指针指向的内容。

如果你有那个,太好了。 如果没有,请要求更多说明。

你想传回两个指针。 所以你需要用一对指针指针来调用它。 像这样的东西:

 void fn(char *baz, char **foo, char **bar) { ... *foo = malloc( ... ); ... *bar = malloc( ... ); ... } 

代码最有可能是段错误,因为你为字符串分配空间但是忘记了字符串末尾有一个额外的字节,即空终止符。

此外,您只传入一个指针。由于指针是一个32位值(在32位机器上),您只需将整数指针的值传递给“fn”。 以同样的方式,你不会表达一个传递给函数的整数返回给调用函数(没有显式地返回它),你不能指望一个指针做同样的事情。 因此,新的指针值永远不会返回到main函数。 通常你通过将指针传递给C中的指针来完成此操作。

另外别忘了免费动态分配内存!!

 void fn(char *baz, char **foo, char **bar) { char *pch; /* this is the part I'm having trouble with */ pch = strtok (baz, ":"); *foo = malloc(strlen(pch) + 1); strcpy(*foo, pch); pch = strtok (NULL, ":"); *bar = malloc(strlen(pch) + 1); strcpy(*bar, pch); return; } int main(void) { char *mybaz, *myfoo, *mybar; mybaz = "hello:world"; fn(mybaz, &myfoo, &mybar); fprintf(stderr, "%s %s", myfoo, mybar); free( myFoo ); free( myBar ); } 

其他答案描述了如何修复你的工作答案,但是一个简单的方法来完成你的意思是strdup(),它分配适当大小的新内存并复制正确的字符。

但仍需要使用char * vs char **修复业务。 没有办法解决这个问题。

基本问题是,尽管存储器(使用malloc() )为您尝试返回的结果分配为myfoomybar ,但指向这些分配的指针实际上并未返回到main() 。 因此,稍后调用printf()很可能会转储核心。

解决方案是将参数声明为指向char ponter,并将myfoomybar的地址mybarfn 。 这样的事情(未经测试)应该可以解决问题:

 void fn(char *baz, char **foo, char **bar) { char *pch; /* this is the part I'm having trouble with */ pch = strtok (baz, ":"); *foo = malloc(strlen(pch)+1); /* include space for NUL termination */ strcpy(*foo, pch); pch = strtok (NULL, ":"); *bar = malloc(strlen(pch)+1); /* include space for NUL termination */ strcpy(*bar, pch); return; } int main(void) { char mybaz[] = "hello:world"; char *myfoo, *mybar; fn(mybaz, &myfoo, &mybar); fprintf(stderr, "%s %s", myfoo, mybar); free(myfoo); free(mybar); } 

不要忘记稍后释放每个分配的字符串,否则会产生内存泄漏。

要在一次调用中同时执行malloc()和strcpy(),最好使用strdup() ,因为它还会记住为您从代码中写出的终止NUL分配空间。 *foo = strdup(pch)更清晰,更容易维护替代方案。 由于strdup()是POSIX而不是ANSI C,您可能需要自己实现它,但是通过这种用法的清晰度可以很好地弥补这一努力。

从C函数返回字符串的另一种传统方式是让调用者分配存储并将其地址提供给函数。 例如,这是sprintf()使用的技术。 它存在的问题是,假设已经分配了比实际可用空间更多的空间,则无法使这样的调用站点完全安全地防止由被调用函数引起的缓冲区溢出错误。 对此问题的传统修复是要求还传递缓冲区长度参数,并在代码审查中仔细validation实际分配和呼叫站点声明的长度。

编辑:

你得到的实际段错误很可能是在strtok()内部,而不是printf()因为你写的样本试图将字符串常量传递给strtok() ,它必须能够修改字符串。 这是官方未定义的行为。

解决此问题的方法是确保将bybaz声明为初始化数组,而不是指向char的指针。 初始化的数组将位于可写内存中,而字符串常量可能位于只读内存中。 在许多情况下,字符串常量存储在用于保存可执行代码本身的内存的相同部分中,而现代系统都试图使程序难以修改其自己的运行代码。

在我工作的嵌入式系统中,代码很可能存储在某种ROM中,并且无法进行物理修改。