核心概念
__func__ 是一个在 C 语言标准(自 C99 起)中预定义的标识符,它属于一个特殊的“函数本地静态字符串”。

在任何函数的内部,你都可以直接使用 __func__,它的值就是一个字符串字面量,代表当前函数的名称。
基本用法
__func__ 的使用非常简单,它就像一个普通的字符数组(const char[])。
#include <stdio.h>
void my_function() {
// 在函数内部直接使用 __func__
printf("This function is named: %s\n", __func__);
printf("The address of __func__ is: %p\n", (void*)__func__);
}
int main() {
my_function();
// 注意:__func__ 只在函数作用域内有效
// 如果在 main 函数外部使用,会编译错误
// printf("This will cause a compile error: %s\n", __func__);
return 0;
}
输出:
This function is named: my_function
The address of __func__ is: 0x55f0d945c014 // 地址可能不同
从输出可以看出,__func__ 确实包含了函数的名字 my_function。

关键特性与工作原理
理解 __func__ 的几个关键特性非常重要:
a. 静态存储
__func__ 具有静态存储期,这意味着它在程序的整个生命周期内都存在,而不是在函数调用时创建,函数返回时销毁,它的内存是在编译时分配的。
void another_function() {
printf("Address of __func__ in another_function: %p\n", (void*)__func__);
}
void my_function() {
printf("Address of __func__ in my_function: %p\n", (void*)__func__);
}
输出(地址可能不同):
Address of __func__ in my_function: 0x55f0d945c020
Address of __func__ in my_function: 0x55f0d945c020 // 注意,这个地址和上面的 my_function 地址一样
Address of __func__ in another_function: 0x55f0d945c034
你会发现,同一个函数里的 __func__ 地址是固定的,而不同函数里的 __func__ 地址也不同,这证明了它是一个为每个函数单独创建的静态字符串。

b. 只读
__func__ 指向的字符串是只读的,你不能修改它。
void invalid_function() {
// __func__ 是一个 const char[],不能修改
// __func__[0] = 'M'; // 这行代码会导致编译错误!
printf("%s\n", __func__);
}
编译器会报错,类似:error: assignment of read-only location '__func__[0]'。
c. 作用域
__func__ 的作用域仅限于其所在的函数体内部,你不能在函数外部使用它。
// 编译错误! // const char* func_name = __func__;
d. 与 #define 宏的区别
在 C99 标准化 __func__ 之前,开发者通常使用一个宏技巧来获取函数名:
#define GET_FUNCTION_NAME() __func__ // 这是 C99 的方式 // 旧方式 (C89/C90) #define GET_FUNCTION_NAME() __FUNCTION__
__func__ vs __FUNCTION__ vs __func__:
| 特性 | __func__ (C99 标准) |
__FUNCTION__ (GCC/Clang 扩展) |
|---|---|---|
| 标准 | C99 及以后的官方标准 | GCC 和 Clang 编译器的非标准扩展 |
| 可移植性 | 高,任何符合 C99 标准的编译器都支持。 | 低,MSVC 使用 __FUNCTION__,但其他编译器可能不支持或行为不同。 |
| 推荐度 | 强烈推荐,为了代码的可移植性,应优先使用 __func__。 |
不推荐,除非你需要兼容非常旧的编译器或特定于 GCC 的代码。 |
最佳实践: 始终使用 __func__,除非你有特殊原因需要使用编译器特定的扩展。
实际应用场景
__func__ 在调试和日志记录中非常有用。
a. 调试和日志记录
这是最常见的用途,在打印日志信息时,包含函数名可以快速定位问题发生的代码位置。
#include <stdio.h>
void log_info(const char* message) {
// 打印文件名、函数名、行号和消息
printf("[INFO] (%s, %s, %d): %s\n", __FILE__, __func__, __LINE__, message);
}
void process_data(int data) {
log_info("Starting data processing...");
if (data < 0) {
log_info("Error: Negative data received!");
return;
}
// ... 数据处理逻辑 ...
log_info("Data processing finished successfully.");
}
int main() {
process_data(100);
process_data(-50);
return 0;
}
输出:
[INFO] (your_source_file.c, process_data, 12): Starting data processing...
[INFO] (your_source_file.c, process_data, 18): Data processing finished successfully.
[INFO] (your_source_file.c, process_data, 12): Starting data processing...
[INFO] (your_source_file.c, process_data, 15): Error: Negative data received!
结合 __FILE__ (文件名), __func__ (函数名), 和 __LINE__ (行号),你可以构建非常强大的日志系统。
b. 错误处理
在错误处理函数中,__func__ 可以用来明确指出是哪个函数触发了错误。
#include <stdio.h>
#include <errno.h>
#include <string.h>
void check_file_open(const char* filename) {
FILE* file = fopen(filename, "r");
if (file == NULL) {
// 使用 __func__ 和 strerror(errno) 提供详细的错误信息
fprintf(stderr, "Error in function %s: Could not open file '%s'. Reason: %s\n",
__func__, filename, strerror(errno));
return;
}
printf("Successfully opened file '%s' in %s\n", filename, __func__);
fclose(file);
}
int main() {
check_file_open("existing_file.txt");
check_file_open("non_existent_file.txt");
return 0;
}
输出:
Successfully opened file 'existing_file.txt' in check_file_open
Error in function check_file_open: Could not open file 'non_existent_file.txt'. Reason: No such file or directory
c. 断言
虽然 C 标准库的 assert 宏已经包含了函数名,但如果你要自己实现一个简单的断言系统,__func__ 是必不可少的。
#include <stdio.h>
#include <stdlib.h>
// 一个简单的自定义断言
void my_assert(int condition, const char* expr, const char* file, int line) {
if (!condition) {
fprintf(stderr, "Assertion failed: (%s), function %s, file %s, line %d\n",
expr, __func__, file, line);
abort();
}
}
#define MY_ASSERT(condition) my_assert((condition), #condition, __FILE__, __LINE__)
void dangerous_operation(int value) {
MY_ASSERT(value >= 0 && "Value must be non-negative");
printf("Operation with value %d is safe.\n", value);
}
int main() {
dangerous_operation(10);
dangerous_operation(-5); // 这将触发断言失败
return 0;
}
输出:
Operation with value 10 is safe.
Assertion failed: (value >= 0 && "Value must be non-negative"), function dangerous_operation, file your_source_file.c, line 19
程序在断言失败后调用 abort() 终止。
| 特性 | 描述 |
|---|---|
| 是什么 | 一个预定义的标识符,代表当前函数的名称。 |
| 类型 | const char[] (只读的静态字符数组)。 |
| 标准 | C99 及以后版本的标准。 |
| 作用域 | 仅限于函数内部。 |
| 生命周期 | 静态存储期,程序运行期间一直存在。 |
| 主要用途 | 调试、日志记录、错误处理,帮助开发者快速定位代码位置和问题源头。 |
| 最佳实践 | 优先使用 __func__,而不是编译器特定的 __FUNCTION__,以保证代码的可移植性。 |
__func__ 是一个虽然小但极其强大的工具,是现代 C 语言编程中提升代码可维护性和调试效率的利器。
