纵然标准C语言持续演进着当下版本是C23但依旧存在一些让人难以理解的缺陷未被修复而在此之前Dlang社区于其D编程语言编译器里嵌入了一个全新的C编译器称作ImportC。欲支持C代码编译需参考https://dlang.org/spec/importc.html 此全新构建之编译器运用现代编译技术解决了传统C的一些问题。问题来了为什么标准 C 语言没有修复这些缺陷譬如常量表达式的评估来看以下 C 代码示例int sum(int a, int b) { return a b; }enum E { A 3, B 4, C sum(5, 6) };使用 gcc 编译时会报错gcc -c test.ctest.c:3:20: error: enumerator value for C is not an integer constantenum E { A 3, B, C sum(5, 6) };^简言之纵使C语言能够凭借常量折叠也就是Constant Folding于编译之际算出一个简易的表达式可是它没办法在编译之时执行函数调用。然而ImportC却能够达成这般情况。建议改进在C语言语法范围里只要函数与IO操作、拜访可变全局变量、开展系统调用等行为无关联那么在任何可以使用常量表达式的地方编译器皆是应当能够于编译时执行该函数的。编译时单元测试C 编译器有了编译时函数求值的能力一旦如此好多新功能就会变得有达成的可能。于C代码里头鲜少能瞅见单元测试缘由浅显直白单元测试得于构建系统里单独去设定目标且以独立的可执行文件样式来运行。这般额外增添的复杂性致使开发者常常将其忽视。或许你身为每日都持之以恒坚持晨跑之人同时对单元测试场景细加认真设置然而大多数开发者可不是这般的。例如int sum(int a, int b) { return a b; }_Static_assert(sum(3, 4) 7, test #1);使用 gcc 编译时会报错gcc -c test.ctest.c:3:16: error: expression in static assertion is not constant_Static_assert(sum(3, 4) 7, test #1);^然而ImportC 可以成功编译这段代码。潜在改进在 C 语言里借助允许于编译之时运行函数的方式能够把单元测试功能轻松予以实现。并不需要额外的构建步骤也不需要独立的测试可执行文件在每次代码进行编译之际单元测试便会自动运行。比如在 ImportC 的测试框架当中这种编译时的测试获得了广泛应用。前向引用声明的问题于C语言里头函数的前向引用属于一种常见的限制以下这般代码将此问题予以展示。int floo(int a, char *s) { return dex(s, a); }char dex(char *s, int i) { return s[i]; }使用 gcc 编译时会报错gcc -c test.ctest.c:4:6: error: conflicting types for dexchar dex(char *s, int i) { return s[i]; }^test.c:2:35: note: previous implicit declaration of dex was hereint floo(int a, char *s) { return dex(s, a); }造成错误的缘由是编译器于解析 floo 之际针对 dex 的类型做了一项隐式假定一般而言假定返回值是 int然而实际所定义的类型与该假定并不相符。要是把 floo 与 dex 的顺序进行颠倒那么这段代码便能够正常编译。这表明 C 编译器仅仅能够识别代码里“词法顺序上处于之前的声明”并且前向引用也就是在定义之前调用函数是不被准许的。为什么这是一个问题那是由于要助力前向引用所以每个函数皆得有一个明晰的声明举例来说char dex(char *s, int i); // 声明int floo(int a, char *s) { return dex(s, a); }char dex(char *s, int i) { return s[i]; } // 定义这种声明和定义的分离增加了无意义的工作量。其次这种限制致使开发者对代码顺序作出调整举例来说把叶子函数底层函数放置在最靠前的位置然而把全局接口函数置于最后面。这种代码组织形式有点类似于从报纸的底部起始进行阅读属实极为不符合直觉。ImportC 允许在丝毫没有特定顺序要求的状况下编译全局声明并进一步表明开发者无需为函数定义的先后顺序这一问题而忧心忡忡。导入声明问题于传统C编程里头就处理多个文件模块之际常常得单独去创建.h头文件用来声明模块接口。下面这个示例呈现出了常见的文件结构// floo.c#include dex.hint floo(int a, char *s) { return dex(s, a); }// dex.hchar dex(char *s, int i);// dex.c#include dex.hchar dex(char *s, int i) { return s[i]; }问题点1.繁重每一个外部模块都得要有一个单独的.h文件如此这般便是增添了开发者的负担。2.容易出错的情况是要是.h文件的声明和.c文件的定义并非完全匹配那么就会致使各种各样的难以排查出来的错误产生。针对于ImportC而言直接将模块的源文件予以导入就行并不需要额外的头文件。比如说// floo.c__import dex;int floo(int a, char *s) { return dexx(s, a); }// dex.cchar dexx(char *s, int i) { return s[i]; }用这样的办法开发者根本用不着去编写.h文件躲开了繁杂的声明事宜与此同时降低了潜在的出错风险。
C语言存在缺陷未修复?看ImportC如何解决并给出改进建议
纵然标准C语言持续演进着当下版本是C23但依旧存在一些让人难以理解的缺陷未被修复而在此之前Dlang社区于其D编程语言编译器里嵌入了一个全新的C编译器称作ImportC。欲支持C代码编译需参考https://dlang.org/spec/importc.html 此全新构建之编译器运用现代编译技术解决了传统C的一些问题。问题来了为什么标准 C 语言没有修复这些缺陷譬如常量表达式的评估来看以下 C 代码示例int sum(int a, int b) { return a b; }enum E { A 3, B 4, C sum(5, 6) };使用 gcc 编译时会报错gcc -c test.ctest.c:3:20: error: enumerator value for C is not an integer constantenum E { A 3, B, C sum(5, 6) };^简言之纵使C语言能够凭借常量折叠也就是Constant Folding于编译之际算出一个简易的表达式可是它没办法在编译之时执行函数调用。然而ImportC却能够达成这般情况。建议改进在C语言语法范围里只要函数与IO操作、拜访可变全局变量、开展系统调用等行为无关联那么在任何可以使用常量表达式的地方编译器皆是应当能够于编译时执行该函数的。编译时单元测试C 编译器有了编译时函数求值的能力一旦如此好多新功能就会变得有达成的可能。于C代码里头鲜少能瞅见单元测试缘由浅显直白单元测试得于构建系统里单独去设定目标且以独立的可执行文件样式来运行。这般额外增添的复杂性致使开发者常常将其忽视。或许你身为每日都持之以恒坚持晨跑之人同时对单元测试场景细加认真设置然而大多数开发者可不是这般的。例如int sum(int a, int b) { return a b; }_Static_assert(sum(3, 4) 7, test #1);使用 gcc 编译时会报错gcc -c test.ctest.c:3:16: error: expression in static assertion is not constant_Static_assert(sum(3, 4) 7, test #1);^然而ImportC 可以成功编译这段代码。潜在改进在 C 语言里借助允许于编译之时运行函数的方式能够把单元测试功能轻松予以实现。并不需要额外的构建步骤也不需要独立的测试可执行文件在每次代码进行编译之际单元测试便会自动运行。比如在 ImportC 的测试框架当中这种编译时的测试获得了广泛应用。前向引用声明的问题于C语言里头函数的前向引用属于一种常见的限制以下这般代码将此问题予以展示。int floo(int a, char *s) { return dex(s, a); }char dex(char *s, int i) { return s[i]; }使用 gcc 编译时会报错gcc -c test.ctest.c:4:6: error: conflicting types for dexchar dex(char *s, int i) { return s[i]; }^test.c:2:35: note: previous implicit declaration of dex was hereint floo(int a, char *s) { return dex(s, a); }造成错误的缘由是编译器于解析 floo 之际针对 dex 的类型做了一项隐式假定一般而言假定返回值是 int然而实际所定义的类型与该假定并不相符。要是把 floo 与 dex 的顺序进行颠倒那么这段代码便能够正常编译。这表明 C 编译器仅仅能够识别代码里“词法顺序上处于之前的声明”并且前向引用也就是在定义之前调用函数是不被准许的。为什么这是一个问题那是由于要助力前向引用所以每个函数皆得有一个明晰的声明举例来说char dex(char *s, int i); // 声明int floo(int a, char *s) { return dex(s, a); }char dex(char *s, int i) { return s[i]; } // 定义这种声明和定义的分离增加了无意义的工作量。其次这种限制致使开发者对代码顺序作出调整举例来说把叶子函数底层函数放置在最靠前的位置然而把全局接口函数置于最后面。这种代码组织形式有点类似于从报纸的底部起始进行阅读属实极为不符合直觉。ImportC 允许在丝毫没有特定顺序要求的状况下编译全局声明并进一步表明开发者无需为函数定义的先后顺序这一问题而忧心忡忡。导入声明问题于传统C编程里头就处理多个文件模块之际常常得单独去创建.h头文件用来声明模块接口。下面这个示例呈现出了常见的文件结构// floo.c#include dex.hint floo(int a, char *s) { return dex(s, a); }// dex.hchar dex(char *s, int i);// dex.c#include dex.hchar dex(char *s, int i) { return s[i]; }问题点1.繁重每一个外部模块都得要有一个单独的.h文件如此这般便是增添了开发者的负担。2.容易出错的情况是要是.h文件的声明和.c文件的定义并非完全匹配那么就会致使各种各样的难以排查出来的错误产生。针对于ImportC而言直接将模块的源文件予以导入就行并不需要额外的头文件。比如说// floo.c__import dex;int floo(int a, char *s) { return dexx(s, a); }// dex.cchar dexx(char *s, int i) { return s[i]; }用这样的办法开发者根本用不着去编写.h文件躲开了繁杂的声明事宜与此同时降低了潜在的出错风险。