用C表示类型转换并键入安全性

时间:2012-12-30 14:53:53

标签: c types casting type-safety

在米切尔的书(概念编程语言)第6.2.1章中,它提到:

  

类型转换。类型转换允许将一种类型的值用作另一种类型。特别是在C中,可以将整数转换为函数,允许跳转到不包含正确形式的指令的位置为C函数。

所以我开始使用这种非安全性并做一些与众不同的事情 我试过这样的事情(伪代码):

int x = 0;
print "loop";
x();

创建无限循环。我试过改变和测试,但我无法应对。 我怎么能做这些东西或其他所有东西呢?

提前致谢

4 个答案:

答案 0 :(得分:6)

这不是它的工作方式。

在C中,可以将整数值强制转换为函数指针值,然后调用这样的函数指针

void (*ptr)() = (void (*)())42;
ptr();

但这可能只会导致崩溃,除非您确切知道自己在做什么,即您已经以某种方式知道在地址42处启动具有该签名的函数;在这个例子中有一个固定的地址可能会在系统编程中发生,而在应用程序编程中则非常罕见。

实际发生的次数比想要的更频繁(特别是在Windows编程中)是传递给某些整数的函数指针(LPARAM / WPARAM中的回调任何人?),然后将它们转换回函数指针以实际使用它们。

此外,执行此类强制转换/调用时会发生什么情况超出了C标准的范围,这使得实现可以自由地在这方面做他们想要的事情。

答案 1 :(得分:3)

@Soroush,这是一个可以帮助您更好地了解幕后情况的例子:

#include <stdio.h>

int main(void)
{
    printf("begin\n");
    printf("loop\n");

    // declare a function pointer
    int (*loopPtr)();
    // set the function pointer to the current function
    loopPtr = main;
    // skip over the first printf();
    loopPtr += 22;
    // call the new location
    loopPtr();
}

对我来说,当用clang -O0编译时,它适用于x86_64(好吧,它一直有效,直到堆栈耗尽,因为这是无限递归,每个函数调用都会通过堆栈空间进行咀嚼)。

我通过编译,然后从第二个main()的地址中反汇编并从printf()开始减去地址来确定偏移量。

首先,我编译了它:

clang -O0 test.c

然后拆开它:

otool -tv a.out

...产生了这个输出:

[...]
_main:
0000000100000ee0    pushq   %rbp
0000000100000ee1    movq    %rsp,%rbp
0000000100000ee4    subq    $0x20,%rsp
0000000100000ee8    leaq    0x00000073(%rip),%rdi
0000000100000eef    movb    $0x00,%al
0000000100000ef1    callq   0x100000f40
0000000100000ef6    leaq    0x0000006c(%rip),%rdi
0000000100000efd    movl    %eax,0xf4(%rbp)
0000000100000f00    movb    $0x00,%al
0000000100000f02    callq   0x100000f40
[...]

_main:表示main()函数的入口点,其第一个地址为0x100000ee0。第一个callq指令对应第一个printf()调用,我想跳过它,所以我选择了之后的地址:0x100000ef6。 0x100000ef6减去0x100000ee0是小数点后22位。

答案 2 :(得分:2)

好吧,x()并没有神奇地调用该地址的函数。我认为他的意思是:

typedef void (*functionPtr)();

int x;
//...
functionPtr foo = (functionPtr)x;
//or
functionPtr goo = (functionPtr)&x;
foo();

答案 3 :(得分:1)

你的书有误导性。标准允许的唯一事情是强制转换操作,只有当值适合时才这样做。在大多数情况下,执行这样的函数指针是未定义的行为。您必须非常了解您的系统才能实现这一目标。所以引用段落的第二部分是:

  

特别是在C中,可以将整数转换为函数,允许a   跳转到不包含正确形式的位置   指令是C函数。

在这种形式中是不正确的。特别是所有体面的现代系统都不允许您执行数据,您必须为页面设置特殊标志,以便可以认为它包含可执行代码。