Ошибка сегментации C malloc с использованием одномерного массива - PullRequest
2 голосов
/ 29 марта 2019

Я использую 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;
}

Ответы [ 3 ]

5 голосов
/ 29 марта 2019

int переполнение.

50000 * 50000 -> 2 500 000 000 -> более чем INT_MAX -> неопределенное поведение (UB).

Во-первых, давайте определимсявозможно вычисление размера этого распределения

assert(SIZE__MAX/n/n/sizeof(float) >= 1);

Затем с проверенным достаточно широким size_t, используйте size_t math для умножения и используйте size_t math для вычисления индекса массива.Вместо 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 враспределение.

3 голосов
/ 29 марта 2019

Во-первых, вы вызываете неопределенное поведение из-за целочисленного переполнения со знаком. Предполагая, что int является 32-разрядным, значение 50000 * 50000 выходит за пределы диапазона 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 МБ.

1 голос
/ 29 марта 2019

Я полагаю, что проблема связана с целочисленным переполнением:

50000 * 50000 = 2,5 млрд.

2 ^ 31 ~ 2,1 млрд.

Таким образом, вы вызываете неопределенное поведениепри расчете индекса массива.Что касается того, почему это работает для одного, а не для другого, это просто так.Неопределенное поведение означает, что компилятор (и компьютер) могут делать все, что он хочет, в том числе делать то, что вы ожидаете, и вылетать программы.

Чтобы исправить, измените типы i, j, n и ii на long long с int,Это должно решить проблему переполнения и ошибки сегментации.

Редактировать:

Вы также должны проверить, что malloc возвращает действительный указатель, прежде чем выполнять операции с указателем.В случае сбоя malloc вы получите нулевой указатель.

...