减去指针

Subtracting pointers

本文关键字:指针      更新时间:2023-10-16

我被要求描述这些代码行对大学作业的作用

int main() {
    int t1[] = {0,0,1,1,1}, t2[] = {0,0,1,1,1};
    int *p1 = t1, *p2 = t2;
    while (!*p1++ || !*p2++);
    cout << (p1-t1) << endl;
    cout << (p2-t2) << endl;
}
我的看法是,创建 2 个

int 类型的数组并填充值,创建 2 个指针并指向每个数组,然后我开始遇到麻烦。

while (!*p1++ || !*p2++);

对我来说,这是说,当0移动*p1的位置时,或者当0移动*p2的位置一个地方时,我真的对这个假设没有信心吗?

cout << (p1-t1) << endl;

然后我们转到cout,现在我的看法是,我从t1的位置中减去p1的位置,其中p1的位置是 while 和 t1 指向数组中的第一个位置。同样,我可能完全错了,我只是在学习指针,所以如果我的假设是错误的,请记住这一点。

while 循环实际上非常可怕。我从未在现实生活中见过这样的代码,并且会宣布任何程序员在现实生活中这样做都是疯狂的。我们需要一步一步地完成:

while (condition);

我们这里有一个带有空语句的 while 语句(";" 本身就是一个空语句(。评估条件,如果为 true,则执行该语句(由于它是一个空语句,因此不执行任何操作(,然后我们重新开始。换句话说,反复评估条件,直到它为假。

condition1 || condition2

这是一个"或"语句。评估第一个条件。如果为 true,则计算第二个条件,结果为"true"。如果为假,则评估第二个条件,结果相应地为"真"或"假"。

while (condition1 || condition2);

这将评估第一个条件。如果这是真的,我们重新开始。如果它是假的,我们评估第二个条件。如果这是真的,我们重新开始。如果两者都是假的,我们退出循环。请注意,仅当第一个条件为 false 时,才会计算第二个条件。现在我们看一下条件:

!*p1++
!*p2++

这与 *(p1++( == 0 和 *(p2++( == 0 相同。无论结果如何,每个条件在评估后都会增加 p1 或 p2。如果 *p1 或 *p2 为零,则每个条件为真,否则为假。现在我们检查每次迭代时会发生什么:

p1 = &t1 [0], p2 = &t2 [0]
*p1++ == 0 is true, *p2++ == 0 is never evaluated, p1 = &t1 [1], p2 = &t2 [0].
*p1++ == 0 is true, *p2++ == 0 is never evaluated, p1 = &t1 [2], p2 = &t2 [0].
*p1++ == 0 is false, *p2++ == 0 is true, p1 = &t1 [3], p2 = &t2 [1].
*p1++ == 0 is false, *p2++ == 0 is true, p1 = &t1 [4], p2 = &t2 [2].
*p1++ == 0 is false, *p2++ == 0 is false, p1 = &t1 [5], p2 = &t2 [3].
t1 与 &t1 [0] 相同。p1 - t1 == &t1 [5]

- &t1 [0] == 5。T2 与 &T2 [0] 相同。p2 - t2 == &t2 [3] - &t2 [0] == 3。

你对t1t2p1p2的评估是正确的。

while (!*p1++ || !*p2++);

我不喜欢这种编码风格,因为很容易假设程序员错误地将分号放在那里。为了表明空体是真正有意的,应该以某种方式区分空体(例如带有注释、放在单独的行上或使用大括号

(。

只要病情truewhile就会进入身体。由于这是一个逻辑 or 表达式,因此在 while 循环终止之前,必须false !*p1++!*p2++。当*p1++*p2++都变为非零时,就会发生这种情况。因为逻辑或短路(如果第一个表达式true,则不计算第二个表达式(,p1p2的进展在每次迭代开始时呈现如下:

iter  p1     *p1    p2     *p2    condition
----  --     ---    --     ---    ---------
 0    &t1[0]  0     &t2[0]  0     !*p1++ is true,  !*p2++ not evaluated
 1    &t1[1]  0     &t2[0]  0     !*p1++ is true,  !*p2++ not evaluated
 2    &t1[2]  1     &t2[0]  0     !*p1++ is false, !*p2++ is true
 3    &t1[3]  1     &t2[1]  0     !*p1++ is false, !*p2++ is true
 4    &t1[4]  1     &t2[2]  1     !*p1++ is false, !*p2++ is false

由于每次迭代都使用后增量,因此p1以值 &t1[5] 结尾,p2以值 &t2[3] 结尾。

同一数组中的指针减法根据数组元素的数量测量两个指针之间的距离。大多数表达式中使用的数组名称将衰减为等于指向其第一个元素的指针的值。所以t1衰变成&t1[0]t2衰变成&t2[0]

因此:

p1 - t1 => 5
p2 - t2 => 3

这里要注意的关键是如何计算表达式(a || b)。首先,计算表达式a。如果a返回 true,则不计算b,因为OR任何带有 True 的东西都是True 。这称为短路。

它有助于通过以下方式扩充代码 -

int main(void){
    int t1[] = {0,0,1,1,1}, t2[] = {0,0,1,1,1};
    int *p1 = t1, *p2 = t2;
    cout << *p1 << " " << *p2 << endl;
    cout << p1 << " " << p2 << endl;
    while (!*p1++ || !*p2++) { 
        cout << *p1 << " " << *p2 << endl;
        cout << p1 << " " << p2 << endl;
    }   
    cout << (p1-t1) << endl;
    cout << (p2-t2) << endl;
    return 0;
}

输出:

0 0
0x7fff550709d0 0x7fff550709f0
0 0
0x7fff550709d4 0x7fff550709f0
1 0
0x7fff550709d8 0x7fff550709f0
1 0
0x7fff550709dc 0x7fff550709f4
1 1
0x7fff550709e0 0x7fff550709f8
5 // Final p1 - t1
3 // Final p2 - t2

!*p1++相当于(!(*(p1++))。这是后增量运算符。它递增指针,但返回旧值(在增量之前(。

循环中的表达式计算 5 次。

  1. 在第一次迭代中,p1 递增。由于 *p1 的当前值(递增前(为 0,因此 ! 为 0 返回 1 。由于短路,不会计算表达式的其余部分。因此,只有p1会递增。

  2. 同样的事情发生在下一个循环中。

现在,我们有p1 = t1 + 2 indicesp2 = t2.

  1. 在第三次迭代中,不再0 *p1 的当前值。因此,p1p2都递增。

  2. 同样的事情发生在第四次迭代中。

请注意,在前四次迭代中,p1p2指向0 - 因此左侧或右侧的not True,因此 while 循环继续。

  1. 在第五次迭代中,p1 和 p2 都递增,但由于两者都不指向 0 值,因此循环退出。

因此,p1递增 5 倍,p2递增 3 倍。

总结 - p1 - t1将包含 1 + 在 t1t2 (2 + 2 + 1( 的开头连续出现的 0 的数量。 p2 - t2的计算结果将是 1 + 在t2开始时连续出现的 0 数 (2 + 1(。

首先:

while (!*p1++ || !*p2++);

这意味着虽然p1的内容0继续循环,但每次都会向p1添加1,直到它变得non-zero。此后,虽然p2的内容0继续循环,但每次都会向p1p2添加1。如果在任何时候p1的内容再次变得0逻辑重复(我知道这很混乱(。

基本上,在while(first || second)式测试中,只有当第一部分失败时,才会测试第二部分。无论测试是通过还是失败,指针都会递增。

你对(p1-t1)的假设是正确的。该计算为您提供了 t1 和 p1 之间的整数数(因为它们是 int 指针(。因为t1数组的开头,所以计算实际上为您提供了p1指向的数组的索引(偏移量(。

注意#1:如果p1t1是指针char,那么减去它们会得到它们之间的字符数。如果它们是指针float那么减去它们会给你浮点数等......指针算术以它们所指向的数据类型为单位进行加减。

注 #2:严格来说,t1 是一种数组类型。当您在指针上下文中使用它时,它会折叠为指针。例如,在指针算术中或将其分配给指针变量时。如果这让您感到困惑,请不要担心,大多数情况下它只是用作指针,因为每当上下文暗示时,编译器都会自动进行转换。

至于问题是这会在控制台上打印什么,在你删除之前答案是 0 0 0 ; 在 while 循环结束时。

这个循环的意义是什么?

首先,您使用OR,这意味着如果p1或p2指向的值为0,则将执行块。因此,直到 p1 指向第 3 个元素 (p1-t1( 将为您提供在 t1 中交叉的元素数,而 (p2-t2( 将为 0,因为 (p1-t1( 将返回 true,因此不会检查第二个条件。当 p1 指向 1 时,它将开始递增 p2,直到它指向 t2 的第 3 个元素并且结束。

我相信这就是这项任务对你的全部。

这种关系可以帮助您更好地理解 while 循环中的条件:

arr[ i ] == * ( arr + i )

执行指针减法时(如果指针类型相同(,结果是两个元素之间的距离(在数组元素中(。

假设 p1p2 都是类型 T* 的指针。然后,计算的值为:

( p2 - p1 ) == ( addr( p2 ) - addr( p1 ) ) / sizeof( T )