«Нарушение доступа на запись» после использования malloc() внутри функции

#c #exception #multidimensional-array #struct #malloc

#c #исключение #многомерный массив #структура #malloc

Вопрос:

Проблема

У меня есть пользовательская структура для 2D-матриц. Я использую эту структуру внутри функции для инициализации 2D-матрицы, где каждому значению элемента присваивается значение 0. У меня также есть другая функция для печати матрицы на терминал (в целях отладки).

Когда я пишу структуру и функции внутри main.c , они работают. Проблема в том, что когда я помещаю их в отдельный файл и вызываю их из этого файла, я получаю ошибку времени выполнения: Exception thrown: write access violation .

В моей программе у меня есть 3 файла: main.c , my_lib.h , my_lib.c . Структура хранится внутри my_lib.h , а функция находится внутри my_lib.c . Внутри main.h

Я использую Windows 10 и кодирую в Visual Studio 2017 версии 15.9.10

Вывод

Программа компилируется, но выдает ошибку времени выполнения Exception thrown: write access violation
введите описание изображения здесь

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

Что ж, похоже, это была моя собственная ошибка в том, что это происходило.

На самом деле, я пытался запустить этот код на своем рабочем компьютере. Я написал исходный код на своем персональном компьютере, где работала версия main.c , my_lib.h amp; my_lib.c . Затем я скопировал папку, над которой работал, и попытался продолжить на своем рабочем компьютере. Оба моих компьютера работают под управлением ОС Windows 10, и оба имеют одинаковую версию VS 2017.

На моем персональном компьютере обозреватель решений был похож:

введите описание изображения здесь

Но на моем рабочем компьютере решение открылось как:

введите описание изображения здесь

Все, включая иерархию папок, одинаковы на обоих компьютерах. Похоже, копирование папки проекта — не очень хорошая идея.

Когда я создал новый проект C на своем рабочем компьютере и добавил my_lib.c и my_lib.h вручную, все заработало.

Но мне все еще любопытно, почему я получал ошибку исключения… И как я могу исправить эту проблему копирования без создания нового проекта в VS?

Код

Просто main.c ( работает)

main.c

 #include <stdio.h>

typedef struct Matrix {
    int rows; // number of rows
    int cols; // number of columns
    double** data; // a pointer to an array of n_rows pointers to rows
}Matrix;

Matrix* make_matrix(int n_rows, int n_cols);
void print_matrix(Matrix* m);

int main() {

    Matrix* m1 = make_matrix(2, 5);

    print_matrix(m1);

    return 0;
}


// CREATE A MATRIX WITH N_ROWS AND N_COLUMNS AND INITIALIZE EACH VALUE AS 0
Matrix* make_matrix(int n_rows, int n_cols) {
    Matrix* matrix = malloc(sizeof(Matrix));
    matrix->rows = n_rows;
    matrix->cols = n_cols;
    double** data = malloc(sizeof(double*) * n_rows);
    for (int x = 0; x < n_rows; x  ) {
        data[x] = calloc(n_cols, sizeof(double));
    }
    matrix->data = data;
    return matrix;
}

// PRINT GIVEN MATRIX TO COMMAND LINE
void print_matrix(Matrix* m) {
    for (int x = 0; x < m->rows; x  ) {
        for (int y = 0; y < m->cols; y  ) {
            printf("%f", m->data[x][y]);
            printf("|");
        }
        printf("n");
    }
}
  

main.c amp; function в отдельных файлах (выдает исключение)

main.c

 #include "my_lib.h"
int main(){
        // Create a 2 by 5 matrix amp; then print it to terminal
        Matrix* m1 = make_matrix(2, 5);
        print_matrix(m1);
        return 0;
}
  

my_lib.h

 #pragma once

// Our custom 2D matrix struct
typedef struct Matrix {
    int rows; // number of rows
    int cols; // number of columns
    double** data; // a pointer to an array of n_rows pointers to rows
}Matrix;

Matrix* make_matrix(int n_rows, int n_cols);
void print_matrix(Matrix* m);
  

my_lib.c

 #include "my_lib.h"
#include <stdio.h>

// CREATE A MATRIX WITH N_ROWS AND N_COLUMNS AND INITIALIZE EACH VALUE AS 0
Matrix* make_matrix(int n_rows, int n_cols) {
    Matrix* matrix = malloc(sizeof(Matrix));
    matrix->rows = n_rows;
    matrix->cols = n_cols;
    double** data = malloc(sizeof(double*) * n_rows);
    for (int x = 0; x < n_rows; x  ) {
        data[x] = calloc(n_cols, sizeof(double));
    }
    matrix->data = data;
    return matrix;
}

// PRINT GIVEN MATRIX TO COMMAND LINE
void print_matrix(Matrix* m) {
    for (int x = 0; x < m->rows; x  ) {
        for (int y = 0; y < m->cols; y  ) {
            printf("%f", m->data[x][y]);
            printf("|");
        }
        printf("n");
    }
}
  

Комментарии:

1. @xing все они находятся в одной папке

2. Не имеет значения, что все они находятся в одной папке. В тот момент, когда main () вызывает make_matrix(), он еще не знает определения make_matrix. Что произойдет, если вы добавите следующую строку в my_lib.h? Matrix* make_matrix(int n_rows, int n_cols);

3. Для верности вам также следует включить <stdlib.h> , чтобы получить правильные прототипы m/calloc .

4. Если вместо этого вы используете стандартный компилятор C, это даже не будет компилироваться. Но, эй, прошло всего 20 лет с тех пор, как был выпущен C99, возможно, нам следует дать MS больше времени для адаптации.

5. @OguzCanbek ты забыл #include <stdlib.h> . Вы не получали никаких предупреждений компилятора?

Ответ №1:

Причина, по которой вы получаете сбой, вообще не связана с тем фактом, что у вас есть один или два файла .c в вашем проекте, но это потому, что вы забыли включить <stdlib.h> в my_lib.c .

Это вызывает следующие предупреждения:

my_lib.c(8) : предупреждение C4013: ‘malloc’ не определено; предполагается, что extern возвращает int
my_lib.c(13): предупреждение C4013: ‘calloc’ не определено; предполагается, что extern возвращает int
my_lib.c(13): предупреждение C4047: ‘=’: ‘double *’ отличается по уровням косвенности от ‘int’
my_lib.c(8): предупреждение C4047: ‘ инициализация ‘: ‘Matrix *’ отличается по уровням косвенности от ‘int’
my_lib.c(11): предупреждение C4047: ‘инициализация’: ‘double **’ отличается по уровням косвенности от ‘int’

Вам это сойдет с рук при 32-разрядной сборке, потому что размер int совпадает с размером указателя.

С другой стороны, если вы создаете свою программу как 64-разрядную, предупреждения становятся действительно актуальными, потому что теперь указатели имеют ширину 64 бита, но поскольку компилятор предполагает, что malloc etc. возвращает int s (32-разрядные значения), все запутывается.

На самом деле эти предупреждения следует рассматривать как ошибки.

Здесь вы решаете, хотите ли вы 32-или 64-разрядную сборку:

введите описание изображения здесь

Добавьте #include <stdlib.h> здесь в my_lib.c :

 #include "my_lib.h"
#include <stdlib.h>   // <<<<<<<<<<<<<
#include <stdio.h>


// CREATE A MATRIX WITH N_ROWS AND N_COLUMNS AND INITIALIZE EACH VALUE AS 0
Matrix* make_matrix(int n_rows, int n_cols) {
  Matrix* matrix = malloc(sizeof(Matrix));
  ...
  

Комментарии:

1. Спасибо за ваш ответ и за подробное объяснение 32-разрядных и 64-разрядных программ 🙂 У меня также есть еще один вопрос новичка: должен ли я также включить <stdlib.h> в свой main.c или, поскольку я добавил его в my_lib.c , это не обязательно?

2. @OguzCanbek нет, в этом нет необходимости, если только вы не используете одну из функций, объявленных в stdlib.h в main.c , чего пока нет.