C语言function定义与调用有哪些关键规则?

99ANYc3cd6
预计阅读时长 21 分钟
位置: 首页 C语言 正文

核心概念

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

c语言 function_
(图片来源网络,侵删)

在任何函数的内部,你都可以直接使用 __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

c语言 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__ 地址也不同,这证明了它是一个为每个函数单独创建的静态字符串。

c语言 function_
(图片来源网络,侵删)

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 语言编程中提升代码可维护性和调试效率的利器。

-- 展开阅读全文 --
头像
while(scanf)在C语言中如何正确使用与终止?
« 上一篇 2025-12-16
Discuz如何免激活整合Dede账户?
下一篇 » 2025-12-16

相关文章

取消
微信二维码
支付宝二维码

目录[+]