可以使用初始或终端 malloc 缓冲区吗?

问题描述 投票:0回答:1

假设我做了如下事情:

size_t length = 1000;
char* p = malloc(length);

然后我想循环遍历元素,所以最基本的是:

for (size_t i = 0; i < length; ++i) {
  p[i] = ...; // or p[length - 1 - i] = ...
}

但也有可能是

char* q = p;
for (size_t i = 0; i < length; ++i) {
  *q = ...;
  ++q;
}

或相反

char* q = p + (length - 1);
for (size_t i = 0; i < length; ++i) {
  *q = ...;
  --q;
}

我的问题是,如果我想避免

i
并执行以下操作怎么办:

char* const final = p + (length - 1);
for (char* q = p; q <= final; ++q) {
  *q = ...;
}

或相反:

char* const final = p + (length - 1);
for (char* q = final; q >= p; --q) {
  *q = ...;
}

似乎在那些避免

i
的循环中出现错误行为的可能性非常小;对于第一个循环,如果
p + length == 0
会怎样,即我们有一个系统,我们在可能的
size_t
限制的最末端分配了内存,并且发生了溢出...对于第二个循环,如果
p == 0
,会怎样,也就是说,我们有一个系统,我们在内存的开头分配了内存...在这两种情况下,循环都不会在需要时结束...

可能这些并没有真正发生,但如果这是未定义的行为,那么也许最好用

i
循环,尽管它看起来稍微不太优雅..


编辑:在 Fe2O3 的评论之后,我记得我确实想以不同的方式问它。也就是说,我想要的不是一个

char
数组,而是某种结构类型的元素数组,因此该结构可能相对较大,例如大小为
3000
。那么,为了使第二个循环失败,
p
 < 3000
就足够了,没有必要为
0
。另外,
final
在最大尺寸减去
3000
就足够了...当然,
3000
可以更大...

c for-loop pointers malloc undefined-behavior
1个回答
0
投票

TL;DR:递增指针版本可以,但递减指针版本未定义。

C 标准将数组中的指针算术定义为有效,只要结果指针指向 arrar 的元素或指向“末尾的一个”。在这种特殊情况下,您会得到一个无法取消引用的有效指针(这样做是未定义的),但它始终会比指向数组中任何元素的任何指针都大

6.5.6.8 当整数类型的表达式与指针相加或相减时, 结果具有指针操作数的类型。如果指针操作数指向一个元素 一个数组对象,并且数组足够大,结果指向从 原始元素,使得结果和原始元素的下标之差 数组元素等于整数表达式。换句话说,如果表达式 P 指向 数组对象的第 i 个元素,表达式 (P)+N(等效于 N+(P))和 (P)-N(其中 N 的值为 n)分别指向第 i+n 个和第 i−n 个元素 数组对象,前提是它们存在。此外,如果表达式 P 指向最后一个 数组对象的元素,表达式 (P)+1 指向最后一个元素 数组对象,如果表达式 Q 指向数组对象的最后一个元素, 表达式 (Q)-1 指向数组对象的最后一个元素。如果两个指针 操作数和结果指向同一个数组对象的元素,或者指向最后一个元素 数组对象的元素,求值不得产生溢出;否则, 行为未定义。如果结果指向数组对象的最后一个元素,则它 不得用作所求值的一元 * 运算符的操作数。

因此,当您递增指针时,当您越过数组末尾时,您将得到这个特殊的“越过末尾”指针,该指针将比指向最后一个元素的指针大,循环将终止。然而,使用递减循环,在到达第一个元素后,您将再次递减指针并得到一个无效指针,该指针可能无法正确比较小于数组的开头。

© www.soinside.com 2019 - 2024. All rights reserved.