以下程序使用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;
}
答案 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)
答案 3 :(得分:0)
您正在取消引用一个单位指针。
真正有趣的是,为什么第二个例子不会让你崩溃,因为它有同样的问题
BTW:对我来说(gcc 4.4,amd64)这两个例子都崩溃了。
如果您真的对为什么第二个示例不会崩溃感兴趣,请使用调试信息进行编译并在调试器中启动它。