在C中的结构内指向char *的指针

时间:2013-05-18 10:08:54

标签: c pointers struct

我有一个可能很愚蠢的问题......但是我已经定义了一个带有一些char *的结构。 现在当我尝试改变那些字符的值时。 它在编译时没有出现问题,但是当我执行它时,程序就会停止。

这是我用来检查问题所在的测试功能:

struct myret {
    int age;
    char *name;
    char *affiliation_number;
};

void obtain_name_affiliation_number(struct myret *r)
{
    int age;
    char *name;
    char *affiliation_number;

    FILE *user_data;
    user_data = fopen("user_data.txt", "r");

    fscanf(user_data, "%d", &age);
    fscanf(user_data, "%s", &name);
    fscanf(user_data, "%s", &affiliation_number);

    fclose(user_data);

    r->age = age;
    r->name = name;
    r->affiliation_number = affiliation_number;

    return 0;
}

int main(void)
{
    struct myret r;
    int rc = obtain_name_affiliation_number(&r);
    if (rc == 0) {
        printf("%d %s %s\n", r.age, r.name, r.affiliation_number);
    }

    getchar();
    return 0;
}

感谢一切=)

5 个答案:

答案 0 :(得分:2)

您无需使用&来传递char *的地址,请删除&

fscanf(user_data, "%s", &name);
                        ^
fscanf(user_data, "%s", &affiliation_number);
                        ^

此外,在name使用affiliation_number之前为mallocchar *name = malloc(100 * sizeof(char)); 分配内存,例如:

{{1}}

答案 1 :(得分:2)

只是声明char *没有为它分配任何内存。您需要指定静态大小(例如char name[100])或需要动态分配内存(例如,使用malloc)。如果在完成后使用free,请记住malloc内存。

另一个问题是在&中使用fscanf char *。格式字符串后面的scanffscanf中的变量列表是放置输入值的指针列表。当我们使用类似int age的类型时,我们需要传递&age,因为&之前age使其成为age的指针,即int * }。但是char *name已经是一个指针,所以这里不需要&。通过使用&name,您将创建一个指针或char **指针,这是您不想要的。

答案 2 :(得分:1)

您只为name和affiliation_number的缓冲区地址分配了空间,但从未为这些地址分配缓冲区本身。

因此,当您在fscanf()中使用它们时,会出现问题。编译器警告你(gcc,无论如何)这些注释指针是错误的类型 - 它们应该是你希望fscanf覆盖的目标缓冲区中第一个字节的地址:

foo.c:19:5: warning: format ‘%s’ expects argument of type ‘char *’, but argument 3 has type ‘char **’ [-Wformat]
foo.c:20:5: warning: format ‘%s’ expects argument of type ‘char *’, but argument 3 has type ‘char **’ [-Wformat]

以下是一些事情要做 - 如果您的系统不支持fscanf的"%ms"规范,请尝试以下操作:

  • 在obtain_name_affiliation中,设置char buffer[1024]以将数据读入。
  • 使用buffer&buffer[0](对于某些纯粹主义者)作为字符串fscanfs中的目标
  • 使用%1023s等来阻止数据读取超出缓冲区的长度 - 超支可能会导致程序疯狂。
  • 如果fscanf返回成功(对于预期的一个字段,fscanf应该返回值1,或者输入可能不好),请使用strdup将数据克隆到name或{{1} }。这将affiliation_number一个新的内存块,其大小适合您读取的字符串并将数据复制到其中。

无论您使用这些步骤还是malloc()方法,都需要稍后"%ms"缓冲区以避免内存泄漏!

这有点过分了(特别是1024的限制),但应该让你开始走正确的道路。

示例:

free()

还有其他方法。例如,#include <stdio.h> #include <string.h> #include <stdlib.h> /* tested with user_data.txt containing "12 Barney 42" or "12 Barney" */ struct myret { int age; char *name; char *affiliation_number; }; int obtain_name_affiliation_number(struct myret *r) { int success = 0; int age; char *name = 0; char *affiliation_number = 0; FILE *user_data = fopen("user_data.txt", "r"); if(user_data) { #if 0 /* use if you have "%ms" */ if((1 == fscanf(user_data, "%d", &age)) && (1 == fscanf(user_data, "%ms", &name)) && (1 == fscanf(user_data, "%ms", &affiliation_number))) { success = 1; } else { /* a small annoyance: if only the first "%ms" succeeded, * we need to free it: */ if(name) free(name); } #else char buffer[1024]; /* This if-structure can be used with the "%ms" as well, and * would make the "annoyance" look a lot cleaner */ if(1 == fscanf(user_data, "%d", &age)) { if(1 == fscanf(user_data, "%1023s", buffer)) { name = strdup(buffer); if(1 == fscanf(user_data, "%1023s", buffer)) { affiliation_number = strdup(buffer); success = 1; } } } #endif fclose(user_data); } else perror("error opening data file"); if(success) { r->age = age; r->name = name; r->affiliation_number = affiliation_number; } return success; } int main(void) { struct myret r; int rc = obtain_name_affiliation_number(&r); if(rc) { printf("%d %s %s\n", r.age, r.name, r.affiliation_number); free(r.name); free(r.affiliation_number); } else fputs("an error occurred reading data\n", stderr); getchar(); return 0; } name可以在结构中声明为affiliation_number,但这个数字几乎不可能提前选择,并且有任何正确性的希望。相反,这很常见:

char name[512];

然后其他两个函数变为(假设#include <stdio.h> #include <string.h> #include <stdlib.h> struct myret *myret_alloc(int age, char *name, char *affiliation_number) { struct myret *r = 0; if(r = (struct myret*)calloc(1, sizeof(struct myret))) { r->age = age; r->name = name; /* or use r->name = strdup(name); */ r->affiliation_number = affiliation_number; /* note that using strdup would mean the calling function should * do its own cleanup of its own name and affiliation_number vars */ } return r; } void myret_free(struct myret *r) { /* this can be called on partially-allocated myret objects */ if(r->affiliation_number) free(r->affiliation_number); if(r->name) free(r->name); free(r); } 可以fscanf):

"%ms"

您也可以同时对所有三个人使用struct myret *obtain_name_affiliation_number(void) { struct myret *r = (struct myret*)0; FILE *user_data = fopen("user_data.txt", "r"); if(user_data) { int age; char *name = 0; /* the 0 allows us to see if it was used later */ char *affiliation_number = 0; if((1 == fscanf(user_data, "%d", &age)) && (1 == fscanf(user_data, "%ms", &name)) && (1 == fscanf(user_data, "%ms", &affiliation_number))) { /* The name and affiliation_number were malloc()ed by "%ms" * so there's nothing to clean up in this function, and * we can let myret_free() just free those memory areas. * This also means myret_alloc doesn't need strdup(). */ r = myret_alloc(age, name, affiliation_number); } else { if(name) /* clean up name if it got allocated */ free(name); } fclose(user_data); } else perror("error opening data file"); return r; } int main(void) { struct myret *r = obtain_name_affiliation_number(); if(r) { printf("%d %s %s\n", r->age, r->name, r->affiliation_number); myret_free(r); } else fputs("an error occurred reading data\n", stderr); getchar(); return 0; }

fscanf
祝你好运!

答案 3 :(得分:0)

首先,您需要malloc内存来存储您的数据,否则将无法存储它:

char *affiliation_number = (char*) malloc(STRING_LENGTH * sizeof (char));

如果malloc失败,它将返回NULL,您必须检查此错误情况:

if (!affiliation_number) { 
     // report error
}

然后,affiliation_number已经是指针,您不需要&

fscanf(user_data, "%s", affiliation_number);

name相同。

答案 4 :(得分:0)

首先,当您使用malloc();

时,必须使用free();char* a="always use malloc";
  

编译器将其用作const char * a

  1. 当a将用作变量字符串时,您必须为char* a;分配内存
  2. 在其后使用free();以避免内存泄漏