C语言中函数指针究竟是什么?栈上执行指令失败解惑
栈上执行机器码失败的原因及解决方案
核心原因:数据执行保护(DEP)
现代Windows系统默认开启数据执行保护(DEP),栈、堆这类数据区域默认被标记为不可执行。你把机器码放在栈上调用时,CPU会直接拒绝执行非可执行区域的代码,触发内存访问异常——这才是程序失败的根本原因,和函数指针的理解、机器码正确性无关。
关于函数指针的理解
你对函数指针的基本认知是对的:它本质是存储函数入口地址的指针,可直接调用。但标准C明确规定,对象指针(比如void*)和函数指针属于不同类型,两者转换属于未定义行为。不过多数编译器(包括Clang)为了兼容底层编程场景,提供了扩展支持,这也是开启-Wpedantic -Werror仍能编译通过的原因,但这个编译层面的允许和你程序运行失败完全无关。
Windows/MSVCRT的影响
MSVCRT本身不影响DEP的开启,DEP是Windows系统级的安全机制(从Vista开始默认启用),不管用哪个C运行时,栈默认都是不可执行的。要让栈上的机器码能执行,必须手动修改对应内存区域的权限。
解决方法:修改内存权限
使用Windows API VirtualProtect将存储机器码的栈区域标记为可执行。以下是示例代码(以x86架构为例):
#include <windows.h> #include <stdio.h> int main() { // x86下返回3的机器码:mov eax, 3; ret unsigned char code[] = {0xB8, 0x03, 0x00, 0x00, 0x00, 0xC3}; int (*func)() = (int (*)())code; DWORD old_protect; // 将内存区域设置为可读写可执行 if (!VirtualProtect(code, sizeof(code), PAGE_EXECUTE_READWRITE, &old_protect)) { printf("VirtualProtect failed: %d\n", GetLastError()); return 1; } int result = func(); printf("Result: %d\n", result); // 可选:恢复原内存权限,降低安全风险 VirtualProtect(code, sizeof(code), old_protect, &old_protect); return 0; }
如果是x64架构,机器码需要对应修改:
// x64下返回3的机器码:mov rax, 3; ret unsigned char code[] = {0x48, 0xC7, 0xC0, 0x03, 0x00, 0x00, 0x00, 0xC3};
额外注意事项
- 必须确保机器码和目标架构(x86/x64)完全匹配,否则即使权限正确,执行也会出错。
- 手动修改内存权限后,建议恢复原权限,避免引入不必要的安全风险。
内容的提问来源于stack exchange,提问作者William Ryman
相关产品推荐
相关产品推荐

