我目前正在练习C语言。
一切看似合理,但是当我遇到这些初始化和功能时
unsigned long get_sp(void) {
__asm__("movl %esp,%eax\n\t"
"and $0xff000000, %eax"
);
}
int (*fp)(char *)=(int(*)(char *))&puts;
我真的不知道这些线是什么意思。
什么是真正的变量?它是什么类型的? ...
有人可以深入解释我吗?
答案 0 :(得分:3)
1,您正在定义一个返回unsigned long的函数get_sp()
。
该函数的内容是一些内联汇编,它获取堆栈指针地址,将其放入寄存器eax,然后将其与0xff000000进行对比。 IE:获取eax中的值,该值设置了堆栈指针地址的前8位中的任何一位。 eax寄存器用于返回值,因此返回此屏蔽堆栈指针。
第二行是为fp分配函数puts
的地址。
puts
是一个返回int
的函数,并期望输入char *
。因此类型/名称int (*fp)(char *)
。
在该行之后,您可以将puts函数调用为fp("hello");
答案 1 :(得分:2)
第二部分只是一个带有初始化器的函数指针(强制&puts
)。
第一部分更有趣:
它将%esp
变为C变量,并屏蔽低24位。即向下舍入到16MiB边界。 IDK是什么意思,但是非内联函数调用的4B偏移量可能会或可能不重要(如果%esp
非常接近16MiB边界,或者明确表示你正在尝试什么检测。)
如果编译器有机会内联(例如使用跨文件内联),问题中发布的版本将以非显而易见的方式破坏您的代码。它不是从asm
语句中正确声明输出操作数,而是在没有return语句的函数内修改%eax
。与告诉编译器你返回的内容相比,这真的很愚蠢,没有任何优势。
/********* Safe version of the function *************/
// Actually unsigned long was fine, since this asm only works on 32bit anyway
static inline uintptr_t get_sp(void) {
uintptr_t result;
__asm__("movl %%esp, %0\n\t"
: "=g" (result)
);
result &= 0xff000000; // do this outside the inline asm so the compiler knows that the low 24b are always zero.
return result;
}
此compiles to the same asm when not inlined,但可以安全地内联。 (例如,将其放在带有static inline
的标题中)。它当然也避免编译器警告有关缺少返回值的函数。
正如Michael Petch在评论中指出的那样,将其作为一个始终内联函数,甚至是一个宏,对于一致性来说可能是一个好主意。 (尽管优化与未优化的构建将消耗不同数量的堆栈空间。)
请参阅inline-assembly标记wiki,了解更多关于如何编写GNU C inline asm的信息。