Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/55.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
可以在C中创建泛型函数吗?_C_Arrays_Generics - Fatal编程技术网

可以在C中创建泛型函数吗?

可以在C中创建泛型函数吗?,c,arrays,generics,C,Arrays,Generics,我正重新开始使用C语言,但我已经被其他语言中的泛型所惯坏了。在可调整大小的数组的实现中,我使用了以下代码: typdef struct { void** array; int length; int capacity; size_t type_size; } Vector; void vector_add(Vector* v, void* entry) { // ... code for adding to the array and resizing } int mai

我正重新开始使用C语言,但我已经被其他语言中的泛型所惯坏了。在可调整大小的数组的实现中,我使用了以下代码:

typdef struct {
  void** array;
  int length;
  int capacity;
  size_t type_size;
} Vector;

void vector_add(Vector* v, void* entry) {
  // ... code for adding to the array and resizing
}

int main() {
  Vector* vector = vector_create(5, sizeof(int));
  vector_add(vector, 4); // This is erroneous...
  // ...
}
在我尝试将其通用化时,我现在无法将整数添加到向量中,而不将其存储在内存中的其他位置


有什么方法可以使这项工作正常进行(或者是更好的泛型方法)?

对于我的回答,我假设您不熟悉内存部分(即内存池的使用)

在我尝试将其通用化的过程中,如果不将整数存储在其他地方的内存中,我现在无法将其添加到向量中

如果您想要创建一个泛型结构(正如您所做的),那么您将需要使用void指针。因此,通过使用void指针,您需要将每个字段的值存储在内存池中,或者不常见地存储在堆栈中。请注意,该结构由空指针组成,因此结构中只包含内存地址,指向内存中值所在的其他位置

如果在堆栈上声明它们,请小心,因为一旦堆栈帧从调用堆栈中弹出,这些内存地址将被视为无效,因此可能会被另一个堆栈帧使用(覆盖该内存地址集合中的现有值)

<> > >:如果你迁移到C++,那么你可以考虑C++模板的使用。

你的想法可以做:

int *new_int = (int*)malloc(sizeof(int));
*new_int = 4;
vector_add(vector, new_int);
当然,最好执行
int*create_int(int x)
函数或类似的操作:

int *create_int(int x)
{
    int *n = (int*)malloc(sizeof(int));
    *n = 4;
    return n;
}
//...
vector_add(vector, create_int(4));

如果你的环境允许,你可以考虑使用一个测试良好的、广泛使用的库,它已经管理了所有这些库,比如Glib。或者甚至C++(

),可以通过存储数据而不是指针来避免很多很多小的分配,比如

typedef struct {
  char* array;
  int length;
  int capacity;
  size_t type_size;
} Vector;

bool vector_add(Vector* v, void* entry)
{
    if (v->length < v->capacity || vector_expand(v)) {
        char* location = v->array + (v->length++)*(v->type_size);
        memcpy(location, entry, v->type_size);
        return 1;
    }
    return 0; // didn't fit
}

int main()
{
    Vector* vector = vector_create(5, sizeof(int));
    int value = 4;
    vector_add(vector, &value); // pointer to local is ok because the pointer isn't stored, only used for memcpy
}
typedef结构{
字符*数组;
整数长度;
国际能力;
尺寸\u t类型\u尺寸;
}载体;
bool vector_add(vector*v,void*entry)
{
如果(v->长度容量| |向量_展开(v)){
字符*位置=v->数组+(v->长度++)*(v->类型大小);
memcpy(位置、条目、v->type_大小);
返回1;
}
返回0;//不适合
}
int main()
{
Vector*Vector=Vector_create(5,sizeof(int));
int值=4;
vector_add(vector,&value);//指向本地的指针是可以的,因为该指针未存储,仅用于memcpy
}

是的,这是我的一个实现(与您的类似),可能会有所帮助。它使用宏,这些宏可以用函数调用包装为立即值

#ifndef VECTOR_H
# define VECTOR_H

# include <stddef.h>
# include <string.h>

# define VECTOR_HEADROOM 4

/* A simple library for dynamic
 * string/array manipulation
 *
 * Written by: Taylor Holberton
 * During: July 2013
 */

struct vector {
    void * data;
    size_t  size, len;
    size_t  headroom;
};

int vector_init (struct vector *);

size_t vector_addc  (struct vector *, int index, char c);
size_t vector_subc  (struct vector *, int index);

// these ones are just for strings (I haven't yet generalized them)
size_t vector_adds (struct vector *, int index, int iend, const char * c);
size_t vector_subs (struct vector *, int ibegin, int iend);

size_t vector_addi (struct vector *, int index, int i);
size_t vector_subi (struct vector *, int index);

# define vector_addm(v, index, datatype, element)                        \
do {                                                                    \
    if (!v) return 0;                                               \
                                                                    \
    if (!v->size){                                                  \
            v->data = calloc (v->headroom, sizeof (datatype));      \
            v->size = v->headroom;                                  \
    }                                                               \
                                                                    \
    datatype * p = v->data;                                         \
                                                                    \
    if (v->len >= (v->size - 2)){                                   \
            v->data = realloc (v->data,                             \
                    (v->size + v->headroom) * sizeof (datatype));   \
            p = v->data;                                            \
            memset (&p[v->size], 0, v->headroom * sizeof(datatype));\
            v->size += v->headroom;                                 \
    }                                                               \
                                                                    \
    if ((index < 0) || (index > v->len)){                           \
            index = v->len;                                         \
    }                                                               \
                                                                    \
    for (int i = v->len; i >= index; i--){                          \
            p[i + 1] = p[i];                                        \
    }                                                               \
                                                                    \
    p[index] = element;                                             \
                                                                    \
    v->len++;                                                       \
                                                                    \
} while (0)


# define vector_subm(v, index, datatype)                                 \
do {                                                                    \
    if (!v || !v->len){                                             \
            return 0;                                               \
    }                                                               \
                                                                    \
    if ((index < 0) || (index > (v->len - 1))){                     \
            index = v->len - 1;                                     \
    }                                                               \
                                                                    \
    datatype * p = v->data;                                         \
                                                                    \
    for (int i = index; i < v->len; i++){                           \
            p[i] = p[i + 1];                                        \
    }                                                               \
                                                                    \
    v->len--;                                                       \
                                                                    \
    if ((v->size - v->len) > v->headroom){                          \
            v->data = realloc (v->data, ((v->size - v->headroom) + 1) * sizeof (datatype));\
            v->size -= v->headroom;                                 \
    }                                                               \
                                                                    \
} while (0)

#endif
我没有让人检查过这段代码,但我在编写的一个大型程序中使用过它,没有任何内存错误(使用
valgrind

唯一真正缺少的(我一直想添加)是从数组中添加和减去数组的能力


编辑:我相信你也可以用
stdarg.h
做同样的事情,但我从未尝试过

不必让vector类存储添加的对象,只需返回一个指向调用者可以存储它的位置的指针:

typdef struct {
    char *buffer;
    size_t length;
    size_t capacity;
    size_t type_size;
} Vector;

void *vector_add(Vector* v)
{
    if (v->length == v->capacity) {
        // ... increase capacity by at least one
        // ... realloc buffer to capacity * type_size
    }
    return v->buffer + v->type_size * v->length++;
}

// in main:
*(int*)vector_add(v) = 4;

你要求一个更好的方法?这里是:(披露:这是我的代码)

让我简单地解释一下:

  • 我想要类型安全的泛型容器(在其他语言中由适当泛型(Ada)或参数多态性(OCaml)提供)

  • 宏就是做不到(我不是 下面将详细解释一下。只需说:模板扩展的结果或 泛型实例化本身应该是一个模块:在C中,这意味着 分别导出的处理器符号可用于模块配置(如 -DUSE_进程_队列_调试代码)如果使用C宏生成 实例

  • 我通过将元素大小和所有相关操作移动到描述性结构中来抽象元素类型。这将传递到泛型代码的每次调用。请注意,描述符描述元素类型,因此每个泛型实例将需要一个描述符实例

  • 我正在使用一个模板处理器创建一个精简类型安全前端模块,以生成通用代码

例如:

这是检索元素的通用代码的原型:

void fifo_get ( fifo_DESCRIPTOR* inst, fifo* , void* var );
这是描述符类型:

typedef struct fifo_DESCRIPTOR {
  size_t maxindex;
  size_t element_size;
} fifo_DESCRIPTOR;
这是类型安全包装模板中的模板代码:

<<eT>>  <<>>get  ( <<T>>* f ) { 
   <<eT>> e; fifo_get( &DESCRIPTOR, (fifo*) f, (void*) &e ); return e; 
}
float   floatq_get  ( floatq* f ) { 
    float e; fifo_get( &DESCRIPTOR, (fifo*) f, (void*) &e ); return e; 
}
所有这些都有一个很好的make集成,但在实例化中几乎没有任何类型安全性。每个错误只有在使用cc编译时才会出现

<>我现在不能证明为什么在C中使用源文本模板而不是迁移到C++。对我来说,这只是一个实验。


注意。

此方法可能会让您感到恐惧,但如果您不需要任何类型专用逻辑,则可以使其工作:

// vector.h
#ifndef VECTOR_H
#define VECTOR_H

#define VECTOR_IMP(itemType) \
   typedef struct {          \
      itemType * array;      \
      int length;            \
      int capacity;          \
   } itemType##_Vector;      \
                             \
   static inline void itemType##_vector_add(itemType##_Vector* v, itemType v) { \
      // implementation of adding an itemType object to the array goes here     \
   }                                                                            \
                                                                                \
   [... other static-inline generic vector methods would go here ...]           \

// Now we can "instantiate" versions of the Vector struct and methods for
// whatever types we want to use.
VECTOR_IMP(int);
VECTOR_IMP(float);
VECTOR_IMP(char);

#endif
…以及一些调用代码示例:

#include "vector.h"

int main(int argc, char ** argv)
{
   float_Vector fv = {0};
   int_Vector iv = {0};
   char_Vector cv = {0};

   int_vector_add(&iv, 5);
   float_vector_add(&fv, 3.14f);
   char_vector_add(&cv, 'A');

   return 0;
}
是的,您可以采用并开发一种完整的C动态语言,在此过程中,开发一种相对干净的C运行时,可以在C中使用

事实上,我就是这么做的,就像我之前的其他人一样

在该项目的C运行时,将从C编号创建一个通用编号,如下所示:

val n = num(42);
val vector_add(val vec, val delta)
{
   val iter;
   for (iter = zero; lt(iter, length(vec)); iter = plus(iter, one)) {
      val *pelem = vecref_l(vec, iter);
      *pelem = plus(*pelem, delta);
   }
   return nil;
}
由于
val
的表示方式,它只占用一个机器字。几个类型标记位用于区分数字与指针、字符等

还有一点:

val n = num_fast(42);
这要快得多(一个位操作宏),因为它不做任何特殊检查,以确保数字42符合“fixnum”范围;它用于小整数

将其参数添加到向量的每个元素的函数可以这样编写(效率非常低):

val n = num(42);
val vector_add(val vec, val delta)
{
   val iter;
   for (iter = zero; lt(iter, length(vec)); iter = plus(iter, one)) {
      val *pelem = vecref_l(vec, iter);
      *pelem = plus(*pelem, delta);
   }
   return nil;
}
由于
plus
是通用的,因此这将适用于fixnums、bignums和
val str = string(L"abcd");
sequence_add(str, num(2));