Чтение строки с помощью scanf

Я немного смущен. У меня создалось впечатление, что правильный способ чтения строки C с помощью scanf() шел по строкам

(не обращая внимания на возможное переполнение буфера, это просто простой пример)

char string[256];
scanf( "%s" , string );

Однако, похоже, тоже работает,

scanf( "%s" , &string );

Это только мой компилятор (gcc), просто удача или что-то еще?

Ответ 1

Массив "распадается" на указатель на его первый элемент, поэтому scanf("%s", string) эквивалентен scanf("%s", &string[0]). С другой стороны, scanf("%s", &string) передает указатель на char[256], но указывает на то же место.

Затем scanf, обрабатывая хвост списка своих аргументов, попытается вытащить a char *. Это правильная вещь, когда вы прошли в string или &string[0], но когда вы прошли в &string, вы зависете от того, что языковой стандарт не гарантирует, а именно, что указатели &string и &string[0] - указатели на объекты разных типов и размеров, которые начинаются в одном и том же месте - представляются одинаково.

Я не верю, что когда-либо сталкивался с системой, на которой это не работает, и на практике вы, вероятно, безопасны. Тем не менее, это неправильно, и это может потерпеть неудачу на некоторых платформах. (Гипотетический пример: реализация "отладки", включающая информацию о типе с каждым указателем. Я думаю, что реализация C на Symbolics "Lisp Machines" сделала что-то вроде этого.)

Ответ 2

Я думаю, что это ниже верно, и это может помочь. Не стесняйтесь исправить это, если найдете какие-либо ошибки. Я новичок в C.

char str[]  
  • массив значений типа char, с собственным адресом в памяти
  • массив значений типа char, с собственным адресом в памяти как много последовательных адресов как элементы в массиве
  • включая символ завершения завершения '\0' &str, &str[0] и str, все три представляют одно и то же место в памяти, которое является адресом первого элемента массива str

    char * strPtr = & str [0];//декларация и инициализация

вы можете разделить это на два:

char *strPtr; strPtr = &str[0];
  1. strPtr является указателем на char
  2. strPtr указывает на массив str
  3. strPtr - это переменная с собственным адресом в памяти
  4. strPtr - это переменная, которая хранит значение адреса &str[0]
  5. strPtr собственный адрес в памяти отличается от адреса памяти, который он хранит (адрес массива в памяти a.k.a & str [0])
  6. &strPtr представляет адрес самого strPtr

Я думаю, что вы могли бы объявить указатель на указатель как:

char **vPtr = &strPtr;  

объявляет и инициализирует адрес указателя strPtr

В качестве альтернативы вы можете разделить на две части:

char **vPtr;
*vPtr = &strPtr
  1. *vPtr указывает на указатель strPtr
  2. *vPtr - это переменная со своим адресом в памяти
  3. *vPtr - это переменная, которая хранит значение address & strPtr
  4. окончательный комментарий: вы не можете сделать str++, str адрес const, но вы можете сделать strPtr++