Повторяя код в вопросе:
char c[10];
char* d;
1.
scanf("%s", &c);
printf("%s\n", &c);
Это, вероятно, работать, как ожидалось, но на самом деле поведение не определено.
scanf
с форматом "%s"
требует аргумента типа char*
. &c
имеет тип char (*)[10]
, то есть это указатель на массив char[10]
. Он указывает на то же место в памяти, что и адрес 0-го элемента c
, но он имеет другой тип. То же самое происходит с printf
: формат "%s"
подсказывает, что он ожидает аргумент char*
, но вы передаете ему аргумент char(*)[10]
.
С scanf
является вариационной функцией, нет необходимости проверять тип аргументов, отличных от строки формата. Компилятор (вероятно) с радостью передаст значение char (*)[10]
в scanf
, предполагая, что он может справиться с этим. И это возможно может, при реализации, где все указатели имеют такой же размер, представление и механизм передачи аргументов. Но, например, компилятор C для экзотической архитектуры может легко сделать указатели char*
больше, чем указатели на более крупные типы. Представьте себе процессор, чей собственный адрес указывает, скажем, на 64-битное слово; a char*
Указатель может быть составлен из указателя слов плюс смещение байта.
2.
scanf("%s", c);
printf("%s\n", c);
Это лучше. c
- это массив, но в этом контексте выражение массива «распадается» на указатель на первый элемент массива - это именно то, что требуется scanf
с форматом "%s"
. То же самое происходит, проходя c
до printf
. (Но есть еще какие-то проблемы, я вернусь к этому после того, как в других примерах
3.
scanf("%s", &d);
printf("%s\n", &d);
Поскольку d
является единственным char*
аргумент, &d
имеет тип char**
, и снова вы. Если все указатели имеют одинаковое представление (и тот же механизм передачи аргументов), а вход для scanf
достаточно короткий, это может случиться с «работой». если это массив из char
. Если char*
- 4 байта, а входная строка больше не равна t han длиной 3 символа, это, вероятно, будет работать - как если бы вы использовали char[4]
и правильно написали звонки. Но это чрезвычайно плохой практикой для хранения символьных строк непосредственно в объект-указатель, и существует огромный риск записи за конец объекта с непредсказуемыми результатами. (Эти непредсказуемые результаты включают записи в память, которая не используется ни для чего другого, что может появиться работать;. Такова природа неопределенного поведения)
(Стандарт C дает специальное разрешение для лечения любого объекта как массив символов, но в этом случае это очень плохая идея.)
4.
scanf("%s", d);
printf("%s\n", d);
Здесь типы все правильно, но если вы не инициализируется d
, чтобы указать на достаточно большом массиве char
, это, вероятно, не в состоянии эффектно (или, что еще хуже, по всей видимости, работать «правильно», что означает, у вас есть тонкая ошибка, которая, вероятно, появится позже).
И теперь мы добираемся до того, что я упоминал выше о других проблемах.
Например, 4, я упомянул, что d
должен указывать на «достаточно большой» массив. Насколько велика «достаточно большая»? На это нет ответа. scanf("%s", ...)
читает последовательность символов, разделенных пробелами, без верхней границы по ее длине. Например, если я запускаю вашу программу и удерживаю клавишу x
, я могу предоставить строку ввода дольше, чем любой предоставленный вами буфер, с непредсказуемыми результатами (неопределенное поведение снова).
формата scanf
функционального направления "%s"
не может безопасно использоваться (если ваша программа не работает в среде, где вы можете контролировать то, что будет отображаться на стандартном потоке ввода).
Один хороший способ читать ввод текста - использовать fgets
для чтения строки за раз, а затем использовать другие функции для анализа результата. fgets
требует указать максимальную длину ввода; если фактический ввод превышает предел, он усечен и остается прочитанным более поздними вызовами. Это не так удобно, как scanf
, но это можно сделать безопасно. (И никогда не использовать функцию gets
, как scanf("%s", ...)
, он не может быть использован безопасно.)
Предложенное чтение:
Раздел 6 comp.lang.c FAQ делает отличную работу объяснения массивов C и указателей, и как они Связанные (и не связанные). В разделе 12 обсуждается стандартный ввод-вывод C.
(Извините, что ответ такой длинный, у меня не было времени, чтобы сделать его короче.)
Как вы думаете? Прямо сейчас, похоже, вы пытаетесь заставить нас сделать домашнее задание для вас. – templatetypedef
Отмечено как домашнее задание; если я ошибаюсь, не стесняйтесь удалить тег. – mc10
Это не домашнее задание. Я просто готовлюсь к собеседованию и столкнулся с этой проблемой, пытаясь создать программу, которая генерирует перестановки. – Ivan