初始或末端 malloc 缓冲液可能吗?

initial or terminal malloc buffer possible?

提问人:Sasha 提问时间:10/7/2023 最后编辑:Sasha 更新时间:10/8/2023 访问量:93

问:

假设我执行以下操作:

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 = ...;
}

似乎在这些循环中出现错误行为的可能性非常小,避免;对于第一个循环,如果 ,即我们有一个系统,在可能的限制和溢出的末尾,我们被分配了内存......对于第二个循环,如果 ,即我们有一个系统,在内存开始时我们被分配了内存......在这两种情况下,循环都不会在需要时结束......ip + length == 0size_tp == 0

可能这些并没有真正发生,但如果这是未定义的行为,那么也许最好循环使用它,尽管它看起来不那么优雅。i


编辑:在Fe2O3的评论之后,我记得我确实想问一点不同。也就是说,我想要的不是 s 的数组,而是某种结构类型的元素数组,因此结构体可能相对较大,大小为 ,比如说。那么第二个循环失败就足够了,没有必要。此外,达到最大尺寸减去就足够了......当然,可以更大......char3000p < 30000final30003000

c for 循环 指针 malloc undefined-behavior

评论

1赞 Fe2O3 10/7/2023
0在大多数实现中,等同于 或 。这是分配失败的“特殊迹象”。因此,它不能同时用于指示成功分配。在另一端,“堆”边界不能渗入位于最高地址的边界。换句话说,这不是一个问题......(void*)0NULLstack
1赞 pmg 10/7/2023
p + length根据标准,始终是正确的。对于第二种情况,请使用do ... while(q-- > p);
1赞 Fe2O3 10/7/2023
实际上,您的第 4 个代码段进入了 UB......更好。。。UB 递减到小于 的值,然后比较两个指针......for (char* q = final +1; q-- > p; )qp
1赞 Sasha 10/7/2023
@pmg谢谢!第二个建议实际上非常好,出于某种原因没有考虑过。关于标准,很高兴知道,似乎它也解决了第一个“问题”......
1赞 pmg 10/7/2023
p + length作为指针总是正确的(例如,对于指针比较)......如果取消引用,则不正确:无效!*(p + length)

答:

1赞 Chris Dodd 10/7/2023 #1

TL;DR:递增的指针版本是可以的,但递减的指针版本是不确定的。

C 标准将数组中的指针算术定义为有效,只要生成的指针指向 arrar 的元素或指向“一个过去的末尾”。在这种特殊情况下,你会得到一个有效的指针,该指针不能被取消引用(这是未定义的),但它将始终比指向数组任何元素的任何指针进行比较

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

因此,当你递增指针时,当你超过数组的末尾时,你会得到这个特殊的“一个超过末尾”的指针,该指针将比指向最后一个元素的指针更大,并且循环将终止。但是,在递减循环中,在到达第一个元素后,您将再次递减指针并“下溢”,从而产生未定义的行为。

评论

0赞 cafce25 10/7/2023
这几乎是真的,除了 OP 的正向版本正在使用 for which is UB 以防万一。p + (length - 1)finallength == 0
0赞 Sasha 10/7/2023
谢谢,除了上面@pmg也给出了递减循环的解决方案;写做...而(q-->p)。但是,如果长度 == 0,则该解决方案有问题,因此应该单独检查,我猜......
0赞 Sasha 10/7/2023
@cafce25对..所以我想对于这两种情况,递增和递减,最好先检查长度 == 0 ......
0赞 cafce25 10/7/2023
@Sasha,不,您可以创建条件并用 FeO3 提供了一个适用于向后情况的版本,而无需明确检查,即使对于与q < finalfinal = p + length;length != 0char
0赞 Fe2O3 10/8/2023
@Sasha 在思考事情时,最好在脑海中有一个画面。问:例如,可容纳 0 个鸡蛋的标准鸡蛋纸盒是什么?A:“空气”......如果你有一个数组,那么你至少有 1 个元素。
1赞 Sasha 10/7/2023 #2

总结一下人们所说的话(谢谢!),以下几点应该始终有效(我认为):

size_t length = ...;
type_t* p = malloc(length * sizeof(type_t));
type_t* const q = p + length;

/* looping incrementally */
for (type_t* r = p; r < q; ++r) {
  *r = ...;
}

/* looping decrementally */
for (type_t* r = q; r > p;) {
  *--r = ...;
}

评论

1赞 Chris Dodd 10/7/2023
对于第三个,您需要...严格正确。否则,即使指针未定义,也会错误地递减指针。for (type_t* r = q; r > p; ) { *--r=r == p
0赞 Sasha 10/7/2023
@ChrisDodd如果我理解正确,我会递减它,但循环会结束,这样我就不会再关心值了......r
1赞 pmg 10/7/2023
对于递减的......确保(为什么它会是 0?哈哈)并坚持循环 :-)length >= 1do ... while
0赞 Eric Postpischil 10/7/2023
@Sasha:回复“我会递减它,但循环会结束”:不,你不能确定你会递减它,仅基于 C 标准。您尝试用 递减它,但有什么作用呢?When 已经 ,所以可以指出之前,C 标准说行为没有定义。所以你不知道会减少.它可能会中止程序。或者,编译器可以根据仅在保持在适当范围内时才定义的事实进行推导,并根据这些推导奇怪地优化您的程序。r-- > pr--rpr--rpr--rr--r
1赞 Sasha 10/7/2023
@EricPostpischil好的,谢谢大家,我很感激。我没有意识到仅仅将指针“域外”是不应该做的。所以如果我现在理解正确的话,代码是UB,不能指望和.但和第二个一样......char* p = malloc(10); --p; ++p;char* p = malloc(10);char* p = malloc(10); p += 10; p -= 10;