为什么我的字符串值会被覆盖?

时间:2018-01-22 09:16:18

标签: c

我试图接受用户的一些值并将它们存储在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函数时,它似乎工作正常。

7 个答案:

答案 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]指向相同的内存位置namei=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)