Указатель, указывающий на себя - PullRequest
0 голосов
/ 02 мая 2019

Я изучал typedefs и наткнулся на эту программу

РЕДАКТИРОВАТЬ : Почти все ответы касались предупреждений, которые он генерирует. Итак, я удалил все предупреждения, но вопрос остался прежним.

#include<stdio.h>
typedef int int3[3];
int main(){
    int a[2][3] = {{1,2,3}, {4,5}};
    int3 *p = a;
    int *ip = (int *) a;
    printf("sizeof:\np: %lu\n(*p+0): %lu\n**p: %lu\nip: %lu\n*ip: %lu\n",sizeof p, sizeof (*p+0), sizeof **p, sizeof ip, sizeof *ip);
    printf("---\n");
    printf("p: %p\tp+1: %p\n*p: %p\t*p+1: %p\n**p: %d\nip: %p\n*ip: %d",p,p+1,*p,*p+1,**p,ip,*ip);
    return 0;
}

за один прогон показывает:

sizeof:
p: 8
(*p+0): 8
**p: 4
ip: 8
*ip: 4
---
p: 0x7ffe36df31b0   p+1: 0x7ffe36df31bc
*p: 0x7ffe36df31b0  *p+1: 0x7ffe36df31b4
**p: 1
ip: 0x7ffe36df31b0
*ip: 1

Мой вопрос: Если p и * p равны
и ** р = 1
тогда почему не * p = 1?

Предусмотренный размер указателя = 8 и размер целого = 4
Даже если принять во внимание арифметику указателя, тогда * p должно быть как минимум 0xkk kk kk kk 00 00 00 01
k - любое шестнадцатеричное число (рассмотрим младший порядок)
потому что разыменование того же адреса, что и int, должно дать 1

EDIT : Чтобы прояснить ситуацию, рассмотрим следующую таблицу:

+-----------+----------------------+------------------+
|Variable   | Value                | Address          |
|           |                      |                  |
|           |                      |                  |
| p         | 0x7ffe36df31b0       |                  |
|           |                      |                  |
|*p         | 0x7ffe36df31b0       |  0x7ffe36df31b0  |
|           |                      |                  |
|**p        | 1                    |  0x7ffe36df31b0  |
+-----------+----------------------+------------------+

Как * p и ** p могут иметь один и тот же адрес, но различное значение?

Ответы [ 3 ]

2 голосов
/ 06 мая 2019

Во-первых, обратите внимание, что этот код в запутанной путанице нарушений ограничений в сочетании с распадом массива , чтобы генерировать вывод сомнительной полезности.Но я постараюсь ответить на ваш вопрос о том, что происходит, и попробую использовать мирские объяснения, не прибегая к цитатам из Стандарта Си, поскольку другие уже ответили таким образом.

Как можно *p и ** p имеют одинаковый адрес, но разное значение?

Поскольку p является указателем на массив.

Дано

typedef int int3[3];
int3 *p;

, что означает *p - трехмерный массив.Обратите внимание, что

int3 *p = a;

является нарушением ограничения, как отмечено в другом месте.Вы можете заставить , чтобы "работать" с приведением, но это все еще в корне неверно, и код только "сходит с рук", потому что адрес массива является адресом первого элемента этого массива - и ваша текущая реализация не достаточно дифференцирует типы указателей, поскольку они принципиально несовместимы.Выберите реализацию, в которой простой int * является, скажем, 32-битным значением смещения, в то время как массив будет иметь в качестве адреса как 32-битный сегмент и 32-битного значения смещения, так и кодскорее всего породит чепуху, если она не подведет полностью.Вот почему стандарт C требует, чтобы указатели были «совместимыми типами» - потому что указатели вообще не должны быть совместимыми.

Если оставить в стороне эту проблему, инициализация / присваивание вашей реализации может выглядеть как

int3 *p = ( int3 * ) a;

Поскольку это «работает», это означает, что p содержит адрес трехэлементного массива значений int.Таким образом, разыменование p с *p - это то же самое, что и a[0] или первый трехэлементный массив int в двумерном массиве a.

Так что же такое *p,тогда?

Это точно так же, как a[0], массив.И такая пустая ссылка на массив распадается на адрес *, и этот адрес того же типа , что и указатель на элемент массива.И значением этого адреса будет значение адреса первого элемента массива.

Таким образом, *p, представляющий собой массив из трех элементов int, может сам разыменовываться и вычисляться какпервый элемент a[0] или a[0][0].Так что **p - это то же самое, что a[0][0].

И a[0][0] - или **p - это int, который инициализируется значением 1.

*p - это a[0], а адрес этого первого элемента.

* - Многие люди говорят, что массив "распадается на указатель" или "массив является указателем"", но мне нравится" распадаться на адрес ", потому что указатель может быть назначен, но адрес просто равен - он не может быть назначен самому себе, так же как сам массив не может быть назначен, но адрес может быть назначен чему-то другому, например, указателю.Обратите внимание, что когда массив передается функции, адрес массива фактически передается как фактический указатель.Указатель является параметром функции, и этот параметр функции может быть назначен ...

1 голос
/ 03 мая 2019

опубликованный код при компиляции с:

gcc -c -Wall -Wextra -Wconversion -pedantic -std=gnu11 

приводит к следующим сообщениям компилятора:

gcc    -ggdb -Wall -Wextra -Wconversion -pedantic -std=gnu11 -c "untitled.c"  (in directory: /home/richard/Documents/forum)
untitled.c: In function ‘main’:
untitled.c:6:15: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
     int *ip = a;
               ^
untitled.c:7:26: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long unsigned int’ [-Wformat=]
     printf("sizeof:\np: %d\n(*p+0): %d\n**p: %d\nip: %d\n*ip: %d\n",sizeof p, sizeof (*p+0), sizeof **p, sizeof ip, sizeof *ip);
                         ~^
                         %ld
untitled.c:7:38: warning: format ‘%d’ expects argument of type ‘int’, but argument 3 has type ‘long unsigned int’ [-Wformat=]
     printf("sizeof:\np: %d\n(*p+0): %d\n**p: %d\nip: %d\n*ip: %d\n",sizeof p, sizeof (*p+0), sizeof **p, sizeof ip, sizeof *ip);
                                     ~^
                                     %ld
untitled.c:7:47: warning: format ‘%d’ expects argument of type ‘int’, but argument 4 has type ‘long unsigned int’ [-Wformat=]
     printf("sizeof:\np: %d\n(*p+0): %d\n**p: %d\nip: %d\n*ip: %d\n",sizeof p, sizeof (*p+0), sizeof **p, sizeof ip, sizeof *ip);
                                              ~^
                                              %ld
untitled.c:7:55: warning: format ‘%d’ expects argument of type ‘int’, but argument 5 has type ‘long unsigned int’ [-Wformat=]
     printf("sizeof:\np: %d\n(*p+0): %d\n**p: %d\nip: %d\n*ip: %d\n",sizeof p, sizeof (*p+0), sizeof **p, sizeof ip, sizeof *ip);
                                                      ~^
                                                      %ld
untitled.c:7:64: warning: format ‘%d’ expects argument of type ‘int’, but argument 6 has type ‘long unsigned int’ [-Wformat=]
     printf("sizeof:\np: %d\n(*p+0): %d\n**p: %d\nip: %d\n*ip: %d\n",sizeof p, sizeof (*p+0), sizeof **p, sizeof ip, sizeof *ip);
                                                               ~^
                                                               %ld
untitled.c:9:17: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 2 has type ‘int (*)[3]’ [-Wformat=]
     printf("p: %x\tp+1: %x\n*p: %x\t*p+1: %x\n**p: %d\nip: %x\n*ip: %d",p,p+1,*p,*p+1,**p,ip,*ip);
                ~^
untitled.c:9:26: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 3 has type ‘int (*)[3]’ [-Wformat=]
     printf("p: %x\tp+1: %x\n*p: %x\t*p+1: %x\n**p: %d\nip: %x\n*ip: %d",p,p+1,*p,*p+1,**p,ip,*ip);
                         ~^                                                ~~~
untitled.c:9:34: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 4 has type ‘int *’ [-Wformat=]
     printf("p: %x\tp+1: %x\n*p: %x\t*p+1: %x\n**p: %d\nip: %x\n*ip: %d",p,p+1,*p,*p+1,**p,ip,*ip);
                                 ~^
                                 %ls
untitled.c:9:44: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 5 has type ‘int *’ [-Wformat=]
     printf("p: %x\tp+1: %x\n*p: %x\t*p+1: %x\n**p: %d\nip: %x\n*ip: %d",p,p+1,*p,*p+1,**p,ip,*ip);
                                           ~^                                     ~~~~
                                           %ls
untitled.c:9:61: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 7 has type ‘int *’ [-Wformat=]
     printf("p: %x\tp+1: %x\n*p: %x\t*p+1: %x\n**p: %d\nip: %x\n*ip: %d",p,p+1,*p,*p+1,**p,ip,*ip);
                                                            ~^
                                                            %ls
Compilation finished successfully.

Компилятор заканчивает, говоря, что компиляция прошла успешно. Это НЕ означает, что можно нормально запускать этот код и ожидать правильных результатов / вывода.

Когда вы исправляете код, чтобы он правильно компилировался, пожалуйста, отправьте РЕДАКТИРОВАТЬ на свой вопрос, который содержит измененный / исправленный код.

gcc    -ggdb -Wall -Wextra -Wconversion -pedantic -std=gnu11 -c "untitled.c"  (in directory: /home/richard/Documents/forum)
untitled.c: In function ‘main’:
untitled.c:6:15: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
     int *ip = a;
               ^
untitled.c:7:26: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long unsigned int’ [-Wformat=]
     printf("sizeof:\np: %d\n(*p+0): %d\n**p: %d\nip: %d\n*ip: %d\n",sizeof p, sizeof (*p+0), sizeof **p, sizeof ip, sizeof *ip);
                         ~^
                         %ld
untitled.c:7:38: warning: format ‘%d’ expects argument of type ‘int’, but argument 3 has type ‘long unsigned int’ [-Wformat=]
     printf("sizeof:\np: %d\n(*p+0): %d\n**p: %d\nip: %d\n*ip: %d\n",sizeof p, sizeof (*p+0), sizeof **p, sizeof ip, sizeof *ip);
                                     ~^
                                     %ld
untitled.c:7:47: warning: format ‘%d’ expects argument of type ‘int’, but argument 4 has type ‘long unsigned int’ [-Wformat=]
     printf("sizeof:\np: %d\n(*p+0): %d\n**p: %d\nip: %d\n*ip: %d\n",sizeof p, sizeof (*p+0), sizeof **p, sizeof ip, sizeof *ip);
                                              ~^
                                              %ld
untitled.c:7:55: warning: format ‘%d’ expects argument of type ‘int’, but argument 5 has type ‘long unsigned int’ [-Wformat=]
     printf("sizeof:\np: %d\n(*p+0): %d\n**p: %d\nip: %d\n*ip: %d\n",sizeof p, sizeof (*p+0), sizeof **p, sizeof ip, sizeof *ip);
                                                      ~^
                                                      %ld
untitled.c:7:64: warning: format ‘%d’ expects argument of type ‘int’, but argument 6 has type ‘long unsigned int’ [-Wformat=]
     printf("sizeof:\np: %d\n(*p+0): %d\n**p: %d\nip: %d\n*ip: %d\n",sizeof p, sizeof (*p+0), sizeof **p, sizeof ip, sizeof *ip);
                                                               ~^
                                                               %ld
untitled.c:9:17: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 2 has type ‘int (*)[3]’ [-Wformat=]
     printf("p: %x\tp+1: %x\n*p: %x\t*p+1: %x\n**p: %d\nip: %x\n*ip: %d",p,p+1,*p,*p+1,**p,ip,*ip);
                ~^
untitled.c:9:26: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 3 has type ‘int (*)[3]’ [-Wformat=]
     printf("p: %x\tp+1: %x\n*p: %x\t*p+1: %x\n**p: %d\nip: %x\n*ip: %d",p,p+1,*p,*p+1,**p,ip,*ip);
                         ~^                                                ~~~
untitled.c:9:34: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 4 has type ‘int *’ [-Wformat=]
     printf("p: %x\tp+1: %x\n*p: %x\t*p+1: %x\n**p: %d\nip: %x\n*ip: %d",p,p+1,*p,*p+1,**p,ip,*ip);
                                 ~^
                                 %ls
untitled.c:9:44: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 5 has type ‘int *’ [-Wformat=]
     printf("p: %x\tp+1: %x\n*p: %x\t*p+1: %x\n**p: %d\nip: %x\n*ip: %d",p,p+1,*p,*p+1,**p,ip,*ip);
                                           ~^                                     ~~~~
                                           %ls
untitled.c:9:61: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 7 has type ‘int *’ [-Wformat=]
     printf("p: %x\tp+1: %x\n*p: %x\t*p+1: %x\n**p: %d\nip: %x\n*ip: %d",p,p+1,*p,*p+1,**p,ip,*ip);
                                                            ~^
                                                            %ls
Compilation finished successfully.
0 голосов
/ 02 мая 2019

Здесь у вас есть нарушение ограничения, потому что вы используете несовместимые типы указателей при следующей инициализации:

int *ip = a;

Согласно стандартному разделу C11 на Деклараторы указателей

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

и Простое назначение / Ограничения

левый операнд имеет атомарный, квалифицированный или неквалифицированный тип указателя, и (учитывая тип, который левый операнд будет иметь после преобразования в lvalue) оба операнда являются указателями на квалифицированные или неквалифицированные версии совместимых типов, а тип указывает на left содержит все квалификаторы типа, на который указывает справа;

В этом случае компилятору требуется выдать диагностическое сообщение, которое он делает:

<source>:8:15: warning: initialization of 'int *' from incompatible pointer type 'int (*)[3]' [-Wincompatible-pointer-types]

     int *ip = a;

См. Демо

При компиляции программы рекомендуется включать все предупреждения и обрабатывать предупреждения как ошибки, чтобы избежать (насколько это возможно) такого поведения.

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