странное поведение malloc в C - PullRequest
       14

странное поведение malloc в C

2 голосов
/ 12 октября 2009

Я пытаюсь создать матрицу с динамическими пропорциями и инициализировать ее Вот код, который я использую для выделения памяти и инициализации:

int **matrix;
//mem allocation
matrix=(int*)malloc(sizeof(int*)*mat_w);
for (i=0;i<mat_w;i++)
    matrix[i]=(int)malloc(sizeof(int)*mat_h);
//init
for (i=0;i<mat_w;i++)
    for (j=0;j<mat_h;j++)
        matrix[i][j]=0;

Это работает нормально, вопрос в том, что если я пытаюсь создать матрицу типа short - я получаю ошибку сегментации при первом проходе init.

Это проблема языка C или я что-то не так делаю?

Код для матрицы типа short:

short **matrix;
//mem allocation
matrix=(short*)malloc(sizeof(short*)*mat_w);
for (i=0;i<mat_w;i++)
    matrix[i]=(short)malloc(sizeof(short)*mat_h);
//init
for (i=0;i<mat_w;i++)
    for (j=0;j<mat_h;j++)
        matrix[i][j]=0;

P.S .: Я убрал проверки безопасности, индексные переменные и объявления границ для ясности кода.

Спасибо,
Alex

Ответы [ 6 ]

17 голосов
/ 12 октября 2009

Ваши приведения для возвращаемого значения malloc() недействительны. Они должны быть int** и int* в первом случае и short** и short* во втором.

Когда вы приводите возвращаемое значение от malloc() к short, возвращаемый указатель усекается до значения short, а затем присваивается указателю short*, давая значение указателя, указывающее на недопустимый место в памяти. Поэтому вы получаете ошибку сегментации, пытаясь получить к ней доступ.

С int вам повезло, поскольку на вашей платформе, скорее всего, sizeof(int)==sizeof(int*), так что указатель, возвращаемый malloc() с приведенным значением int, не усекается и все работает тихо. Скорее всего, это произойдет сбой аналогичным образом на 64-разрядной платформе.

Должно быть:

short **matrix;
matrix=(short**)malloc(sizeof(short*)*mat_w);
for (i=0;i<mat_w;i++)
    matrix[i]=(short*)malloc(sizeof(short)*mat_h); 
for (i=0;i<mat_w;i++)
    for (j=0;j<mat_h;j++)
        matrix[i][j]=0;

Если ваш код - чистый C (не C ++), вы можете пропустить приведение, как в C приведение от void* к любому другому типу указателя допустимо.

short **matrix;
matrix = malloc(sizeof(short*)*mat_w);
for (i=0;i<mat_w;i++)
    matrix[i] = malloc(sizeof(short)*mat_h); 
for (i=0;i<mat_w;i++)
    for (j=0;j<mat_h;j++)
        matrix[i][j]=0;
15 голосов
/ 12 октября 2009

Какой компилятор вы используете, чтобы он не кричал вам обо всех этих очевидных ошибках?

gcc -Wall выдал пять предупреждений с этим кодом.

#include <stdlib.h>

int main ()
{
    int mat_w = 99;
    int mat_h = 666;
    int i;
    int j;

    int **imatrix;
    short **smatrix;
    //mem allocation
    imatrix=(int*)malloc(sizeof(int*)*mat_w);
    for (i=0;i<mat_w;i++)
    imatrix[i]=(int)malloc(sizeof(int)*mat_h);
    //init
    for (i=0;i<mat_w;i++)
    for (j=0;j<mat_h;j++)
        imatrix[i][j]=0;

    //mem allocation
    smatrix=(short*)malloc(sizeof(short*)*mat_w);
    for (i=0;i<mat_w;i++)
    smatrix[i]=(short)malloc(sizeof(short)*mat_h);
    //init
    for (i=0;i<mat_w;i++)
    for (j=0;j<mat_h;j++)
        smatrix[i][j]=0;
    return 0;
}

дает мне

malloc.c: In function 'main':
malloc.c:13: warning: assignment from incompatible pointer type
malloc.c:15: warning: assignment makes pointer from integer without a cast
malloc.c:22: warning: assignment from incompatible pointer type
malloc.c:24: warning: cast from pointer to integer of different size
malloc.c:24: warning: assignment makes pointer from integer without a cast
6 голосов
/ 12 октября 2009

Есть серьезный урок, который вы должны извлечь из этой ошибки. И он говорит следующее: никогда не приводит результат 'malloc' .

Более того, это часть более широкого руководства по добросовестной практике, которое лучше всего соблюдать при любой возможности: никогда не упоминайте имена типов в вашем коде, за исключением объявлений .

Вот так должен выглядеть ваш код с самого начала

  int **matrix;

  matrix = malloc(mat_w * sizeof *matrix);
  for (i = 0; i < mat_w; i++)
    matrix[i] = malloc(mat_h * sizeof *matrix[i]);

  for (i = 0; i < mat_w; i++)
    for (j = 0; j < mat_h; j++)
      matrix[i][j] = 0;

Обратите внимание, что для переключения с 'int' на 'short' в этой версии вам просто нужно изменить объявление 'matrix' и ничего больше.

(Конечно, в этом коде можно улучшить еще кое-что, но я просто хотел устранить непосредственную причину ошибки.)

5 голосов
/ 12 октября 2009

Вы приводите int** к int* возвращаемому значению malloc (то же самое для краткости). malloc следует использовать так:

matrix = (int**)malloc(sizeof(int*) * mat_w);

или

matrix = (short**)malloc(sizeof(short*) * mat_w);

То же самое для каждого распределения внутри матрицы:

matrix[i] = (int*)malloc(sizeof(int) * mat_h);

или

matrix[i] = (short*)malloc(sizeof(short) * mat_h);
2 голосов
/ 12 октября 2009

Да, вы делаете что-то не так.

 int *matrix;

означает, что matrix - это массив целых чисел. Если вы хотите, чтобы это был массив массивов целых чисел, вы должны объявить это так:

 int **matrix;
 //mem allocation
 matrix=(int**)malloc(sizeof(int*)*mat_w);
 for (i=0; i<mat_w; i++)
     matrix[i]=(int*)malloc(sizeof(int)*mat_h);
 //init
 for (i=0; i<mat_w; i++)
     for (j=0; j<mat_h; j++)
         matrix[i][j]=0; 

Конечно, если вы заранее знаете размеры матрицы, просто сделайте это так:

int matrix[mat_w][mat_h];
 //init
 for (i=0; i<mat_w; i++)
     for (j=0; j<mat_h; j++)
         matrix[i][j]=0; 
0 голосов
/ 12 октября 2009

sizeof(int) соответствует ширине шины конкретной системы. Вы пытаетесь поместить 32-битное (или 64-разрядное, в зависимости от вашей платформы) значение адреса в 16-битную выделенную память.

Посмотрите второй пример в сообщении «Шашки». Это правильный и предпочтительный способ выделения памяти.

...