喜欢此链接http://gcc.gnu.org/onlinedocs/gcc-3.3.1/gcc/Labels-as-Values.html
我可以获取标签的内存地址,所以如果我声明一个标签,获取你的地址,并添加你的地址,我会转到下一条指令吗?一些ilustration>
int main () {
void *ptr;
label:
instruction 1;
instruction 2;
ptr = &&label;
// So if I do it...
ptr = ptr + 1;
// I will get the instruction 2 correct??
感谢所有答案。
答案 0 :(得分:4)
不,我不这么认为。
首先,您似乎采用了标签的地址,但这不起作用。标签由编译器解释,但它不代表代码中的实际地址。
其次,C / C ++中的每个语句(实际上是任何语言)都可以转换为许多机器语言指令,因此指令1可以转换为3,5,10或甚至更多的机器指令。
第三,你的指针指向无效。 C编译器不知道如何递增void指针。通常,当您递增指针时,它会将您指向的数据类型的大小添加到该地址。因此,增加一个长指针将增加4个字节;递增一个char指针将增加1个字节。在这种情况下,你有一个void-pointer,它指向什么都没有,因此无法递增。
第四,我不认为x86机器语言中的所有指令都用相同的字节数表示。因此,您不能指望向指针添加内容以使其到达下一条指令。您可能也会在下一条指令的中间结束。
答案 1 :(得分:1)
请注意,GCC文档中的 C Extensions 部分描述了&&label
语法。它不是C,它是 GC C。
另外,void*
不允许指针算术 - 它是C中用于指向任何事物的 catch-all 类型。假设编译器不知道它指向的对象的大小(但程序员应该:)。
甚至更多,指令大小在不同的体系结构上有很大的不同 - 例如SPARC上的四个字节,但x86上的长度可变。例如。
即。它在C中不起作用。你必须使用inline assembler来做这类事情。
答案 2 :(得分:1)
不,因为你不能增加void *。
void fcn() { printf("hello, world\n"); }
int main()
{
void (*pt2Function)() = fcn;
pt2Function(); // calls fcn();
// error C2171: '++' : illegal on operands of type 'void (__cdecl *)(void)'
// ++pt2Function;
return 0;
}
这是VC ++,但我怀疑gcc是相似的。
已编辑添加
只是为了好玩,我试过这个 - 它崩溃了:
int nGlobal = 0;
__declspec(naked) void fcn()
{
// nop is 1-byte instruction that does nothing
_asm { nop }
++nGlobal;
_asm { ret }
}
int main()
{
void (*pt2Function)() = fcn;
// this works, incrementing nGlobal:
pt2Function();
printf("nGlobal: %d", nGlobal);
char *p = (char *) pt2Function;
++p; // point past the NOP?
pt2Function = (void (*)()) p;
// but this crashes...
pt2Function();
printf("nGlobal: %d", nGlobal);
return 0;
}
它崩溃了,因为这条线没有按照我的想法做到:
void (*pt2Function)() = fcn;
我思考它将采用fcn()
的第一条指令的地址,并将其放入pt2Function
。这样我的++p
会指向第二条指令(nop
长度为一个字节)。
没有。它将 jmp
指令的地址(在大跳转表中找到)放入pt2Function
。当你将它递增一个字节时,它指向跳转表中无意义的位置。
我认为这是特定于实现的。
答案 3 :(得分:0)
我会说“可能不是”。指针的值是正确的,因为编译器知道,但我怀疑+ 1会知道指令的长度。
答案 4 :(得分:0)
你不能对void *执行算术运算,并且编译器不会知道要添加到指针的内容,无论如何都指向下一个'指令' - C语句和C语句之间没有1对1的对应关系编译器发出的机器代码。即使对于具有“常规”指令集的CPU,其中指令的大小相同(与x86中的指令具有可变字节数的情况相反),单个C语句可能会产生多个CPU指令(或者可能只有一个) - 谁知道?)。
扩展GCC文档中的示例,您可能可以使用以下内容,但它需要为您要定位的每个语句添加标签:
void *statements[] = { &&statement1, &&statement2 };
void** ptr;
statement1:
instruction 1;
statement2:
instruction 2;
ptr = statements;
// goto **ptr; // <== this will jump to 'instruction 1'
// goto **(ptr+1); // <== this will jump to 'instruction 2'
答案 5 :(得分:0)
让我们假设有一种获取标签地址的方法(不是特定编译器的扩展)。那么问题就是“下一个指令”的想法:很难知道下一条指令是哪一个。这取决于处理器,并且在像x86这样的处理器上知道你必须解码它的指令的长度,当然不是完全的,但无论如何它是一些复杂的工作...在着名的RISC架构上,指令的长度要容易得多获取下一条指令可能就像将地址递增4一样简单。但是在运行时没有通用的方法,而在编译时它可能更容易,但是以C连贯的方式允许它,C应该具有类型“指令”,因此“指令*”可以是指向指令的指针,并且递增这样的指针将正确指向下一条指令,前提是代码在编译时已知(因此,这样的指针可以' t指向真正指向一般指针的一切)。在编译时,编译器可以轻松地在“第一个”“标签”指向的生成指令之外添加另一个“标签”来实现此功能。但这会是作弊......
此外,让我们假设您获得C标签或C函数的地址,或者其他什么。如果你跳过第一条指令,可能你将无法“使用”该地址来执行代码(少于第一条指令),因为如果没有那条指令,代码可能会变成错误......除非你确定你知道可以跳过那条单指令并获得你想要的东西,但你不能确定...除非你看一下代码(可能与编译器不同,编译器),然后做这样的事情的所有要点从C消失。
所以,简单来说,答案是否定的,你无法计算指向下一条指令的指针;如果你做某事,你指向代码的事实变得毫无意义,因为你不能跳到那个地址并确保最终的行为。