В 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;
}
Вывод программы будет
x before foo = 0
x after foo = 0
потому что bar
получает значение x
(0), а не ссылку на саму 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;
}
Теперь вывод программы
x before foo = 0
x after foo = 1
На этот раз формальным параметром bar
является не int, а указатель на int, и он получает адрес x
(заданный выражением &x
в вызове foo
), а не значение, содержащееся в x. Выражение *bar
означает «получить значение в строке местоположения указывает на », поэтому *bar = *bar + 1
соответствует x = x + 1
.
Поскольку scanf()
необходимо записать в свои аргументы, он ожидает, что эти аргументы будут напечатаны как указатели. Спецификатор преобразования "% d" ожидает, что соответствующий аргумент будет указателем на int (int *
), спецификатор преобразования "% u" ожидает указатель на unsigned int (unsigned *
), "% s" ожидает указатель на char (char *
), "% f" ожидает указатель с плавающей точкой (float *
) и т. д. В вашем примере, поскольку a
набирается int
, вам нужно использовать выражение &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 *"
...
}