0

おそらくばかげた質問があります...しかし、内部に 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;
}

すべてに感謝 =)

4

5 に答える 5

2

を宣言するだけでchar *は、メモリは割り当てられません。静的サイズを指定するか (例: char name[100])、動的にメモリを割り当てる必要があります (例: を使用malloc)。free使いmalloc終わったら思い出に残してください。

もう 1 つの問題は、&in fscanfforの使用ですchar *scanfフォーマット文字列内およびその後の変数リストはfscanf、入力値を配置するポインタのリストです。int age渡す必要があるような型を使用するときは&age、その&前にageへのポインターage、つまり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"仕様をサポートしていない場合は、これを試してください:

  • acquire_name_affiliation で、char buffer[1024]データを読み込む a を作成します。
  • 文字列 fscanfs のターゲットとしてbufferor (特定の純粋主義者向け) を使用します。&buffer[0]
  • などを使用%1023sして、データの読み取りがバッファの長さを超えないようにします。オーバーランはプログラムを狂わせます。
  • fscanf が成功を返した場合 (期待される 1 つのフィールドについて、fscanf は値 1 を返す必要があります。そうでない場合、入力が正しくない可能性があります)、 を使用してデータをまたはstrdupに複製します。これにより、読み取った文字列に収まるサイズの新しいメモリ ピースが作成され、データがコピーされます。nameaffiliation_numbermalloc()

これらの手順を使用するか、アプローチを使用するかに関係なく、メモリ リークを回避するために、後で"%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;
}

他のアプローチもあります。nameandは、たとえば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次に、他の 2 つの関数は次のようになります ( canと仮定"%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;
}

fscanf3 つすべてを一度に使用することもできます。

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

幸運を!

于 2013-05-18T11:08:54.397 に答える
0

まず、使用する必要があり、使用malloc();free();ている場合char* a="always use malloc";

コンパイラはそれを const char* a として使用します

  1. char* a;が可変文字列として使用される場合は、メモリを割り当てる必要があります
  2. その後に使用free();して、メモリリークを回避します
于 2016-03-23T04:55:46.760 に答える