C/C++:C语言中的__FILE__、__LINE__等几种C标准用法

C语言中的几种特殊标准定义

__FILE__ :正在编译文件的文件名

__LINE__ :正在编译文件的行号

__DATE__:编译时刻的日期字符串 如“Sep 22 2020”

__TIME__:编译时刻的时间字符串  如”10:00:00“

__STDC__:判断该文件是不是标准C程序

__func__:它指示所在的函数

标准C语言预处理要求定义某些对象宏,每个预定义宏的名称为两个下划线字符开头和结尾,这些预定义宏不能被取消定义(#undef)或由编程人员重新定义。 

  • FILE‌:这是一个预处理宏,它在编译时会被替换为当前源代码文件的名称。这个宏通常用于在错误消息中提供发生错误的文件名,帮助开发者快速定位到具体的代码文件。
  • LINE‌:这也是一个预处理宏,它在编译时会被替换为当前源代码的行号。这个宏用于提供发生错误的行号,进一步精确到代码的具体行数,从而帮助开发者快速找到问题所在。

 1.__FILE__   编译文件名称

File中文意思即文件,这里的意思主要是指:正在编译文件对应正在编译文件的路径和文件的名称。

Keil版本对应的路径是相对于工程文件而言的路径,IAR版本路径是相对Windows路径。

源代码:

char BuildFile[] = __FILE__;
printf("编译文件路径:%s\n", BuildFile);

Keil:  编译文件路径:App\main.c

IAR:  编译文件路径:C:\Users\strongerHuang\Desktop\工程主目录\App\main.c

#include <stdio.h>
int main()
{
    printf("%s\n",__FILE__);
}

在gcc编译生成a.out,执行后输出结果为:

test.c

在windows的vc6.0下编译执行结果为:

c:\documents and settings\administrator\桌面\test.c

2.__LINE__   编译文件行号

上面说的是编译的文件名,是一个字符串,而这里说的是行号,是一个整型变量,这是这两者的区别,

比如在工程中添加源代码:

char BuildLine = __LINE__;
printf("编译代码所在行:%d\n", BuildLine);

可以看不是数组的字符串,打印信息:  编译代码所在行:44

一般情况下,__FILE__是和__LINE__结合一起使用,用于打印我们代码信息,方便快速定位代码位置。

#include <stdio.h>

main()
{
    printf("%d\n",__LINE__);
    printf("%d\n",__LINE__);
    printf("%d\n",__LINE__);
};

 该程序在linux用gcc编译,在windows的vc6.0下编译都可以通过,执行结果都为:

7

8

9

3.__DATE__   编译日期

__DATE__日期,需要注意的是:这个日期是你在编译时Windows系统的日期,如果对应那部分代码之前编译好了,后面没有编译,这个日期还是之前的日期,而不是后面编译的日期。因此,如果这里用于定版本,就需要在定版本时对工程进行全部重新编译,它才会更新至你最后编译的日期。

代码:

char BuildDate[] = __DATE__;
printf("编译日期:%s\n", BuildDate);

输出结果: 编译日期:Sep 22 2017

4.__TIME__   编译时间

这个和__DATE__一样的原理,编译时的时间,也是一个字符串。

再次提醒:用于定版本:需要重新编译,这样才是最后一次编译时间。

代码:

char BuildTime[] = __TIME__;
printf("编译时间:%s\n", BuildTime);

输出结果:编译时间:10:00:00

5.__STDC__   标准C代码

这个标准在我们单片机及嵌入式编程中运用的比较少,当要求程序严格遵循ANSIC标准时该标识符被赋值为1,主要是判断我们的程序文件是不是标准C程序。

#ifdef __STDC__
  printf("标准C代码文件\n");
#else
  printf("非标准C代码文件\n");
#endif

6.__func__   当前所在的函数的名称

使用 __func__ 可以简化代码的编写,以及便于在报错信息中定位问题。例如,当出现错误时,可以在报错信息中打印函数名称以更方便地追踪问题所在的代码。

#include <stdio.h>
 
int main() {
    printf("%s\n", __func__);
    return 0;
}

 输出结果为:mian

 

 

打印出来的结果是:

<think>好的,我现在需要帮助用户理解C语言中_Generic关键字的具体用法,包括示例和解释。用户已经提供了一些引用内容,我需要结合这些引用和之前的回答来生成一个详细的解释。 首先,回顾用户的问题,他们想了解_Generic的用法、示例和解释。之前的回答中,我提到了C++中类似_Generic的功能,但现在需要专注于C语言本身的实现。 根据引用[1]和[2],用户提供的示例代码展示了_Generic的基本使用,比如类型判断和返回不同的字符串。引用[3]则讨论了_Generic在类型判断时的时机,比如变量运算后类型不变的情况。引用[4]可能涉及如何利用_Generic实现泛型编程的技巧。 我需要先解释_Generic的基本语法,然后给出不同场景的示例,比如类型判断、泛型函数、宏封装等。同时要提到注意事项,比如编译时处理、类型匹配顺序、default分支的重要性,以及类型提升的影响。 需要确保每个示例都有代码和解释,说明_Generic如何根据类型选择不同的分支。例如,引用[1]中的PRINT_TYPE宏展示了如何根据变量类型输出不同的字符串。引用[2]中的GET_TYPENAME宏返回类型名称,这可以用来演示如何获取变量类型的信息。 另外,引用[3]中的例子需要解释清楚,为什么a的类型在运算后仍然是int,导致选择int分支。这说明_Generic是在编译时根据变量声明类型来判断,而不是运算后的结果类型,这一点很重要,需要在注意事项中强调。 还要注意用户可能存在的疑问点,比如_Generic与宏的结合使用,如何处理未定义的类型,以及类型提升带来的影响。例如,当传入一个char类型的变量时,如果没有char分支,但存在int分支,可能会因为整数提升而匹配到int,这需要特别提醒用户。 最后,总结_Generic的应用场景,比如类型安全宏、泛型函数、代码复用和调试,帮助用户理解其实际用途。同时,相关问题部分需要覆盖常见疑问,如与C++重载的区别、处理复杂类型、类型提升问题等,以便用户进一步学习。</think>以下是对C语言`_Generic`关键字的详细解释及用法示例,结合引用内容进行说明: --- ### 一、`_Generic`基本语法与原理 `_Generic`是C11标准引入的**编译时类型分发机制**,通过宏定义实现类似多态的效果。其核心结构为: ```c _Generic(控制表达式, 类型1: 返回值1, 类型2: 返回值2, ... default: 默认返回值 ) ``` - **控制表达式**:需要检测类型的表达式(不实际执行) - **类型匹配**:编译器根据控制表达式的类型选择对应分支 - **返回值**:返回对应分支的表达式结果[^1] --- ### 二、典型使用场景与示例 #### 1. 类型判断与响应 ```c #include <stdio.h> #define PRINT_TYPE(x) _Generic((x), \ char: "char", \ short: "short", \ int: "int", \ long: "long", \ float: "float", \ double: "double", \ default: "unknown" \ ) int main() { printf("%s\n", PRINT_TYPE('A')); // 输出 "char" printf("%s\n", PRINT_TYPE(3.14f)); // 输出 "float" printf("%s\n", PRINT_TYPE("test")); // 输出 "unknown" return 0; } ``` **解析**:编译器根据输入值类型选择匹配分支,返回对应字符串[^1] #### 2. 泛型函数实现 ```c #include <stdio.h> #define SQUARE(x) _Generic((x), \ int: (x)*(x), \ double: (x)*(x), \ default: 0 \ ) int main() { printf("%d\n", SQUARE(5)); // 25 printf("%.2f\n", SQUARE(2.5)); // 6.25 printf("%d\n", SQUARE("abc")); // 0 return 0; } ``` **解析**:为不同类型实现相同功能的泛型计算[^2] #### 3. 类型相关操作分发 ```c #include <stdio.h> #define PROCESS(x) _Generic((x), \ int: (printf("整数操作: %d\n", x)), \ double: (printf("浮点操作: %.2f\n", x)), \ char*: (printf("字符串: %s\n", x)) \ ) int main() { PROCESS(42); // 输出 "整数操作: 42" PROCESS(3.1415); // 输出 "浮点操作: 3.14" PROCESS("Hello"); // 输出 "字符串: Hello" return 0; } ``` **解析**:根据类型执行不同的格式化输出[^2] --- ### 三、关键注意事项 1. **编译时处理**:类型检查在编译阶段完成,不会产生运行时开销 2. **类型匹配顺序**:按声明顺序匹配,第一个匹配的类型分支会被执行 3. **必须包含default**:未匹配类型必须提供默认分支,否则编译报错 4. **类型提升问题**: ```c char c = 'A'; printf("%s", PRINT_TYPE(c)); // 输出 "int"(若未定义char分支) ``` 因C语言整数提升规则,char可能被提升为int类型[^3] --- ### 四、高级应用场景 #### 1. 类型安全宏 ```c #define MAX(a,b) _Generic((a)+(b), \ int: ((a)>(b)?(a):(b)), \ float: ((a)>(b)?(a):(b)), \ default: (fmax((a),(b))) \ ) ``` #### 2. 调试工具封装 ```c #define DEBUG_VAR(x) do { \ printf("[%s:%d] %s = ", __FILE__, __LINE__, #x); \ _Generic((x), \ int: printf("%d\n", x), \ double: printf("%f\n", x), \ char*: printf("\"%s\"\n", x) \ ); \ } while(0) ``` --- ### 五、引用案例解析 引用[3]的代码说明: ```c int a = 10; a += 1.1f; // a仍为int类型 _Generic(a, int: b++, // 匹配此分支 float: c++ ); ``` **解析**:`_Generic`根据变量声明类型判断,而非运算结果类型。虽然进行了浮点运算,但a的类型仍然是int[^3] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值