Печать указателей в C

Я пытался понять что-то с указателями, поэтому я написал этот код:

#include  int main(void) { char s[] = "asd"; char **p = &s; printf("The value of s is: %p\n", s); printf("The direction of s is: %p\n", &s); printf("The value of p is: %p\n", p); printf("The direction of p is: %p\n", &p); printf("The direction of s[0] is: %p\n", &s[0]); printf("The direction of s[1] is: %p\n", &s[1]); printf("The direction of s[2] is: %p\n", &s[2]); return 0; } 

При компиляции с помощью gcc я получаю следующие предупреждения:

 $ gcc main.c -o main-bin -ansi -pedantic -Wall -lm main.c: In function 'main': main.c:6: warning: initialization from incompatible pointer type main.c:9: warning: format '%p' expects type 'void *', but argument 2 has type 'char (*)[4]' main.c:11: warning: format '%p' expects type 'void *', but argument 2 has type 'char **' main.c:12: warning: format '%p' expects type 'void *', but argument 2 has type 'char ***' 

(Флаги для gcc – это потому, что я должен быть C89)

Почему несовместимые типы указателей? Не является ли имя массива указателем на его первый элемент? Итак, если s является указателем на ‘a’, &s должен быть char ** , нет? И почему я получаю другие предупреждения? Должен ли я указывать указатели с ( void * ), чтобы напечатать их?

И при запуске я получаю что-то вроде этого:

 $ ./main-bin The value of s is: 0xbfb7c860 The direction of s is: 0xbfb7c860 The value of p is: 0xbfb7c860 The direction of p is: 0xbfb7c85c The direction of s[0] is: 0xbfb7c860 The direction of s[1] is: 0xbfb7c861 The direction of s[2] is: 0xbfb7c862 

Как значение s и его направление (и, конечно, значение p ) одинаковы?

«s» не является «char *», это «char [4]». Итак, «& s» не является «char **», а фактически «указателем на массив из 4 символов». Ваш компилятор может обрабатывать «& s», как если бы вы написали «& s [0]», что примерно одно и то же, но является «char *».

Когда вы пишете «char ** p = & s;» вы пытаетесь сказать «Я хочу, чтобы p был установлен на адрес объекта, который в настоящее время указывает на« asd ». Но в настоящее время нет ничего, что указывает на« asd ». Существует только массив, который содержит « asd »;

 char s[] = "asd"; char *p = &s[0]; // alternately you could use the shorthand char*p = s; char **pp = &p; 

Да, ваш компилятор ожидает void *. Просто брось их в пустоту *.

 /* for instance... */ printf("The value of s is: %p\n", (void *) s); printf("The direction of s is: %p\n", (void *) &s); 

Если вы передаете имя массива в качестве аргумента функции, оно обрабатывается так, как будто вы передали адрес массива. Итак, s и s являются одинаковыми аргументами. См. K & R 5.3. & s [0] совпадает с & s, так как он берет адрес первого элемента массива, который совпадает с адресом самого массива.

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

  • void* p; говорит, что p – адрес памяти, но я не знаю, что в памяти
  • char* s; говорит, что s – адрес памяти, а первый байт содержит символ
  • char** ps; говорит, что ps является адресом памяти, а четыре байта (для 32-разрядной системы) содержат указатель типа char *.

cf http://www.oberon2005.ru/paper/kr_c.pdf (электронная книга K & R)

Это не указатель на символ char* а указатель на массив из 4 символов: char* [4] . С g ++ он не компилируется:

main.cpp: В функции ‘int main (int, char **)’: main.cpp: 126: error: не может преобразовать ‘char (*) [4]’ в ‘char **’ при инициализации

Более того, страницы руководства Linux говорят :

п

Аргумент указателя void * печатается в шестнадцатеричном виде (как будто на% # x или% # lx). Это должно быть указателем на пустоту.

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

 char* s = "asd"; char** p = &s; printf("The value of s is: %p\n", s); printf("The address of s is: %p\n", &s); printf("The value of p is: %p\n", p); printf("The address of p is: %p\n", &p); printf("The address of s[0] is: %p\n", &s[0]); printf("The address of s[1] is: %p\n", &s[1]); printf("The address of s[2] is: %p\n", &s[2]); 

результат:

Значение s равно: 0x403f00

Адрес s: 0x7fff2df9d588

Значение p равно: 0x7fff2df9d588

Адрес p: 0x7fff2df9d580

Адрес s [0]: 0x403f00

Адрес s [1]: 0x403f01

Адрес s [2]: 0x403f02

строка изменения:

char s [] = “asd”;

чтобы:

char * s = “asd”;

и все станет понятнее

Вы не можете изменить значение (т. Е. Адрес) статического массива. С технической точки зрения, lvalue массива является адресом его первого элемента. Следовательно, s == &s . Это просто причуда языка.

Как правило, считается, что плохой стиль для ненужных указателей бросания (void *). Здесь, однако, вам нужны приведения к (void *) в аргументах printf, поскольку printf является переменным. Прототип не сообщает компилятору, какой тип конвертировать указатели на сайт вызова.

Вы использовали:

 char s[] = "asd"; 

Здесь s фактически указывает на байты «asd». Адрес s также укажет на это местоположение.

Если вы использовали:

 char *s = "asd"; 

значение s и & s будет отличаться, так как s фактически будет указателем на байты «asd».

Ты использовал:

 char s[] = "asd"; char **p = &s; 

Здесь s указывает на байты «asd». p является указателем на указатель на символы и был установлен на адрес символов. Другими словами, у вас слишком много указаний на стр. Если вы использовали char * s = “asd”, вы можете использовать эту дополнительную косвенность.