Почему printf ("% s", ptr) может разыменовать пустоту *? - PullRequest
7 голосов
/ 28 июля 2010

Когда мы говорим о разыменовании, необходимо ли в нем использовать *?Если мы обращаемся к референту указателя каким-либо другим способом, можно ли его рассматривать как разыменование указателя или нет, например:

char *ptr = "abc" ;
printf( "%c" , *ptr ); // Here pointer is dereferenced.
printf( "%s" , ptr );  // What about this one?

Это первая часть моего вопроса.

Теперь, если printf( "%s" , ptr ) является примером разыменования, любезно ответьте и на следующую часть моего вопроса.

K & R говорит

"указатель на пустоту"используется для хранения любого типа указателя, но не может быть разыменована сама по себе

Следовательно,

char a = 'c' ;
char *p = &a ;
void *k = &a;
printf( "\n%c\n" , *p );
printf( "\n%c\n" , *k );

Не компилируется, компилятор выдает ошибку

Вфункция 'main': предупреждение: разыменование ошибки указателя 'void *': недопустимое использование выражения void

Но если мы используем

char *a = "c" ;
char *p = a ;
void *k = a;
printf( "\n%c\n" , *p );
printf( "\n%s\n" , k );

, оно компилируется и работает.Это означает, что указатель void может быть разыменован - у нас есть указатель объекта, на который ссылается.Если это так, то что означает вышеупомянутая цитата K & R в этом контексте?

Спасибо за ваше время.

Ответы [ 2 ]

9 голосов
/ 28 июля 2010

Нет. то, что у вас есть, это «неопределенное поведение» - стандарт языка Си не говорит, что должно произойти. В вашем случае это «работает», но для другого пользователя / компилятора / платформы это может не сработать. Ваше заявление:

printf( "\n%s\n" , k );

эквивалентно:

int k = 42;
printf( "\n%s\n" , k );

и в равной степени не определено.

0 голосов
/ 03 декабря 2014

В приведенном примере:

char *a = "c";
char *p = a;
void *k = a;
printf( "\n%c\n" , *p );
printf( "\n%s\n" , k );

Что происходит, в первом printf значение 'c' передается из разыменования указателя char, printf знает только то, что вы его далиchar из-за тега формата %c.

Теперь void* - это просто необработанный указатель с неизвестным типом, вы не можете разыменовать его, потому что компилятор не знает, какой типчитать из него, если вы не приведете его к чему-то другому.

Во втором printf указатель void* передается внутрь. printf просто видит его как простое число и не знает, что это такоепока он не прочитает данный тег форматирования.Используя %s, вы сообщаете функции printf, которую вы фактически передали в char*, поэтому она соответственно преобразует ее и правильно читает в виде строки, т.е. она разыменовывает указатель char*, а не void*указатель.

Это допустимый код, если указатель void* указывает на массив char с нулевым символом в конце.Если указатель void* указывает на что-то другое, функция printf все равно с радостью попытается прочитать его как указатель char* (если вы укажете %s) и вызовет неопределенное поведение.

AДругой плохой пример:

char *a = "ce";
int b = (int)a;
printf( "\n%s\n" , b );

Вы также не можете разыменовать целое число, но я сказал printf, что это char*, поэтому оно работает.

...