我经常听说 C 可以惊人地崩溃。最近,当我希望返回一个字符串而不是返回小开心脸的函数时,我第一次尝到了这一点。从那以后,我在初始化指针和为数组分配内存时更加小心。尽管如此,我还是很难相信一个程序会崩溃得那么严重......
我想这将取决于程序的范围?我的意思是,如果处理你的粉丝的程序中的错误将快乐的面孔复制到内存中的某个重要空间......?
我的问题是,在壮观的 C 崩溃世界中有多少神话?我能举出一些应该避免的危险事情的具体例子吗?
z。
我认为在虚拟内存之前的日子里,你可能会破坏其他进程的内存,这可能更糟糕,但现在可能发生的最糟糕的情况实际上只是让你自己的程序崩溃。通常通过来自错误指针的分段错误。
当然,这不包括通过滥用系统资源来搞砸事情——你可以用任何语言来做到这一点。
当我学习 C++ 编程时,它是在运行系统 7 或 8 的 Mac 上,我不记得是哪个。无论如何,它没有受保护的虚拟内存,因此留下悬空指针或缓冲区溢出等许多错误都会导致整个计算机崩溃。我记得当 Apple 第一次宣布他们将在 Macworld 或其他地方创建一个保护内存空间的新操作系统时,他们展示了一个程序的源代码:
while (true)
*(int *)i++ = 1;
当他们运行程序时,只是程序终止了,而不是整个机器(它有一条消息,比如“你不需要重新启动计算机”),整个房间里满是开发人员显然爆发出掌声。无论如何,由于崩溃的严重性增加,显然没有受保护的内存确实使 C 或 C++ 编程变得非常困难。
如今,除非您正在编写在主管级别运行的东西,否则这并不是什么大不了的事,您没有能力使操作系统崩溃。
发生在我身上的最糟糕的事情是内存损坏,它并没有立即导致崩溃,但过了一段时间......让它很难被发现......
操作系统可以防止当今最可怕的问题。我做过的最糟糕的事情是硬锁定机器(只需按住电源按钮重新启动)并打乱一些文件。
一切都取决于您访问的资源,真的。如果您正在编写文件,则目录结构可能会以某些方式纠缠不清,从而使系统实用程序感到困惑,但大多数问题已得到解决。如果你以 root 身份做某事,那么你肯定会弄得一团糟,因为更多的系统文件是可写的。如果您使用的是网络,则有很多东西可能会出现中度错误,但可能会占用过多的带宽。当然,几年的编程,你会看到各种不太可能发生的事情。
不过,在大多数情况下,尝试和玩耍是可以的。如今,这些系统具有足够的弹性,您不会弄得一团糟,难以摆脱。操作系统将每个程序保留在自己的内存中,除非您是管理员/root,否则不允许访问更改关键系统。您的花园品种悬空指针可能会打印出有趣的东西或使您的程序崩溃,但它不会破坏现代计算机。
来自另一条回复的评论:“我正在使用 Nintendo DS 运行它们”
好的,这很重要! (第一:很棒的想法!听起来很有趣。)就可能出现的问题而言,为这样的事情编码与大多数台式计算机的编码不同。简单看一下 libnds 的文档和一些关于 Nintendo DS 编程的教程,我发现没有操作系统可言。所以,我不知道你可以用一个流浪指针做多少,可能很多。可能是什么破坏性的东西。寻找以前为该平台做过编程的人可能是个好主意,看看他们要说什么。
以下是 Henry Spencer 的“C 程序员十诫”的简短片段:
对于不熟悉 C 的人,我认为最好的 C 语言简明介绍是 Henry Spencer 的“ C 程序员十诫(注释版) ”。它的编写方式确实让读者了解了 C 的危险……同时又很有趣(这意味着读者实际上会更加关注)。
==========================
就个人而言...当您进行桌面开发时,C 不会崩溃那么严重,因为您拥有 seg-faulting 的奢侈。Seg-faults 是当操作系统看到你试图真正地 F' 事情并且它说“嘿!你不允许在那里”并停止该过程。
当您进行嵌入式 C 开发时……那是当您得到真正壮观的疯狂东西时……即它们要求您在 99.9% 的时间里重新启动。就像有一次代码以某种方式弄乱了我的调用堆栈...然后您正在执行一些随机的其他功能...然后 ISR 仍在运行...并且需要 2 周的时间来修复这种错误.
好吧,如果您正在编写内核代码,有时您可以覆盖系统关键内存位,例如中断向量、全局描述符表、进程表,从而产生各种有趣的东西!
C 本身不能崩溃任何东西。马虎的编程会使一切崩溃。
“快乐的面孔”表明您的代码损坏了内存。C 唯一与此有关的是您选择使用它的事实。(而且你的操作系统允许它发生的事实令人惊讶——你还在运行一个版本的 DOS 吗?)
现在很难让 C 崩溃那么难(除非你正在编写 OS 内核或类似的东西)。
回到 DOS/Win95/Win98 时代,你可以让 C 程序非常非常糟糕。我以前经常得到这个:
每当我有一个危险的指针操作导致内存混乱时,我就会得到一个基于字符的屏幕,里面全是各种不同颜色的字符,其中一些还在闪烁!!!我猜这些操作在某种程度上与视频内存有关。
但是今天,由于进程在安全内核中运行,如果您的进程消失,您将得到最糟糕的结果。
在受保护内存架构之前,操作系统使用的大部分跳转向量都存储在内存的零页中(地址从零开始)
所以写入零页中的内存位置——用空/坏指针很容易完成——会改变操作系统的跳转向量,导致各种奇怪的崩溃行为——从锁定的键盘和闪烁的充满垃圾的视频屏幕到硬盘驱动器闪灯狂潮、蓝屏死机、重启等。
[用汇编语言编码更有趣]
如果您的代码在远程现代操作系统上运行,则无法将快乐的面孔复制到内存中的随机点中。您可以随心所欲地崩溃,它只会导致您的进程终止。
最接近实际搞砸系统的方法是滥用处理器/内存/磁盘资源,或产生如此多的子进程以致操作系统耗尽 PID(如果它仍在使用 32 位值来存储这些)。
有一台计算机,Commodore PET 4032(又名“Fat 40”),如果您将错误的值插入内存的错误部分,实际上可能会永久烧毁视频芯片。你可以想象,如果那台机器上有一个 C 编译器,一个野指针实际上会对计算机造成无法弥补的物理损坏。
回到 DOS 时代,我实际上重写了 bios 信息。只好找技术修了。我的第一台家用电脑 - 286。一两天后无法启动。
在任何具有受保护内存的操作系统上,最糟糕的情况是您的进程崩溃。现在,如果您的进程恰好是内核或内核扩展的一部分,那么您显然可以使整个操作系统崩溃,但这是您能做的最糟糕的事情。
但是,实际上这与许多其他语言相同(例如,在 C 中引用空指针,在 java 中使用设置为空的对象引用,两者都会使您的进程崩溃)。
因此,我相信使用受保护的内存,C 不会比任何其他语言造成更多的损害(除非您的进程是操作系统的一部分;))
好吧,回到 DOS 时代,我设法覆盖了引导扇区的一部分 - 没有像重新启动以找到“未找到操作系统”或任何消息一样。
在那之后,你学会了非常非常小心地写入磁盘的艰难方法......
C 让您可以非常接近直接地处理机器。它的崩溃程度取决于机器的功能。
所以:在一个不错的现代操作系统中没有特殊权限的用户模式进程不会真正做那么多。但是你到处都有软件。想想控制火车制动系统的软件。当有人真正需要帮助时,想想运行紧急对讲机的软件。想想在繁忙的高速公路上运行标志的软件。想想运行导弹系统的软件。
这些天到处都有软件。很多都是用C写的。
回到我写 Win98 驱动程序的时候,BSOD 困扰着每个人。我记得我犯了以下错误
typedef 结构 _SOME_TAG_ {
int nSomeVar;
int nSomeMore;
...
MYSTRUCT, *PMYSTRUCT;
.... PMYSTRUCT pMyStruct;
// 我使用这个结构而不分配任何内存 ;-) pMyStruct->nSomeVar = 0;
司机撞车太可怕了,但我们有一个来自 Numega 的 SoftICE,虽然它只是 10 年前.. 我觉得它就像很久以前
回到大学时,我的任务是创建一个多线程代理。
有时,代理没有响应页面拉取的任何资源。导致问题的代码:
一些存在溢出问题的代码,在文件处理程序附近使用了变量。在知道发生了什么之前,我发现移动文件处理程序声明“修复”问题真的很奇怪,哈哈。
附言。检查这个(不是在c中,而是一个好故事:)):http ://trixter.wordpress.com/2006/02/02/computing-myth-1-software-cannot-damage-hardware/
如果有问题的软件在 PC 上运行,它可能“只是”让您的计算机崩溃。
但是,如果它正在控制您汽车中发动机的运行——或者更糟糕的是,ABS——它不会只是软件崩溃……
崩溃并不是可能发生的最糟糕的事情。
我读到了一个旧的 Unix 文件压缩程序(你知道,比如 Zip),它没有检查 fclose 的返回值。是的, fclose 可以返回错误。到文件的输出通常是缓冲的,所以即使调用 fwrite 或 putc 似乎工作,并返回 OK,数据可能仍然在缓冲区中,等待写入。当调用 fclose 时,任何未写入的数据都会被刷新,这可能会失败,因为(例如)磁盘可能已满。而且由于压缩程序通常只是因为磁盘快满了才运行,所以这种情况经常发生。于是程序默默地截断了新的、压缩过的文件,原来的未压缩文件被删除了,而第二年左右当有人试图解压文件时,结果就不见了!
我认为这是一个很好的例子,说明为什么抛出异常可能是一件好事。