Вызов функции заставляет атрибут const структуры менять свое значение - PullRequest
0 голосов
/ 05 марта 2020

Я пишу эту простую библиотеку для работы с матрицами int:

#ifndef MATRIX_H_
#define MATRIX_H_

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>

typedef struct Matrix Matrix;

Matrix* newMatrix(const unsigned int rows, const unsigned int columns);

void setElementAt(Matrix* self, const unsigned int row, const unsigned int column, const int value);

int getElementAt(const Matrix* self, const unsigned int row, const unsigned int column);

int getRowsNo(const Matrix* self);

int getColumnsNo(const Matrix* self);

void initMatrix(Matrix* self, int value);

#endif
#include "Matrix.h"

struct Matrix {

    int* grid;
    const unsigned int rowsNo;
    const unsigned int columnsNo;
};

Matrix* newMatrix(const unsigned int rowsNo, const unsigned int columnsNo) {

    assert(rowsNo > 0 && columnsNo > 0);

    Matrix new = {

        .grid = malloc(rowsNo * columnsNo * sizeof(int)),
        .rowsNo = rowsNo,
        .columnsNo = columnsNo
    };

    Matrix* self = &new;

    return self;
}

int getRowsNo(const Matrix* self) {

    return self->rowsNo;
}

int getColumnsNo(const Matrix* self) {

    return self->columnsNo;
}

int getElementAt(const Matrix* self, const unsigned int row, const unsigned int column) {

    assert(row < self->rowsNo && column < self->columnsNo);

    return self->grid[row * self->rowsNo + column];
}

void setElementAt(Matrix* self, const unsigned int row, const unsigned int column, const int value) {

    assert(row < self->rowsNo && column < self->columnsNo);

    self->grid[row * self->rowsNo + column] = value;
}

void initMatrix(Matrix* self, int value) {

    for(int row = 0; row < self->rowsNo; row++) {

        for(int column = 0; column < self->columnsNo; column++) {

            setElementAt(self, row, column, value);
        }
    }
}

Проблема, с которой я сталкиваюсь, заключается в том, что каждый раз при вызове функций getElementAt() или setElementAt() поле columnsNo (и только это) в экземплярах struct Matrix изменяется на огромное случайное значение, несмотря на то, что оно помечено как const. Какую проблему я не вижу здесь?

Ответы [ 2 ]

4 голосов
/ 05 марта 2020

В newMatrix, вы возвращаете адрес локальной переменной new. Как только функция возвращает self, new выходит из области видимости и self никуда не указывает. Разыменование этого указателя вызывает неопределенное поведение . Вам нужно динамически распределять память, используя malloc или связанную функцию, чтобы она сохранялась за пределами newMatrix.

Это тег c ++, но точно такая же концепция применима к c: Можно ли получить доступ к памяти локальной переменной за пределами ее области действия?

Это один из способов решения этой проблемы

Matrix* newMatrix(const unsigned int rowsNo, const unsigned int columnsNo) {

    assert(rowsNo > 0 && columnsNo > 0);

    Matrix* new = malloc(sizeof *new);
    if (new != NULL)
    {
      new->grid = malloc(rowsNo * columnsNo * sizeof(int));
      if (new->grid == NULL) { /* handle error */ }
      new->rowsNo = rowsNo;
      new->columnsNo = columnsNo;
    }

    return new;
    // on return, the caller should check `newMatrix` returned a valid pointer.
}

Обратите внимание, что назначения new->rowsNo и new->columnsNo не будет работать, как показано выше, поскольку они const. Один из способов назначить их - использовать memcpy, как показано в верхнем ответе здесь: Как инициализировать константные элементы структур в куче

2 голосов
/ 05 марта 2020

Эта функция

Matrix* newMatrix(const unsigned int rowsNo, const unsigned int columnsNo) {

    assert(rowsNo > 0 && columnsNo > 0);

    Matrix new = {

        .grid = malloc(rowsNo * columnsNo * sizeof(int)),
        .rowsNo = rowsNo,
        .columnsNo = columnsNo
    };

    Matrix* self = &new;

    return self;
}

является причиной неопределенного поведения, поскольку функция возвращает указатель на локальный объект new с автоматическим c сроком хранения

    Matrix* self = &new;

    return self;

, который не будет живым после выхода из функции. Таким образом, возвращаемый указатель имеет недопустимое значение, которое не указывает на действительный объект.

На самом деле нет смысла возвращать из функции указатель. Вы можете просто вернуть созданный объект. Также элемент данных grid должен иметь нулевую инициализацию.

Функция может выглядеть следующим образом

Matrix  newMatrix(const unsigned int rowsNo, const unsigned int columnsNo) {

    assert(rowsNo > 0 && columnsNo > 0);

    Matrix m = {
        .grid = calloc( rowsNo * columnsNo, sizeof(int) ),
        .rowsNo = rowsNo,
        .columnsNo = columnsNo
    };

    return m;
}

А в вызывающей программе вы можете написать

Matrix m = newMatrix( rowsNo, columnsNo );

Функции getElementAt и setElementAt имеют ошибку.

Вместо выражения

row * self->rowsNo + column

Вы должны использовать

row * self->columnsNo + column

Также вам нужно написать функцию, которая освободит выделенную память для элемента данных grid.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...