Разыменовывать локальный массив в 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, не указано: это неопределенное поведение. Нет гарантий ошибки сегмента или сбоя, это только два из многих возможных результатов неопределенного поведения.