繁体   English   中英

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

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM