scanf
関数にアンパサンド (&) が必要なのはなぜですか。次の C コードの出力またはエラーの種類 (コンパイルまたはランタイム) は何ですか?
#include <stdio.h>
void main() {
int a;
printf("enter integer:");
scanf("%d", a);
}
Cの&
は、オペランドのアドレスを返す演算子です。このように考えてください。単にscanf
変数a
を なし&
で指定すると、値によって渡されます。つまりscanf
、値を設定して表示することはできません。参照渡し (を使用して&
実際に へのポインターを渡すa
) を使用scanf
すると、呼び出し元の関数にも変更が表示されるように設定できます。
特定のエラーに関しては、実際にはわかりません。動作は未定義です。scanf
プログラムのどこかで値が変更されたことを知らずに、黙って実行し続けることがあります。次の場合のように、プログラムがすぐにクラッシュすることがあります。
#include <stdio.h>
int main()
{
int a;
printf("enter integer: ");
scanf("%d",a);
printf("entered integer: %d\n", a);
return 0;
}
コンパイルすると、次のようになります。
$ gcc -o test test.c
test.c: In function ‘main’:
test.c:6: warning: format ‘%d’ expects type ‘int *’, but argument 2 has type ‘int’
実行すると、セグメンテーション違反が表示されます。
$ ./test
enter integer: 2
Segmentation fault
C では、すべての関数引数が値渡しされます。関数の仮パラメータを変更しても、実パラメータには反映されません。例えば:
void foo(int bar)
{
bar = bar + 1;
}
int main(void)
{
int x = 0;
printf("x before foo = %d\n", x);
foo(x);
printf("x after foo = %d\n", x);
return 0;
}
プログラムの出力は次のようになります。
foo = 0 の前の x foo = 0 の後の x
自分自身への参照ではなく、(0)bar
の値を受け取るためです。変更しても には影響しません。x
x
bar
x
C では、これを回避する方法は、変数へのポインターを渡すことです。
void foo(int *bar)
{
*bar = *bar + 1;
}
int main(void)
{
int x = 0;
printf("x before foo = %d\n", x);
foo(&x);
printf("x after foo = %d\n", x);
return 0;
}
これで、プログラムの出力は
foo = 0 の前の x foo の後の x = 1
今回は、仮パラメーターbar
は int ではなく int へのポインターであり、x に含まれる値ではなく、 (への呼び出しの式で指定された) のアドレスを受け取ります。この式は「バーが指しているロケーションの値を取得する」という意味なので、 に相当します。 x
&x
foo
*bar
*bar = *bar + 1
x = x + 1
引数に書き込む必要があるためscanf()
、それらの引数がポインターとして型付けされることを期待しています。"%d" 変換指定子は、対応する引数が int ( int *
) へのポインターであることを予期し、"%u" 変換指定子は unsigned int ( unsigned *
) へのポインターであることを予期し、"%s" は char ( char *
) へのポインターであることを予期し、"% f" は float ( float *
) などへのポインターを期待しています。あなたの例では、a
is typedint
であるため、式を使用し&a
てポインターを取得する必要があります。
既にポインター型である場合は、への呼び出しで演算子a
を使用する必要がないことに注意してください。&
scanf()
int main(void)
{
int a, *pa; // declare pa as a pointer to int
...
pa = &a; // assign address of a to pa
scanf("%d", pa); // scanf() will write to a through pa
...
}
&
また、配列を関数に渡す場合 ("%s" 変換指定子を使用して文字列を読み取る場合など)、演算子を使用する必要がないことにも注意してください。配列式は暗黙的にポインター型に変換されます。
int main(void)
{
char name[20];
...
scanf("%19s", name); // name implicitly converted from "char [20]" to "char *"
...
}
このような質問をしている場合は、とりあえず「それはできる」ことを学ぶことをお勧めします。
scanf
は 1 つ以上のポインター引数を取るため、アンパサンドが必要であることがわかります。a が int 変数の場合、それはポインターではありません。&a (「a のアドレス」) はポインターなので、 で動作しscanf
ます。
これは、C では関数のパラメーターが値で渡されるためです。関数がmain() 関数の ' ' 変数scanf()
を変更するために、 ' ' のアドレスがに与えられるため、アンパサンド (のアドレス) が使用されます。a
a
scanf()
withを常に使用する必要はありません。あなたがする必要があるのは、ポインタを渡すことです。C を初めて使用する場合は、comp.lang.c FAQ を読むのに時間を費やす必要があります。&
scanf
具体的には:
scanf
値が入る変数へのポインタ(つまり参照)が必要だからです。
「&」scanf
は、変数のアドレスを取得するためにのみ必要です。ポインターを使用すると、「&」なしで 使用できます。scanf
int myInt;
int * pointer_to_int;
pointer_to_int = &myInt;
scanf("%d", pointer_to_int);
一般に、「&」の使用を避けるためにポインターを作成するよりも、「&」を使用する方が簡単な場合がよくあります。