двойной указатель на постоянный указатель - PullRequest
0 голосов
/ 06 марта 2020

эй, может кто-нибудь скажет мне, почему в этом примере появится значение 11 11, если я правильно понимаю это, p в главном блоке является постоянным указателем на целое число, что означает, что я не могу изменить значение его адреса, почему может сделать это в fuction foo через

 #include <stdio.h>

int j = 11;
void foo(int **);

int main()
{
    int i = 10;
    int *const p = &i;
    foo(&p);
    printf("%d\n", *p);
}
void foo(int **p)
{
    *p = &j;
    printf("%d\n", **p);
}

Ответы [ 4 ]

1 голос
/ 06 марта 2020

Большинство компиляторов, включая clang и g cc, будут реагировать на ваш фрагмент с предупреждением, которое выглядит примерно так.

Передача аргумента const int * в качестве int * отбрасывает квалификаторы

Это означает, что компиляторы видят, что вы нарушаете постоянство указатель. Несмотря на жалобы, компиляторы попытаются компенсировать это. Компилятор будет игнорировать модификатор const в вашем объявлении p. эффективный тип p во время выполнения будет изменяемым int *.

Это поведение не является частью какого-либо стандарта C. Строго говоря, const - это как обещание, которое вы даете компилятору, что вы не будете что-то изменять. Это обещание допускает определенную оптимизацию в машинном коде, позволяя компилятору предполагать, что определенные значения никогда не изменятся. Когда вы коварно нарушаете обещание const через неявное преобразование типа приведение , более наивный (но все же технически совместимый) компилятор может создать машинный код, который сталкивается с ошибками сегментации или другими проблемами, потому что вы предаете предположение о постоянстве.

Современные компиляторы всегда проверяют, являются ли const вещи действительно постоянными. Если это не так, то компиляторы «отбрасывают квалификаторы», то есть они делают вывод, что вы на самом деле не хотели сказать const. Большинство компиляторов также делают вывод, что вы действительно имели в виду const, если никогда не меняете значение переменной, независимо от того, объявили ли вы ее константу.

Дальнейшее чтение

1 голос
/ 06 марта 2020

У вас есть неопределенное поведение .

Сначала ваша функция foo пытается изменить main переменную p, которая является константой . Если бы вы предоставили правильное объявление функции / прототип foo перед вызовом, компилятор мог бы перехватить это и предупредить вас о передаче несовместимых типов.

Более того в foo у вас есть присвоение

*p = &j;

С этим вы заставляете *p указывать на локальную переменную j, переменную, срок жизни которой заканчивается, когда заканчивается функция foo делая указатель недействительным при разыменовании его в функции main.

0 голосов
/ 06 марта 2020

Программа имеет неопределенное поведение.

Для начала функция f должна быть объявлена ​​перед ее вызовом в main.

В этом случае компилятор выдаст ошибку о том, что квалификатор const аргумента был отброшен.

Изменение константного объекта (в данном случае постоянного указателя) через указатель приводит к неопределенному поведению.

Вторая причина неопределенного поведения заключается в том, что переменная j является локальной переменной функции f. Таким образом, после выхода из функции переменная больше не является активной, и указатель p имеет недопустимое значение.

Если вы напишите функцию правильно, объявив ее перед вызовом в main, например,

#include <stdio.h>

void f( int *const *p )
{
    int j = 11;

    *p = &j;

    printf( "**p = %d\n", **p );
}

int main(void) 
{
    int i = 10;

    int * const p = &i;

    f( &p );

    printf( "*p = %d\n", *p );

    return 0;
}

вы получите сообщение об ошибке, подобное следующему

prog.c: In function ‘f’:
prog.c:7:5: error: assignment of read-only location ‘*p’
  *p = &j;
     ^
0 голосов
/ 06 марта 2020

При передаче p в foo квалификатор const вашего указателя отбрасывается, потому что параметр функции имеет тип int **

При включенных надлежащих предупреждениях ваш компилятор должен выдать предупреждение:

передача аргумента 1 из 'foo' отбрасывает квалификатор 'const' из целевого типа указателя [-Wdiscarded-qualifiers]

Так что вы должны изменить foo на: void foo (int * const * p), тогда ваш компилятор прервется с ошибкой

назначение местоположения только для чтения

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

...