Печать указателей в 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 **
, no?
И почему я получаю другие предупреждения? Должен ли я указывать указатели (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
) одинаково?
Ответы
Ответ 1
"s" не является "char *", это "char [4]". Итак, "& s" не является "char **", а фактически "указателем на массив из 4 символов". Ваш компилятор может обрабатывать "& s", как если бы вы написали "& s [0]", что примерно одно и то же, но является "char *".
Когда вы пишете "char ** p = & s;" вы пытаетесь сказать "Я хочу, чтобы p был установлен на адрес объекта, который в настоящее время указывает на" asd ".Но в настоящее время нет ничего, что указывает на" asd ". Существует только массив, который содержит" asd ";/p >
char s[] = "asd";
char *p = &s[0]; // alternately you could use the shorthand char*p = s;
char **pp = &p;
Ответ 2
Да, ваш компилятор ожидает void *. Просто отбросьте их на void *.
/* for instance... */
printf("The value of s is: %p\n", (void *) s);
printf("The direction of s is: %p\n", (void *) &s);
Ответ 3
Если вы передаете имя массива в качестве аргумента функции, оно обрабатывается так, как будто вы передали адрес массива. Итак, & 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)
Ответ 4
изменить строку:
char s [] = "asd";
в
char * s = "asd";
и все станет понятнее
Ответ 5
Вы не можете изменить значение (т.е. адрес) статического массива. С технической точки зрения, lvalue массива является адресом его первого элемента. Следовательно, s == &s
. Это просто причуда языка.
Ответ 6
Обычно он считал плохой стиль ненужными указателями наведения (void *). Здесь, однако, вам нужны приведения к (void *) в аргументах printf, поскольку printf является переменным. Прототип не сообщает компилятору, какой тип конвертировать указатели на сайт вызова.
Ответ 7
Это не указатель на символ char*
, а указатель на массив из 4 символов: char* [4]
. С g++ он не компилируется:
main.cpp: В функции 'int main (int, char **): main.cpp: 126: error: не может преобразовать 'char (*) [4] в' char ** при инициализации
Кроме того, страницы man linux говорят:
р
Аргумент указателя void * печатается в шестнадцатеричном виде (как будто на% # x или % # Лк). Это будет указателем на пустоту.
Вы можете изменить свой код на:
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
Ответ 8
Вы использовали:
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", вы можете использовать эту дополнительную косвенность.