如何解决在C中为不同的数据类型分配内存的正确方法是什么? ImageData结构 Record结构示例 像素工厂功能程序逻辑输出示例示例代码
我正在将图像读取到动态分配的数组中,图像像素类型可以是以下任何一种类型:
typedef enum {
Byte = 1,//unsigned char
Int32 = 2,//int
Float32 = 3,//float
Float64 = 4//double
}DataType;
我正在考虑使用switch
块在void
指针上分配内存:
int NumPix = GetimageSize(Image);
DataType theDataType = GetimageDataType(Image);
void *data;
switch (theDataType)
{
case Byte:
data = (unsigned char *)malloc(NumPix * sizeof(unsigned char));
break;
case Int32:
data = (int *)malloc(NumPix * sizeof(int));
break;
case Float32:
data = (float *)malloc(NumPix * sizeof(float));
break;
case Float64 :
data = (double*)malloc(NumPix * sizeof(double));
break;
}
// do something with data
free(data);
这样合法吗?还有另一种方法可以用更少的代码和更通用的方法来做到这一点?
解决方法
您将需要在许多地方知道像素的大小。因此,定义一个函数来计算像素大小,并在每次需要时使用它。
size_t pixel_size(DataType type) {
switch (type) {
case Byte: return sizeof(unsigned char);
case Int32: return sizeof(unsigned int); // Shouldn't this be uint32_t?
case Float32: return sizeof(float);
case Float64: return sizeof(double);
}
}
// Allocate an image with indeterminate content.
// Return NULL if the allocation fails.
void *allocate_indeterminate_image(DataType type,size_t x,size_t y) {
size_t pixel_count = x * y; // overflow checking omitted
size_t byte_count = pixel_count * pixel_size(type); // overflow checking omitted;
return malloc(byte_count);
}
// Allocate an image with all-bits-zero. On almost every platform,// this means that each pixel has the value 0 (+0.0 for floats).
// Return NULL if the allocation fails.
void *allocate_blank_image(DataType type,size_t y) {
size_t pixel_count = x * y; // overflow checking omitted
size_t bytes_per_pixel = pixel_size(type);
return calloc(pixel_count,bytes_per_pixel);
}
,
我以某种方式在这里用于其他用途,对您的程序进行了一些更改,并将其与测试数据一起留在此处作为可能对您的案例有用的示例
ImageData结构
typedef struct
{
uint32_t _limit;
uint32_t _imgC; // argc
Record** _imgV; // argv
} ImageData;
-
ImageData
是一个动态结构,容量为_limit
Record
个数据结构。 -
imgC
是正在使用的实际记录数 -
Record**
是一个指向Record
结构的指针数组的指针,该结构的可变长度为1、2、4或8字节像素数据。这样,它可以同时保存任意数量的任何大小的像素数据 - 这与
argc
中的argv
/main()
对完全相同
Record
结构
typedef void Data;
typedef struct
{
uint8_t _id; // 1,2,4 or 8
uint32_t _NumPix; // argc
Data* _data; // argv
} Record; // one record of image data
-
_id
是像素数据长度 -
_NumPix
是显示的像素总数 - 这里的情况与您程序中的有所不同:像素数据分配在这里,并且是
_id * _NumPix
字节的区域,像素数据按顺序排列,就像在我的位图构建程序中一样,使用了数据;) - 此处的像素数据按
_id
组打包,并根据需要解包,只使用void*
指针。
示例
我使用了随机数据和一个“工厂”功能,该功能可以构建并返回1--2-4-或8-字节随机图像
像素工厂功能
Record* getImage()
{
const uint8_t len[4] = { 1,4,8 };
Record* one = (Record*)malloc(sizeof(Record));
one->_id = len[rand() % 4];
one->_NumPix = 1 + rand() % 8;
// now builds the pixels with random values under 255
// here the actual size of EACH record is computed
uint32_t total = one->_NumPix * one->_id;
void* p = (Data**)malloc(total);
uint8_t value = rand() % 255;
// here you get the actual pixel data
memset(p,value,total);
one->_data = (Data**)p;
return one;
};
程序逻辑
- 开头的3个常量定义运行:
#define _BLOCK_SIZE_ 50
#define _IMAGES_ 5
#define _SEED_ 201001
-
图像存储空间以
_BLOCK_SIZE_
Record*
指针的块分配。在数据采集阶段结束时,将数组修剪为确切使用的大小。-
_IMAGES_
是要构建的图像数,所有图像将在构建ImageData数组时显示。 -
_SEED
是随机数据的种子
-
-
创建数组时,最终未使用的数据为
free()
,因此ImageData
仅具有与图像数据相对应的记录。 -
该程序然后显示一些随机记录,这次是由
ImageData
数组构成的,因此我们可以将其与构建阶段中显示的数据进行比较。
输出示例
DumpImage(Sample 0): 8 8-pixels images [64 bytes]:
0: EC EC EC EC EC EC EC EC
1: EC EC EC EC EC EC EC EC
2: EC EC EC EC EC EC EC EC
3: EC EC EC EC EC EC EC EC
4: EC EC EC EC EC EC EC EC
5: EC EC EC EC EC EC EC EC
6: EC EC EC EC EC EC EC EC
7: EC EC EC EC EC EC EC EC
DumpImage(Sample 1): 4 2-pixels images [8 bytes]:
0: 02 02
1: 02 02
2: 02 02
3: 02 02
DumpImage(Sample 2): 3 2-pixels images [6 bytes]:
0: DC DC
1: DC DC
2: DC DC
DumpImage(Sample 3): 5 2-pixels images [10 bytes]:
0: 75 75
1: 75 75
2: 75 75
3: 75 75
4: 75 75
DumpImage(Sample 4): 7 4-pixels images [28 bytes]:
0: 54 54 54 54
1: 54 54 54 54
2: 54 54 54 54
3: 54 54 54 54
4: 54 54 54 54
5: 54 54 54 54
6: 54 54 54 54
5 Test images loaded
now shows 2 random images from list
DumpImage(From Array: image 3): 5 2-pixels images [10 bytes]:
0: 75 75
1: 75 75
2: 75 75
3: 75 75
4: 75 75
DumpImage(From Array: image 0): 8 8-pixels images [64 bytes]:
0: EC EC EC EC EC EC EC EC
1: EC EC EC EC EC EC EC EC
2: EC EC EC EC EC EC EC EC
3: EC EC EC EC EC EC EC EC
4: EC EC EC EC EC EC EC EC
5: EC EC EC EC EC EC EC EC
6: EC EC EC EC EC EC EC EC
7: EC EC EC EC EC EC EC EC
50 pointers allocated
5 arguments read
45 pointers to free
Block size trimmed for a total of 5 pointers
Image array is ready for use
示例程序到此结束。我没有移植内存的分配,这是微不足道的,并且因为我对此一无所知,所以不使用数据写任何东西:)
我也没有做太多测试。而且请不要在这里进行宗教对话:是的,我在结构域的开头使用了下划线。我只使用了现在在此处运行的Microsoft编译器CL 16.7.4。
我将这里的数字仅设置为5条记录,因此我可以发布所有输出数据。但是由于内存中只存储了数据,而元数据仅需几个字就可以对成千上万的记录运行。而且速度很快。
对于大型数据集,最好将 BLOCK_SIZE 更改为更大的数字
示例代码
#define _BLOCK_SIZE_ 50
#define _IMAGES_ 5
#define _SEED_ 201001
#include <math.h>
#include <memory.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
typedef void Data;
typedef struct
{
uint8_t _id; // 1,4 or 8
uint32_t _NumPix; // argc
Data* _data; // argv
} Record; // one record of image data
typedef struct
{
uint32_t _limit;
uint32_t _imgC; // argc
Record** _imgV; // argv
} ImageData;
int dumpImage(const char*,Record*);
Record* getImage();
int main(void)
{
srand(_SEED_);
char message[30];
ImageData image;
image._imgC = 0;
image._limit = _BLOCK_SIZE_;
image._imgV = (Record**)malloc( image._limit * sizeof(Record*) );
Record* sandbox;
for (int i = 0; i < _IMAGES_; i += 1)
{
// gets an image and a Record to hold it
sprintf(message,"Sample %d",i);
sandbox = getImage(); // gets a random image
dumpImage(message,sandbox);
Record* target = (Record*)malloc(sizeof(Record)); // new one
target->_id = sandbox->_id;
target->_NumPix = sandbox->_NumPix;
uint32_t total = sandbox->_NumPix * sandbox->_id;
target->_data = (void*)malloc(total);
memcpy(target->_data,sandbox->_data,total);
image._imgV[image._imgC] = target;
// expands the block if there is no space left
if (image._imgC >= image._limit)
{ // block is full
image._limit += _BLOCK_SIZE_;
char* new_block = realloc(image._imgV,(image._limit * sizeof(char*)));
printf("Block extended for a total of %d pointers\n",image._limit);
image._imgV = (Record**)new_block;
}; // if()
image._imgC += 1;
}; // for
printf("\n\n%d Test images loaded\n",image._imgC);
printf("now shows %d random images from list\n\n\n",_IMAGES_ / 2 );
for (int i = 0; i < _IMAGES_ / 2; i += 1)
{
int j = rand() % _IMAGES_;
sprintf(message,"From Array: image %d",j);
sandbox = getImage(); // gets a random image
dumpImage(message,image._imgV[j]);
}; // for()
// now trims the end of the block
// allocated: _limit
// used: argc
printf("\t%d pointers allocated\n",image._limit);
printf("\t%d arguments read\n",image._imgC);
if (image._limit == image._imgC)
printf("\tNothing to free()\n");
else
{
printf("\t%d pointers to free\n",image._limit - image._imgC);
char* new_size = realloc(image._imgV,(image._imgC * sizeof(char*)));
printf("\tBlock size trimmed for a total of %d pointers\n",image._imgC);
image._imgV = (Record**)new_size;
};
printf("\tImage array is ready for use\n");
return 0;
}; // main()
int dumpImage(const char* msg,Record* img)
{
/*
uint8_t _id; // len
uint32_t _NumPix; // argc
Data** _data; // argv
*/
uint32_t total = img->_NumPix * img->_id;
uint8_t* p = (void*)img->_data;
printf("DumpImage(%s): %d %d-pixels images [%d bytes]:\n\n",msg,img->_NumPix,img->_id,total);
for (uint32_t i = 0; i < img->_NumPix; i += 1)
{
printf("%6d: ",i);
for (int j = 0; j < img->_id; j++)
printf("%02X ",*p);
printf("\n");
}; // for()
printf("\n");
return total;
};
//
// Image factory: build random "images"
// id is 1 to 4,NumPix is 1 to 100,// data is NumPix Pixels with id bytes
//
/*
uint8_t _id; // len
uint32_t _NumPix; // argc
Data** _data; // argv
*/
Record* getImage()
{
const uint8_t len[4] = { 1,8 };
Record* one = (Record*)malloc(sizeof(Record));
one->_id = len[rand() % 4];
one->_NumPix = 1 + rand() % 8;
// now builds the pixels with random values under 255
uint32_t total = one->_NumPix * one->_id;
void* p = (Data**)malloc(total);
uint8_t value = rand() % 255;
memset(p,total);
one->_data = (Data**)p;
return one;
};
,
这样合法吗?
好的。顺便说一句。您可以仅提取大小,并具有一个仅根据数据类型返回sizeof
的函数,然后执行malloc(NumPix * data_get_size_of_object(DataType))
。 Real world example - a big function that returns sizeof(type)
in a big switch。
还有另一种方法可以用更少的代码和更通用的方法来做到这一点?
好吧,不要带有“更少的代码”。
您最终会得到很多我称之为“ fat 开关”的信息。您将拥有的每个功能将是一个很大的switch(type) { case sometype: do_something_with_that_type((cast_to_type*)pointer); case someothertype: etc. }
,它将变成1000行长的不可读的开关。它不会很灵活。添加新功能将很慢。维护会很慢。因为您每次必须考虑每种情况,所以会出现错误。颠倒思路-不是函数应该知道如何处理类型, type 本身应该知道操作。因此,术语面向对象编程。创建一个 interface 并创建实现该接口的 objects -这样,对象可以跟踪发生了什么。 Real life example - struct file_operations
from linux kernel。
我最近的经历使我相信函数指针会带来一些恐惧。功能指针可用于选择一次执行什么操作,然后根据先前的选择执行不同的操作。一个开关,其中分配了功能指针,然后仅跳转了一个先前分配的功能。无需一次又一次地选择。带有虚拟表和接口的某些设计可能如下所示:
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <errno.h>
typedef struct dataif_s dataif_t;
struct dataif_vtable_s {
void (*free)(dataif_t *t); // destructor
int (*read_data)(dataif_t *t,FILE *from); // read data from file
int (*save)(dataif_t *t,FILE *to); // serialize data to file
int (*fprint)(dataif_t *t,FILE *to); // print in human readable format
int (*add)(dataif_t *t,int a); // add integer to each number
};
struct dataif_s {
// a pointer to virtual table that implements operations
const struct dataif_vtable_s *vtable;
// a pointer to provide object's data
void *priv;
};
// shortcut accessors
int dataif_read_data(dataif_t *t,FILE *from) {
// add assert(t != NULL); etc. to ease bugs detection
// or maybe handle 'if(t->vtable->read_data != NULL) return -ENOSYS;' ?
return t->vtable->read_data(t,from);
}
int dataif_print(dataif_t *t) {
return t->vtable->fprint(t,stdout);
}
void dataif_free(dataif_t *t) {
t->vtable->free(t);
}
// etc. for each function
/* ------------------------------------------ */
// private data implementing unsigned char operations
struct bytedata_s {
size_t count;
unsigned char *data;
};
int bytedata_read_data(dataif_t *t,FILE *from);
void bytedata_free(dataif_t *t);
// the virutal table
// note it's static const - it's stored in .rodata,saves RAM memory
static const struct dataif_vtable_s bytedata_vtable = {
.free = bytedata_free,.read_data = bytedata_read_data,/* etc. fill with custom function pointers */
};
// Constructor for data of unsigned chars
int bytedata_init(dataif_t *t,size_t NumPix) {
// construct bytedata object
struct bytedata_s *p = malloc(sizeof(*p));
if (!p) goto ERR_p;
p->data = malloc(NumPix * sizeof(*p->data));
if (!p->data) goto ERR_data;
p->count = NumPix;
// create the interface
t->priv = p;
t->vtable = &bytedata_vtable;
return 0;
free(p->data);
ERR_data:
free(p);
ERR_p:
return -ENOMEM;
}
// reading values
int bytedata_read_data(dataif_t *t,FILE *from) {
struct bytedata_s *p = t->priv; // extract out object
for (size_t i = 0; i < p->count; ++i) {
if (fscanf(from,"%hhu",&p->data[i]) != 1) return -1;
}
return 0;
}
void bytedata_free(dataif_t *t) {
// free data allocated in constructor
struct bytedata_s *p = t->priv;
free(p->data);
free(t->priv);
}
/* ---------------------------------------------- */
// create same interfaces for each type
// note - only one single function is visible externally
// all other functions are accessible via virtual table
int int32data_init(dataif_t *t,size_t NumPix);
int float32data_init(dataif_t *t,size_t NumPix);
int float64data_init(dataif_t *t,size_t NumPix);
/* ---------------------------------------------- */
// map value types to constructors,ie. "object factory"
typedef enum {
DATATYPE_BYTE,//unsigned char
DATATYPE_INT32,//int
DATATYPE_FLOAT32,//float
DATATYPE_FLOAT64,//double
} datatype_t; // I will not use UpperCamelCase
// map of datatypes to constructors
static const int (*dataif_inits[])(dataif_t *t,size_t NumPix) = {
[DATATYPE_BYTE] = bytedata_init,[DATATYPE_INT32] = int32data_init,[DATATYPE_FLOAT32] = float32data_init,[DATATYPE_float64] = float64data_init,};
int dataif_init(dataif_t *t,datatype_t datatype,size_t NumPix) {
if (datatype < 0 || datatype > sizeof(dataif_inits)/sizeof(*dataif_inits)) {
// the datatype not found in the array
return -EINVAL;
}
// note how the switch... doesn't exists anymore.
return dataif_inits[datatype](t,NumPix);
}
# if 0
// if enum values doesn't start from zero
// use a structure and a for to find the mapping between an enum to constructor
struct datatype_to_init_s {
datatype_t t;
int (*init)(dataif_t *t,size_t NumPix);
};
static const struct datatype_to_init_s datatype_to_init[] = {
{ DATATYPE_BYTE,bytedata_init },/* etc. */
};
int dataif_init(dataif_t *t,size_t NumPix) {
// find datatype in datatype_to_init
for (size_t i = 0; i < sizeof(datatype_to_init)/sizeof(*datatype_to_init); ++i) {
if (datatype_to_init[i].t == datatype) {
// found it? create the object
return datatype_to_init[i].init(NumPix);
}
}
// the datatype not found in the array
return -EINVAL;
}
# endif
/* ---------------------------------------------- */
size_t image_get_size(const char *);
datatype_t image_get_datatype(const char *);
static const char *image = "/tmp/a.png";
int main() {
int err = 0;
size_t numpix = image_get_size(image);
datatype_t thedatatype = image_get_datatype(image);
FILE *some_file = fopen(image,"r");
if (some_file == NULL) { err = -1; goto ERR_fopen; }
dataif_t data;
err = dataif_init(&data,thedatatype,numpix); // initializes data depending on datatype
if (err) goto ERR_dataif_init; // add a friendly error message
err = dataif_read_data(&data,some_file);
if (err) goto ERR_read_data;
dataif_do_something(&data);
dataif_print(&data);
ERR_read_data:
dataif_free(&data);
ERR_dataif_init:
fclose(some_file);
ERR_fopen:
return err;
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。