2010-07-28 5 views
7

Когда мы говорим о разыменовании, необходимо ли в нем использовать *? Если доступ к референта указателя каким-либо другим способом, это может рассматриваться как разыменования указателя или нет, как:Почему printf ("% s", ptr) способен разыменовывать пустоту *?

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

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

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

K & R говорит

a "pointer to void" is used to hold any type of pointer but cannot be dereferenced itself

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

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

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

In function ‘main’: warning: dereferencing ‘void *’ pointer error: invalid use of void expression

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

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

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

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

+1

Я не вижу, где вы разыскиваете 'void *' успешно. Пожалуйста, перечитайте свой код. – leppie

ответ

9

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

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

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

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

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

+0

У меня есть ваше мнение. Благодарю. Как насчет первой части вопроса? Является ли 'printf ("% s ", ptr)' также считаться разыменованием указателя? –

+1

@andrew printf(), безусловно, придется разыграть указатель на то, что он делает для форматирования «% s». OTOH, если вы использовали форматировщик «% p» на том же указателе, никакого уважения не требуется. – 2010-07-28 11:01:24

+0

Я не знаю, как стандарт определяет 'printf', или если я что-то упускаю, но возможно, что' void *' передается в 'char *', так как 'printf' может сделать вывод о том, что это строка '% s'. Опять же, проблема может быть проблемой vararg. Мне не хочется врываться в нее. –

0

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

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

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

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

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

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

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

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

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

Смежные вопросы