微小的崩溃程序

时间:2010-08-01 16:49:11

标签: c++ debugging class pointers g++

以下程序使用g ++编译,但在运行时崩溃:

class someClass
{
public:
    int const mem;
    someClass(int arg):mem(arg){}
};

int main()
{
    int arg = 0;
    someClass ob(arg);

    float* sample;
    *sample = (float)1;

    return 0;
}

以下程序不会崩溃:

int main()
{

    float* sample;
    *sample = (float)1;

    return 0;
}

4 个答案:

答案 0 :(得分:8)

float* sample;
*sample = (float)1;

sample永远不会被初始化为指向一个对象,因此当您取消引用它时,您的程序会崩溃。您需要在使用它之前对其进行初始化,例如:

float f;
float* sample = &f;
*sample = (float)1;

你的第二个程序仍然是错误的,即使它没有崩溃。取消引用不指向有效对象的指针会导致未定义的行为。结果可能是您的程序崩溃,内存中的其他一些数据被覆盖,您的应用程序出现以继续正常运行,或任何其他结果。你的程序今天似乎运行良好,但明天运行时崩溃。

答案 1 :(得分:4)

经过一番思考后,我可以肯定地告诉你为什么第二个例子没有崩溃。

执行程序时,crt(c运行时)会推送堆栈3的值:参数的数量(int),参数的数量为char **,环境字符串也是char **,然后调用main

现在当你编写main函数时,据我所知,它总是读取前两个值并将它们传递给函数的参数(如果有的话)。如果包含第3个参数,它也会传递第3个值,否则它将保留在堆栈中。所以程序开头的堆栈如下所示:

+--------+
| # args |  
+--------+    
|  args  |  
+--------+  <------ stack pointer
|  envs  |  
+--------+  

在第一个示例中,您在堆栈上分配int和struct,然后是指针,因此第一个示例中的完整堆栈如下所示:

+--------+
| # args |  
+--------+    
|  args  |  
+--------+  <------ stack pointer
|  arg   |  <------ your integer, initialized in code
+--------+  
|   ob   |  <------ your struct, initialized in code
+--------+  
| sample |  <------ your pointer, uninitalized = garbage
+--------+  

所以sample是纯粹的垃圾,并试图取消引用它会导致程序崩溃。

现在在第二个例子中,堆栈如下所示:

+--------+
| # args |  
+--------+    
|  args  |  
+--------+  <------ stack pointer
| sample |  <------ pointer, uninitalized (!)
+--------+  

指针仍未被初始化,但它覆盖的值是envp,它是指向数组的实际指针:char **。当你取消引用它时,你会得到一个“指向char的指针”的数组,所以你可以安全地覆盖它(只要你不再试图将它当作原始指针)。内存已分配,您可以使用它。

现在这当然是特定于实现的,但它似乎适合您的编译器吗?在覆盖之前,您可以使用gdb确认(char**)sample确实指向一系列环境变量。

MSVC ++ 10的截图,在32位发布模式下(调试模式强制初始化所有变量以防止此类内容):

the pointer in action http://img651.imageshack.us/img651/5918/69916340.png

答案 2 :(得分:0)

取消引用未初始化的指针:http://www.cprogramming.com/debugging/segfaults.html

答案 3 :(得分:0)

您正在取消引用一个单位指针。

真正有趣的是,为什么第二个例子不会让你崩溃,因为它有同样的问题

BTW:对我来说(gcc 4.4,amd64)这两个例子都崩溃了。

如果您真的对为什么第二个示例不会崩溃感兴趣,请使用调试信息进行编译并在调试器中启动它。