Подтвердить что ты не робот

Печать указателей в 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) одинаково?

4b9b3361

Ответ 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", вы можете использовать эту дополнительную косвенность.