微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在C中为不同的数据类型分配内存的正确方法是什么? ImageData结构 Record结构示例 像素工厂功能程序逻辑输出示例示例代码

如何解决在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 举报,一经查实,本站将立刻删除。