单片机中用printf输出函数的地址
在 单片机 C 语言 中,如果你想使用 printf
输出一个函数的地址,需要注意一些细节,因为 %p
在嵌入式环境中可能行为不同,而且函数指针和普通对象指针也有区别。
1. 直接用 %p
输出函数指针
标准 C 并没有保证 %p
能正确打印函数指针,因为函数指针不一定能转换为 void *
。
但在很多单片机编译器(如 Keil C51、Keil MDK-ARM、IAR)中,%p
会直接输出函数的地址(程序存储器地址)。
示例:
#include <stdio.h>void my_func(void) {printf("This is my_func\n");
}void main(void) {printf("Function address: %p\n", my_func);
}
Keil C51 可能的输出:
Function address: 000Ch
(这里的地址是程序存储区的地址,即 ROM 地址)
2. 如果 %p
不能正常工作
某些编译器对 %p
的实现只接受 void *
类型,如果传入函数指针会有警告或错误,这时可以强制转换为整数类型输出。
示例(推荐):
#include <stdio.h>// 定义一个无参数、无返回值的函数
void my_func(void) {printf("Hello\n");
}void main(void) {// 强制转换为无符号长整型输出printf("Function address: 0x%04lX\n", (unsigned long)my_func);
}
输出(C51,假设函数在 0x000C):
Function address: 0x000C
3. 为什么不推荐直接用 %p
打印函数指针?
- C 标准:
%p
要求参数是void *
类型,函数指针转换到void *
是条件支持的。 - 嵌入式平台差异:
- 在哈佛架构单片机(如 8051、AVR)中,代码空间和数据空间是分开的,函数指针和数据指针宽度可能不同。
- 在 Keil C51 中,函数指针是 code 指针(16位或24位),而
void *
是 data 指针。
- 可移植性差:不同编译器对
%p
的实现不一致。
4. 最佳实践
如果需要在单片机中打印函数地址,建议用整数类型强制转换:
printf("Func addr: 0x%04lX\n", (unsigned long)my_func);
这样在绝大多数嵌入式编译器中都能正常工作,且可读性好。
5. 51 单片机注意事项(Keil C51)
- 在 C51 中,函数默认在 code 区(程序存储区)。
- 函数指针类型是
void (*)(void)
,占用 2 字节(16位地址)。 printf
输出时,用%p
会显示成十进制或十六进制地址(取决于编译器库实现)。- 推荐用
%04X
或%04lX
格式化输出,这样能显示 4 位十六进制地址。