我试图接受用户的一些值并将它们存储在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函数时,它似乎工作正常。
答案 0 :(得分:2)
names[i] = &name;
将names
的每个元素分配给相同的字符缓冲区,因此只有name
中的内容的最终版本会在输出中保留。
您需要使用strcpy
(更好的是strncpy
)将name
的内容复制到names[i]
。
当你完成时,不要忘记在free
的每个元素上调用names
。
答案 1 :(得分:2)
代码中存在内存泄漏,并且分配了错误的分配类型。正确的类型分配将是names[i]=name
,但仍然无法解决问题。然后它也不会工作。您需要使用strcpy
来存储不同的name
。在这里,您已将names[i]
分配给同一个变量 - 这就是您获得相同输出的原因。
请注意,&name
的类型为char(*)[20]
,您已将其分配给char*
(编译器会对此发出警告)。对于所有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
时,你必须这样做)动态分配的内存。
#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;
}
答案 2 :(得分:1)
names[0]
,names[1]
和names[2]
指向相同的内存位置name
,i=0
标记将存储在i=1
的名称中Drew将被存储,并且i=2
Andrew将被存储,因此在循环结束时,您的数组指向name
,其值为Andrew
答案 3 :(得分:1)
C字符串是简单的字符数组,以尾随空字符结尾。您不能将数组分配给其他数组,但可以将它们分配给指针。
现在通过names[i] = &name;
,你正在做这样的指针赋值。除了Java或C ++字符串之外,只有地址被复制到指针,没有复制所涉及的字符串内容(顺便说一下,&amp; name是坏类型:char(*)[20]
,即指向长度数组的指针20,你需要一个指向char的指针,只需直接指定名称即可: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]);
您可能因为数组的价格太长而无法复制。仔细查看格式字符串:通过添加最大长度(您需要为终止空字符再次留出空间,因此少一个!),可以防止用户在缓冲区之外写入(将是未定义的行为,并可能导致崩溃)。如果你没有在任何地方返回数组,甚至更好:
char names[3][20];
编辑:不错的选择:strdup
(参见coderredoc的回答);另外两个要点:
malloc
的结果为空(内存分配可能失败了! - 再次看到coderredoc的答案)。答案 4 :(得分:0)
您在每次循环迭代中都使用names[i] = &name;
,但name
会被不同的名称覆盖,这意味着在每次迭代结束时,您拥有所有names[i]
(最多为到目前为止的迭代)都指向name
,它显然拥有一个特定的名称(在你的情况下是&#34; Andrew&#34; )。
考虑代码运行时内存的变化情况:
|--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);
答案 5 :(得分:0)
行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)
使用navigationItem
。
答案 6 :(得分:0)
错误在以下行中,
names[i] = &name;
它只接触名称字符串的地址,但是它指向用户输入的最后一个值,这意味着它仅与最后一个值保持一致。
您必须使用字符串副本'strcpy()'函数来复制指针字符串'name'数组中的输入'name'字符串。
strcpy(name[i],&name)