簡體   English   中英

在C中的結構內指向char *的指針

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

我有一個可能很愚蠢的問題...但是我定義了一個內部帶有一些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;
}

謝謝一切=)

您無需使用&傳遞char *的地址,而刪除那些&

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

另外,在通過malloc使用nameaffiliation_number之前分配內存,例如:

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

僅聲明一個char *不會為其分配任何內存。 您要么需要指定一個靜態大小(例如char name[100] ),要么需要動態分配內存(例如通過使用malloc )。 如果完成后使用malloc ,請記住要free內存。

另一個問題是在fscanfchar *使用& 格式字符串后面的scanffscanf的變量列表是放置輸入值的指針的列表。 當我們使用int age類的類型int age我們需要傳遞&age ,因為在age之前, &使其成為指向age的指針,即int * 但是char *name已經是一個指針,因此您不需要&在這里。 通過使用&name您將創建一個在此處不需要的指針或char **指針。

您僅為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"規范,請嘗試以下操作:

  • 在get_name_affiliation中,創建一個char buffer[1024]以將數據讀入其中。
  • buffer&buffer[0] (對於某些純粹主義者)用作字符串fscanfs中的目標
  • 使用%1023s之類的方法防止讀取的數據超過緩沖區的長度-溢出會導致程序瘋狂。
  • 如果fscanf返回成功(對於預期的一個字段,fscanf應該返回值1,否則輸入可能是錯誤的),請使用strdup將數據克隆為nameaffiliation_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;
}

還有其他方法。 nameaffiliation_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用於所有這三個:

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

祝好運!

首先,你需要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相同。

首先,您必須使用malloc(); free(); 當您使用char* a="always use malloc";

編譯器將其用作const char * a

  1. 您必須為char* a;分配內存char* a; 當a將用作變量字符串時
  2. 使用free(); 之后避免內存泄漏

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM