Как% s ведет себя в C, когда мы передаем ему символ *, указывающий на один символ - PullRequest
0 голосов
/ 14 января 2019

Это код -

 int main() {
     char c = 'A';
     char *p = &c;

     char *str = "Hello";

     printf("%s %s", p, str);
     return 0;
 }

Вывод выглядит следующим образом

A????? Hello
A?*??? Hello
A?z??? Hello
A???? Hello
A????? Hello
A??y?? Hello
A?*?? Hello
A?Z??? Hello
A?*+?? Hello
A?l?? Hello
A?z?? Hello
A??i?? Hello
A?ʜ?? Hello

Теперь я знаю, что% s пытается найти '\ 0', чтобы остановить печать, поэтому указатель на символ str выводит отлично.

Но я хочу знать, что когда мы указываем символ * на один символ, он печатает некоторые значения мусора после фактического символа - как он решает останавливаться после 4 или 5 значений мусора каждый раз? Как работает эта проверка безопасности? Насколько он знает, "?" может быть частью строки.

Ответы [ 4 ]

0 голосов
/ 14 января 2019

С printf , спецификатор преобразования s [выделение добавлено] :

writes a character string 

Аргумент должен быть указателем на начальный элемент массива символов. Точность определяет максимальное количество байтов для записи. Если Precision не указан, записывает каждый байт до и без первого нулевого терминатора . Если используется спецификатор l, аргумент должен быть указателем на начальный элемент массива wchar_t, который преобразуется в массив char, как будто при вызове wcrtomb с нулевым инициализированным состоянием преобразования.

В этом утверждении:

 printf("%s %s", p, str);

p - указатель на переменную типа char c, и вы используете спецификатор формата %s. Обратите внимание, что если точность не указана с помощью спецификатора преобразования s, то он записывает байты, пока не найдет нулевой завершающий символ. Спецификатор формата %s, при записи байтов, указанных p, будет записывать байты, начиная с памяти, указанной p, пока не найдет нулевой завершающий символ. Это означает, что он может получить доступ к памяти за пределами памяти, принадлежащей объекту c. Чтение памяти, которой не владеет программа, равно неопределенное поведение , которое включает в себя программу, которая может выполняться некорректно (либо аварийно завершать работу, либо генерировать неверные результаты в режиме без вывода сообщений), либо, к счастью, делать то, что задумал программист.

Вместо этого вы можете сделать:

printf("%.1s %s", p, str);
         ^^
      precision of the conversion

С %.1s он будет читать только один символ из ячейки памяти, указанной p.

0 голосов
/ 14 января 2019

Это магия «неопределенного поведения». Вы не можете предсказать, что произойдет или как оно будет себя вести. В разных средах более вероятно получение разных результатов, поэтому ...

0 голосов
/ 14 января 2019

Для формата "%s" соответствующий параметр должен указывать на действительную строку C, что означает, что он должен удовлетворять двум условиям: (1) он должен иметь тип char* (или const char*) и ( 2) он должен указывать на объект, представляющий последовательность символов, оканчивающуюся на '\0' в границах объекта.

Если вы делаете

char c;
char* ptr = &c;

тогда переменная ptr удовлетворяет только первому условию; это char *, но объект, на который он указывает, содержит только один символ, и если этот символ не '\0', то «последовательность» символов не заканчивается в границах объекта.

Следовательно, printf будет обращаться к памяти вне объекта и, таким образом, приведет к неопределенному поведению. Одним из таких действий может быть то, что он просто продолжает читать память (даже если он не принадлежит), пока где-нибудь не встретится '\0'. Это то, что вы, вероятно, наблюдаете.

0 голосов
/ 14 января 2019

Это даст разные результаты. В основном неожиданный результат. Попробуйте запустить код на другой машине, например, на другом онлайн-компиляторе Си. Вы сможете увидеть другой результат. Это будет всегда неожиданно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...