当两个指针以不同的方式分配字符串时,strcpy表现不同

时间:2015-05-09 19:03:18

标签: c

对不起,我可能会问一个愚蠢的问题,但我想了解以下作业有什么不同吗? strcpy适用于第一种情况但不适用于第二种情况。

char *str1;
*str1 = "Hello";
char *str2 = "World";
strcpy(str1,str2);    //Works as expected
char *str1 = "Hello";
char *str2 = "World";
strcpy(str1,str2);    //SEGMENTATION FAULT

编译器如何理解每个作业?请澄清。

4 个答案:

答案 0 :(得分:3)

很抱歉,这两个示例都非常错误并导致未定义的行为,可能会也可能不会崩溃。让我试着解释一下原因:

  • str1是一个悬垂的指针。这意味着str1指向内存中的某处,写入str1会产生任意后果。例如,崩溃或覆盖内存中的一些数据(例如,其他局部变量,其他函数中的变量,一切都是可能的)
  • *str1 = "Hello";也是错误的(即使str1是有效指针),因为*str1的类型为char不是 {{ 1}})并且是悬挂的char *的第一个字符。但是,您为它指定了一个指针(str1,类型"Hello"),这是编译器将告诉您的类型错误
  • char *是一个有效的指针,但可能指向只读内存(因此崩溃)。通常,常量字符串存储在二进制文件中的只读数据中,不能写入它们,但这正是您在str2中所做的。

您想要实现的更正确的示例可能是(在堆栈上有一个数组):

strcpy(str1,str2);

其他选项(使用动态管理的内存):

#define STR1_LEN 128
char str1[STR1_LEN] = "Hello"; /* array with space for 128 characters */
char *str2 = "World";
strncpy(str1, str2, STR1_LEN);
str1[STR1_LEN - 1] = 0; /* be sure to terminate str1 */

编辑:@chqrlie在评论中要求将#define STR1_LEN 128 char *str1 = malloc(STR1_LEN); /* allocate dynamic memory for str1 */ char *str2 = "World"; /* we should check here that str1 is not NULL, which would mean 'out of memory' */ strncpy(str1, str2, STR1_LEN); str1[STR1_LEN - 1] = 0; /* be sure to terminate str1 */ free(str1); /* free the memory for str1 */ str1 = NULL; 命名为#define而不是STR1_SIZE。大概是为了减少混乱,因为它不是"字符串"的字符长度。但是分配的缓冲区的长度/大小。此外,@ chqrlie要求不提供STR1_LEN函数的示例。这不是我的选择,因为OP使用strncpy非常危险,所以我选择了可​​以正确使用的最接近的函数。但是,我应该补充说,不推荐使用strcpystrcpy和类似功能。

答案 1 :(得分:3)

修改:在您编写*str1 = "Hello"的第一个代码段中,这相当于分配给str[0],这显然是错误的,因为str1未初始化,因此是一个无效的指针。如果我们假设您的意思是str1 = "Hello",那么您仍然错了:

根据C规范,尝试修改字符串文字导致未定义的行为:它们可能存储在只读存储(例如.rodata)中或与其他字符串文字结合所以两个片段您提供的将产生未定义的行为。

我只能猜测,在第二个片段中,编译器将字符串存储在某个只读存储中,而在第一个片段中它不存在,所以它可以工作,但不能保证。

答案 2 :(得分:2)

这里似乎有些混乱。两个片段都调用未定义的行为。让我解释一下原因:

  • char *str1;定义了指向字符的指针,但它未初始化。它的定义发生在函数体中,其值无效。如果此定义发生在全局级别,则会将其初始化为NULL

  • *str1 = "Hello";是一个错误:您正在为str1指向的字符分配字符串指针。 str1未初始化,因此它不指向任何有效的内容,并且您不会指定指向字符的指针。你应该写str1 = "Hello";。此外,字符串"Hello"是常量,因此str1的定义应该是const char *str1;

  • char *str2 = "World";在此定义一个指向常量字符串"World"的指针。此声明是正确的,但最好将str2定义为const char *str2 = "World";,原因与上述相同。
  • strcpy(str1,str2); //Works as expected 不,它根本不起作用! str1没有指向足够大的char数组来保存字符串副本&# 34;世界"包括最终的'\0'。鉴于这种情况,此代码会调用未定义的行为,这可能会也可能不会导致崩溃。

你提到代码按预期工作:它只是没有外观:真正发生的是:str1未初始化,如果它指向一个不能存储的内存区域写入,写入它可能会导致程序崩溃,并出现分段错误;但是如果碰巧指向可以写入的内存区域,并且下一个语句*str1 = "Hello";将修改该区域的第一个字节,那么strcpy(str1, "World");将修改该位置的前6个字节。然后str1指向的字符串将是" World",正如预期的那样,但是您已经覆盖了某些可能用于其他目的的内存区域,因此您的程序可能会在以后以意外方式崩溃,一个很难找到的bug!这绝对是未定义的行为

第二个片段出于不同的原因调用未定义的行为:

  • char *str1 = "Hello";没问题,但应该是const
  • char *str2 = "World";也可以,但也应该是const
  • strcpy(str1,str2); //SEGMENTATION FAULT 当然无效:您正尝试使用字符串"Hello"中的字符覆盖常量字符串"World"。如果字符串常量存储在可修改的内存中,它将起作用,并且随着字符串常量的值被更改,将在程序中稍后引起更大的混淆。幸运的是,大多数现代environemnts通过将字符串常量存储在只读存储器中来防止这种情况。尝试修改所述内存会导致段违规,即:您正在以错误的方式访问内存的数据段。

您应该仅使用strcpy()将字符串复制到您定义为char buffer[SOME_SIZE];的字符数组,或者将char *buffer = malloc(SOME_SIZE);分配为SOME_SIZE,其大小足以保存您要复制的内容加上最后的'\0'

答案 3 :(得分:1)

这两个代码都是错误的,即使"它有效"在你的第一种情况下。希望这只是一个学术问题! :)

首先,让我们看一下您要修改的*str1

char *str1;

这声明了一个悬空指针,它是一个带有内存中某个未指定地址值的指针。这里的程序很简单没有重要的东西,但你可以在这里修改非常关键的数据!

char *str = "Hello";

这声明了一个指针,指向内存的受保护部分,即使程序本身在执行过程中也无法更改,这就是分段错误的含义。

要使用strcpy(),第一个参数应该是使用malloc()动态分配的char数组。如果不使用strcpy(),请学习使用strncpy(),因为它更安全。