web-dev-qa-db-ja.com

CポインターをNULLに初期化することは可能ですか?

私は次のようなものを書いていました

char *x=NULL;

という仮定で

 char *x=2;

アドレス2へのcharポインターを作成します。

しかし、 The GNU C Programming Tutorial では、int *my_int_ptr = 2;が整数値2を、my_int_ptrに割り当てられたランダムなアドレスに格納すると述べています。

これは、自分のchar *x=NULLNULLにキャストされたcharの値がメモリ内のランダムなアドレスに割り当てられていることを暗示しているようです。

ながら

#include <stdlib.h>
#include <stdio.h>

int main()
{
    char *x=NULL;

    if (x==NULL)
        printf("is NULL\n");

    return EXIT_SUCCESS;
}

実際、印刷します

無効です

コンパイルして実行するとき、未定義の動作、または少なくとも指定されていない動作に依存していること、そして

char *x;
x=NULL;

代わりに。

86
fagricipni

CポインターをNULLに初期化することは可能ですか?

TL; DRはい、非常に。


ガイドに関する実際の主張は次のように読みます

一方、単一の初期割り当てint *my_int_ptr = 2;のみを使用する場合、プログラムはmy_int_ptrが指すメモリ位置の内容を値2で埋めようとします。 my_int_ptrはゴミでいっぱいです。どのアドレスでもかまいません。 [...]

まあ、彼らare間違っています、あなたは正しいです。

ステートメントについては、(現時点では、整数変換へのポインターが実装定義の動作であるという事実を無視

int * my_int_ptr = 2;

my_int_ptrは(intへのポインター型の)変数であり、独自のアドレス(型:整数へのポインターのアドレス)を持ち、2の値を-に格納していますthatアドレス。

ポインタ型であるmy_int_ptrは、指すメモリ位置の "type"の値pointed byに保持される値my_int_ptr。つまり、本質的には値を割り当てていますofポインター変数であり、ポインターが指すメモリー位置の値ではありません。

だから、結論のために

 char *x=NULL;

ポインタ変数xNULLに初期化します。ポインタが指すメモリアドレスの値ではありません。

これはsameと同じです

 char *x;
 x = NULL;    

拡張:

今、厳密に適合しているようなステートメント

 int * my_int_ptr = 2;

制約違反を伴うため、違法です。明確にするために、

  • my_int_ptrはポインター変数、タイプint *
  • 整数定数2は、定義によりint型を持ちます。

また、これらは「互換性のある」型ではないため、この初期化は、§6.5.16.1/ P1で説明されている単純な割り当ての規則に違反しているため無効です Lundin's answer で説明しています。

初期化が単純な割り当て制約にどのようにリンクされているかに興味がある場合は、C11、§6.7.9、P11を引用してください

スカラーの初期化子は、オプションで中括弧で囲まれた単一の式でなければなりません。オブジェクトの初期値は、式の初期値です(変換後)。 単純な割り当てと同じ型の制約と変換が適用され、スカラーの型は宣言された型の非修飾バージョンになります。

110
Sourav Ghosh

チュートリアルが間違っています。 ISO Cでは、int *my_int_ptr = 2;はエラーです。 GNU Cでは、int *my_int_ptr = (int *)2;と同じ意味です。これにより、コンパイラによって決定された何らかの方法で、整数2がメモリアドレスに変換されます。

そのアドレスでアドレス指定された場所(存在する場合)には何も保存しようとしません。続けて*my_int_ptr = 5;を記述すると、そのアドレスでアドレス指定された場所に数値5を保存しようとします。

53
M.M

チュートリアルが間違っている理由を明確にするために、int *my_int_ptr = 2;は「制約違反」であり、コンパイルが許可されていないコードであり、コンパイラはそれに遭遇したときに診断を提供する必要があります。

6.5.16.1による単純な割り当て:

制約

次のいずれかが当てはまります。

  • 左のオペランドはアトミック、修飾、または非修飾の算術型を持ち、右は算術型を持ちます。
  • 左側のオペランドには、右側の型と互換性のある構造体または共用体型のアトミックバージョン、修飾バージョン、または非修飾バージョンがあります。
  • 左のオペランドは、アトミック、修飾、または非修飾のポインター型を持ち、(左辺値変換後の左のオペランドの型を考慮して)両方のオペランドは、互換型の修飾または非修飾バージョンへのポインターであり、左が指す型はすべて右側が指すタイプの修飾子。
  • 左のオペランドはアトミック、修飾、または非修飾のポインター型を持ち、(左のオペランドが左辺値変換後に持つ型を考慮して)1つのオペランドはオブジェクト型へのポインターであり、もう一方は修飾または非修飾バージョンへのポインターですvoid。左で示される型には、右で示される型のすべての修飾子があります。
  • 左のオペランドはアトミック、修飾、または非修飾のポインターであり、右はNULLポインター定数です。または
  • 左側のオペランドの型はatomic、qualified、またはunqualified _Boolで、右側はポインターです。

この場合、左のオペランドは非修飾ポインターです。右側のオペランドが整数(算術型)であることが許可されていることはどこにも言及されていません。そのため、コードはC標準に違反しています。

GCCは、標準Cコンパイラーであると明示的に指示しない限り、動作が悪いことが知られています。コードを-std=c11 -pedantic-errorsとしてコンパイルすると、必要に応じて正しく診断が行われます。

17
Lundin

int *my_int_ptr = 2

my_int_ptrに割り当てられたランダムなアドレスに整数値2を保存します。

これは完全に間違っています。これが実際に書かれている場合は、より良い本またはチュートリアルを入手してください。

int *my_int_ptr = 2は、アドレス2を指す整数ポインターを定義します。アドレス2にアクセスしようとすると、ほとんどの場合クラッシュします。

*my_int_ptr = 2、つまり行にintがない場合、my_int_ptrが指す任意のランダムアドレスに値2を格納します。これを言って、定義されたときにNULLをポインターに割り当てることができます。 char *x=NULL;は完全に有効なCです。

編集:これを書いている間、私は整数からポインタへの変換が実装定義の動作であることを知りませんでした。詳細については、@ M.Mおよび@SouravGhoshによる適切な回答を参照してください。

15
taskinoor

Cポインタに関する多くの混乱は、もともとコーディングスタイルに関して行われた非常に悪い選択に由来し、言語の構文の非常に悪い選択によって裏付けられています。

int *x = NULL;は正しいCですが、非常に誤解を招くものであり、無意味だとさえ言い、多くの初心者にとって言語の理解を妨げています。後で、*x = NULL;を行うことができると考えるようになりますが、これはもちろん不可能です。ご覧のとおり、変数の型はintではなく、変数の名前は*xではなく、宣言の*=と連携して機能的な役割を果たしません。純粋に宣言的です。それで、もっと理にかなっているのはこれです:

int* x = NULL;も正しいCですが、元のK&Rコーディングスタイルに準拠していません。タイプがint*であり、ポインター変数がxであることを完全に明確にしているので、値NULLxへのポインターであるintに格納されていることは、初心者にとっても明白です。

さらに、規則の導出が容易になります。星が変数名から離れている場合、それは宣言であり、名前に付加されている星はポインターの逆参照です。

したがって、さらに下にx = NULL;または*x = 2;を実行できることで、variable = expressionpointer-type variable = pointer-expressiondereferenced-pointer-variable = expressionにつながる方法を初心者が簡単に理解できるようになったことが、より理解しやすくなりました。 (「式」によって開始された場合、「右辺値」を意味します。)

言語の構文における不幸な選択は、ローカル変数を宣言するときに、整数と整数へのポインターを宣言するint i, *p;と言うことができるため、*が名前の有用な部分であると考えるようになることです。しかし、それはそうではなく、この構文は単に奇妙な特殊なケースであり、便宜上追加されたものであり、私が上で提案した規則を無効にするため、存在しないはずです。私の知る限り、この構文に意味のある言語は他にありませんが、たとえそうであっても、ポインター型がCで定義されている方法に矛盾があることを示しています。その他の場所、単一変数宣言、パラメーターリスト、 structメンバなどでは、type* pointer-variableではなくtype *pointer-variableとしてポインタを宣言できます。それは完全に合法であり、より理にかなっています。

14
Mike Nakis

多くの優れた答えに直交する何かを追加したいと思います。実際、NULLに初期化することは悪い習慣とはほど遠いものであり、そのポインターを使用して、動的に割り当てられたメモリブロックを格納する場合としない場合に便利です。

int * p = NULL;
...
if (...) {
    p = (int*) malloc(...);
    ...
}
...
free(p);

ISO-IEC 9899規格freeによると、引数がNULLの場合はnopであるため、上記のコード(または同じ行に沿ったより意味のあるもの)は正当です。

6
Luca Citi

これはヌルポインタです

int * nullPtr = (void*) 0;

これは正しいです。

int main()
{
    char * x = NULL;

    if (x==NULL)
        printf("is NULL\n");

    return EXIT_SUCCESS;
}

この機能は、その機能に適しています。 0のアドレスをcharポインターxに割り当てます。つまり、ポインタxがメモリアドレス0を指すようにします。

代替案:

int main()
{
    char* x = 0;

    if ( !x )
        printf(" x points to NULL\n");

    return EXIT_SUCCESS;
}

あなたが望んでいたことについての私の推測は:

int main()
{
    char* x = NULL;
    x = alloc( sizeof( char ));
    *x = '2';

    if ( *x == '2' )
        printf(" x points to an address/location that contains a '2' \n");

    return EXIT_SUCCESS;
}

x is the street address of a house. *x examines the contents of that house.
1
Vanderdecken