[英]Why do my string values get overwritten?
我试图接受用户的一些值,并将它们存储在char指针数组中,如下所示:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
char *names[3];
char name[20];
int i;
for(i = 0; i < 3; i++) {
printf("Enter your name\n");
scanf("%s", name);
names[i] = (char *)malloc(strlen(name));
names[i] = &name;
// strcpy(names[i], name);
}
printf("Printing the names\n");
for(i = 0; i < 3; i++) {
printf("%s\n", names[i]);
}
}
但是,对于以下输入,我得到以下输出
输入: Mark Drew Andrew
输出: Andrew Andrew Andrew
为什么会这样呢? 当我使用strcpy函数时,我已将其注释掉了,它似乎工作正常。
names[i] = &name;
被分配的每一个元素names
相同的字符缓冲区,所以只有一个什么样的最终版本name
将在输出持续。
您需要使用strcpy
(更好的是strncpy
)将name
的内容复制到names[i]
。
完成后,别忘了对names
每个元素进行free
调用。
您的代码中存在内存泄漏,并且分配了错误的分配类型。 正确的类型分配应为names[i]=name
但仍然不能解决问题。 然后它也不会工作。 您需要使用strcpy
存储不同的name
s。 在这里,您已经将names[i]
分配给了相同的变量-这就是为什么您得到相同的输出的原因。
请注意, &name
的类型是您分配给char*
char(*)[20]
类型(编译器对此有所警告)。 对于所有3个输入,您都获得了指向char
数组的指针,该指针始终是相同的-因此它指向相同的数组。 现在,它包含的最后一个值是输入"Andrew"
。 那就是它打印的内容。
所以事情会是
strcpy(names[i],name);
另外scanf
应该是
if( scanf("%19s",name)!=1 ){
fprintf(stderr,"Error in input\n");
exit(EXIT_FAILURE);
}
应该检查malloc
的返回值,并且不需要强制转换。因为void*
到char*
转换是隐式完成的。
另一种简单的方法是使用strdup
复制字符串。
names[i]=strdup(name);
另外,别忘了释放(使用free()
-在strdup
情况strdup
也必须这样做)在完成动态分配的内存使用后free()
它。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char *names[3];
char name[20];
for(size_t i = 0; i < sizeof(names)/sizeof(names[0]); i++) {
printf("Enter your name\n");
if(scanf("%19s", name)!= 1){
fprintf(stderr, "%s\n","Error in input" );
exit(EXIT_FAILURE);
}
names[i] = malloc(strlen(name)+1);
if( names[i] == NULL){
perror("malloc");
exit(EXIT_FAILURE);
}
strcpy(names[i],name);
}
printf("Printing the names\n");
for(size_t i = 0; i < sizeof(names)/sizeof(names[0]); i++) {
printf("%s\n", names[i]);
}
for(size_t i = 0; i < sizeof(names)/sizeof(names[0]); i++)
free(names[i]);
return 0;
}
names[0]
, names[1]
和names[2]
指向相同的存储位置name
,对于i=0
标记将存储在名称中,对于i=1
将存储Drew,对于i=2
将存储安德鲁,因此,在循环结束时,您的数组指向的值是Andrew的name
C字符串是简单的字符数组,以结尾的空字符结尾。 您不能将数组分配给其他数组,但是可以将它们分配给指针。
现在通过做names[i] = &name;
,您正在执行这样的指针分配。 除了Java或C ++字符串,仅将地址复制到指针,不复制涉及的字符串内容(顺便说一句,&name的类型错误: char(*)[20]
,即,指向长度数组的指针) 20,您需要一个指向char的指针,您可以通过直接直接分配name来获得它: names[i] = name;
在这种情况下,name自动衰减为一个指针)。
其结果是,在所有的字符串指针names
指向同一个字符数组name
,覆盖指向由malloc的创建阵列(然后这些丢失完全,所以你不能再任他们自由,即你有内存泄漏!)。
相反,您必须显式复制字符串。 但是,不要忘记尾随空字符:
int len = strlen(name) + 1;
// trailing null char(!): ^^^
names[i] = malloc(len);
memcpy(names[i], name, len);
注意:使用memcpy。 替代方案可能是strcpy或strncpy,但是无论如何都知道长度(包括结尾的空字符!),因此memcpy是最有效的...
替代方案可能是:
names[i] = malloc(20);
scanf("%19s", names[i]);
您不愿意复制,因为阵列的价格可能太长。 仔细看一下格式字符串:通过添加最大长度(您需要再次为终止null字符留出空间,因此要少一个!),可以防止用户写超出缓冲区(这是未定义的行为,并可能导致坠毁)。 如果您未在任何地方返回数组,则更好:
char names[3][20];
编辑:也是不错的选择: strdup
(请参阅coderredoc的答案); 另外两个要点:
malloc
的结果是否为null(内存分配可能失败!-再次参见coderredoc的答案)。 您使用names[i] = &name;
在每一个循环迭代,但是name
被用不同的名字覆盖,在每次迭代结束的意思,你把所有的names[i]
最多至今迭代次数)都指向了name
,这显然拥有一个特定的名称(在您的情况下是“ Andrew” )。
考虑一下代码运行时内存如何变化:
|--name--| ... |--names[0]--|,|--names[1]--|,|--names[2]--| (loop starting)
|--"Mark"--|...|--points to name, i.e. points to "Mark"--|,|--names[1]--|,|--names[2]--| (1st iteration)
|--"Drew "--|...|--points to name, i.e. points to "Drew "--|,|--points to name, i.e. points to "Drew "--|,|--names[2]--| (2nd iteration)
|--"Andrew"--|...|--points to name, i.e. points to "Andrew"--|,|--points to name, i.e. points to "Andrew"--|,|--points to name, i.e. points to "Andrew"--| (3rd iteration)
如前所述,通过使用strcpy
来解决此问题,这将导致:
|--name--| ... |--names[0]--|,|--names[1]--|,|--names[2]--| (loop starting)
|--"Mark"--|...|--copied the value of name, i.e. holds "Mark"--|,|--names[1]--|,|--names[2]--| (1st iteration)
|--"Drew "--|...|--copied the value of name, i.e. holds "Mark"--|,|--copied the value of name, i.e. holds "Drew "--|,|--names[2]--| (2nd iteration)
|--"Andrew"--|...|--copied the value of name, i.e. holds "Mark"--|,|--copied the value of name, i.e. holds "Drew "--|,|--copied the value of name, i.e. holds "Andrew"| (3rd iteration)
基本上,您指向随时间变化的变量( name
),而不是保存其值(使用strcpy
)
#note: names[i] = (char *)malloc(strlen(name));
应该是names[i] = (char *)malloc(strlen(name) + 1);
行names[i]=&name;
没有道理。 您应该遇到编译错误,并且如果没有打开编译器上的所有警告和错误。
这没有任何意义,因为names[i]
是指向字符( char*
)的指针, &name
是指向字符( char**
或更准确地说是char(*)[20]
)的指针。
但是将其更改为names[i]=name;
不会帮助。 它使names[i]
指向数组name
的开头。 只有name
一个实例。 因此, names
所有元素在循环末尾将指向相同的位置( name
)。
这是修复了基本问题的版本:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
char *names[3];
char name[20];
int i;
for(i = 0; i < 3; i++) {
printf("Enter your name\n");
scanf("%s", name);
names[i] = (char *)malloc(strlen(name)+1);//+1 to include NUL terminator.
strcpy(names[i], name);//Copy name into the space allocated.
}
printf("Printing the names\n");
for(i = 0; i < 3; i++) {
printf("%s\n", names[i]);
free(names[i]);//Release malloc'ed memory after use.
}
}
仍然存在安全问题,即用户可以输入超过19个字符来超过name
缓冲区。 您应该对scanf
施加限制和/或使用scanf_s
如果可以通过scanf("%19s",name)
。
错误在以下行中,
names[i] = &name;
它仅与名称字符串的地址联系,但是它指向用户输入的最后一个值,这意味着它仅能使用最后一个值。
您必须使用字符串复制'strcpy()'函数将输入的'名称'字符串复制到指针字符串'名称'的数组中。
strcpy(name[i],&name)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.