#c #segmentation-fault #malloc
#c #ошибка сегментации #malloc
Вопрос:
Я использую malloc для создания массива в C. Но я получил ошибку сегментации, когда попытался присвоить массиву случайные значения в 2 цикла.
Ошибка сегментации отсутствует, когда я присваиваю значения этому массиву в 1 цикле. Размер массива велик. Пожалуйста, посмотрите код, который я прикрепил. Любой может дать мне подсказку, что здесь происходит. Я довольно новичок в C. Заранее большое спасибо.
int n=50000;
float *x = malloc(n*n*sizeof(float));
// there is segmentation fault:
int i, j;
for (i=0; i<n; i ){
for (j=0; j<n; j ){
x[i*n j] = random() / (float)RAND_MAX;
}
}
// there is no segmentation fault:
int ii;
for (ii=0; ii<n*n; ii ){
x[ii] = random() / (float)RAND_MAX;
}
Ответ №1:
int
переполнение.
50000 * 50000
—> 2,500,000,000 -> более INT_MAX
-> неопределенное поведение (UB).
Во-первых, давайте убедимся, что вычисление размера этого выделения возможно
assert(SIZE__MAX/n/n/sizeof(float) >= 1);
Затем с помощью verified wide enough size_t
используйте size_t
математику для выполнения умножения и используйте size_t
математику для вычисления индекса массива. Вместо этого int*int*size_t
сделайте size_t*int*int
.
// float *x = malloc(n*n*sizeof(float));
// Uses at least `size_t` math by leading the multiplication with that type.
float *x = malloc(sizeof(float) * n*n);
// or better
float *x = malloc(sizeof *x * n*n);
for (i=0; i<n; i ){
for (j=0; j<n; j ){
x[(size_t)n*i j] = random() / (float)RAND_MAX;
}
}
2-й цикл не не «сбой», поскольку n*n
это не такое большое значение, как ожидалось, но, вероятно, то же самое значение UB в распределении.
Комментарии:
1. Спасибо! Я многому научился. Проблема решена, когда я использую size_t для вычисления индекса x [(size_t)n * i j]. В итоге я определяю n как size_t в начале.
Ответ №2:
Во-первых, вы вызываете неопределенное поведение из-за переполнения целого числа со знаком. Предполагая, что an int
является 32-разрядным, значение 50000 * 50000 превышает диапазон an int
, вызывая переполнение.
Вы можете исправить это, поставив sizeof(float)
на первое место в выражении. Результатом sizeof
является size_t
, которое не имеет знака и по меньшей мере такого же размера, как int
. Затем, когда каждый из них n
умножается, он сначала преобразуется в size_t
, таким образом избегая переполнения.
float *x = malloc(sizeof(float)*n*n);
Однако, даже если вы исправите это, вы запрашиваете слишком много памяти.
Предполагая, что sizeof(float)
равно 4 байтам, n*n*sizeof(float)
это около 10 ГБ памяти. Если вы проверите возвращаемое значение malloc
, вы, вероятно, увидите, что оно возвращает NULL.
Вам нужно будет сделать ваш массив намного меньше. Вместо этого попробуйте n=1000
, что займет всего около 4 МБ.
Ответ №3:
Я полагаю, что проблема связана с переполнением целых чисел:
50 000 * 50 000 = 2,5 миллиарда
2 ^ 31 ~ 2,1 миллиарда
Таким образом, вы вызываете неопределенное поведение при вычислении индекса массива. Что касается того, почему это работает для одного, но не для другого, так оно и есть. Неопределенное поведение означает, что компилятор (и компьютер) может делать все, что захочет, включая выполнение того, что вы ожидаете, и сбой программы.
Чтобы исправить, измените типы i, j, n и ii из int на long long. Это должно решить проблему переполнения и ошибку сегментации.
Редактировать:
Вы также должны проверить, что malloc возвращает действительный указатель, прежде чем выполнять операции с указателем. В случае сбоя malloc вы получите нулевой указатель.