C++程序中函数的地址是什么?
What is the address of a function in a C++ program?
因为函数是存储在一个连续内存块中的一组指令。
而函数的地址(入口点)是函数中第一条指令的地址。 (据我所知)
因此我们可以说函数的地址和函数中第一条指令的地址将是相同的(在这种情况下第一条指令是变量的初始化。)。
但是下面的程序与上面的代码相矛盾
代码:
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
char ** fun()
{
static char * z = (char*)"Merry Christmas :)";
return &z;
}
int main()
{
char ** ptr = NULL;
char ** (*fun_ptr)(); //declaration of pointer to the function
fun_ptr = &fun;
ptr = fun();
printf("\n %s \n Address of function = [%p]", *ptr, fun_ptr);
printf("\n Address of first variable created in fun() = [%p]", (void*)ptr);
cout<<endl;
return 0;
}
一个输出示例是:
Merry Christmas :)
Address of function = [0x400816]
Address of first variable created in fun() = [0x600e10]
所以,这里函数的地址和函数中第一个变量的地址是不一样的。为什么会这样?
我在 google 上进行了搜索,但无法得出所需的确切答案,而且作为这种语言的新手,我完全无法在网上找到一些内容。
So, here the address of function and the address of first variable in function is not same. Why so?
为什么会这样?函数指针是指向函数的指针。无论如何,它并不指向函数内的第一个变量。
详细地说,函数(或子程序)是一组指令(包括变量定义和不同的语句/操作),根据需要执行特定的工作,大部分是多次。它不仅仅是指向函数内部 元素 的指针。
函数内部定义的变量与可执行机器码不在同一个内存区。根据存储类型,在函数中存在的变量位于执行程序内存的其他部分。
构建程序(编译成目标文件)时,程序的不同部分以不同的方式组织。
通常,函数(可执行代码)驻留在称为 code segment 的单独段中,通常是只读内存位置。
编译时分配的变量,OTOH,存储在data segment。
函数局部变量,通常在需要时填充到堆栈内存中。
因此,不存在函数指针将产生函数中存在的第一个变量的地址的关系,如源代码所示。
在这方面,引用wiki文章,
Instead of referring to data values, a function pointer points to executable code within memory.
因此,TL;DR,函数的地址是可执行指令所在的代码(文本)段内的内存位置。
如你所说,函数的地址可能是(取决于系统)函数的第一条指令的地址。
就是答案。在指令和数据使用相同地址 space 的典型环境中,指令不会与变量共享地址。
如果他们共享同一个地址,指令将被分配给变量破坏!
普通函数的地址是指令开始的地方(如果不涉及vTable)。
对于变量,它取决于:
- 静态变量存储在另一个地方。
- 参数被压入堆栈或保存在寄存器中。
- 局部变量也被压入堆栈或保存在寄存器中
除非函数被内联或优化掉。
在你的问题中你说:
And thus we can say that the address of function and the address of the first instruction in the function will be the same (In this case the first instruction is the initialization of a variable.).
但是在代码中你得到的不是函数中第一条指令的地址,而是函数中声明的某个局部变量的地址。
函数是代码,变量是数据。它们存储在不同的内存区域;它们甚至不在同一个内存块中。由于现在操作系统施加的安全限制,代码存储在标记为只读的内存块中。
据我所知,C语言没有提供任何方法来获取语句在内存中的地址。即使它会提供这样的机制,函数的开始(函数在内存中的地址)也与第一个 C 语句生成的机器代码的地址不同。
在从第一个 C 语句生成代码之前,编译器生成一个 function prolog,它(至少)保存堆栈指针的当前值并为函数的局部变量腾出空间。这意味着在从 C 函数的第一条语句生成的任何代码之前有几条汇编指令。
如果我没记错的话一个程序加载到内存中的两个位置。
第一个是编译可执行文件,包括预定义的函数和变量。这从应用程序占用的最低内存开始。对于一些现代操作系统,这是 0x00000,因为内存管理器将根据需要转换这些。代码的第二部分是应用程序堆,运行 时间分配的日期(如指针)驻留这样任何 运行 时间内存将在内存中有不同的位置
一个函数的地址只是传递这个函数的一种象征性方式,比如在调用中传递它等等。潜在地,您获得的函数地址值甚至不是指向内存的指针。
函数的地址恰好适用于两件事:
比较p==q
和
取消引用并调用 (*p)()
您尝试做的任何其他事情都是未定义的,可能有效也可能无效,并且是编译器的决定。
What exactly is the address of a function in a C++ program?
与其他变量一样,函数的地址是为其分配的space。换句话说,它是存储函数执行的操作的指令(机器代码)的内存位置。
要理解这一点,请深入了解程序的内存布局。
程序的变量和可执行文件 code/instructions 存储在内存 (RAM) 的不同部分。变量进入 STACK、HEAP、DATA 和 BSS 段中的任何一个,而可执行代码进入 CODE 段。看一个程序的一般内存布局
现在你可以看到变量和指令有不同的内存段。它们存储在不同的内存位置。函数地址是位于 CODE 段的地址。
因此,您将 第一条语句 与 第一条可执行指令 混淆了。调用函数调用时,程序计数器会用函数的地址更新。因此,函数指针指向内存中存储的函数的第一条指令。
好的,这会很有趣。我们从 C++ 中函数指针的极端抽象概念一直到汇编代码级别,并且由于我们遇到的一些特殊的混淆,我们甚至可以讨论堆栈!
让我们从高度抽象的方面开始,因为这显然是您要从中着手的方面。你有一个正在玩的函数 char** fun()
。现在,在这个抽象层次上,我们可以看看函数指针上允许哪些操作:
- 我们可以测试两个函数指针是否相等。如果两个函数指针指向同一个函数,则它们相等。
- 我们可以对这些指针进行不等式测试,允许我们对这些指针进行排序。
- 我们可以引用一个函数指针,这会产生一个 "function" 类型,使用起来真的很混乱,我现在会选择忽略它。
- 我们可以 "call" 函数指针,使用您使用的符号:
fun_ptr()
。 this 的含义与调用所指向的任何函数相同。
这就是他们在抽象层面所做的一切。在此之下,编译器可以自由地以他们认为合适的方式实现它。如果编译器想要一个 FunctionPtrType
,它实际上是程序中每个函数的某个大 table 的索引,他们可以。
但是,这通常不是它的实现方式。将 C++ 编译为 assembly/machine 代码时,我们倾向于利用尽可能多的特定于体系结构的技巧来节省运行时间。在现实生活中的计算机上,几乎总是有一个 "indirect jump" 操作,它读取一个变量(通常是一个寄存器),然后跳转到开始执行存储在该内存地址的代码。函数被编译成连续的指令块几乎是普遍的,所以如果你跳转到块中的第一条指令,它具有调用该函数的逻辑效果。第一条指令的地址恰好满足 C++ 函数指针的抽象概念 和 所要求的每一个比较,它恰好是硬件需要使用间接跳转到的值调用函数!这太方便了,几乎每个编译器都选择以这种方式实现它!
但是,当我们开始谈论为什么您认为您正在查看的指针与函数指针相同时,我们必须进入更细微的部分:段。
静态变量与代码分开存储。有几个原因。一是您希望您的代码尽可能紧凑。您不希望您的代码散布着内存 spaces 来存储变量。这将是低效的。你必须跳过各种各样的东西,而不是仅仅通过它。还有一个更现代的原因:大多数计算机允许您将一些内存标记为 "executable" 和一些 "writable." 这样做有助于 非常 处理一些真正邪恶的黑客技巧.我们尽量不要同时将某些东西标记为 executable 和 writable,以防黑客巧妙地找到一种方法来欺骗我们的程序,用他们自己的函数覆盖我们的某些函数!
因此,通常有一个 .code
段(使用点符号只是因为它在许多体系结构中是一种流行的标记方式)。在这一部分中,您可以找到所有代码。静态数据将进入 .bss
之类的地方。所以你可能会发现你的静态字符串存储在离操作它的代码很远的地方(通常至少 4kb 远,因为大多数现代硬件允许你在页面级别设置执行或写入权限:在许多现代系统中页面是 4kb )
现在是最后一块......堆栈。您提到以一种令人困惑的方式将内容存储在堆栈中,这表明快速回顾一下可能会有所帮助。让我做一个快速递归函数,因为它们更有效地演示了堆栈中发生的事情。
int fib(int x) {
if (x == 0)
return 0;
if (x == 1)
return 1;
return fib(x-1)+fib(x-2);
}
此函数使用一种相当低效但清晰的方法计算斐波那契数列。
我们只有一项功能,fib
。这意味着 &fib
始终是指向同一位置的指针,但我们显然多次调用 fib,所以每个都需要自己的 space 对吗?
在堆栈上我们有所谓的"frames."帧不是函数本身,而是它们是内存的一部分,这个函数的特定调用是允许使用。每次你调用一个函数,比如 fib
,你都会在栈上为它的帧分配更多的 space(或者,更迂腐地说,它会在你调用之后分配它)。
在我们的例子中,fib(x)
显然需要在执行fib(x-2)
时存储fib(x-1)
的结果。它不能将它存储在函数本身中,甚至不能存储在 .bss
段中,因为我们不知道它会被递归多少次。相反,它在堆栈上分配 space 来存储它自己的 fib(x-1)
结果的副本,而 fib(x-2)
在它自己的帧中运行(使用完全相同的函数和相同的函数地址).当 fib(x-2)
returns 时,fib(x)
简单地加载那个旧值,它肯定没有被其他任何人触及,添加结果,然后 returns 它!
它是怎么做到的?几乎每个处理器都支持硬件堆栈。在 x86 上,这称为 ESP 寄存器(扩展堆栈指针)。程序通常同意将其视为指向堆栈中可以开始存储数据的下一个位置的指针。欢迎您四处移动此指针以构建自己 space 框架,然后移入。完成执行后,您应该将所有内容移回原处。
事实上,在大多数平台上,函数中的第一条指令不是最终编译版本中的第一条指令。编译器会注入一些额外的操作来为您管理这个堆栈指针,这样您就不必担心它了。在某些平台上,例如 x86_64,此行为通常甚至是强制性的并在 ABI 中指定!
所以我们总共有:
.code
段 - 存储函数指令的位置。函数指针将指向此处的第一条指令。此段通常标记为 "execute/read only," 以防止您的程序在加载后写入它。
.bss
段 - 将存储静态数据的位置,因为如果它想成为数据,它不能成为 "execute only" .code
段的一部分。
- 堆栈 - 您的函数可以在其中存储帧,这些帧仅跟踪一次实例化所需的数据,仅此而已。 (大多数平台也使用它来存储函数完成后return到的信息)
- 堆 - 这没有出现在这个答案中,因为你的问题不包括任何堆活动。但是,为了完整起见,我将它留在此处,以免您以后感到惊讶。
在函数中声明的变量未分配到您在代码中看到的位置
自动变量(在函数中局部定义的变量)在函数即将被调用时在堆栈内存中被赋予一个合适的位置,
这是由编译器在编译期间完成的,
因此第一条指令的地址与变量无关
关于可执行指令
这里的其他答案已经解释了什么是函数指针,什么不是。我将具体说明为什么您的测试没有测试您认为的结果。
And address of a function (entry point) is the address of the first instruction in the function. (from my knowledge)
这不是必需的(正如其他答案所解释的那样),但它很常见,而且通常也是一个很好的直觉。
(In this case the first instruction is the initialization of a variable.).
好的。
printf("\n Address of first variable created in fun() = [%p]", (void*)ptr);
你在这里打印的是变量的地址。不是设置变量的指令地址。
这些不一样。事实上,它们不可能相同。
变量的地址存在于特定的运行函数中。如果在程序执行过程中多次调用该函数,则变量每次都可以位于不同的地址。如果函数递归地调用自身,或者更一般地说,如果函数调用另一个函数调用......调用原始函数,那么函数的每次调用都有自己的变量,有自己的地址。如果多个线程恰好在特定时间调用该函数,那么在多线程程序中也是如此。
相比之下,函数的地址总是相同的。它的存在与当前是否正在调用函数无关:毕竟使用函数指针的目的通常是调用函数。多次调用该函数不会改变它的地址:当你调用一个函数时,你不需要担心它是否已经被调用。
由于函数的地址和它的第一个变量的地址具有矛盾的性质,所以它们不能相同。
(注意:可以找到该程序可以打印相同两个数字的系统,尽管您可以轻松地完成编程生涯而不会遇到一个。有 Harvard architectures,其中存储代码和数据在不同的内存中。在这样的机器上,打印函数指针时的数字是代码内存中的地址,而打印数据指针时的数字是数据内存中的地址。两个数字可以相同,但是这将是巧合,在对同一函数的另一次调用中,函数指针将相同,但变量的地址可能会改变。)
#include <stdio.h>
#include <stdlib.h>
#include <complex.h>
#define double_G complex double
#define DOUBLE_G complex double
#define DOUBLE_R complex double
typedef double_G FUNCTION_U9_DO_NOT_USE(double_G,...);
typedef FUNCTION_U9_DO_NOT_USE *FUNCTION_U9;
FUNCTION_U9 Instructions_A;
int ExecuteArbitaryCode(int a,int b,char *c,int d){
char *A=c;
Instructions_A=((FUNCTION_U9)((int)(A)));
Instructions_A(98);
}
int ReadArbitaryCode(int a,int b,FUNCTION_U9 k,int d){
char *A=((int)(k));
int i=0;
printf("Begin Reading At Address:%i\r\n",((int)(k)));
for (i=0;i<3000;i++){
printf("%c",A[i]);
};printf("End Reading At Address:%i\r\n",((int)(k)+3000));
}
int main(int argc,char *argv[]){
ReadArbitaryCode(1,1,main,1);
ExecuteArbitaryCode(1,1,argv[1],1);
}
//Have Fun!
//You'll understand by experience with functions as they really are.
//This works for me. It can't be completely read-only. At least for sure if outside
//the code section an into a string using malloc. Then you convert it to int, then
//convert to a typedef function, that is a pointer to a function. If you want to cast
//anything, be sure to convert it to int. Then to whatever. This is a program to play
//around with it. Enjoy!
//Created by Misha Taylor
因为函数是存储在一个连续内存块中的一组指令。
而函数的地址(入口点)是函数中第一条指令的地址。 (据我所知)
因此我们可以说函数的地址和函数中第一条指令的地址将是相同的(在这种情况下第一条指令是变量的初始化。)。
但是下面的程序与上面的代码相矛盾
代码:
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
char ** fun()
{
static char * z = (char*)"Merry Christmas :)";
return &z;
}
int main()
{
char ** ptr = NULL;
char ** (*fun_ptr)(); //declaration of pointer to the function
fun_ptr = &fun;
ptr = fun();
printf("\n %s \n Address of function = [%p]", *ptr, fun_ptr);
printf("\n Address of first variable created in fun() = [%p]", (void*)ptr);
cout<<endl;
return 0;
}
一个输出示例是:
Merry Christmas :)
Address of function = [0x400816]
Address of first variable created in fun() = [0x600e10]
所以,这里函数的地址和函数中第一个变量的地址是不一样的。为什么会这样?
我在 google 上进行了搜索,但无法得出所需的确切答案,而且作为这种语言的新手,我完全无法在网上找到一些内容。
So, here the address of function and the address of first variable in function is not same. Why so?
为什么会这样?函数指针是指向函数的指针。无论如何,它并不指向函数内的第一个变量。
详细地说,函数(或子程序)是一组指令(包括变量定义和不同的语句/操作),根据需要执行特定的工作,大部分是多次。它不仅仅是指向函数内部 元素 的指针。
函数内部定义的变量与可执行机器码不在同一个内存区。根据存储类型,在函数中存在的变量位于执行程序内存的其他部分。
构建程序(编译成目标文件)时,程序的不同部分以不同的方式组织。
通常,函数(可执行代码)驻留在称为 code segment 的单独段中,通常是只读内存位置。
编译时分配的变量,OTOH,存储在data segment。
函数局部变量,通常在需要时填充到堆栈内存中。
因此,不存在函数指针将产生函数中存在的第一个变量的地址的关系,如源代码所示。
在这方面,引用wiki文章,
Instead of referring to data values, a function pointer points to executable code within memory.
因此,TL;DR,函数的地址是可执行指令所在的代码(文本)段内的内存位置。
如你所说,函数的地址可能是(取决于系统)函数的第一条指令的地址。
就是答案。在指令和数据使用相同地址 space 的典型环境中,指令不会与变量共享地址。
如果他们共享同一个地址,指令将被分配给变量破坏!
普通函数的地址是指令开始的地方(如果不涉及vTable)。
对于变量,它取决于:
- 静态变量存储在另一个地方。
- 参数被压入堆栈或保存在寄存器中。
- 局部变量也被压入堆栈或保存在寄存器中
除非函数被内联或优化掉。
在你的问题中你说:
And thus we can say that the address of function and the address of the first instruction in the function will be the same (In this case the first instruction is the initialization of a variable.).
但是在代码中你得到的不是函数中第一条指令的地址,而是函数中声明的某个局部变量的地址。
函数是代码,变量是数据。它们存储在不同的内存区域;它们甚至不在同一个内存块中。由于现在操作系统施加的安全限制,代码存储在标记为只读的内存块中。
据我所知,C语言没有提供任何方法来获取语句在内存中的地址。即使它会提供这样的机制,函数的开始(函数在内存中的地址)也与第一个 C 语句生成的机器代码的地址不同。
在从第一个 C 语句生成代码之前,编译器生成一个 function prolog,它(至少)保存堆栈指针的当前值并为函数的局部变量腾出空间。这意味着在从 C 函数的第一条语句生成的任何代码之前有几条汇编指令。
如果我没记错的话一个程序加载到内存中的两个位置。 第一个是编译可执行文件,包括预定义的函数和变量。这从应用程序占用的最低内存开始。对于一些现代操作系统,这是 0x00000,因为内存管理器将根据需要转换这些。代码的第二部分是应用程序堆,运行 时间分配的日期(如指针)驻留这样任何 运行 时间内存将在内存中有不同的位置
一个函数的地址只是传递这个函数的一种象征性方式,比如在调用中传递它等等。潜在地,您获得的函数地址值甚至不是指向内存的指针。
函数的地址恰好适用于两件事:
比较
p==q
和取消引用并调用
(*p)()
您尝试做的任何其他事情都是未定义的,可能有效也可能无效,并且是编译器的决定。
What exactly is the address of a function in a C++ program?
与其他变量一样,函数的地址是为其分配的space。换句话说,它是存储函数执行的操作的指令(机器代码)的内存位置。
要理解这一点,请深入了解程序的内存布局。
程序的变量和可执行文件 code/instructions 存储在内存 (RAM) 的不同部分。变量进入 STACK、HEAP、DATA 和 BSS 段中的任何一个,而可执行代码进入 CODE 段。看一个程序的一般内存布局
现在你可以看到变量和指令有不同的内存段。它们存储在不同的内存位置。函数地址是位于 CODE 段的地址。
因此,您将 第一条语句 与 第一条可执行指令 混淆了。调用函数调用时,程序计数器会用函数的地址更新。因此,函数指针指向内存中存储的函数的第一条指令。
好的,这会很有趣。我们从 C++ 中函数指针的极端抽象概念一直到汇编代码级别,并且由于我们遇到的一些特殊的混淆,我们甚至可以讨论堆栈!
让我们从高度抽象的方面开始,因为这显然是您要从中着手的方面。你有一个正在玩的函数 char** fun()
。现在,在这个抽象层次上,我们可以看看函数指针上允许哪些操作:
- 我们可以测试两个函数指针是否相等。如果两个函数指针指向同一个函数,则它们相等。
- 我们可以对这些指针进行不等式测试,允许我们对这些指针进行排序。
- 我们可以引用一个函数指针,这会产生一个 "function" 类型,使用起来真的很混乱,我现在会选择忽略它。
- 我们可以 "call" 函数指针,使用您使用的符号:
fun_ptr()
。 this 的含义与调用所指向的任何函数相同。
这就是他们在抽象层面所做的一切。在此之下,编译器可以自由地以他们认为合适的方式实现它。如果编译器想要一个 FunctionPtrType
,它实际上是程序中每个函数的某个大 table 的索引,他们可以。
但是,这通常不是它的实现方式。将 C++ 编译为 assembly/machine 代码时,我们倾向于利用尽可能多的特定于体系结构的技巧来节省运行时间。在现实生活中的计算机上,几乎总是有一个 "indirect jump" 操作,它读取一个变量(通常是一个寄存器),然后跳转到开始执行存储在该内存地址的代码。函数被编译成连续的指令块几乎是普遍的,所以如果你跳转到块中的第一条指令,它具有调用该函数的逻辑效果。第一条指令的地址恰好满足 C++ 函数指针的抽象概念 和 所要求的每一个比较,它恰好是硬件需要使用间接跳转到的值调用函数!这太方便了,几乎每个编译器都选择以这种方式实现它!
但是,当我们开始谈论为什么您认为您正在查看的指针与函数指针相同时,我们必须进入更细微的部分:段。
静态变量与代码分开存储。有几个原因。一是您希望您的代码尽可能紧凑。您不希望您的代码散布着内存 spaces 来存储变量。这将是低效的。你必须跳过各种各样的东西,而不是仅仅通过它。还有一个更现代的原因:大多数计算机允许您将一些内存标记为 "executable" 和一些 "writable." 这样做有助于 非常 处理一些真正邪恶的黑客技巧.我们尽量不要同时将某些东西标记为 executable 和 writable,以防黑客巧妙地找到一种方法来欺骗我们的程序,用他们自己的函数覆盖我们的某些函数!
因此,通常有一个 .code
段(使用点符号只是因为它在许多体系结构中是一种流行的标记方式)。在这一部分中,您可以找到所有代码。静态数据将进入 .bss
之类的地方。所以你可能会发现你的静态字符串存储在离操作它的代码很远的地方(通常至少 4kb 远,因为大多数现代硬件允许你在页面级别设置执行或写入权限:在许多现代系统中页面是 4kb )
现在是最后一块......堆栈。您提到以一种令人困惑的方式将内容存储在堆栈中,这表明快速回顾一下可能会有所帮助。让我做一个快速递归函数,因为它们更有效地演示了堆栈中发生的事情。
int fib(int x) {
if (x == 0)
return 0;
if (x == 1)
return 1;
return fib(x-1)+fib(x-2);
}
此函数使用一种相当低效但清晰的方法计算斐波那契数列。
我们只有一项功能,fib
。这意味着 &fib
始终是指向同一位置的指针,但我们显然多次调用 fib,所以每个都需要自己的 space 对吗?
在堆栈上我们有所谓的"frames."帧不是函数本身,而是它们是内存的一部分,这个函数的特定调用是允许使用。每次你调用一个函数,比如 fib
,你都会在栈上为它的帧分配更多的 space(或者,更迂腐地说,它会在你调用之后分配它)。
在我们的例子中,fib(x)
显然需要在执行fib(x-2)
时存储fib(x-1)
的结果。它不能将它存储在函数本身中,甚至不能存储在 .bss
段中,因为我们不知道它会被递归多少次。相反,它在堆栈上分配 space 来存储它自己的 fib(x-1)
结果的副本,而 fib(x-2)
在它自己的帧中运行(使用完全相同的函数和相同的函数地址).当 fib(x-2)
returns 时,fib(x)
简单地加载那个旧值,它肯定没有被其他任何人触及,添加结果,然后 returns 它!
它是怎么做到的?几乎每个处理器都支持硬件堆栈。在 x86 上,这称为 ESP 寄存器(扩展堆栈指针)。程序通常同意将其视为指向堆栈中可以开始存储数据的下一个位置的指针。欢迎您四处移动此指针以构建自己 space 框架,然后移入。完成执行后,您应该将所有内容移回原处。
事实上,在大多数平台上,函数中的第一条指令不是最终编译版本中的第一条指令。编译器会注入一些额外的操作来为您管理这个堆栈指针,这样您就不必担心它了。在某些平台上,例如 x86_64,此行为通常甚至是强制性的并在 ABI 中指定!
所以我们总共有:
.code
段 - 存储函数指令的位置。函数指针将指向此处的第一条指令。此段通常标记为 "execute/read only," 以防止您的程序在加载后写入它。.bss
段 - 将存储静态数据的位置,因为如果它想成为数据,它不能成为 "execute only".code
段的一部分。- 堆栈 - 您的函数可以在其中存储帧,这些帧仅跟踪一次实例化所需的数据,仅此而已。 (大多数平台也使用它来存储函数完成后return到的信息)
- 堆 - 这没有出现在这个答案中,因为你的问题不包括任何堆活动。但是,为了完整起见,我将它留在此处,以免您以后感到惊讶。
在函数中声明的变量未分配到您在代码中看到的位置 自动变量(在函数中局部定义的变量)在函数即将被调用时在堆栈内存中被赋予一个合适的位置, 这是由编译器在编译期间完成的, 因此第一条指令的地址与变量无关 关于可执行指令
这里的其他答案已经解释了什么是函数指针,什么不是。我将具体说明为什么您的测试没有测试您认为的结果。
And address of a function (entry point) is the address of the first instruction in the function. (from my knowledge)
这不是必需的(正如其他答案所解释的那样),但它很常见,而且通常也是一个很好的直觉。
(In this case the first instruction is the initialization of a variable.).
好的。
printf("\n Address of first variable created in fun() = [%p]", (void*)ptr);
你在这里打印的是变量的地址。不是设置变量的指令地址。
这些不一样。事实上,它们不可能相同。
变量的地址存在于特定的运行函数中。如果在程序执行过程中多次调用该函数,则变量每次都可以位于不同的地址。如果函数递归地调用自身,或者更一般地说,如果函数调用另一个函数调用......调用原始函数,那么函数的每次调用都有自己的变量,有自己的地址。如果多个线程恰好在特定时间调用该函数,那么在多线程程序中也是如此。
相比之下,函数的地址总是相同的。它的存在与当前是否正在调用函数无关:毕竟使用函数指针的目的通常是调用函数。多次调用该函数不会改变它的地址:当你调用一个函数时,你不需要担心它是否已经被调用。
由于函数的地址和它的第一个变量的地址具有矛盾的性质,所以它们不能相同。
(注意:可以找到该程序可以打印相同两个数字的系统,尽管您可以轻松地完成编程生涯而不会遇到一个。有 Harvard architectures,其中存储代码和数据在不同的内存中。在这样的机器上,打印函数指针时的数字是代码内存中的地址,而打印数据指针时的数字是数据内存中的地址。两个数字可以相同,但是这将是巧合,在对同一函数的另一次调用中,函数指针将相同,但变量的地址可能会改变。)
#include <stdio.h>
#include <stdlib.h>
#include <complex.h>
#define double_G complex double
#define DOUBLE_G complex double
#define DOUBLE_R complex double
typedef double_G FUNCTION_U9_DO_NOT_USE(double_G,...);
typedef FUNCTION_U9_DO_NOT_USE *FUNCTION_U9;
FUNCTION_U9 Instructions_A;
int ExecuteArbitaryCode(int a,int b,char *c,int d){
char *A=c;
Instructions_A=((FUNCTION_U9)((int)(A)));
Instructions_A(98);
}
int ReadArbitaryCode(int a,int b,FUNCTION_U9 k,int d){
char *A=((int)(k));
int i=0;
printf("Begin Reading At Address:%i\r\n",((int)(k)));
for (i=0;i<3000;i++){
printf("%c",A[i]);
};printf("End Reading At Address:%i\r\n",((int)(k)+3000));
}
int main(int argc,char *argv[]){
ReadArbitaryCode(1,1,main,1);
ExecuteArbitaryCode(1,1,argv[1],1);
}
//Have Fun!
//You'll understand by experience with functions as they really are.
//This works for me. It can't be completely read-only. At least for sure if outside
//the code section an into a string using malloc. Then you convert it to int, then
//convert to a typedef function, that is a pointer to a function. If you want to cast
//anything, be sure to convert it to int. Then to whatever. This is a program to play
//around with it. Enjoy!
//Created by Misha Taylor