2008-10-13 5 views
33

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

#include <stdio.h> 

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 

Как можно значение с, и это направление (и, конечно, значение p) будет то же самое?

ответ

24

«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; 
0

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

char s[] = "asd"; 

Здесь S на самом деле указывает на байт "ASD". Адрес s также укажет на это местоположение.

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

char *s = "asd"; 

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

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

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

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

+0

Но я хочу р быть указателем с, не ' а». – alcuadrado 2008-10-13 14:32:07

+0

@alcuadrado: Но это IS 'a'. Ваше основное недоразумение в этом - коренная проблема. – 2008-10-13 14:49:06

+0

К сожалению, я загрузил сообщение, когда он отправил сообщение. – selwyn 2008-10-13 15:00:32

13

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

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

Это неправильно. Кастинг в пустоту * просто скрывает проблему, а не решает ее. – 2008-10-13 14:46:19

+1

Моя первая мысль заключалась в том, что он просто хотел напечатать цифры, чтобы увидеть значения, которые был назначен компилятором, и отбрасывание в пустоту позволило бы ему, независимо от каких-либо проблем в его заявлениях. Но да, это, безусловно, избегает проблемы. – indiv 2008-10-13 15:19:24

1

линии изменения:

Char S [] = "ASD";

к:

символ * S = "ASD";

и все станет более ясным

4

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

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

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

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

1

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

1

Обычно это считается плохой стиль излишне отливать указатели (недействительными *) Здесь, однако, необходимо слепки в (аннулируются *) на аргументы PRINTF, потому что Printf является VARIADIC прототип. не сообщает компилятору, какой тип конвертировать указатели на сайт вызова.

2

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

main.cpp: In function ‘int main(int, char**)’: main.cpp:126: error: cannot convert ‘char (*)[4]’ to ‘char**’ in initialization

Кроме того, Linux человек страниц says:

p

The void * pointer argument is printed in hexadecimal (as if by %#x or %#lx). It shoud be pointer to void.

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

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

Значение р: 0x7fff2df9d588

Адрес р: 0x7fff2df9d580

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

адрес с [1] является: 0x403f01

адрес с [2] является: 0x403f02

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