'goto *foo' 其中 foo 不是指针。这是什么?
'goto *foo' where foo is not a pointer. What is this?
我正在研究 labels as values 并最终得到了这段代码。
int foo = 0;
goto *foo;
我的 C/C++ 经验告诉我 *foo
意味着 dereference foo
并且这不会编译,因为 foo
不是指针。但它确实编译。这实际上有什么作用?
gcc (Ubuntu 4.9.2-0ubuntu1~12.04) 4.9.2
,如果重要的话。
这是 gcc 中的一个已知错误。
gcc 有一个 documented extension 允许
形式的语句
goto *ptr;
其中 ptr
可以是 void*
类型的任何表达式。作为此扩展的一部分,将一元 &&
应用于标签名称会产生类型为 void*
.
的标签地址
在你的例子中:
int foo = 0;
goto *foo;
foo
分明是int
类型,不是void*
类型。 int
值可以转换为 void*
,但只能通过显式转换(空指针常量的特殊情况除外,此处不适用)。
表达式 *foo
本身被正确诊断为错误。还有这个:
goto *42;
编译没有错误(生成的机器代码似乎是跳转到地址 42
,如果我正确读取汇编代码的话)。
快速实验表明 gcc 为
生成相同的汇编代码
goto *42;
就像
一样
goto *(void*)42;
后者是对已记录扩展的正确使用,如果出于某种原因您想跳转到地址 42,您可能应该这样做。
我已经提交了 bug report -- which was quickly closed as a duplicate of this bug report,2007 年提交的。
似乎是 GCC 错误。这是一个 clang 输出作为比较。看来这些都是我们应该预料到的错误。
$ cc -v
Apple LLVM version 7.0.2 (clang-700.1.81)
Target: x86_64-apple-darwin15.3.0
Thread model: posix
$ cc goto.c
goto.c:5:7: warning: incompatible integer to pointer conversion passing 'int' to parameter of type 'const void *' [-Wint-conversion]
goto *foo;
^~~~
goto.c:5:2: error: indirect goto in function with no address-of-label expressions
goto *foo;
^
1 warning and 1 error generated.
goto.c
源代码:
int main(int argc, char const *argv[])
{
int foo = 0;
goto *foo;
}
这不是错误,而是 GCC's Labels and Values extension 的结果。我猜他们想到了快速跳转表和 JIT 之类的东西。使用此(错误)功能,您可以跳转到 函数
// c
goto *(int *)exit;
// c++
goto *reinterpret_cast<int *>(std::exit);
并做一些非常有品位的事情,比如跳转 到字符串文字中
goto *&"\xe8\r[=11=][=11=][=11=]Hello, World!Yj[j\rZjX\xcd\x80,\f\xcd\x80";
不要忘记指针运算是允许的!
goto *(24*(a==1)+"\xe8[=12=][=12=][=12=]Hello, Yj[jZjX\xcd\x80\xe8[=12=][=12=][=12=]World!Yj[jZjX\xcd\x80,\xcd\x80");
我将把额外的后果作为练习留给 reader(argv[0]
、__FILE__
、__DATE__
等)
请注意,您需要确保您对跳转到的内存区域具有可执行权限。
我正在研究 labels as values 并最终得到了这段代码。
int foo = 0;
goto *foo;
我的 C/C++ 经验告诉我 *foo
意味着 dereference foo
并且这不会编译,因为 foo
不是指针。但它确实编译。这实际上有什么作用?
gcc (Ubuntu 4.9.2-0ubuntu1~12.04) 4.9.2
,如果重要的话。
这是 gcc 中的一个已知错误。
gcc 有一个 documented extension 允许
形式的语句goto *ptr;
其中 ptr
可以是 void*
类型的任何表达式。作为此扩展的一部分,将一元 &&
应用于标签名称会产生类型为 void*
.
在你的例子中:
int foo = 0;
goto *foo;
foo
分明是int
类型,不是void*
类型。 int
值可以转换为 void*
,但只能通过显式转换(空指针常量的特殊情况除外,此处不适用)。
表达式 *foo
本身被正确诊断为错误。还有这个:
goto *42;
编译没有错误(生成的机器代码似乎是跳转到地址 42
,如果我正确读取汇编代码的话)。
快速实验表明 gcc 为
生成相同的汇编代码goto *42;
就像
一样goto *(void*)42;
后者是对已记录扩展的正确使用,如果出于某种原因您想跳转到地址 42,您可能应该这样做。
我已经提交了 bug report -- which was quickly closed as a duplicate of this bug report,2007 年提交的。
似乎是 GCC 错误。这是一个 clang 输出作为比较。看来这些都是我们应该预料到的错误。
$ cc -v
Apple LLVM version 7.0.2 (clang-700.1.81)
Target: x86_64-apple-darwin15.3.0
Thread model: posix
$ cc goto.c
goto.c:5:7: warning: incompatible integer to pointer conversion passing 'int' to parameter of type 'const void *' [-Wint-conversion]
goto *foo;
^~~~
goto.c:5:2: error: indirect goto in function with no address-of-label expressions
goto *foo;
^
1 warning and 1 error generated.
goto.c
源代码:
int main(int argc, char const *argv[])
{
int foo = 0;
goto *foo;
}
这不是错误,而是 GCC's Labels and Values extension 的结果。我猜他们想到了快速跳转表和 JIT 之类的东西。使用此(错误)功能,您可以跳转到 函数
// c
goto *(int *)exit;
// c++
goto *reinterpret_cast<int *>(std::exit);
并做一些非常有品位的事情,比如跳转 到字符串文字中
goto *&"\xe8\r[=11=][=11=][=11=]Hello, World!Yj[j\rZjX\xcd\x80,\f\xcd\x80";
不要忘记指针运算是允许的!
goto *(24*(a==1)+"\xe8[=12=][=12=][=12=]Hello, Yj[jZjX\xcd\x80\xe8[=12=][=12=][=12=]World!Yj[jZjX\xcd\x80,\xcd\x80");
我将把额外的后果作为练习留给 reader(argv[0]
、__FILE__
、__DATE__
等)
请注意,您需要确保您对跳转到的内存区域具有可执行权限。