В C, почему я могу видеть значение, записанное после конца массива в другой переменной?
Автор вопроса: boraseoksoonОтветов: 4
В свободное время я проводил некоторые тесты, забавные вещи и реализовывал различные вещи, такие как простой алгоритм, структура данных для моей личной радости в C в эти дни....
Но, в конце концов, я узнал кое-что интересное для себя. Я не знаю, почему этот результат происходит до сих пор.
max_arr_count_index присваивается в зависимости от значения arr[5], которое находится за концом массива +1.
Есть ли кто-нибудь, кто может мне это объяснить? Я знаю, что этого не должно быть. Я присвоил значение последнему индексу массива (здесь arr[5] = 30 в проблемном случае), и это небезопасно, и это неопределенное поведение, как определено стандартом.
Я не собираюсь делать то же самое в реальном поле, но я просто хочу получить больше информации здесь.
LLVM и GCC дали мне тот же результат.
Код и результат ниже:
[Случай без проблем: я не присваиваю значение после конца индекса]
#include <stdio.h>
int arr[] = {11,33,55,77,88};
int max_arr_count_index = (sizeof(arr) / sizeof(arr[0]));
// print all
void print_all_arr(int* arr)
{
// just print all arr datas regarding index.
for(int i = 0; i < max_arr_count_index; i++) {
printf("arr[%d] = %d \n", i, arr[i]);
}
}
int main(int argc, const char * argv[]) {
// insert code here...
printf("[before]max_arr_count_index : %d\n", max_arr_count_index);
printf("[before]The original array elements are :\n");
print_all_arr(arr);
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
arr[3] = 4;
arr[4] = 5;
// arr[5] = 1000;
printf("[after]max_arr_count_index : %d\n", max_arr_count_index);
printf("[after]The array elements after :\n");
print_all_arr(arr);
return 0;
}
Результат без проблем приведен ниже:
[before]max_arr_count_index : 5
[before]The original array elements are :
arr[0] = 11
arr[1] = 33
arr[2] = 55
arr[3] = 77
arr[4] = 88
[after]max_arr_count_index : 5
[after]The array elements after :
arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[3] = 4
arr[4] = 5
Program ended with exit code: 0
[Проблемный случай: я присвоил значение за концом индекса]
#include <stdio.h>
int arr[] = {11,33,55,77,88};
int max_arr_count_index = (sizeof(arr) / sizeof(arr[0]));
// print all
void print_all_arr(int* arr)
{
// just print all arr datas regarding index.
for(int i = 0; i < max_arr_count_index; i++) {
printf("arr[%d] = %d \n", i, arr[i]);
}
}
int main(int argc, const char * argv[]) {
// insert code here...
printf("[before]max_arr_count_index : %d\n", max_arr_count_index);
printf("[before]The original array elements are :\n");
print_all_arr(arr);
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
arr[3] = 4;
arr[4] = 5;
/* Point is this one.
If I assign arr[5] 30, then, max_arr_count_index is changed also as
30. if I assign arr[5] 10000 max_arr_count_index is assigned 10000.
*/
arr[5] = 30;
/* Point is this one.
If I assign arr[5] 30, then, max_arr_count_index is changed also as
30. if I assign arr[5] 10000 max_arr_count_index is assigned 10000.
*/
printf("[after]max_arr_count_index : %d\n", max_arr_count_index);
printf("[after]The array elements after arr[5] is assigned 30 :\n");
print_all_arr(arr);
return 0;
}
Таким образом, очевидно, что с точки зрения стандарта C это неопределенное поведение, и компилятор может выпусти демонов из своего носа, и все будет в порядке.
Но вы хотите пойти глубже, так как вы просите "под капотом", поэтому нам, по сути, придется искать вывод ассемблера. Отрывок (созданный с участием gcc -g test test.c и objdump -S --disassemble test):
Как видите, даже на этом уровне дизассемблер уже знает, что вы фактически устанавливаете max_arr_count_index. Но почему?
Это потому, что структура памяти, созданная GCC, просто такая (и мы использовали -g с gcc, чтобы встроить отладочную информацию, чтобы дизассемблер мог знать, какая ячейка памяти является каким полем). У вас есть глобальный массив из пяти целых чисел и глобальная переменная типа int, объявленные сразу друг за другом. Глобальная переменная int просто находится сразу за массивом в памяти. Таким образом, доступ к целому числу сразу за концом массива дает max_arr_count_index.
Помните, что доступ к элементу i массива arr, например. ints (по крайней мере, на всех известных мне архитектурах) просто обращается к ячейке памяти arr+sizeof(int)*i, где arr - адрес первого элемента.
Как уже было сказано, это поведение undefined. GCC также может упорядочить глобальную переменную int перед массивом, что приведет к различным эффектам, возможно, даже к завершению программы при попытке доступа к arr[5], если в этом месте нет действительной страницы памяти.
Ответ 2 · рейтинг 7 · haccks
Доступ к массиву за пределами вызывает неопределенное поведение. Ничего хорошего в этом случае ждать не приходится. Размер arr равен 5. Вы можете получить доступ к arr с arr[0] по arr[4].
Если на мгновение отвлечься от УБ, объяснение поведения
/* Point is this one.
If I assign arr[5] 30, then, max_arr_count_index is changed also as
30. if I assign arr[5] 10000 max_arr_count_index is assigned 10000.
*/
может быть переменная max_arr_count_index объявлена сразу после массива arr. Компилятор может выделить память для max_arr_count_index сразу за последним элементом массива arr. Например, если arr[4] находится в 0x100, то память для max_arr_count_index выделяется в 0x104. Таким образом, за массивом arr находится адрес 0x104. Поскольку &arr[5] - это тот же адрес, что и max_arr_count_index, присвоение значения arr[5] запишет это значение на адрес max_arr_count_index. Обратите внимание, что это не совсем то, что происходит. Это интуиция для такого поведения. Как только есть UB, все ставки сняты.
Ответ 3 · рейтинг 3 · Gabriel
Вы объявили max_arr_count_index после arr, поэтому max_arr_count_index, вероятно, находится в самом следующем адресе относительно arr, поэтому присвоение arr+5 является присвоением max_arr_count_index адресу.
Ответ 4 · рейтинг 3 · Bathsheba
Ответы на вопросы о том, почему неопределенное поведение работает определенным образом, являются оксюмороном и рецептом для отрицательного голосования. Но на том основании, что я уже достиг предела репутации на сегодня и поэтому значительно спокойнее отношусь к этому, вот мое послание:
arr[i] оценивается как *(arr + i). Очевидно, что между arr и max_arr_count_index, вероятно, нет заполнения, поэтому arr + 5, вероятно, равно &max_arr_count_index. Но арифметика указателей допустима только в массивах. Вы можете установить указатель на единицу за концом массива, но поведение при разыменовывании будет неопределенным.
Конечно, в другой день компилятор может съесть вашу кошку.