Ошибка сегментации C malloc с использованием одномерного массива

#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 вы получите нулевой указатель.