1

「Understanding pointers in C」という本には、引数が説明された後に解決された問題がいくつかあります。22ページ、問題N.5 コードと解説を添付します。その後、私の質問があります。

#include <stdio.h>

int main()
{
    int *c;
    c = check(10, 20);
    printf("c = %p\n",c);
    return 0;
}

int * check(int i, int j)
{
    int *p, *q;
    p = &i;
    q = &j;
    if(i >= 45)
    {
        return p;
    }
    else
    {
        return q;
    }
}

出力: エラー メッセージ: メインでの移植性のないポインター割り当て

説明 エラーの理由は簡単です。check( ) に渡される整数は i と j に集められ、それらのアドレスが p と q に割り当てられます。次に、次のステートメントで i の値が 45 に対してテストされ、p に格納されているアドレスまたは q に格納されているアドレスのいずれかが返されます。このアドレスは main( ) の c に収集され、印刷されたようです。そして、そこにエラーがあります。関数 check( ) は整数ポインタを返すことができません。返すことができるのは通常の整数だけです。したがって、c を整数ポインターとして宣言するだけでは十分ではありません。プログラムを正しく動作させるには、プログラムに次の変更を加える必要があります

#include <stdio.h>

int * check(int, int);

int main()
{
    int *c;
    c = check(10, 20);
    printf("c = %p\n",c);
    return 0;
}
int *check(int i, int j)
{
    ......
    ......
}

私の意見では、これは意味がありません。作成者は、コードの最初の部分で意図的にエラーを作成するために、割り当てられていないメモリ領域を指すポインタをメインで使用しようとします。しかし、彼が問題を解決しようとする方法はまったく正しくありません。彼は何も変更していません。代わりに、check() 関数でいくつかのメモリ領域を malloc する必要がありました。私は正しいですか?

4

7 に答える 7

7

これは、まだ宣言されていない関数のデフォルトの戻り値に関するものです。最初の例では、コンパイラが行を見たとき、c = check(10, 20);この関数が何を返すかまだわかりません。標準では、コンパイラは戻り値がint. そして、これがすべてです: anのサイズは a (ポインター)intのサイズとは異なる場合があります。int *コンパイラが常にこれに対して正しいマシン コードを出力するようにするにcheckは、 がポインタを返すことを認識している必要があるため、宣言する必要があります。これが 2 番目の例の動作です。これはコンパイラに「'check' という名前の関数があり、次のようになります」と伝えます。

それとは別に、例は本当に悪いです。への引数checkがスタックにプッシュされ、関数はこれらのスタックの場所へのポインターを返します。ただし、関数が終了した後、これらのスタックの場所が有効であるという保証はありません。これは未定義の動作であり、コンパイラが好きなことを何でもできることを意味し、コンピューターを爆発させることさえできます。この特定の例は、ポインター(つまり、ポインターが指しているアドレス) を出力しているだけなので、実際には機能するはずですが、実際には逆参照していません (つまり、ポインターが指しているアドレスから読み取ります)。ほとんどのマシン/コンパイラでは、逆参照も「正しく」機能するはずですが、それに依存しない場合があります。

于 2013-04-23T10:13:13.463 に答える
2

関数を最初に使用する前に関数プロトタイプがない場合、コンパイラは関数が を返すと想定しますint

intしたがって、コンパイラは、変数を に代入していると想定しますint*。とのサイズは、プラットフォームintint*コンパイラによって異なる場合があります。

たとえば、符号なし整数とポインタの型が同じ組み込みシステムがあるため、コードではどちらの場合もuint32_t.

于 2013-04-23T10:12:58.050 に答える
2

プロトタイプを追加すると、コンパイラの警告/エラーのみが修正されますが、check関数がローカル変数へのポインターを返すという問題は修正されません。

ただし、返されたポインターを逆参照しようとするまでは、未定義の動作ではありません。印刷するだけでOKです。

于 2013-04-23T10:13:34.003 に答える
1

問題は2つ。

  1. ローカル変数のアドレスを返します。

    このシナリオはhttp://en.wikipedia.org/wiki/Dangling_pointerと呼ばれます。実行が完了するCheck()と、ローカル変数に割り当てられたメモリが他の関数に割り当てられる場合があります。したがって、そのメモリにアクセスすると、未定義の結果が発生します。

  2. 前方宣言が必要です。デフォルトでは戻り値の型は と見なされますintが、ここでint*は使用されます。追加int * check(int, int);すると解決しError message: Non portable pointer assignment in mainます。

于 2013-04-23T10:13:45.107 に答える
1

コードの主な「ポイント」は、宣言されていない関数にはデフォルトの戻り値の型があることを伝えることだと思いますint。これは本当です。

ただし、関数はローカル変数 ( の値に応じてパラメーターまたは)checkへのポインターを返すため、コードにはそれ以上の欠陥があります。これは未定義の動作です (ありがたいことに、そのポインターは決して逆参照されません)。iji

于 2013-04-23T10:16:06.883 に答える
0

ローカル変数のアドレスを返しています。これらを逆参照するのはundefinedC の動作です。pqはどちらも関数 のローカル変数へのポインターです。check()関数check()が制御をmain()に返した後、pqは未定義の値を指します。さらに、コードに次の修正を加えます。

printf("c = %p\n",(void*)c);

あおむしさん、あの著者の本について警告しましたよね?:-)

編集check()beforeの関数宣言を忘れmain()ました。私のコンパイラでは、常に警告メッセージがスローされます。main() 関数の前に次を追加します。

int *check(int,int);
于 2013-04-23T10:12:48.480 に答える