简单的功能大小;了解指针与指针的区别

Simple function-size; Understanding pointer-pointer difference

我正在测试一些计算函数大小的方法,以字节为单位(我熟悉 x86 上的操作码)。代码是不言自明的:

void exec(void* addr){
    int (WINAPI *msg)(HWND,LPCSTR,LPCSTR,UINT)=(int(WINAPI *)(HWND,LPCSTR,LPCSTR,UINT))addr;

    msg(0,"content","title",0);
}
void dump(){};

int main()
{

    cout<<(char*)dump-(char*)exec;  // this is 53
    return 0;
}

应该是'dump'减去'exec'的地址。这可行,但我注意到使用其他类型的指针(如 DWORD*:

时值不同
void exec(void* addr){
    int (WINAPI *msg)(HWND,LPCSTR,LPCSTR,UINT)=(int(WINAPI *)(HWND,LPCSTR,LPCSTR,UINT))addr;

    msg(0,"content","title",0);
}
void dump(){};

int main()
{
    cout<<(DWORD*)dump-(DWORD*)exec;  // this is 13
    return 0;
}

根据我的理解,无论指针类型如何,它始终是最大可能的数据类型(以便它可以处理大地址),在我的情况下是 4 字节(x86 系统)。指针之间唯一变化的是它指向的数据类型。

解释是什么?

指针减法告诉您两个地址之间的元素数,因此使用 DWORD * 它将以 DWORD 大小为单位。

C/C++ 中的指针算法专为访问数组元素而设计。事实上,数组索引只是指针运算的一种更简单的语法。例如,如果您有一个名为 array 的数组,则 array[1]*(array+1) 相同,而不管 array.[=23= 中元素的数据类型如何]

(我在这里假设没有运算符重载正在进行;这可能会改变一切。)

如果您有 char*unsigned char*,指针指向单个字节,递增指针会将其前进到下一个字节。

在Windows中,DWORD是一个32位的值(四个字节),DWORD*指向一个32位的值。如果你递增 a DWORD*,指针前进 四个字节,就像 array[1] 给你数组的第二个元素,它是四个字节(一个DWORD) 在第一个元素之后。同样,如果您将 10 添加到 DWORD*,它会增加 40 个字节,而不是 10 个字节。

无论哪种方式,仅当结果指针指向与原始指针相同的数组或指向末尾的一个元素时,递增或添加到指针才有效。否则它是未定义的行为。

指针减法和加法一样。当您从一个指针中减去另一个指针时,它们必须是同一类型,并且必须是指向同一数组的指针或指向末尾的指针。

你正在做的是计算两个指针之间的元素数量,就好像它们是指向同一个数组(或最后一个数组)的指针一样。但是当两个指针 指向同一个数组时(或者再次指向末尾),结果是未定义的行为。

这是卡内基梅隆大学关于此的参考资料:

ARR36-C. Do not subtract or compare two pointers that do not refer to the same array - SEI CERT C Coding Standard

你有:

cout<<(char*)dump-(char*)exec;

其中 dumpexec 是函数的名称。每个转换将函数指针转换为 char*.

我不确定这种转换在 C++ 中的状态。我认为它要么具有未定义的行为,要么是非法的(使您的程序格式错误)。当我使用带有选项 -pedantic -std=c++11 的 g++ 4.8.4 编译时,它会抱怨:

warning: ISO C++ forbids casting between pointer-to-function and pointer-to-object [-Wpedantic]

(对于 C 也有类似的诊断,我认为这并不完全正确,但那是另一回事了。)

不能保证对象指针和函数指针之间存在任何有意义的关系。

显然您的编译器允许您摆脱强制转换,并且大概结果是函数地址的 char* 表示。两个指针相减得到两个地址之间的距离,以指针指向的类型为单位。减去两个 char* 指针会产生 ptrdiff_t 结果,即字节差。减去两个 DWORD* 指针产生 sizeof (DWORD) 单位的差异(可能是 4 个字节?)。这就解释了为什么你会得到不同的结果。如果两个 DWORD 指针不指向内存中不是整数 DWORD 的地址,则结果是不可预测的,但在您的示例中得到 13 而不是 53(截断)是似是而非。

但是,只有当两个指针操作数都指向同一个数组对象的元素,或者刚好超过它的末尾时,才定义指针减法。对于任何其他操作数,行为未定义。

对于允许转换的实现,它对对象指针和函数指针使用相同的表示,并且函数指针的值引用内存地址的方式与对象的值相同pointer 确实如此,您可以通过将其地址转换为 char* 并从相邻函数的转换地址中减去结果来确定函数的大小。但是编译器 and/or 链接器可以按照它喜欢的任何顺序自由地为函数生成代码,包括可能在源代码中定义相邻的两个函数之间插入其他函数的代码。

如果要确定字节大小,请使用指向字节大小类型的指针,例如 char。请注意,您使用的方法不可移植且不能保证有效。

如果您真的需要一个函数的大小,看看您是否可以让您的链接器生成某种映射,显示您的函数的分配大小和位置。在 C++ 中没有可移植的方法。