当前位置: 首页 > 知识库问答 >
问题:

为什么这个. c文件#包含它自己?

郁宾鸿
2023-03-14

为什么这个. c文件#包含本身?

  • https://github.com/powturbo/TurboPFor-Integer-Compression

简单的

#define USIZE 8
#include "vsimple.c"
#undef USIZE

#define USIZE 16
#include "vsimple.c"
#undef USIZE

#define USIZE 32
#include "vsimple.c"
#undef USIZE

#define USIZE 64
#include "vsimple.c"
#undef USIZE

共有2个答案

冷越泽
2023-03-14

@chqrlie 100%的公认答案解释了正在发生的事情。这只是一个补充评论。

如果使用C,我们可以定义两个模板函数来提供vsenc8vsenc16vsenc32vsenc64vsdec8vsdec16vsdec32vsdec64。然而,相比之下,C是一种非常简单的语言,不支持模板。要想拥有同样的能力(在更丑陋的包装中),一个常见的技巧是使用该语言的哑宏功能,让C预处理器为我们做同样的工作。大多数有一定经验的C程序员在职业生涯中会反复遇到并使用这种结构。

理解这个特定示例有点乏味的是,实现文件被非常规地解析了5次,首先有一些预备定义,然后是两个函数的四个变体。第一个过程(在#ifndef USIZE预处理器块内)将定义所需的宏和非变量,并将递归#使用不同的USIZE值(8163264)作为模板值,包括本身四次。当递归包含时,相应的#else预处理器块将使用根据过程中使用的USIZE宏常量值生成的两个函数的结果进行解析。

更传统、概念更清晰、易于理解的方法是包含来自不同文件的模板函数,比如vsimple。impl

#define USIZE 8
/* Generate vsenc8(), vsdec8()... */ 
#include "vsimple.impl"

#undef USIZE
#define USIZE 16
/* Generate vsenc16(), vsdec16()... */ 
#include "vsimple.impl"

#undef USIZE
#define USIZE 32
/* Generate vsenc32(), vsdec32()... */ 
#include "vsimple.impl"

#undef USIZE
#define USIZE 64
/* Generate vsenc64(), vsdec64()... */ 
#include "vsimple.impl"

包含文件vsimple。c和包含的文件vsimple。impl也可以被组织起来,以便更清楚地定义它们的定义和时间。大多数C程序员会识别实现模式,并立即知道发生了什么。

以这种方式递归地、反复地包含它自己,会让人产生一种hocus pocery的感觉,这种感觉会因为一个模糊的C竞争条目而受到欢迎,但对于关键任务的生产代码则不会。

澹台鸿光
2023-03-14

该文件包含自身,因此可以使用相同的源代码为宏USIZE的特定值生成4组不同的函数。

#include指令实际上封装在#ifndef中:

#ifndef USIZE

// common definitions
...
//

#define VSENC vsenc
#define VSDEC vsdec

#define USIZE 8
#include "vsimple.c"
#undef USIZE

#define USIZE 16
#include "vsimple.c"
#undef USIZE

#define USIZE 32
#include "vsimple.c"
#undef USIZE

#define USIZE 64
#include "vsimple.c"
#undef USIZE

#else // defined(USIZE)

// macro expanded size specific functions using token pasting

...

#define uint_t TEMPLATE3(uint, USIZE, _t)

unsigned char *TEMPLATE2(VSENC, USIZE)(uint_t *__restrict in, size_t n, unsigned char *__restrict out) {
   ...
}

unsigned char *TEMPLATE2(VSDEC, USIZE)(unsigned char *__restrict ip, size_t n, uint_t *__restrict op) {
   ...
}

#endif

本模块中定义的功能有

// vsencNN: compress array with n unsigned (NN bits in[n]) values to the buffer out. Return value = end of compressed output buffer out
unsigned char *vsenc8( unsigned char  *__restrict in, size_t n, unsigned char  *__restrict out);
unsigned char *vsenc16(unsigned short *__restrict in, size_t n, unsigned char  *__restrict out);
unsigned char *vsenc32(unsigned       *__restrict in, size_t n, unsigned char  *__restrict out);
unsigned char *vsenc64(uint64_t       *__restrict in, size_t n, unsigned char  *__restrict out);

// vsdecNN: decompress buffer into an array of n unsigned values. Return value = end of compressed input buffer in
unsigned char *vsdec8( unsigned char  *__restrict in, size_t n, unsigned char  *__restrict out);
unsigned char *vsdec16(unsigned char  *__restrict in, size_t n, unsigned short *__restrict out);
unsigned char *vsdec32(unsigned char  *__restrict in, size_t n, unsigned       *__restrict out);
unsigned char *vsdec64(unsigned char  *__restrict in, size_t n, uint64_t       *__restrict out);

它们都是从videe. c中的两个函数定义扩展而来的:

unsigned char *TEMPLATE2(VSENC, USIZE)(uint_t *__restrict in, size_t n, unsigned char *__restrict out) {
   ...
}

unsigned char *TEMPLATE2(VSDEC, USIZE)(unsigned char *__restrict ip, size_t n, uint_t *__restrict op) {
   ...
}

TEMPLATE2TEMPLATE3宏在conf.h中定义为

#define TEMPLATE2_(_x_, _y_) _x_##_y_
#define TEMPLATE2(_x_, _y_) TEMPLATE2_(_x_,_y_)

#define TEMPLATE3_(_x_,_y_,_z_) _x_##_y_##_z_
#define TEMPLATE3(_x_,_y_,_z_) TEMPLATE3_(_x_, _y_, _z_)

因此,unsigned char*TEMPLATE2(VSENC,USIZE)(uint_t*_\u restrict in,size_t n,unsigned char*_restrictout)被扩展为unsigned char*vsenc8(uint8_t*_restrictin,size_t n,unsigned char*_restrictout)在第一个包含中,vsenc16(uint16_t*_restrictin,在第二个包含中,等等。

预处理源代码的这种用法在单独的文件中更常见:一个用于实例化部分,该部分具有所有公共定义,特别是宏,另一个用于代码和数据模板的单独文件,该文件通过不同的宏定义被包含多次。

一个很好的例子是从QuickJS中的atom和操作码定义生成枚举、字符串和结构数组。

 类似资料:
  • 问题内容: 我最近遇到了一个扩展名为.pid的文件,并在其中进行了探索,但没有发现太多。该文件说: Pid-File是一个包含进程标识号(pid)的文件,该文件存储在文件系统定义明确的位置,因此允许其他程序找出正在运行的脚本的pid。 任何人都可以对此进行更多说明,或指导我了解pid文件中包含的内容的详细信息吗? 问题答案: pid文件包含给定程序的进程ID(数字)。例如,Apache HTTPD

  • 我试图打印包含,但是,HashSet没有检测到整数[]值。这和它被引用有关系吗?如果我不想将实际的整数[]对象传递给方法的参数,我如何克服这个问题?

  • 我见过类似的问题,但他们仍然没有意义,我的猿类大脑。 这里有一个例子。如果我在名为的头文件中声明了一个函数:并且在文件中声明了一个函数:。我在另一个文件(例如)中看到过这样的人,它只包含头,它只有声明(里面没有代码),而不包含带有代码的。但令我震惊的是,当他们调用中的函数时,它使用了中的代码并打印“hello”。怎么做?当我只包含了文件时,它如何打印在文件中添加的“hello”,而它只是一个声明,

  • 我正在编写一个小的Java库,其中包含我通常在大多数android应用程序中包含的相关代码。我决定将库导出为一个jar文件,然后将该文件放到我未来项目的libs文件夹中。 null 现在,当我在我的一个android应用程序中使用这个jar时,除了Doc部分之外,所有的一切都像预期的那样运行。当我悬停在我的库的类和方法上时,我看不到我编写的文档注释。 Q1:我是不是又少了一步? Q2:jar文件应

  • 我是否遗漏了包装类中的某些细节? 我有以下程序,在其中我定义了一个类,它包装并提供运算符: 我以为这会编译成同样的东西--它在做同样的计算,所有的东西都是内联的。 编辑-如果我使用而不是,它将产生相同的输出。 编辑-我发布了错误的ASM版本(而不是),因此本节没有帮助。 我在我的Mac电脑上使用Xcode的gcc,在一个64位系统上。除了for-loop的主体之外,结果是相同的。

  • 问题内容: 作为实习生,我在项目中使用公司代码,并且通常会向我发送文件以供使用。我将其添加到构建中,并且通常一切都很好。 但是,我很好奇,每个类包含什么,当我尝试打开文件中的一个类时,它告诉我需要一个源文件。 这是什么意思?我来自 C / C ++ 背景,因此类似于已经编译的文件,我所看到的只是东西?还是我正在使用的文件中包含加密的实际代码,所以我无法读取它? 感谢所有的答案! 编辑: 谢谢,伙计