C语言分配存储字符串Arr结构体的三维张量实现方案
问题背景
我实现了一个可存储字符串的类结构(二维数组/矩阵),目前功能运行正常,现在需要存储多个这类矩阵,因此需要使用calloc分配三维张量内存,但我仅掌握二维矩阵的分配方法,不清楚三维张量的后续实现逻辑。
我实现的二维数组代码如下:
头文件arr.h代码:
#pragma once #include <stdlib.h> #include <stdint.h> /* uints */ #include <string.h> /* char */ /* struct that holds data about the matrix, so it can be resized when needed */ struct Arr { char** cArr; unsigned int currentSize, maxSize; }; /* initial allocation function that is used when a array needs te be calloced at first */ uint8_t initialAlloc(struct Arr* arr); /* reallocate the array to a hardcoded size */ uint8_t reallocArr(struct Arr* arr); /* add an element to the array in arr */ void addToArr(struct Arr* arr, char* element);
对应实现arr.c代码:
#include "arr.h" uint8_t initialAlloc(struct Arr* arr) { arr->currentSize = 0; arr->maxSize = 2; arr->cArr = calloc(2, sizeof(char*)); if (!arr->cArr) { return 0; } for (uint8_t x = 0; x < arr->maxSize; ++x) { arr->cArr[x] = calloc(600, sizeof(char)); if (!arr->cArr[x]) { return 0; } } return 1; } void addToArr(struct Arr* arr, char* element) { if (arr->currentSize == arr->maxSize) { if (!reallocArr(arr)) { /* if the memory allocation fails something is bad wrong */ exit(1); } } strncpy(arr->cArr[arr->currentSize], element, strlen(element)); ++arr->currentSize; } uint8_t reallocArr(struct Arr* arr) { uint16_t resizeTo = arr->maxSize * 2; arr->cArr = realloc(arr->cArr, resizeTo * sizeof(char*)); if (!arr->cArr) { return 0; } for (uint16_t x = arr->currentSize; x < resizeTo; ++x) { arr->cArr[x] = calloc(600, sizeof(char)); if (!arr->cArr[x]) { return 0; } } arr->maxSize = arr->maxSize * 2; return 1; }
我希望在三维张量中使用上述Arr结构体,已编写的张量头文件代码如下:
#pragma once #include <stdlib.h> /* basic functions */ #include <stdint.h> /* uint */ /* user includes */ #include "characterArr.h" /* user includes */ struct Tensor { char*** mArr; unsigned int currentSize, maxSize; }; uint8_t initialAllocTensor(struct Tensor* tensor); uint8_t reallocTensor(struct Tensor* tensor); void addToTensor(struct Tensor* tensor, struct Arr* arr);
张量初始分配的实现目前仅完成部分:
/* user includes */ #include "tensor.h" /* user includes */ /* initial allocation function that is used when a array needs te be calloced at first */ uint8_t initialAllocTensor(struct Tensor* tensor) { tensor->currentSize = 0; tensor->maxSize = 2; tensor->mArr = calloc(2, sizeof(struct *Arr)); if (!tensor->mArr) { return 0; } for (uint8_t x = 0; x < arr->maxSize; ++x) { arr->cArr[x] = calloc(600, sizeof(char)); if (!arr->cArr[x]) { return 0; } } return 1; }
核心疑问
在分配完用于存储Arr结构体指针的内存后,我就不清楚后续该如何实现了:我后续需要使用Arr结构体中的全部信息,完全没必要将已有Arr结构体中的数据重新拷贝到新创建的三维张量中。
- 我是否可以直接在Tensor中存储指向Arr结构体的指针来完成功能?
- 如果采用这种实现方式,我是否可以正常访问Arr结构体的内容?
- 由于相关内存是通过malloc/calloc类函数分配在堆上的,我是否不需要担心变量离开作用域后内存被回收的问题?
解答
方案修正
你当前的Tensor结构体定义和实现逻辑存在错误,不需要用char***类型存三维字符数组,直接存储Arr结构体指针是完全可行的,也是这类场景下最高效的方案,不需要做任何数据深拷贝。
首先修正Tensor结构体定义:
struct Tensor { // 直接存储Arr结构体指针的数组,不需要三级char指针 struct Arr** arrList; unsigned int currentSize, maxSize; };
问题答复
- 是否可以直接在Tensor中存储指向Arr结构体的指针完成功能?
完全可以。你只需要给Arr*类型的数组分配堆内存即可,存入的指针直接指向你已经初始化好的Arr实例,不需要额外拷贝内部字符串数据。 - 采用这种实现方式是否可以正常访问Arr结构体的内容?
可以正常访问。你通过数组下标拿到Arr*指针后,和普通指针用法完全一致,直接用->操作符就能访问cArr、currentSize、maxSize所有成员,和你之前操作单个Arr没有区别。 - 堆分配内存是否不需要担心作用域回收问题?
堆上分配的内存不会随栈变量离开作用域自动回收,只要你没有主动调用free释放对应内存,指针指向的内容就一直有效。但需要注意:如果你把栈上创建的Arr结构体的地址存进去,那栈变量离开作用域后地址会失效,必须保证存入Tensor的Arr*指向的也是堆上分配的实例,或者Arr实例的生命周期长于Tensor的生命周期。
修正后的参考实现
张量初始化函数
uint8_t initialAllocTensor(struct Tensor* tensor) { tensor->currentSize = 0; tensor->maxSize = 2; // 分配存储2个Arr*的内存,注意sizeof的参数是struct Arr* tensor->arrList = calloc(2, sizeof(struct Arr*)); if (!tensor->arrList) { return 0; } return 1; }
张量扩容函数
uint8_t reallocTensor(struct Tensor* tensor) { uint16_t resizeTo = tensor->maxSize * 2; struct Arr** newList = realloc(tensor->arrList, resizeTo * sizeof(struct Arr*)); if (!newList) { return 0; } tensor->arrList = newList; // 新扩容的位置初始化为NULL,避免野指针 for (uint16_t x = tensor->currentSize; x < resizeTo; ++x) { tensor->arrList[x] = NULL; } tensor->maxSize = resizeTo; return 1; }
向张量添加Arr的函数
void addToTensor(struct Tensor* tensor, struct Arr* arr) { if (tensor->currentSize == tensor->maxSize) { if (!reallocTensor(tensor)) { exit(1); } } // 直接存入指针,不需要拷贝数据 tensor->arrList[tensor->currentSize] = arr; ++tensor->currentSize; }
访问示例
// 读取第i个矩阵的第j个字符串 char* str = tensor.arrList[i]->cArr[j];
注意事项
- 你之前写的
initialAllocTensor实现里引用了未定义的arr变量,逻辑是错误的,Tensor初始化阶段不需要操作任何Arr实例的内部成员,只需要分配好存储指针的数组即可。 - 你原有
addToArr里的strncpy用法有问题,没有给字符串末尾补'\0',会导致字符串越界读取,建议改成strncpy(arr->cArr[arr->currentSize], element, 599); arr->cArr[arr->currentSize][599] = '\0';,因为你每个字符串块分配了600字节,最多存599个有效字符加结束符。 - 释放内存的时候需要先遍历释放每个
Arr实例内部的字符串数组和cArr本身,再释放Tensor的arrList,避免内存泄漏。 - 不要在初始化Tensor的时候提前给
Arr的内部字符串数组分配内存,等你实际创建Arr实例的时候用原来写的initialAlloc分配即可,Tensor只负责存指针。
内容的提问来源于stack exchange,提问作者CoN
相关产品推荐
相关产品推荐

