Почему C программирует cra sh при разыменовании указателя на массив? - PullRequest
2 голосов
/ 27 января 2020

У меня есть простой C код:

int main()
{
    int test[10] = {1,2,3,4,5,6,7,8,9,10};
    int **ptr = &test;

    printf("%d\n", (*ptr)[0]);

    return 0;
}

Как только он достигает строки printf, происходит сбой:

Process returned -1073741819 (0xC0000005)   execution time : 0.865 s

При компиляции кода в Ubuntu , это дает мне предупреждение:

test.c:8:17: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
     int **ptr = &test;

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

int main()
{
    int *test = malloc(sizeof(int)*10);
    for (int i = 0; i < 10; i++) {
        test[i]=i;
    }
    int **ptr = &test;

    printf("%d\n", (*ptr)[1]);

    return 0;
}

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

1 Ответ

2 голосов
/ 27 января 2020

Чтобы ваша переменная ptr была "адресом массива целых чисел", вам действительно нужно более тонкое (и загадочное) объявление.

Это будет работать:

int (*ptr)[10] = &test;

, поскольку он объявляет, что при разыменовании (то есть когда вы на самом деле используете выражение *ptr), это будет массив (из 10 элементов).

In в некотором смысле, структура объявления немного похожа на структуру для указателей на функции .

EDIT: во втором случае test - это простой старый указатель (с значение, присвоенное ему вызовом malloc); как таковая, она сама является переменной , адрес которой может быть взят (как ваша строка int **ptr = &test; делает - правильно).

Однако в первом случае (массив 'fixed') test относится к блоку памяти; во многих отношениях это может быть , используемый в качестве указателя на первый элемент (как, например, при вызове функции). Но какое значение компилятор может присвоить «адресу адреса первого элемента»? Это то, что в данном случае не выполняется при попытке присвоения с использованием &test.

Но вы, согласно предыдущему абзацу, имеете право спросить, как компилятор определяет значение для &test (для присвоить ptr) с кодом, указанным в этом ответе? Хорошо, если вы добавите следующую строку в вашу программу:

printf("%p %p\n", test, &test);

вы увидите, что test и &test (вы можете изменить &test на ptr - вывод будет таким же ) имеют точно такое же значение ! Таким образом, используя это «тайное» объявление, вы даете компилятору достаточно информации, чтобы знать, что делать с «указателем массива» - по сути, он как бы «игнорирует» (или обходит) первый уровень разыменования, заканчивающийся адресом первого элемента массива (как есть).

...