功能简单;理解指针指针差异

时间:2018-01-17 21:51:28

标签: c++ function pointers memory-address

我正在测试一些计算函数大小的方法,以字节为单位(我熟悉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;
}

应该减去&#39; exec&#39;的地址。来自&#39; dump&#39;。这有效但我注意到使用其他类型的指针(如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系统)。指针之间唯一变化的是它指向的数据类型。

解释是什么?

3 个答案:

答案 0 :(得分:3)

C / C ++中的指针运算用于访问数组的元素。实际上,数组索引仅仅是指针算法的一种更简单的语法。例如,如果您有一个名为array的数组,则array[1]*(array+1)相同,无论array中元素的数据类型如何。

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

如果你有char*unsigned char*,指针指向一个字节,递增指针使其前进到下一个字节。

在Windows中,DWORD是32位值(四个字节),DWORD*指向32位值。如果递增DWORD*,则指针前进四个字节,正如array[1]为您提供数组的第二个元素,即四个字节(一个{{1}在第一个元素之后。同样,如果向DWORD添加10,则前进40个字节,而不是10个字节。

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

指针减法就像添加一样。当你从另一个指针中减去一个指针时,它们必须是同一个类型,并且必须是指向同一个数组的指针或者指向结尾的指针。

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

以下是卡内基梅隆大学的参考资料:

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

答案 1 :(得分:1)

指针减法告诉你两个地址之间的元素数量,所以使用DWORD *它将是DWORD大小的单位。

答案 2 :(得分:0)

你有:

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(截断)是合理的。

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

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

如果要确定字节大小,请使用指向字节大小类型的指针,例如char。请注意,您使用的方法不可移植,并且无法保证正常工作。

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