что делает указатель на указатель? символ ** - PullRequest
1 голос
/ 25 января 2010
char x[16];
int y = 42 + 256;
char* p = &y;
int* q = p;
char** r = &p;
printf("%d %d %d\n", p, q, r);

почему значение r всегда на 12 единиц меньше p и q? Спасибо!

Ответы [ 4 ]

6 голосов
/ 25 января 2010

То, что вы печатаете, имеет три адреса памяти. Поскольку они расположены в стеке (при условии, что они не глобальные), я ожидал бы что-то вроде:

2030 2026 2022

при условии 32-битной машины и 4-байтовых указателей.

Двойной * - указатель на указатель. Если вы используете правильные типы, это становится понятнее.

char *str = "hello world";

str в этом случае - указатель на завершенную нулем последовательность символов.

char **p = &str;

означает указатель на указатель на строку символов.

Я говорю «правильные типы», потому что у вас есть, например:

int y = 42 + 256;
char* p = &y;

На самом деле это должно быть:

int *p = &y;

Обычной причиной использования char * в этом случае является проверка отдельных байтов.

4 голосов
/ 25 января 2010

Давайте проанализируем ваш код построчно:

char x[16];

x - это «массив [16] из char», то есть он может хранить 16 char значений. Кстати, вы вообще не используете x в своем коде.

int y = 42 + 256;

y - это int, равный 298.

char* p = &y;

p - указатель на char, и вы пытаетесь присвоить ему адрес int. Если вы хотите указатель на int, вы должны объявить p как int *p, или, если вы хотите универсальный указатель, вы должны объявить его как void *p. Если вы действительно хотите делать то, что делаете, вам нужен актерский состав: char *p = (char *)&y;. Результатом вашей инициализации p является то, что p указывает на младший адресуемый байт y.

int* q = p;

q относится к типу int *, а p относится к типу char *. Опять же, вам нужен актерский состав. Предполагая приведение, стандарт говорит это о ваших последних двух утверждениях:

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

Итак, чистый эффект такой, как если бы вы сделали: int *q = &y;. Итак, q содержит адрес y.

char **r = &p;

r относится к типу char **. &p относится к типу int **. Опять же, вам нужен как минимум бросок, но вы не можете переносить int ** в char **. Я не уверен, почему вы используете char ** в любом случае. Если вам нужен адрес p в переменной, вы должны использовать void *, или, если вы действительно хотите char, использовать char *.

printf("%d %d %d\n", p, q, r);

Вы печатаете указатели с указателем формата %d. %d занимает int, а не указатель. Для печати следует использовать %p и использовать p, q и r до void *.

Предполагая, что я понял, что вы хотели сделать, я переписал вашу программу:

#include <stdio.h>

int main(void)
{
    int y = 42 + 256;
    void *p = &y;
    void *q = p;
    void *r = &p;
    printf("%p %p %p\n", p, q, r);
    return 0;
}

На моем компьютере это печатает:

0x7fff5fbff7dc 0x7fff5fbff7dc 0x7fff5fbff7d0

p и q явно равны, а r - адрес объекта p.

Если я не правильно понял ваше намерение, вы должны сообщить нам, что вы пытаетесь сделать и почему.

3 голосов
/ 25 января 2010

Двойной указатель является указателем на указатель.

почему значение r всегда 12 единиц меньше р и q?

Это не останется, если вы перейдете в другую среду выполнения или другую операционную систему / компилятор.

0 голосов
/ 25 января 2010

В вашем коде нет гарантии относительно значения r по сравнению с p или q. Поскольку вы говорите, что он меньше, я думаю, это автоматические переменные в функции. То, что вы видите, это тот факт, что компилятор (вероятно) выделяет 4 байта для каждого указателя, и вы выделили 3 из них, что получается до 12 байтов - хотя это в значительной степени случайность, а не C Стандарт требует или что-то в этом роде. Например, в 64-битной системе вполне вероятно, что вы увидите разные цифры.

Char ** - указатель на указатель на символ. Типичное использование - создание динамического массива строк. Это может быть проще, если вы начали с чего-то вроде:

typedef char *string;

string *string_array;

Если вы сложите эти кусочки вместе, будет написано: char **string_array;

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