1

私はまだCにかなり慣れていません.私はまだポインタを使ってすべてを理解していません. 文字列を返すメソッドを作成しようとしています。ここに関数がありますが、まだ不完全です。

char getS(char *fileName){
    FILE *src;
    if((src = fopen(fileName, "r")) == NULL){
        printf("%s %s %s", "Cannot open file ", fileName, ". The program is now ending.");
        exit(-1);
    }
    char *get = " ";        
    //insert getting a random word here
    return(*get);
}

そして、私はこのようにメソッドを呼び出そうとしています

char *article = getS("articles.txt");
char *noun = getS("nouns.txt");
char *verb = getS("verbs.txt");

コンパイラは私にこれを与えています:

error: invalid type argument of unary ‘*’ (have ‘int’)

私は何をすべきか?

4

5 に答える 5

15

以下は、おそらくあなたが探しているよりもはるかに多くの情報になるでしょう. 今すぐすべてを吸収することについてあまり心配する必要はありませんが、後で必要になる可能性があります。

まず、用語に関する重要な注意事項です。C には「文字列」型はありません。ISO C標準を引用:

文字列は、最初の null 文字で終了し、最初の null 文字を含む連続した文字列です。[...]文字列へのポインターは、最初の (アドレス指定が最も低い) 文字へのポインターです。

特に、char*値は文字列ではなくポインターです (通常、文字列char*へのアクセスと操作にはポインターを使用します)。配列は文字列を含むことはできますが、それ自体は文字列ではありません。

(比較的単純な)関数の場合、char*返す値は文字列リテラル(の最初の文字)を指しているため、メモリ管理は問題になりません。より複雑なケースでは、この言語は率直に言って特に役に立たず、自分でメモリを管理するために何らかの作業を行う必要があります。

char*関数は文字列を指す値を簡単に返すことができ、呼び出し元はその文字列を好きなように処理できますが、その文字列を構成する文字はどこに格納されているのでしょうか?

(少なくとも) 3 つの一般的なアプローチがあります。

(1) 関数は、次の静的配列の先頭へのポインターを返しますchar

char *func(void) {
    static char result[100];
    // copy data into result
    return result;
}

これは機能しますが、いくつかの欠点があります。配列のコピーは 1 つしかなくresult、連続してfunc()を呼び出すと、その配列の内容が破壊されます。また、配列のサイズは固定です。返すことができる最大の文字列を保持するのに十分な大きさである必要があります。標準 Casctime()関数はこのように機能します。

(2) 呼び出し元は文字列へのポインターを渡し、関数にそれを入力させることができます。

void func(char *buffer) {
    // code to copy data into the array pointed to by buffer
}

これは、 の配列を割り当てなければならない呼び出し元に負担をかけ、char特に必要な大きさを知る必要があります。

(3) 関数は、次を使用して文字列にメモリを割り当てることができますmalloc()

char *func(void) {
    char *result = malloc(some_number);
    if (result == NULL) {
         // allocation failed, cope with the error
    }
    // copy data into the array pointed to by result
    return result;
}

これには、割り当てる必要があるメモリの量を関数が決定できるという利点があります。ただし、呼び出し元は、文字列がヒープに割り当てられていることを認識している必要があるため、後で を呼び出して解放できますfree()malloc()and関数も比較的高価になる可能性があります(free()ただし、プログラムのパフォーマンスが十分でないことが確実でない限り、心配する必要はありません)。

実際には、4 番目の方法がありますが、それは間違っています。

char *bad_func(void) {
    char result[100];
    // copy data into result
    return result; // equivalent to "return &result[0];"
}

ここでの問題は、resultが関数に対してローカルであり、 として定義されていないstaticため、関数が戻るとすぐに配列オブジェクトが存在しなくなることです。呼び出し元は、予約されていないメモリへのポインタを受け取り、背後で再利用できます。ローカル オブジェクトへのポインターを返すことはできstaticます (単一のコピーがプログラムの存続期間中に存在するため)。ローカルの非オブジェクトのを返すことはできますが、ローカルの非オブジェクトのアドレスstatic安全に返すことはできません。物体。static

comp.lang.c FAQは優れたリソースです。

于 2012-09-25T02:11:31.267 に答える
7

関数は、char ではなく char * (文字列) を返す必要があり、同じものを返す必要があります。したがって、関数は次のようになります。

char * getS(char *fileName) {
    FILE *src;
    if((src = fopen(fileName, "r")) == NULL) {
        printf("%s %s %s", "Cannot open file ", fileName, ". The program is now ending.");
        exit(-1);
    }

    char *get = " ";        
    //insert getting a random word here

    return get;
}
于 2012-09-25T01:37:52.003 に答える
2

文字列のサイズが一定かどうかによって異なります。通常、人々はそのような関数にバッファパラメータを使用します。関数内に文字列メモリスペースを割り当て、それへのポインタを返す場合、関数の外でメモリを解放する必要があり、そうしないと、メモリ参照を失うことによってメモリ リークが発生します。

したがって、文字列を返す関数の最善のアプローチは、次のようなものです。

void getS(char *fileName, char *output, size_t len)
{
    FILE *src;
    if((src = fopen(fileName, "r")) == NULL)
    {
        printf("Cannot open file '%s'. The program is now ending.", fileName);
        exit(-1);
    }
    fread(output, sizeof(char), len, src); // Just an example without error checking
    fclose(src);
}

次に、次のように使用します。

char content[512];
getS("example.txt", content, 512);
// From now, you can use 'content' safely

またはmalloc.h、動的割り当てに使用します。

char *content = (char *)malloc(512 * sizeof(char));
getS("example.txt", content, 512);
// Use 'content', and after using it, free its memory:
free(content);

しかし、これを正しく使用することを学ぶ時が来ます。ただし、何らかの方法でリテラル文字列を返したい場合 (不完全なコード例を除いて、そうではありません) 、char *関数の戻り値の型として andを使用する必要があります。 (たとえば最初のもの)、文字列へのポインタではありません。return get;*get*get

FrankieTheKneeMan の回答は、返されたものと、コードが機能しなかった理由を理解するのに非常に役立ちます...

于 2012-09-25T02:08:16.993 に答える
1

C のポインターは、他の情報のアドレスです。C には、それに対処するための 2 つの単項演算子が付属しています。

*

ポインターを逆参照します-または、指されているメモリ空間で情報を取得します。

&

あなたが話している情報のアドレスを取得します。

int p;
int* q = &p; //int* is a pointer to an it.

q==p; //Error (or at least a warning)
*q == p; //true
q == &p; //true
*q == &p; //ERROR
&q == &p; //false, but also probably an error, depending on your compiler and settings

したがって、 your を宣言するときchar * getは、「char へのポインター」を宣言していることになります。C は、慣例により、char の配列として扱うことができることを認識しています。ただし、 を試みるとreturn * get、C は、 によってアドレス指定されたメモリで char を返そうとしていると見なしますget

代わりに、return get探しているポインターを返します。

それはあなたにとって全能のポインターを分かりやすくしましたか?

malloc(余談ですが、ポインタがスタックメモリによってクリアされるのを避けるためにそれをしたいかもしれませんがchar *、それはまったく別の問題です)。

于 2012-09-25T01:48:44.300 に答える
1

の型はgetSis ですcharが、 type の変数に割り当てていますchar*。おそらく、文字列を返したいgetSので、その型は である必要があり、 ( の最初の文字にすぎません)ではなくchar*返す必要があります。get*getget

于 2012-09-25T01:43:41.417 に答える