简体   繁体   English

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

[英]Pointers to char* inside a struct in C

I have a probably stupid question... but I have defined a struct with some char* inside. 我有一个可能很愚蠢的问题...但是我定义了一个内部带有一些char *的结构。 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 =) 谢谢一切=)

You don't need to use & to pass the address of a char * , remove thoses & : 您无需使用&传递char *的地址,而删除那些&

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

Also, allocate memory for name and affiliation_number before using them by malloc for example: 另外,在通过malloc使用nameaffiliation_number之前分配内存,例如:

char *name = malloc(100 * sizeof(char));

Just declaring a char * does not allocate any memory for it. 仅声明一个char *不会为其分配任何内存。 You either need to specify a static size (eg char name[100] ) or need to allocate memory dynamically (eg by using malloc ). 您要么需要指定一个静态大小(例如char name[100] ),要么需要动态分配内存(例如通过使用malloc )。 Remember to free the memory if you use malloc when you are done. 如果完成后使用malloc ,请记住要free内存。

Another problem is the use of & in fscanf for char * . 另一个问题是在fscanfchar *使用& The variable list in scanf and fscanf after format string is a list of pointers where to place the input values. 格式字符串后面的scanffscanf的变量列表是放置输入值的指针的列表。 When we use type like int age we need to pass &age , as that & before age makes it a pointer to age , ie a int * . 当我们使用int age类的类型int age我们需要传递&age ,因为在age之前, &使其成为指向age的指针,即int * But char *name is already a pointer, so you don't need & here. 但是char *name已经是一个指针,因此您不需要&在这里。 By using &name you are creating a pointer to pointer or char ** which you don't want here. 通过使用&name您将创建一个在此处不需要的指针或char **指针。

You've allocated space for only the addresses of buffers for name and affiliation_number, but never allocated the buffers themselves for those addresses to point to. 您仅为name和affiliation_number的缓冲区地址分配了空间,但从未为这些地址指向缓冲区分配缓冲区。

Hence, when you use them in fscanf() , problems occur. 因此,当您在fscanf()使用它们时,会出现问题。 The compiler warns you (gcc, anyway) with these notes that the pointers are the wrong type - they should be the address of the first byte in the target buffer you want fscanf to overwrite: 编译器会通过以下注释警告您(无论如何为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]

Here are some things to do instead - if your system doesn't support fscanf's "%ms" spec, try this: 相反,有些事情要做-如果您的系统不支持fscanf的"%ms"规范,请尝试以下操作:

  • In obtain_name_affiliation, make a char buffer[1024] to read data into. 在get_name_affiliation中,创建一个char buffer[1024]以将数据读入其中。
  • Use buffer or &buffer[0] (for certain purists) as the targets in the string fscanfs buffer&buffer[0] (对于某些纯粹主义者)用作字符串fscanfs中的目标
  • Use %1023s or the like to prevent the data read from overrunning the length of the buffer - overruns can drive your program insane. 使用%1023s之类的方法防止读取的数据超过缓冲区的长度-溢出会导致程序疯狂。
  • If the fscanf returns success (for one field expected, fscanf should return the value 1, or the input may be bad), use strdup to clone the data into name or affiliation_number . 如果fscanf返回成功(对于预期的一个字段,fscanf应该返回值1,否则输入可能是错误的),请使用strdup将数据克隆为nameaffiliation_number This will malloc() a new memory piece sized to fit the string you read and copy the data into it. 这将malloc()一个新的内存块,其大小适合您读取的字符串并将数据复制到其中。

Whether you use those steps or the "%ms" approach, the buffers need to be free() ed later to avoid memory leaks! 无论您使用这些步骤还是使用"%ms"方法,缓冲区都需要稍后进行free()编辑,以避免内存泄漏!

This is a bit simplistic (the limit of 1024 in particular), but should get you started down the right path. 这有点简单(特别是限制为1024),但是应该让您正确地开始。

Example: 例:

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

There are other approaches. 还有其他方法。 The name and affiliation_number could be declared in the struct as char name[512]; nameaffiliation_number可以在结构中声明为char name[512]; for example, but that number is almost impossible to choose in advance with any hope of correctness. 例如,但是几乎不可能事先选择这个数字,并且希望正确无误。 Instead, this is common: 相反,这很常见:

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

Then the other two functions become (assuming fscanf can "%ms" ): 然后,其他两个函数变为(假设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;
}

You could also use fscanf for all three at once: 您也可以一次将fscanf用于所有这三个:

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

Good luck! 祝好运!

First, you need to malloc memory for your data, otherwise there will be nowhere to store it: 首先,你需要malloc内存为您的数据,否则将无处可存储它:

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

If malloc fails, it will return NULL , you have to check for this error condition: 如果malloc失败,它将返回NULL ,您必须检查以下错误情况:

if (!affiliation_number) { 
     // report error
}

Then, affiliation_number is already a pointer, you don't need & in: 然后, affiliation_number已经是一个指针,您不需要&在:

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

Same for name . name相同。

First at all you must use malloc(); 首先,您必须使用malloc(); and free(); free(); when you're using char* a="always use malloc"; 当您使用char* a="always use malloc";

compiler uses it as const char* a 编译器将其用作const char * a

  1. You must assign memory to your char* a; 您必须为char* a;分配内存char* a; when a will be used as a variable string 当a将用作变量字符串时
  2. Use free(); 使用free(); after it to avoid memory leaks 之后避免内存泄漏

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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