web-dev-qa-db-ja.com

「for」ループ初期化子でポインターを逆参照すると、セグメンテーション違反が発生します

forループでポインターを使用すると問題が発生します。私のforループ初期化子で、intポインターを逆参照し、それに「0」の値を与えます。ループでこの逆参照されたポインターを使用すると、セグメンテーション違反が発生し、その理由がわかりません。私は Code :: Blocks およびC GNU GCCコンパイラを使用しています。

  1. ウォッチウィンドウを見ると、forループ中に変数に乱数が含まれていることがわかります。

  2. 逆参照されたポインターは、forループ中にスコープを失うようです。

コード:

#include <stdio.h>

int main(void)
{
    int val = 0;
    int *p = NULL;
    int answer = 0;

    p = &val;

    *p = 1; // This dereferences and sets to one successfully

    for (int i=3, (*p)=0 ; i>=0; i--) // Here *p is a random number
    {
        printf("do stuff");
        (*p) += 1; // Here it causes a segmentation fault
    }
    answer = *p;
}

私のようにポインターを使っても問題ないと思いました。

45
Mark R

ここをよく見てください:

for (int i=3, (*p)=0 ; i>=0; i--)

forの最初の部分では、前に定義したpをシャドウし、NULLに初期化するpという名前のnewポインター変数を定義しています。 。次に、segfaultの原因となるループでNULLポインターを逆参照します。

このように、変数の定義と既存の変数への割り当ての両方を同時に持つことはできないため、*pの割り当てをループの前に移動します。

*p = 0;
for (int i=3; i>=0; i--)

または、ループの外でiを定義できます。

int i;
for (i=3, (*p)=0 ; i>=0; i--)

あなたcouldは、コンマ演算子を悪用することにより、これらを一緒に絞ります:

for (int i=(*p=0,3) ; i>=0; i--)

ここで、pへの割り当てはiの初期化子の一部として行われるため、新しい変数は宣言されません。ただし、コードの読み取りと理解が難しくなるため、これはお勧めしません。

61
dbush

pという完全に新しい変数を宣言しています。

for (int i=3, (*p)=0 ; i>=0; i--)

これは次と同じです:

for (int i=3, *p=0 ; i>=0; i--)

だからあなたはint iint *p、アドレス0を指します。これは、以前に定義されたものと同じpではありません。それはただそれを覆っています。したがって、それを逆参照すると、segfaultが発生します。

28
Nikos C.

ヒント:-Wshadow変数が別の変数をシャドウするときに警告を表示します。

[] $ gcc main.c -Wshadow
main.c: In function ‘main’:
main.c:13:21: warning: declaration of ‘p’ shadows a previous local [-Wshadow]
   13 |     for (int i=3, (*p)=0 ; i>=0; i--) // Here *p is a random number
      |                     ^
main.c:6:10: note: shadowed declaration is here
    6 |     int *p = NULL;
      |          ^

https://coliru.stacked-crooked.com/a/5de37f53cf0b094d

3
0x6773