制作一个接受C中任何类型的动态数组

时间:2017-02-17 08:42:43

标签: c dynamic-arrays

我试图找到一种方法来构建一个结构来保存一个可以处理任何数据类型的动态数组(包括用户定义的数据类型),到目前为止,这就是我想出来的。

#define Vector(DATATYPE) struct {   DATATYPE* data; size_t size; size_t used; }

typedef Vector(int) int_Vector;

int main(int argc, char* argv[]){
    int_Vector vec;
    return 0;
}

虽然这是有效的,但我想知道,这是一个好习惯吗?我应该做这样的事情还是有更好的方法?还有一种方法可以用typedef Vector(int) int_vector部分来实现它。基本上这种方式可以让我使用数组,就像c ++使用模板一样,它看起来像这样:

#define Vector(DATATYPE) struct {   DATATYPE* data; size_t size; size_t used; }

int main(int argc, char* argv[]){
    Vector(int) vec;
    return 0;
}

主要是为了避免这么多的typedef并将它们全部放在一个名字下。

5 个答案:

答案 0 :(得分:15)

嗯不,C没有模板系统,所以你不能使用它。

你可以像你一样模仿宏的效果(非常聪明的解决方案),但这当然有点不标准,需要你的代码用户学习宏及其局限性。

通常C代码不会尝试,因为它太尴尬了。

最“通用”的典型向量类似于glib的GArray,但并不假装知道每个元素的类型。相反,在访问时留给用户关心,并且数组只是将每个元素建模为n个字节。

C11中有_Generic()可能会有所帮助,老实说,我并不是很有经验。

答案 1 :(得分:5)

第二个例子不起作用,因为两个变量被定义为不同的类型,即使它们的成员是相同的。为什么会如此,我的existing answer已涵盖。

然而,使用稍微不同的方法可以保持语法相同:

#include <stdlib.h>

#define vector(type)    struct vector_##type

struct vector_int
{
    int* array;
    size_t count;
} ;

int main(void)
{
    vector(int) one = { 0 };
    vector(int) two = { 0 };

    one = two;
    ( void )one ;

    return 0;
}

这个用法与C ++的vector<int>惊人相似,可以在这里看到一个完整的例子:

#include <stdlib.h>

#define vector_var(type)    struct vector_##type

struct vector_int
{
    int* array;
    size_t count;
};

void vector_int_Push( struct vector_int* object , int value ) 
{
    //implement it here
}

int vector_int_Pop( struct vector_int* object ) 
{
    //implement it here
    return 0;
}    

struct vector_int_table
{
    void( *Push )( struct vector_int* , int );
    int( *Pop )( struct vector_int* );

} vector_int_table = { 
                         .Push = vector_int_Push ,
                         .Pop = vector_int_Pop 
                     };

#define vector(type)   vector_##type##_table

int main(void)
{
    vector_var(int) one = { 0 };
    vector_var(int) two = { 0 };

    one = two;

    vector(int).Push( &one , 1 );
    int value = vector(int).Pop( &one );
    ( void )value;

    return 0;
}

答案 2 :(得分:5)

Vector(DATATYPE) struct { DATATYPE* data; size_t size; size_t used; }也无法指向函数。

对于指向任何对象的指针,

void*已足够且定义良好,但对于指向函数的指针则不然。

C允许将指向一种类型函数的指针保存为指向另一种类型函数的指针。通过使用下面两个中的union,代码有足够的空间来保存指向任何类型的指针。管理什么类型和使用的成员仍然是开放的。

union u_ptr {
  void *object;
  void (*function)();
}

答案 3 :(得分:2)

不错。而且我没有看到任何不利因素。 只是为了解释另一种方法,在这种情况下最常用的是使用union:

typedef union { int i; long l; float f; double d; /*(and so on)*/} vdata;
typedef enum  {INT_T,LONG_T,FLOAT_T, /*(and so on)*/} vtype;
typedef struct 
{
    vtype t;
    vdata data
} vtoken;
typedef struct
{
    vtoken *tk;
    size_t sz;
   size_t n;
} Vector;

所以这是可能的方式。数据类型的枚举,你可以避免使用typedef,但是如果你使用mixed(ex:sum long,to double,to float等)你必须使用它们,因为int + double不等于double + int;这也是一个原因,因为更容易看到工会做这项工作。你不要理会算术规则。

答案 4 :(得分:2)

扩展this answer关于多态解决方案,我们也可以使它包含指针类型或用户定义类型。这种方法的主要优点是摆脱了数据类型&#34;枚举和所有运行时检查开关语句。

<强> variant.h

#ifndef VARIANT_H
#define VARIANT_H

#include <stdio.h>
#include <stdint.h>

typedef void print_data_t (const void* data);
typedef void print_type_t (void);

typedef struct 
{
  void* data;
  print_data_t* print_data;
  print_type_t* print_type;
} variant_t;

void print_data_char    (const void* data);
void print_data_short   (const void* data);
void print_data_int     (const void* data);
void print_data_ptr     (const void* data);
void print_data_nothing (const void* data);

void print_type_char        (void);
void print_type_short       (void);
void print_type_int         (void);
void print_type_int_p       (void);
void print_type_void_p      (void);
void print_type_void_f_void (void);

void print_data (const variant_t* var);
void print_type (const variant_t* var);

#define variant_init(var) {                \
  .data = &var,                            \
                                           \
  .print_data = _Generic((var),            \
    char:  print_data_char,                \
    short: print_data_short,               \
    int:   print_data_int,                 \
    int*:  print_data_ptr,                 \
    void*: print_data_ptr,                 \
    void(*)(void): print_data_nothing),    \
                                           \
  .print_type = _Generic((var),            \
    char:  print_type_char,                \
    short: print_type_short,               \
    int:   print_type_int,                 \
    int*:  print_type_int_p,               \
    void*: print_type_void_p,              \
    void(*)(void): print_type_void_f_void) \
}


#endif /* VARIANT_H */

<强> variant.c

#include "variant.h"

void print_data_char    (const void* data) { printf("%c",  *(const char*)  data); }
void print_data_short   (const void* data) { printf("%hd", *(const short*) data); }
void print_data_int     (const void* data) { printf("%d",  *(const int*)   data); }
void print_data_ptr     (const void* data) { printf("%p",  data); }
void print_data_nothing (const void* data) {}

void print_type_char        (void) { printf("char");          }
void print_type_short       (void) { printf("short");         }
void print_type_int         (void) { printf("int");           }
void print_type_int_p       (void) { printf("int*");          }
void print_type_void_p      (void) { printf("void*");         }
void print_type_void_f_void (void) { printf("void(*)(void)"); }


void print_data (const variant_t* var)
{
  var->print_data(var->data);
}

void print_type (const variant_t* var)
{
  var->print_type();
}

<强>的main.c

#include <stdio.h>
#include "variant.h"

int main (void) 
{
  char c = 'A';
  short s = 3;
  int i = 5;
  int* iptr = &i;
  void* vptr= NULL;
  void (*fptr)(void) = NULL;

  variant_t var[] =
  {
    variant_init(c),
    variant_init(s),
    variant_init(i),
    variant_init(iptr),
    variant_init(vptr),
    variant_init(fptr)
  };

  for(size_t i=0; i<sizeof var / sizeof *var; i++)
  {
    printf("Type: ");
    print_type(&var[i]);
    printf("\tData: ");
    print_data(&var[i]);
    printf("\n");
  }

  return 0;
}

输出:

Type: char      Data: A
Type: short     Data: 3
Type: int       Data: 5
Type: int*      Data: 000000000022FD98
Type: void*     Data: 000000000022FDA0
Type: void(*)(void)     Data:

_Generic用于此目的的缺点是它阻止我们使用私有封装,因为它必须用作宏才能传递类型信息。

另一方面,&#34;变体&#34;在这种情况下,必须为所提出的所有新类型进行维护,因此它不是所有实用或通用的。

对于各种类似的目的,这些技巧仍然很有用。