简体   繁体   English

纯ANSI-C:制作通用数组

[英]Pure ANSI-C: make generic array

Is it possible to replicate an generic array in pure ANSI-C? 是否可以在纯ANSI-C中复制通用数组?

I have this struct which holds an array (for floats at the moment) and some variables like size and capacity for mutation in the array. 我有这个结构,它包含一个数组(目前用于浮点数)和一些变量,如数组中的变异大小和容量。

typedef struct _CustomArray
{
    float* array; //the array in which the objects will be stored
    int size; //the current size of the array
    int capacity; //the max capacity of the array
} CustomArray; 

I use this struct so I can make an array in pure C where I can add/remove items, dynamically expand the array size when needed etc. all the things a "standard" array does, except it is made in C only. 我使用这个结构,所以我可以在纯C中创建一个数组,我可以在其中添加/删除项目,在需要时动态扩展数组大小等所有“标准”数组所做的事情,除了它只用C制作。 And now I want to make this so that when you initialize this struct you can set the datatype of the elements it should hold, at this moment it's only capable of storing float datatypes, but I want to make it so that it can store any datatype/other structs. 现在我想这样做,这样当你初始化这个结构时,你可以设置它应该保存的元素的数据类型,此时它只能存储浮点数据类型,但我想使它能够存储任何数据类型/其他结构。 But I don't know if this is even possible. 但我不知道这是否可行。

At this moment the function to make this array is: 此时制作此数组的函数是:

CustomArray* CustomArray_Create(int initCapacity, /*type elementType*/)
{
    CustomArray* customArray_ptr; //create pointer to point at the structure
    float* internalArray = (float*)malloc(sizeof(float) * initCapacity); //create the internal array that holds the items
    if(internalArray != NULL)
    {
        CustomArray customArray = { internalArray, 0, initCapacity }; //make the struct with the data
        customArray_ptr = &customArray; //get the adress of the structure and assign it to the pointer
        return customArray_ptr; //return the pointer
    }
    return NULL;
}

Is it possible to give a datatype as parameter so I can malloc memory for that datatype and cast it as that given datatype in an array dynamically? 是否可以将数据类型作为参数提供,以便我可以为该数据类型的malloc内存并将其作为动态数组中的给定数据类型进行转换?

Thanks in advance, 提前致谢,

Marnix van Rijswijk Marnix van Rijswijk

Your code has a serious problem... you're returning the address of a local variable (CustomArray) and when the function returns that variable is destroyed so you cannot keep using it with the pointer. 你的代码有一个严重的问题...你正在返回局部变量的地址(CustomArray),当函数返回时,该变量被销毁,所以你不能继续使用它与指针。 You have to malloc also that structure so that the memory will be still available once the function returns. 你还必须使用malloc结构,以便在函数返回后内存仍然可用。

About making the type a parameter you can get somewhat close using macros... for example with something like: 关于使类型成为参数,您可以使用宏来稍微接近...例如:

#include <stdlib.h> 
#define DefArray(type) \
typedef struct T_##type##Array {\
    type *array; \
    int size, capacity; \
} type##Array; \
static type##Array *type##ArrayCreate(int capacity)\
{\
    type##Array *s = malloc(sizeof(type##Array));\
    if (!s) return NULL;\
    s->array = malloc(sizeof(type) * capacity);\
    if (!s->array) { free(s); return NULL; }\
    s->size=0; s->capacity = capacity;\
    return s;\
}

Then you can use it this way 然后你就可以这样使用它

#include "customarray.h"
DefArray(float);
DefArray(double);

void foo()
{
    floatArray *fa = floatArrayCreate(100);
    ...
}

Note that you've to use macros to define all your custom functions. 请注意,您必须使用宏来定义所有自定义函数。 Note also that this approach will duplicate the code in each module (I'd say not a big issue but if you can't use C++ probably your target platform is pretty small). 另请注意,此方法将复制每个模块中的代码(我说不是一个大问题,但如果您不能使用C ++,则可能您的目标平台非常小)。 With a slightly more complex approach you could generate separate .h file and .c files for the implementation. 使用稍微复杂的方法,您可以为实现生成单独的.h文件和.c文件。

Boy, this really sounds like a job for C++. 男孩,这听起来像是C ++的工作。

I think the closest you could come to this in C is to not pass the type, but rather the size (sizeof(type)). 我认为你在C中最接近这个是不通过类型,而是大小(sizeof(type))。

You could make your function more generic so that it can do what it needs to do if all it knows is the size of each item in the array. 您可以使您的函数更通用,以便它可以执行它需要做的事情,如果它只知道数组中每个项目的大小。 This is how functions like bsearch() work. 这就是像bsearch()这样的函数的工作方式。

One way of achieving this is to use so-called X-macros . 实现此目的的一种方法是使用所谓的X宏

Here is a (probably buggy) generic vector implementation using this technique. 是使用这种技术的(可能是错误的)通用矢量实现。

It is then used as 然后用它作为

// defining generic parameters
#define PREFIX tv
#define ITEM token
#define NAME token_vector
#include "vector.h"

...
token_vector tv = tv_new(100);
*(tv.front) = some_token;
tv_push_back(&tv, other_token);

I messed around with generic programming in C a few years ago, just for the heck of it. 几年前,我在C语言中乱用通用编程,只是为了它。

Basically, I ended up exploiting the preprocessor. 基本上,我最终利用了预处理器。 I guess I was mildly successfull: I did accomplish some macro notation for several of the most important generic data structures. 我想我有点成功:我确实为几个最重要的通用数据结构完成了一些宏表示法。

What I definitely DIDN'T accomplish (in any automatic way at least) was recursively running the macros - ie, creating an array-of-arrays or array-of-hashes etc. That's due to the interesting cough crazy cough semantics of C preprocessor macros. 我绝对没有完成的任务(至少以任何自动方式)是递归运行宏 - 即创建数组数组或哈希数组等。这是由于C预处理器有趣的咳嗽疯狂咳嗽语义宏。

If you're interested, here's the code: https://github.com/christianfriedl/CGenerics/blob/master/src/cgArray.h 如果您有兴趣,请输入以下代码: https//github.com/christianfriedl/CGenerics/blob/master/src/cgArray.h

So, there's this notion of an "effective type" for objects with no declared type of their own. 因此,对于没有自己声明类型的对象,存在“有效类型”的概念。 (as it shakes out, those pretty much consist only of "the other end of an *alloc pointer" and a couple of weird union rules) Basically, the "effective type" of such an object is whatever you last used to assign to it, not counting times that was char or char[] because reasons. (因为它震动了,那几乎只包括“* alloc指针的另一端”和一些奇怪的联合规则)基本上,这种对象的“有效类型”是你上次用来赋予它的任何东西,不计算charchar[]因为原因。

One interesting interaction there has to do with the rules for declaring structure types. 一个有趣的交互与声明结构类型的规则有关。 Namely, that you can be freely re-declare the same tag name (or lack of tag name), and each declaration introduces a totally new type (past names are shadowed out, but objects with the old types don't get reinterpreted). 也就是说,您可以自由地重新声明相同的标记名称(或缺少标记名称),并且每个声明都引入了一个全新的类型(过去的名称被遮蔽,但旧类型的对象不会被重新解释)。

So you can do something like this: 所以你可以这样做:

# define DECL_VECTOR(NAME,TYPE,SIZE) PUN_STRUCT(NAME,TYPE,SIZE)  INIT_STRUCT(NAME,TYPE,SIZE) 

# define PUN_SIZE sizeof(void*)+sizeof(int)*2


# define PUN_STRUCT(NAME,TYPE,SIZE)                      \
   struct {                                              \
      TYPE (*p)[(SIZE)];                                 \
      int size;                                          \
      int capacity;                                      \
   } *NAME = malloc(PUN_SIZE);                        


# define INIT_STRUCT(NAME,TYPE,SIZE)  do {               \
   if (!NAME) {                                          \
        perror("malloc fail");                           \
        abort();                                         \
   }else {                                               \
        NAME->size = (SIZE);                             \
        NAME->capacity = 0;                              \
        NAME->p = malloc(sizeof(*NAME->p));              \
        if (!NAME->p) {                                  \
            perror("malloc fail");                       \
            abort();                                     \
        }                                                \
        NAME->p = (TYPE(*)[(SIZE)])(NAME->p);            \
   }                                                     \
   }while(false)


int main(int argc, char *argv[]) 
 {

   DECL_VECTOR(vec1,int,8);


    printf("size of handle struct:  %zu,\n\
size of vector array:   %zu,\n\
size of vector element: %zu\n\
address of handle struct: %p,\n\
address of vector array:  %p\n",  sizeof(*vec1),       \
                                  sizeof(*vec1->p),    \
                                  sizeof(*vec1->p[0]), \
                                  vec1,                \
                                  vec1->p);
   free(vec1->p);
   free(vec1);
   return 0;
 }

(however, people may accuse you of abusing your macro privileges, and they may not be entirely wrong) (但是,人们可能会指责你滥用你的宏权限,而且他们可能并非完全错误)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM