Как работает функция scanf в C? - PullRequest
6 голосов
/ 14 января 2010

Зачем вам нужен амперсанд (&) в функции scanf. Каким будет вывод или тип ошибки (компиляция или время выполнения) в следующем C-коде?

#include <stdio.h>

void main() {
    int a;
    printf("enter integer:");
    scanf("%d", a);
}

Ответы [ 7 ]

12 голосов
/ 14 января 2010

& в 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
3 голосов
/ 14 января 2010

В 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 *"
  ...
}
2 голосов
/ 14 января 2010

Это потому, что в C параметры функций передаются по значению . Чтобы функция scanf() могла изменить переменную 'a' в вашей функции main (), адрес из 'a' должен быть присвоен scanf(), следовательно, использование амперсанд (адрес).

2 голосов
/ 14 января 2010

Если вы задаете вопрос, подобный этому, я бы порекомендовал просто изучить сейчас "оно просто делает".

Вы узнаете, что вам нужен амперсанд, потому что scanf принимает один или несколько аргументов указателя. Если переменная типа int, это не указатель. & a («адрес a») - указатель, поэтому он будет работать с scanf.

1 голос
/ 14 января 2010

Вы не всегда должны использовать & с scanf. Что вам нужно сделать, это передать указатели . Если вы новичок в C, вам следует потратить некоторое время на чтение FAQ по comp.lang.c:

http://c -faq.com /

В частности:

1 голос
/ 14 января 2010

Поскольку scanf требует указатель на переменную (т.е. ссылку), в которую будет входить значение.

0 голосов
/ 15 января 2010

'&' в scanf требуется только для получения адреса переменной. Вы можете использовать scanf без '&', используя указатели:

int myInt;
int * pointer_to_int;
pointer_to_int = &myInt;
scanf("%d", pointer_to_int);

В целом, использование '&' часто проще, чем создание указателя, чтобы избежать использования '&'.

...