11

我正在用 C 编写一个链表实现,并希望使用与 C++模板等效的语言功能来简化我的工作。

有这样的功能吗?

4

6 回答 6

7

C has no templates like C++, though you can achieve something similar with "clever" (or WTFey, depending on how you look at it) use of #define macros.

However, take a look at how for example GLib does it for singly linked lists or doubly linked lists.

于 2013-02-07T08:14:14.367 回答
5

模板是 C++ 的特性,但如果您想要单链表或双链表的类型无关实现,可以借助宏来实现,或者您可以简单地将void*指针存储在结构中。

当然,互联网上有很多这样的实现。@MohamedKALLEL@hyde已经给出了来自 Linux 内核和 GLib 的示例,我只想添加一个关于漂亮的小库uthash的注释。

它在 C 中实现了一个哈希表,但它也有一个utlist.h ,它完全在宏上实现单链和双链(甚至循环)列表。也就是说,您可以简单地获取此文件,将其包含在内,然后按原样使用这些宏,或者根据需要对其进行修改。同样好的是您可以使用任何数据结构:它只需要有next指针(并且prev,如果是双重链接的)。

Ps 但是在使用宏时要记住:能力越大,责任越大。宏很强大,但可能会变得非常不安全和不可读。谨防!

于 2013-02-07T08:30:14.647 回答
3

是的,有list.h。它是一个循环链表:

以下链接包含如何使用它的示例。

包含与管理循环链表相关的list.h所有函数,如定义、添加头部、添加尾部、删除、foreach 函数以浏览循环链表......

于 2013-02-07T08:12:50.153 回答
3

如果您make用来构建您的软件,您可以用来实现类似结果的一种方法是让 make 根据您编写的模板生成您的代码,方法是调用 sed 或 awk 之类的东西。我已经多次使用这种方法,虽然它缺乏 C++ 模板提供的灵活性和功能,但它非常透明(与宏不同),构建非常高效,并且除了久经考验的老式 unix 实用程序外,不需要添加任何工具(例如makesed)。主要缺点是您将在与代码不同的位置(Makefile中的一行)指定查找/替换字符串。

请注意,通常情况下,使用函数指针和 void 指针来编写一个通用的代码库会更好。但是,如果您希望通过消除不必要的函数调用来获得额外的性能,例如在紧密循环中,模板可能会更好,并且可以模拟如下:

  1. 使用占位符名称编写模板代码,并使用诸如code.template.c 之类的模板名称

  2. 在替换为适当的名称后,使用对模板代码的函数调用编写非模板代码,例如my_int_func()my_string_func()

  3. 可能,使用#include 将您的模板代码包含在您的非模板代码中(例如,如果您的模板将具有inline功能)

  4. 将您的 Makefile 写入:

    • 从 template.c 文件生成“真正的”C 文件。每个模板目标都有一个 Makefile 条目,例如code.generated.csed是一个很好的替代工具,但您也可以使用 egreplaceawk或其任何 Windows 等效工具
    • 酌情编译生成的 C 文件并链接生成的对象

例如:

代码模板.c

/* Makefile will call sed to replace DATANAME, DATATYPE and SPECIFIER */
void print_DATANAME_data(DATATYPE x) {
  printf("%SPECIFIER\n", x);
}

代码.c

#include <stdio.h>
#include "printfuncs.generated.c"

int main() {
  int i = 99;
  print_int_data(99);

  char *s = "hello";
  print_str_data(s);

  float f = 1.234;
  print_float_data(f);
}

生成文件

all: my_program

my_program: code.c
    CC -o $@ code.c

code.c: printfuncs.generated.c

printfuncs.generated.c: code.template.c
    rm -f printfuncs.generated.c
    cat code.template.c | sed 's/DATANAME/int/g;s/DATATYPE/int/g;s/SPECIFIER/i/g;' >> printfuncs.generated.c
    cat code.template.c | sed 's/DATANAME/str/g;s/DATATYPE/char */g;s/SPECIFIER/s/g;' >> printfuncs.generated.c
    cat code.template.c | sed 's/DATANAME/float/g;s/DATATYPE/float/g;s/SPECIFIER/f/g;' >> printfuncs.generated.c

建造

make

这将生成 printfuncs.generated.c(如果该文件不存在或修改时间少于 code.template.c),如下所示:

/* Makefile will call sed to replace int, int and i */
void print_int_data(int x) {
  printf("%i", x);
}
/* Makefile will call sed to replace str, char * and s */
void print_str_data(char * x) {
  printf("%s", x);
}
/* Makefile will call sed to replace float, float and f */
void print_float_data(float x) {
  printf("%f", x);
}

任何编译器错误都将指向此文件,您可以直接修改此文件以使其编译(之后您需要更新模板以防止您的更改丢失),或编辑模板。无论哪种情况,在您编辑之后,您需要做的就是make再次运行(尝试)编译。

./my_program(或者my_program.exe如果建立在胜利之上)

于 2019-08-22T05:08:53.290 回答
1

嗨,我不知道链表,但是对于模板函数,您可能可以使用宏或具有不同数量参数的函数作为该程序的示例

#include <stdarg.h>
#include <stdio.h>
#define INT 0
#define STR 1
void foo( int type, ... )
{
    va_list ap;
    int i;
    char *s;
    va_start( ap, type );
    switch( type ) {
    case INT:
        i = va_arg( ap, int );
        printf( "INT: %i\n", i );
        break;
    case STR:
        s = va_arg( ap, char * );
        printf( "STR: %s\n", s );
        break;
    default:
        break;
    }
    va_end( ap );
}
#define SWAP( type, a, b ) {                    \
        type t;                                 \
        t = a;                                  \
        a = b;                                  \
        b = t;                                  \
    }
int main( void )
{
    foo( INT, 3 );
    foo( STR, "baz" );
    int ia = 0, ib = 3;
    SWAP( int, ia, ib );
    printf( "%i %i\n", ia, ib );
    float fa = 0.5, fb = 3.14;
    SWAP( float, fa, fb );
    printf( "%f %f\n", fa, fb );
    return 0;
}

将产生输出

INT: 3
STR: baz
3 0
3.140000 0.500000
于 2017-02-27T02:36:45.187 回答
0

看看这篇文章

C 没有静态模板,但您可以使用宏来模拟它们。

最简单的静态模板形式,定义宏:

// define a macro 
#define DEF(type, name, val) type name = val 
// call the macro 
DEF(int, foo, 5); 
// print the called macro 
printf("%d", foo);

“这个模板允许定义具有基本类型的变量所需的代码被概括和抽象。目标是使代码在类型之间可共享和相似。然而,这个例子是微不足道的,因为它只提供了很少的简化写出声明。真正的力量来自执行更复杂任务的代码。

这是您可以尝试的剪切和粘贴示例

#include <stdio.h>

int main()
{
#define FOREACH(type, start, end, fn)                                    \
    for (type _foreach_var = start; _foreach_var != end; _foreach_var++) \
    {                                                                    \
        fn(_foreach_var);                                                \
    }

#define PRINT_INT(n) printf("%d\n", n)
    // use FOREACH
    FOREACH(int, 0, 5, PRINT_INT)
}
  • 首先我们定义我们的 FOREACH 宏。
  • 然后我们可以使用它来循环,例如这里:FOREACH(int, 0, 5, PRINT_INT)
于 2021-03-14T01:56:18.947 回答