Почему код А работает нормально, а код Б выдает ошибку? Единственная разница — это printf. Также обратите внимание, что я обращаюсь к памяти за пределами диапазона. И GCC с этим согласен. В примере А я даже легко перезаписал ОЗУ.
Ошибка компилятора:
Fatal glibc error: malloc assertion failure in sysmalloc: (old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)
Мой код мира:
#include <stdio.h>
#include <stdlib.h>
int main()
{
#if 1
// A
int *items = malloc(10 * sizeof(int));
printf("allocated: %lu bytes\n", 10 * sizeof(int));
items = items;
items[0] = 5;
items[9] = 5;
items[10] = 5;
printf("[");
for (size_t i = 0; i <= 10; i++)
{
printf(" %d", items[i]);
}
printf(" ]\n");
#else
// B
int *items = malloc(10 * sizeof(int));
items = items;
items[0] = 5;
items[9] = 5;
items[10] = 5;
printf("[");
for (size_t i = 0; i <= 10; i++)
{
printf(" %d", items[i]);
}
printf(" ]\n");
#endif
return 0;
}
Я попробовал спросить в чатгпте, почему он работает, он говорит, что у меня недостаточно оперативной памяти, а это ерунда. Я ожидаю сегментированных ошибок в обоих примерах.
"Я пытался спросить в чатгпте, почему он работает, он говорит, что это потому, что у меня не хватает оперативной памяти, а это нонсенс.": Да, и хорошо, что вы это здесь заметили. Проблема с ChatGPT заключается в том, что он также может дать вам более убедительный ответ, который по-прежнему бессмысленен, и тогда, особенно тот, кто еще не является экспертом в этом вопросе, может ему поверить. Так что не используйте ChatGPT для чего-либо большего, чем получение указаний для исследования более надежных источников.
Кстати. как ты думаешь, чего items = items;
собирается достичь? Кроме того, спецификатор формата `%lu` неверен. Спецификатор формата для sizte_t
(тип sizeof(/*...*/)
— %zu
).
ок, понял, я думаю, что в обоих примерах есть ошибка сегментации.
этот код вырван из контекста, я забыл удалить эти строки.
@ user17732522 Тсс, никому этого не говори! Это часть естественного отбора: теперь некоторые жадные менеджеры увольняют половину своей команды, веря в шумиху вокруг ИИ ;)
Я не знаю, это работает только с lu, но спасибо, я буду использовать zu!
Ошибка сегментации возникает только в том случае, если виртуальный адрес, к которому вы пытаетесь получить доступ, не сопоставлен вашему процессу (или сопоставлен с неправильными разрешениями). Длина страницы обычно составляет 4 КБ, и ваше выделение malloc
находится где-то в ней. После него могут быть сопоставлены и другие страницы. Пока вы не выходите за пределы сопоставленных страниц, вы не получите ошибку сегментации. Какие страницы будут отображаться, зависит от того, что malloc
решит сделать. Никаких гарантий, так или иначе, нет. Если страницы сопоставлены, но находятся за пределами вашего распределения, вы просто перезаписываете все, что там есть.
Этот вопрос похож на: Насколько опасно получать доступ к массиву за пределами его границ?. Если вы считаете, что это другое, отредактируйте вопрос, поясните, чем он отличается и/или как ответы на этот вопрос не помогают решить вашу проблему.
Я ожидаю сегментированных ошибок в обоих примерах.
Ничто в стандарте C не говорит о том, что реализация C должна вызывать нарушение доступа к памяти при переполнении выделенной памяти. Ни один надлежащий учебник или учебник не делает этого. В некоторых учебных материалах может упоминаться, что может произойти ошибка сегментации, но это не факт.
Как правило, невозможно гарантировать нарушение доступа к памяти, поскольку аппаратное обеспечение обычно обрабатывает память блоками, называемыми страницами, обычно по 4096 или 8192 байта, но malloc
выделяет память меньшими блоками, часто блоками по 16 байт. (Даже если вы запрашиваете меньшую сумму или сумму, которая не кратна 16 байтам, malloc
все равно может выполнять свою работу блоками по 16 байт.) Итак, если вы выделите 40 байтов для десяти int
, нет никакой возможности чтобы пометить память сразу после этих 40 байт как недоступную. Аппаратное обеспечение предоставляет страницу размером 4096 байт, и оно не может сказать, что к 40 из этих байт можно получить доступ, а к остальным — нет.
Стандартные процедуры управления памятью C не указаны для предотвращения некорректного доступа. Язык C обычно не предназначен для предотвращения неправильных действий вашей программы. Это поможет вам писать программы с низкими накладными расходами. В частности, это позволяет избежать накладных расходов на проверку всего, что делает ваша программа. Ваша задача как программиста — обеспечить корректность вашей программы.
Что касается разницы между двумя вашими последовательностями кода, то одна из них, переполнив массив, случайно изменила данные, которые процедуры управления памятью используют для ведения записей. Когда они это обнаружили, они сообщили об ошибке. Разница, вероятно, в том, что в памяти две кодовые последовательности располагались по-разному.
«Невозможно пометить память сразу после этих 40 байт как недоступную». -- точно есть (электрический забор). Просто нет практического способа сделать это.
Запись за пределами вашего распределения приводит к неопределенному поведению. Должно быть объяснено на первых страницах любого введения в использование динамической памяти в C. Компилятор никоим образом не обязан следить за тем, чтобы ваша программа работала разумным образом. Чего вы ожидаете вместо этого и почему?