我正在将其作为社区维基,以便更好地理解这些错误与其运行时或编译后果之间的语义差异。另外,我用Java编写的代码太长了,我想在C ++中更好地学习指针 - 所以我需要其他人来做。
Edit2:我正在重构这个问题。我试图得出的区别是,在托管代码上,这些错误都是通过异常统一处理的。但是,C ++并不是那么简单 - 我想知道在每种情况下你是否可能来获取错误,段错误,可恢复行为或更糟糕的沉默错误传播。请参阅我的新具体示例(是的,我知道答案总是“完全按照编码”;毕竟我是程序员。我想知道你经常遇到的有趣细节。)< / p>
编辑3:在下文中,“class”代替了一个类的实例。感谢
错误1: 指针值为NULL,又称指针== 0
错误2: 指针指向内存中以前的类,其值为NULL或== 0
错误3:指针指向不属于正确类或子类的类
错误4:指针指向类的中间(未对齐)或未初始化的垃圾
先感谢所有贡献者。
答案 0 :(得分:1)
其中大多数都会导致不可预测的行为。引用Steve McConnell的Code Complete第2版“使用指针本质上很复杂,正确使用它们需要您对编译器的内存管理方案有很好的理解”。
答案 1 :(得分:1)
好。在C ++中,取消引用除了案例2之外的任何指针都会产生未定义的行为,所以你不知道会发生什么。但是,对于大多数操作系统,取消引用空指针将导致分段错误。
在比较中使用指针对于空指针来说很好,但对于除了那个和情况2之外的任何其他情况都没有精确定义(未指定)。
案例2是完美定义的。你可以让你的指针指向一个值为0的int。我不明白为什么这样的东西在C#中是非法的。可能我误解了你的情况2
对于案例3,您必须区分指针是否已经指向该错误对象,或者您是否仍在尝试使其指向该错误对象。 C ++ dynamic_cast
将检查您指向的对象的类型,如果它不是派生的或者类型与您输入的类型相同,那么它将为您提供一个空指针。但是还有其他的演员没有做那个检查,并会给你留下无效的指针。
答案 2 :(得分:1)
总之,你不能依赖任何有价值的东西。设计代码非常重要,这样才不会发生。编译器可以帮助它,因此在尝试欺骗它时要非常小心(例如,强制转换)。编译器最终会报复。
答案 3 :(得分:0)
#1应该抛出一个段错误。
#2,#3和#4可能有效,具体取决于方法尝试的方法。请记住,在C ++中,类代码只存储一次(并且与实例数据分开,这是对象指针引用的),因此可以在随机的内存块上调用类方法。例如,以下打印“-1”(使用g ++ 4测试):
#include <iostream>
class Foo
{
public:
int x;
void foo()
{
std::cout << x << std::endl;
}
};
int main(void)
{
void* mem = malloc(1024);
memset(mem, 0xff, 1024);
Foo* myFoo = (Foo*)mem;
myFoo->foo();
return 0;
}
答案 4 :(得分:0)
在Windows错误1下,当您尝试访问您没有读取权限的虚拟内存页面时,将导致针对访问冲突引发结构化(win32)异常。 Unix派生的操作系统有一个类似的机制,虽然术语不同。
这是明确定义的(如果通常是不合适的!)行为,并且可以被结构化异常处理程序捕获。通常,托管运行时将依赖于底层操作系统引发此异常,然后处理它并将其转换为托管异常。这比在跟踪它之前检查每个指针访问空间要高效得多。
答案 5 :(得分:0)
这是我修改过的错误版本:
错误1:空指针/引用
错误2:指向已释放的对象的指针
错误3:
错误4:
答案 6 :(得分:0)
我只想添加这一点信息。指针会做任何你告诉他们要做的事情。如果程序可以访问所述内核,则包括覆盖内核。
以第3点为例,这是许多内核攻击中使用的技术。找出内核所在的位置,并使用指针来更改信息。我绝不建议有人试试这个,我不会宽恕使用Rootkit或任何其他恶意软件。
答案 7 :(得分:0)
如果您真的想了解指针,因为您想更好地了解您的计算机,请专注于C或汇编。实际上,有一些用C编写的很棒的小型C编译器,将它们分开并将它们重新组合在一起。
C ++降级为C(我的意思是它可以编译C文件),但是在C ++中还有很多要处理的东西,而对于C,你可以只考虑指针的基础知识。
我还强烈建议您使用汇编语言编译C程序并跟踪(单步调试)。如果你真的想要理解底层系统,理解堆栈帧和调用期间发生的事情是非常关键的。
学习这些东西的其他方法: