取消引用返回 malloc 指针然后通过 & 符号传递变量地址的函数是否正确?

问题描述 投票:0回答:2

所以我在这里写了这个函数来学习我最近发现的一种有趣的分配二维数组的方法:

//############################## single array matrix
struct sam *alloc_sam (struct sam *matrix, int row, int col) {
  matrix -> single_arr_mtrx = malloc (row * col * sizeof(int) + row * sizeof(int*));
  printf ("matrix at: %p\n", (void*) (matrix -> single_arr_mtrx));
  matrix -> ptr = (int*) (matrix -> single_arr_mtrx + row);

  if (!matrix -> single_arr_mtrx) {
    printf ("can't allocate single array matrix\n");
    exit(1);
  } else {
    printf ("Allocated chunk of %ld bytes(single array matrix).\n", row * col * sizeof (int) + row * sizeof (int*));
  }

  matrix -> row = row;
  matrix -> col = col;

  for (int i = 0; i < row; ++i) {
    matrix -> single_arr_mtrx[i] = matrix -> ptr + col * i;
    printf ("Addr %d: %p\n", i, (void*) &(matrix -> single_arr_mtrx[i]));
  }

  return matrix;
}

我这样使用它:

void run_sam (void) {
  struct sam matrix;
  matrix = *alloc_sam (&matrix, ROW, COL);

  fill_sam (&matrix);
  print_sam (&matrix);

  dealloc_sam (&matrix);
}

简单地取消引用 alloc_sam 函数的返回值是否正确? 我通过 valgrind 运行我的代码,它没有发现任何泄漏,但仍然......

此外,是否可以使用与 calloc 函数相同的分配方法? 我觉得这很棘手,因为语句中有两个不同的 sizeof:

malloc (row * col * sizeof(int) + row * sizeof(int*))

c pointers stack dynamic-memory-allocation
2个回答
1
投票

如果我理解正确,您正在尝试分配一块内存,然后可以将其用作二维矩阵,而无需明确说明它是二维的。

如果是这样的话,你传递给

malloc
的东西太多了。你想找回一个块,它有足够的空间来容纳矩阵的所有成员,这是
row * col
数量,每个都是
sizeof(int)
大;所以就这样通过吧。

... = malloc(row * col * sizeof(int));

现在介绍如何访问它。 假设

row
是3,
col
是2。你会得到这样的东西:

你想细分成这样:

这意味着,从头开始,对于你想跳过的每一“行”,你必须增加我的那个数字乘以行的宽度。在更简单的情况下,当您有一个与“矩阵”元素具有相同大小的类型的指针时(即存储

int *
s的
int
),行的宽度是列数:

mtrx[2 * col] // Accessing third row (row n. 2)

在某些情况下情况并非如此,例如当您有一个

unsigned char *
实际上指向更大的事物时。
unsigned char
是 1 个字节大,因此您必须显式乘以要跳过的字节数:

mtrx[2 * col * sizeof(Thing)] // Accessing third row (row n. 2)

col * sizeof(Thing)
通常称为pitchstride

要像在一维数组中一样到达所需的实际单元格:通过添加该单元格的列号/字节偏移量:

mtrx[2 * col + 1] mtrx[2 * col * sizeof(Thing) + 1 * sizeof(Thing)]

现在,回答您的实际问题:您从该 alloc 函数返回的指针与您传入的指针完全相同。所以是的,它完全有效,但也无用。即使您忽略了返回值,该结构仍将被初始化,因为该函数在完全相同的内存位置运行。

关于

calloc
:是的,很奇怪,我也不喜欢。但是不管你传递了多少
sizeof
s,最后它们只是数字。如果遇到问题,阅读文档


1
投票

代码中存在一些问题:

    如果
  • malloc(row * col * sizeof(int) + row * sizeof(int *));

    导致算术溢出,

    row * col
    可能会失败,因为
    row
    col
    的类型为
    int
    。将
    row
    col
    的类型更改为
    size_t
    是一种可能,但更一般地说,您应该写:

    matrix->single_arr_mtrx = malloc(sizeof(int) * row * col +
                                     sizeof(int *) * row);
    
  • 大小参数有点令人困惑,因为行指针存储在

    int
    数组之前的内存中。这样写会更容易理解:

    matrix->single_arr_mtrx = malloc(sizeof(int *) * row +
                                     sizeof(int) * row * col);
    

    或更通用的:

    matrix->single_arr_mtrx = malloc(sizeof(*matrix->single_arr_mtrx) * row +
                                     sizeof(**matrix->single_arr_mtrx) * row * col);
    
  • 此方法假设

    int
    的对齐要求不大于
    int *
    的对齐要求,这很可能是正确的,但在一些古老的奇异系统上并非如此,例如经典的Cray One

  • alloc_sam
    如果分配失败而不是总是返回矩阵指针,则可能会返回错误。

  • 请注意,

    matrix->ptr
    是多余的,因为它始终与
    matrix->single_arr_mtrx[0]
    相同,并且仅在初始化循环中使用。同样,
    single_arr_mtrx
    这个名字不必要地复杂,我会推荐
    data
    rowdata
    代替。

用法示例令人困惑:

matrix = *alloc_sam(&matrix, ROW, COL);
特别令人不安,但由于
alloc_sam
总是返回它的第一个参数,赋值只是将
matrix
复制到自身上。这是没用的。只需编写
alloc_sam(&matrix, ROW, COL);
传递结构的地址进行初始化。

这是一个替代方案:

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

struct sam {
    int rows, cols;
    int **data;
    int *ptr;
};

int alloc_sam(struct sam *matrix, int rows, int cols) {
    matrix->rows = rows;
    matrix->cols = cols;
    matrix->data = malloc(sizeof(matrix->data[0]) * rows +
                          sizeof(matrix->data[0][0]) * rows * cols);
    if (matrix->data == NULL) {
        matrix->rows = 0;
        matrix->cols = 0;
        matrix->ptr = NULL;
        return -1;
    }
    int *ptr = (int *)(matrix->data + rows);
    matrix->ptr = ptr;
    for (int i = 0; i < rows; i++, ptr += cols) {
        matrix->data[i] = ptr;
    }
    return 0;
}

void fill_sam(struct sam *matrix) {
    int value = 0;
    for (int row = 0; row < matrix->rows; row++) {
        for (int col = 0; col < matrix->cols; col++) {
            matrix->data[row][col] = value++;
        }
    }
}

void print_sam(struct sam *matrix) {
    for (int row = 0; row < matrix->rows; row++) {
        for (int col = 0; col < matrix->cols; col++) {
            printf("%d%c", matrix->data[row][col],
                   "\t\n"[col != matrix->cols - 1]);
        }
    }
}

void dealloc_sam(struct sam *matrix) {
    free(matrix->data);
    matrix->rows = 0;
    matrix->cols = 0;
    matrix->data = NULL;
    matrix->ptr = NULL;
}

void run_sam(void) {
    struct sam matrix[1];
    if (alloc_sam(matrix, ROWS, COLS)) {
        // handle allocation error
    }
    printf("matrix row pointers: %p\n", (void *)matrix->data);
    printf("matrix 2D array: %p\n", (void *)matrix->ptr);

    fill_sam(matrix);
    print_sam(matrix);
    dealloc_sam(matrix);
}
© www.soinside.com 2019 - 2024. All rights reserved.