0

I have a probably stupid question... but I have defined a struct with some char* inside. Now when I try to change the values of that chars. It doesn't give problems when it's compiled, but when I execute it, the program stops.

This is a test function that I made for checking where is the problem:

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;
}

Thanks for everything =)

4

5 回答 5

2

您不需要使用&传递 a 的地址char *,删除那些&

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

此外,在使用它们之前name和之前分配内存,例如:affiliation_numbermalloc

char *name = malloc(100 * sizeof(char));
于 2013-05-18T10:11:40.620 回答
2

仅仅声明 achar *并不会为它分配任何内存。您要么需要指定静态大小(例如char name[100]),要么需要动态分配内存(例如,使用malloc)。free完成后使用请记住malloc

另一个问题是&in fscanffor的使用char *scanf格式字符串中和之后的变量列表fscanf是放置输入值的指针列表。当我们使用 type 时,int age我们需要传递&age,因为&之前age使它成为指向 的指针age,即 a int *。但是char *name已经是一个指针,所以这里不需要&。通过使用&name,您正在创建指向指针的指针或char **您不想要的指针。

于 2013-05-18T10:11:48.227 回答
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将数据克隆到nameoraffiliation_number中。这将malloc()是一个新的内存块,其大小适合您读取的字符串并将数据复制到其中。

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

这有点简单(特别是 1024 的限制),但应该能让你走上正确的道路。

例子:

#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;
}

还有其他方法。例如, nameandaffiliation_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);
}

然后其他两个函数变为(假设fscanfcan "%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所有三个:

if(3 == fscanf(user_data, "%d %ms %ms", &age, &name, &affiliation_number))
      ...

祝你好运!

于 2013-05-18T11:08:54.397 回答
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.

于 2013-05-18T10:16:20.220 回答
0

首先,您必须使用malloc();以及free();何时使用char* a="always use malloc";

编译器将其用作 const char* a

  1. 您必须将内存分配给您的char* a;when a 将用作变量字符串
  2. 在它之后使用free();以避免内存泄漏
于 2016-03-23T04:55:46.760 回答