如何在C语言中将char *str复制到char c[]中?

17

尝试将 char *str 复制到 char c[] 中,但出现分段错误或无效的初始化程序错误。

为什么这段代码会给我一个分段错误

char *token = "some random string";
char c[80];  
strcpy( c, token);
strncpy(c, token, sizeof c - 1); 
c[79] = '\0';
char *broken = strtok(c, "#");

你确定你不是想说反过来吗? - AJ.
1
源字符串所需的数组大小是否足够? - pmg
代码看起来没问题。它在哪一行出错了? - qrdl
“token”字符串的最长长度是多少?如果它超过了79个字符,第3行可能会出现问题... - Chris J
你应该缩小获取段错误的位置。 - glglgl
显示剩余3条评论
7个回答

26

使用strncpy()而不是strcpy()

/* code not tested */
#include <string.h>

int main(void) {
  char *src = "gkjsdh fkdshfkjsdhfksdjghf ewi7tr weigrfdhf gsdjfsd jfgsdjf gsdjfgwe";
  char dst[10]; /* not enough for all of src */

  strcpy(dst, src); /* BANG!!! */
  strncpy(dst, src, sizeof dst - 1); /* OK ... but `dst` needs to be NUL terminated */
      dst[9] = '\0';
  return 0;
}

2
为什么这段代码会给我一个“seg fault”错误?char c[80]; strcpy( c, token); strncpy(c, token, sizeof c - 1); c[79] = '\0'; char *broken = strtok(c, "#"); - Alex Xander
1
我点了个赞,因为你的代码明确地做到了BANG!这让我咯咯笑了起来。 - Gerrit
2
strncpy非常低效,最重要的是如果目标缓冲区比源小,则不会以空字符结尾。请不要使用它。 - Kasper
@kmm,你有什么备选的函数推荐吗? - nsg
@Nate 我建议使用strlcpy和strlcat,只要你注意始终检查返回值。静默截断与缓冲区溢出一样危险。 - Kasper
显示剩余2条评论

3
使用strncpy确保不会复制超过char[]所能容纳的字符数。
char *s = "abcdef";
char c[6];

strncpy(c, s, sizeof(c)-1);
// strncpy is not adding a \0 at the end of the string after copying it so you need to add it by yourself
c[sizeof(c)-1] = '\0';

编辑:已将代码添加到问题中。
查看您的代码,段错误可能是由以下行引起的。
strcpy(c, token)

问题在于如果令牌长度大于c长度,则内存会填满c变量,从而引起问题。

2
char *str = "Hello";
char c[6];
strcpy( c, str );

使用strncpy可以增加安全性。 strncpy(c, str, sizeof(str)); - Tobias Wärre
1
如果字符串长度大于c的长度,这将会失败。 - Patrice Bernassola
1
真是个奇怪的评论,帕特里斯。字符串长度并不比c长度大。字符串长度是5。再加上空终止符就是6。最初的问题并不涉及安全性或其他任何事情,只是简单地问“如何将char*复制到char[]”。我给出了最简单的解决方案。现在他完全改变了问题,变成了“为什么我的代码会崩溃”。 - Paul Mitchell

0

字符数组 c[] 必须有一定的大小;

例如:

char c[]= "example init string";

// 这将设置表c为c[19]; 您可以直接在程序开头分配它;

char c[19] = {0}; // null filled table

char c[i]是指针,因此您不需要复制任何内容; char c[19]; c =“示例初始化字符串”; //现在&c[0]指向相同的地址;

可以使用以下方式进行复制:

 strcpy(dst, src);

但是微软强制要求您使用安全函数:

strcpy_s(dst,buffsize,src);

char c[]的大小与指针相同。 char c [] =“example init string”; 与char * c =“example init string”; 完全相同。在Linux上,它会将该字符串文字放入ELF对象文件的.rodata部分,然后仅移动地址到指针变量中。它不会在声明时在堆栈上分配新的19个字节。 - Sean A.O. Harney
@Sean:抱歉,你错了。数组不是指针。char c[] = "test"; 创建了一个有5个元素的数组。char *c = "test" 则创建了一个指向有5个元素的数组的指针。它们的 sizeof 也不同! - pmg
@pmg 或许我的编译器正在优化它,当我在 gdb 中有那个声明时,它会执行一个 mov 指令将字符串的地址移动到 %eax 中。对于 sizeof(c),它是在编译时而不是运行时完成的,但你是正确的,它们并不完全等价。但它实际上并没有从 .rodata 或其他地方 memcpy 字符串到堆栈中,只是移动了地址,因此在这方面它就像使用指针一样。 - Sean A.O. Harney

0

编辑:

感谢您添加代码。

也许段错误发生在这里:

strncpy(c, token, sizeof c - 1); 

sizeof 的优先级与从右到左的减号相同,因此它很可能被处理为:

strncpy(c, token, sizeof( c - 1 ) ); 

而不是

strncpy(c, token, sizeof(c) - 1); 

这可能是您想要的

(参考: http://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B#Operator_precedence)


你可能想把那种东西作为评论而不是答案发布。 - Dominic Rodger
@alex-xander,感谢你的代码。根据这个代码修改了我的答案。 - dstibbe

0

我已经有一段时间没有编写C/C++代码了,但是c[80]可能是在堆栈上分配的。如果您使用char * c和strdup或类似函数,则可以将其分配在堆上,以便strtok可以访问它。

尝试像这样做。

char *token = "some random string";
char *c;
c = strdup(token);
char *broken = strtok(c, "#");
free(c);

strtok不关心它操作的字符串是在堆栈还是堆中。 - Paul Mitchell
好的,这里有一个遇到类似问题的人。 http://refactormycode.com/codes/912-strtok-wtf - Johan Soderberg
相似但不完全相同。他的问题在于strtok的源字符串是一个字符串字面量,无法修改。strtok喜欢通过用空字符替换匹配的字符来修改输入字符串。 - Paul Mitchell
1
strtok()的问题不在于字符串是在堆栈上还是在堆上,而在于字符串是否为只读。如果你有char* thing = "String";,那么编译器可能会给你一个指向只读地址的指针。我不确定严格的C/C++规则是什么,这也将取决于你的硬件,尝试写入只读内存会静默失败还是出错。 - AAT

-1
 char text[] = "Some sample text";
 char* str = &text;

 // To create a copy of string into another string of same size from pointer array.
 char temp[strlen(str)+1];
 strcpy(temp,str);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接