2008-12-03 53 views
2

[这是PC/VISUAL C++明确(尽管任何其他的答案将是相当启发:))如何找出是否一个指针是PC /的Visual C++

你怎么能告诉我们,如果在堆栈上一个指针来自栈中的一个对象?例如:

int g_n = 0; 

void F() 
{ 
    int *pA = &s_n; 
    ASSERT_IS_POINTER_ON_STACK(pA); 
    int i = 0; 
    int *pB = &i; 
    ASSERT_IS_POINTER_ON_STACK(pB); 
} 

所以仅第二断言(PB)应该跳闸。我正在考虑使用一些内联程序集来确定它是否在SS段寄存器内或类似的东西。有没有人知道是否有任何内置的功能,或者一个简单的方法来做到这一点?

谢谢! RC

+0

为什么(!)你需要这个吗? – Tim 2008-12-03 19:30:07

+0

与蒂姆在此。你有什么可能需要这些信息? – TheSmurf 2008-12-03 19:47:07

+0

为了模仿其他硬件并更容易陷入bug ... – 2008-12-03 20:08:43

回答

1

从技术上讲,在便携式C你不知道。参数栈是硬件细节,在很多但不是所有的编译器上都很受欢迎。有些编译器会在寄存器可用时使用寄存器(即fastcall)。

如果您使用的是Windows NT具体工作,你想从调用NtCurrentTeb抢线程执行块()。 Joe Duffy's blog有这方面的信息,从它你可以得到堆栈范围。你检查范围内的指针,你应该很好去。

2

不管你做什么,这将是非常特定于平台的和非便携式。假设你没有问题,继续阅读。如果指针指向堆栈中某处,它将位于当前堆栈指针%esp和堆栈顶部之间。获得堆栈的顶部

一种方法是在main()开始读入。然而,这有几个问题: - 由于C运行时在初始化堆栈之前初始化堆栈的顶部实际上稍高,因此在输入前main() - 在C++中,全局对象的构造函数在main()之前调用 - 如果您的应用程序是多线程的,每个线程都有自己独立的堆栈。在这种情况下,你需要一个线程局部变量描述栈的基础

一种方式来获得当前的堆栈指针使用内联汇编:

uint32_t GetESP(void) 
{ 
    uint32_t ret; 
    asm 
    { 
     mov esp, ret 
    } 
    return ret; 
} 

谨防内联和优化!优化器可能会破坏此代码。

+0

我会更担心的是优化不会运行在任何特定的翻译单元这个功能。 – 2008-12-03 20:17:57

0

忽视的问题,“为什么” ......如果一个简单的方法,你有顶部栈帧的控制将设置一个全局变量是一个栈对象的地址,然后使用来检查,如果一个函数目标指针位于该地址和它在堆栈上创建的变量的地址之间。

void* TopOfStack; // someone must populate this in the first stack frame 

bool IsOnTheStack(void* p) 
{ 
    int x; 

    return (size_t) p < (size_t) TopOfTheStack && 
     (size_t) p > (size_t) &x; 
} 

这当然不适用于多个线程,除非您使TopOfTheStack线程在本地。

堆栈由编译器优化,也可能会引起问题。

2

我将第二问题 - 为什么你需要知道什么?这不会有好处。

我认为这种方法可能会奏效,如果编译器做合理的事情指针比较和堆栈向下增长:

static void * markerTop = NULL; 

int main() 
{ 
    char topOfStack; 
    markerTop = &topOfStack; 
    ... 
} 

bool IsOnStack(void * p) 
{ 
    char bottomOfStack; 
    void * markerBottom = &bottomOfStack; 
    return (p > markerBottom) && (p < markerTop); 
} 
0

是的,我知道这是非常不可移植的,但是这是一个内部应用模仿其他硬件设施来做到这一点。似乎线程执行块可能是要走的路。

0

我不相信Visual C++或几乎所有在现代Windows(或古老的32位OS/2)平台上运行的任何东西,因为在这些平台上堆栈动态增长,也就是说,堆栈的新页面是只有当程序试图访问所谓的保护页面时才会分配,这是一个4 KB的块(无论如何是32位Windows)在当前分配的堆栈块顶部的特制内存。操作系统拦截当你的程序试图访问这个保护页面时产生的异常,并且(1)在当前分配的堆栈的顶部之上映射一个普通的,有效的堆栈来代替它,并且(2)创建另一个保护页面位于新顶端的上方,因此可以根据需要随后增加堆叠。操作系统执行此操作直到堆栈达到其限制,并且通常将此限制设置得非常高。

如果您的程序试图访问属于堆栈未分配部分但位于守护页之上的任何地址,则您的程序将崩溃,因为操作系统无法解释此问题。即使指针在理论上属于任务的堆栈段,您的程序也会尝试访问其地址空间之外的内存。但是,如果您需要一种方法来查找某个地址是否属于堆栈的已分配部分(即“堆栈中的对象”),那么对Joe Duffy的博客的引用是很好的。只是不要使用这里描述的StackLimit,而是使用其他方法获取当前堆栈的顶部,这些方法已经在此线程中描述过,因此您可以在堆栈的分配部分进行操作,而不是整个,可能部分未分配,一个

0

我同意那些在不断调整堆栈大小的环境中很难做到可靠的人。

但是作为'为什么?'人们 - 足够有趣我想今天在一个小型嵌入式平台上做这件事。我有一个函数,它接受一个指向对象的指针,然后在函数返回后保持该指针一段时间(因为它正在处理指针指向的数据)。

我不希望我的函数的调用者传递自动变量的地址,因为我不希望数据在仍然处于工作状态时被踩踏。调用者必须传递静态数据或常量数据的地址,并且一个不错的'ASSERT_IS_ON_STACK()'宏可能是一个有用的提示。

便携式?一点也不。可怕的糖果机界面?绝对。

这就是小型嵌入式系统的本质 - 良好的断言可以提供帮助。

0

确定,作为对“为什么”:

一些处理器存储器控制器也不能执行的DMA或从堆栈段(多个)存储器映射到/;所以在跨平台的世界中,为了确保我不从那里发送数据,跨平台声明是非常有用的。

1

既然您指定了Visual C并断言,我会假设您可以使用调试版本。 在这种情况下,你可以把这个特定的编译器把内存检查fenceposts的优势:

#define IS_POINTER_TO_STACK(vp) (*((int*)(vp)-1)==0xCCCCCCCC) 

工作正常,在所有这些情况下,在调试版本:

#define ASSERT(v) printf("assert: %d\n", v); //so it doesn't really quit 
int g_n = 0; 
void test_indirectly(void* vp) { 
    ASSERT(IS_POINTER_TO_STACK(vp)); 
} 
void F() { 
    int *pA = &g_n; 
    ASSERT(IS_POINTER_TO_STACK(pA));   //0 

    int i = 0; 
    int j = 0; 
    int *pB = &i; 
    ASSERT(IS_POINTER_TO_STACK(pB));   //1 
    ASSERT(IS_POINTER_TO_STACK(&j));   //1 

    int *pC = (int*)malloc(sizeof(int)); 
    ASSERT(IS_POINTER_TO_STACK(pC));   //0 
    free(pC); 
    ASSERT(IS_POINTER_TO_STACK(pC));   //0 
    pC = new int; 
    ASSERT(IS_POINTER_TO_STACK(pC));   //0 
    delete pC; 

    char* s = "HelloSO"; 
    char w[6]; 
    ASSERT(IS_POINTER_TO_STACK("CONSTANT")); //0 
    ASSERT(IS_POINTER_TO_STACK(s));   //0 
    ASSERT(IS_POINTER_TO_STACK(&w[0]));  //1 
    test_indirectly(&s);      //1 

    int* pD; //uninit 
    ASSERT(IS_POINTER_TO_STACK(pD)); //runtime error check 

} 

(除极最后一个由于未初始化的内存导致运行时错误 - 但仍然用于验证指针的目的)。

这只适用于Debug构建 - Release构建报告全部为false。

相关问题