首页 > 编程知识 正文

索引超出数组界限解决办法,c语言中%s和%c

时间:2023-05-04 01:06:22 阅读:144524 作者:758

为什么数组索引超出范围时会区分c

#包含

int main () )

{

int a[10];

a[3]=4;

a[11]=3; //does not give segmentation fault

a[25]=4; //does not give segmentation fault

a[20000]=3; //gives segmentation fault

返回0;

}

已知a[11]和a[25]试图访问分配给进程和线程的内存,而a[20000]超出了堆栈范围。

为什么编译器或链接器没有发出错误? 你不知道数组的大小吗? 否则,sizeof(a )如何工作?

问题是丙/丙实际上不对数组进行边界检查。 根据操作系统的不同,请确保您正在访问有效的内存。

在这种情况下,声明基于堆栈的数组。 根据特定实现,在数组范围外访问时,只访问分配的堆栈区域的另一部分。 大多数操作系统和线程保留堆栈内存的特定部分。 只要在预先分配的堆栈区域玩,一切都不会崩溃(请注意,不是说工作)。

在最后一行中,访问权限超过了分配给堆栈的部分内存。 因此,对未分配给进程或分配为只读的部分内存进行了索引。 操作系统会看到这一点,并将段错误发送到此进程。

这是丙/丙在进行边界检查时如此危险的原因之一。

但是,为什么编译器和链接器不出现错误,不知道数组的大小呢? 否则,sizeof(a )如何工作?

@ Kazoom,c可以知道数组访问的特定子集是否合法。 但是,这些远远超过了检测不到的病例数。 据我推测,这个功能还没有实现。 这是因为成本高昂,仅在部分场景中有用

作为上述例子,简单的情况' a [b]=1;' 请想象一下。 -必须在运行时完成数组绑定检查。 这将为每个数组操作添加一个CPU周期。

Sizeof(a )是如何工作的?

@Kazoom,编译器知道a的长度为10,int的单位大小为4 (例如),因此只使用值40。

@Kazoom :是的。 在这种简单的情况下,编译器可以检测到问题并抛出错误。 但是,这不是标准需要的。 C本来应该很容易实现。 没有华丽的功能。

@jxdpd对不知道如何正确使用它的人来说是唯一的危险。 Ive总是把语言比作强大的工具。 如果你不知道电锯的使用方法,就没关系。 如果断腿,那是你自己的错:-)

@Pax,我绝对同意。 但是丙/丙不是电锯,它更像是缓慢作用的毒药。 修剪腿部很快就会取得明显的效果。 毒药种类繁多,但见效慢,反应快,症状多样。

真正的问题是,c和c实现通常在编译和运行时都不检查边界。 他们完全被允许这样做。 请不要为此责备我的话。

请注意,使用malloc动态分配内存无法解决此问题。 只有分配的内存保持未初始化状态,其馀内存可以具有随机/不确定的值,这可能会导致意外的分段错误

段错误指示索引超出范围,而不是c程序的预期操作。 相反,那是未定义行为的意外结果。

在c和c中,如果声明数组,例如

类型名称[ size ];

只能访问索引从0到size-1的元素。 超出此范围的行为可能会导致未定义的行为。 如果索引位于此范围附近,则很可能读取了自己程序的内存。 如果索引超出范围,程序很可能会被操作系统杀死。 但是你不知道,什么事情都会发生。

c为什么允许那个? 嗯,c和c的基本要点是,如果牺牲性能,就不提供功能。 C和C已经在高性能的重要系统中使用了很长时间。 c用作内核和程序的实现语言,对数组边界的访问有助于快速访问内存中相邻对象。 编译器禁止它是徒劳的。

你为什么不警告这个? 是的,我希望你提高警告级别,对编译器有同情心。 这称为实施质量(QoI )。 在某些编译器中,使用开放行为(例如未定义的行为)进行某些操作时,在这方面具有很好的实现质量。

[ js @ host2CPP ] $ gcc-wall-O2 main.c

main.c : in function ' main ' :

main.c : warning : arraysubscriptisabovearraybounds

[js@HOST2 cpp]$

相反,访问超出范围的阵列时,如果看到硬盘被格式化,则实施质量会变差。 很高兴在ANSI C Rationale的文档中读到这些内容。

香港专业教育学院删除了我自己的帖子。 你早就提供了更多的扩展回答:

通常,尝试访问不属于进程的内存时,只会发生碎片错误。

对于a[11] (顺便说一下a[10] ),可以看到进程实际拥有但不属于a[]数组的内存。 虽然a[25000]和a[]的距离很远,但这是可能的

能完全在内存之外。

更改a[11]更加隐蔽,因为它会默默地影响另一个变量(或堆栈帧,当函数返回时,堆栈帧可能会导致另一个分段错误)。

C没有这样做。操作系统的虚拟内存子系统是。

在您只是稍微超出界限的情况下,您要解决为程序分配的内存(在这种情况下,在堆栈调用堆栈中)。如果您的内存超出界限,那么您将寻址未分配给程序的内存,并且操作系统将引发分段错误。

在某些系统上,还有一个操作系统强制执行的"可写"内存的概念,您可能正在尝试写入自己拥有但被标记为不可写的内存。

就我理解的问题和评论而言,您了解了为什么超出范围访问内存时会发生不好的事情,但是您想知道为什么您的特定编译器没有警告您。

允许编译器向您发出警告,并且许多警告器会以最高警告级别发出警告。但是,编写该标准是为了允许人们为各种设备运行编译器,并且编译器具有各种功能,因此,该标准在确保人们可以做有用的工作的同时,对它的要求最少。

该标准几次要求某种编码样式将生成诊断。在其他几次情况下,该标准不需要诊断。即使需要诊断,我也不知道标准在哪里写出确切的措辞。

但是您并没有完全在这里冷落。如果您的编译器没有警告您,则可能是Lint。此外,有许多工具(在运行时)可检测堆上阵列的此类问题,其中最著名的一种是Electric Fence(或DUMA)。但是,甚至Electric Fence也无法保证会捕获所有超限错误。

如litb所述,某些编译器可以在编译时检测到某些超出范围的数组访问。但是在编译时进行边界检查并不能解决所有问题:

int a[10];

int i = some_complicated_function();

printf("%d

", a[i]);

为了检测到这一点,必须使用运行时检查,并且由于它们对性能的影响,因此在C语言中避免使用它们。即使知道编译时a的数组大小(即sizeof(a)),也无法在不插入运行时检查的情况下防止这种情况。

仅添加其他人的话,您就不能仅仅依赖于这些情况下崩溃的程序,就无法保证如果您尝试访问超出"数组界限"的内存位置,将会发生什么情况。就像执行以下操作一样:

int *p;

p = 135;

*p = 14;

那只是随机的;这可能有效。可能不会。不要这样防止出现此类问题的代码。

不一样。取消引用未初始化的指针应假定为随机指针。访问数组末尾的一项很有可能不会崩溃,因为系统通常一次分配一个完整的内存页(4KB或更多),在数组末尾留下一些空间。

是一样的。 C没有任何保证。如果一个系统以这种方式工作,那很好,那又如何呢?另外,我认为您应该完全重读我的观点,然后重新阅读我写的内容。我不知道你为什么回应这个,我很困惑。

p = 135是类型错误,不能将int分配给int*。

C哲学永远是程序员的信任。同样,不检查边界也可以使C程序运行得更快。

那不是C问题,而是操作系统问题。您的程序已被授予一定的内存空间,您在其中进行的所有操作都可以。仅当您在进程空间之外访问内存时,才会发生分段错误。

并非所有操作系统的每个进程都有单独的地址空间,在这种情况下,您可以在不发出警告的情况下破坏另一个进程或操作系统的状态。

正如jxdpdPar所说,C / C ++并不总是执行范围检查。如果程序访问分配的数组之外的内存位置,则程序可能会崩溃,也可能不会崩溃,因为它正在访问堆栈上的其他变量。

要回答有关C语言中的sizeof运算符的问题:

您可以可靠地使用sizeof(array)/ size(array [0])确定数组大小,但是使用它并不意味着编译器将执行任何范围检查。

我的研究表明,C / C ++开发人员认为您不应该为不使用的东西付费,并且他们信任程序员知道自己在做什么。 (请参见已接受的答案:超出范围访问数组不会出错,为什么?)

如果您可以使用C ++而不是C,也许可以使用vector?您可以在需要性能时使用vector [](但不进行范围检查),或更优选地,使用vector.at()(以性能为代价进行范围检查)。请注意,向量在充满时不会自动增加容量:为安全起见,请使用push_back(),必要时会自动增加容量。

有关矢量的更多信息:http://www.cplusplus.com/reference/vector/vector/

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。