Разыменовывать локальный массив в C

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

Я обычно передаю указатель на указатель (например, char ** pvar) когда мне нужно вернуть указатель, так что я вижу, как бессмысленно передавать его обратно в локальный массив с разыменованными ссылками, поскольку вы не можете изменить положение переменной.

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

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

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

void ptrptr(uint32_t** dptr)
{
    printf("%x, %x\n",  dptr, *dptr);
}

void oneptr(uint32_t* ptr)
{
    printf("%08x, %x\t",  ptr, *ptr);
    ptrptr(&ptr);
}

int main()
{
    uint32_t array[] = {1};
    uint32_t *ptr = calloc(1, sizeof( uint32_t));
    ptr[0] = 3;
    oneptr(ptr);    /* OK, passes an (uint32_t *)  */
    oneptr(array);  /* OK, passes an (uint32_t *)  */
    ptrptr(&ptr);   /* OK, passes an (uint32_t **) */
    ptrptr(&array); /* ??, passes an (uint32_t *)  */
    return 0;
}

Компиляция это дает мне предупреждение

cc     test.c   -o test
test.c: In function ‘main’:
test.c:24:9: warning: passing argument 1 of ‘ptrptr’ from incompatible pointer type [-Wincompatible-pointer-types]
  ptrptr(&array);
     ^
test.c:5:6: note: expected ‘uint32_t ** {aka unsigned int **}’ but argument is of type ‘uint32_t (*)[1] {aka unsigned int (*)[1]}’
 void ptrptr(uint32_t** dptr)
      ^~~~~~
0061a008, 3     7ebfa144, 61a008
7ebfa154, 1     7ebfa144, 7ebfa154
7ebfa150, 61a008
7ebfa154, 1

Я получаю тот же результат, когда использую gcc, clang и cl для его компиляции, поэтому я вполне уверен, что это не ошибка компилятора. Тогда возникает вопрос, почему C молча передает указатель (uint32_t*) вместо указателя на указатель (uint32_t**) когда я пытаюсь разыменовать массив?

2 ответа

Решение

Тогда возникает вопрос, почему C молча передает указатель (uint32_t*) вместо указателя на указатель (uint32_t**) когда я пытаюсь разыменовать массив?

  • Это не так.

  • C передает указатель на массив из одного uint32_t (uint32_t(*)[1]).

  • Это указатель на массив из одного uint32_t, потому что это был массив из одного uint32_t, и вы получили указатель на него.

  • Это не тихо. Вы получаете предупреждение компилятора, говорящее "эй, это неправильный тип указателя!". Что ты думаешь это?

    test.c: In function ‘main’:
    test.c:24:9: warning: passing argument 1 of ‘ptrptr’ from incompatible pointer type [-Wincompatible-pointer-types]
      ptrptr(&array);
         ^
    test.c:5:6: note: expected ‘uint32_t ** {aka unsigned int **}’ but argument is of type ‘uint32_t (*)[1] {aka unsigned int (*)[1]}’
     void ptrptr(uint32_t** dptr)
    
  • Вы не разыменовываете массив. Вы делаете указатель на массив, преобразовываете его в неправильный тип указателя и разыменовываете его.

  • Причина, по которой вы получаете это число 1, заключается в том, что указатель на массив фактически указывает на тот же адрес, что и указатель на первую вещь в массиве. Тем не менее, это другой тип указателя, который означает, что такие вещи, как ++ работать по-другому, но затем вы конвертируете его в указатель того же типа, чтобы ваш код не заметил.

Тогда возникает вопрос: почему C молча передает указатель ( uint32_t*) вместо указателя на указатель (uint32_t**), когда я пытаюсь разыменовать массив?

Он не молчит, он дал вам предупреждение. Стандарт C не упоминает термины "ошибки" и "предупреждения", но говорит о диагностических сообщениях. Чтобы следовать стандарту C, достаточно, чтобы компилятор показал диагностическое сообщение программисту.

Если вы хотите получить сообщение об ошибке вместо предупреждения о стандартных нарушениях C с помощью gcc или clang, вы должны скомпилировать -std=c11 -pedantic-errors,

Что касается того, почему код не является правильным, &array дает адрес массива в виде указателя массива, uint32_t(*)[1], Этот тип не совместим с uint32_t**, Что произойдет, если вы запустите программу, несмотря на то, что она содержит стандартное нарушение ограничения C, не указано: это неопределенное поведение. Нет гарантий ошибки сегмента или сбоя, это только два из многих возможных результатов неопределенного поведения.

Другие вопросы по тегам