减去指针

JTK*_*JTK 38 c++ pointers

我被要求描述这些代码行正在为大学作业做些什么

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;
}
Run Code Online (Sandbox Code Playgroud)

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

while (!*p1++ || !*p2++);
Run Code Online (Sandbox Code Playgroud)

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

cout << (p1-t1) << endl;
Run Code Online (Sandbox Code Playgroud)

那么我们继续前进cout,现在我对此的看法是,我正在从位置减去p1位置t1,在那里p1被while定位并t1指向数组中的第一个位置.我可能完全错了,我只是在学习指针,所以如果我的假设错了,请记住这一点.

gna*_*729 46

while循环实际上非常可怕.我从来没有在现实生活中看到这样的代码,并且会宣称任何程序员在现实生活中都是疯狂的.我们需要逐步完成这个步骤:

while (condition);
Run Code Online (Sandbox Code Playgroud)

我们在这里有一个带有空语句的while语句(";"只是一个空语句).评估条件,如果它是真实的,则执行该语句(什么也不做,因为它是一个空语句),我们从头再来.换句话说,重复评估条件直到它为假.

condition1 || condition2
Run Code Online (Sandbox Code Playgroud)

这是一个"或"声明.评估第一个条件.如果为真,则不评估第二个条件,结果为"真".如果为假,则评估第二个条件,结果为"真"或"假".

while (condition1 || condition2);
Run Code Online (Sandbox Code Playgroud)

这评估了第一个条件.如果这是真的,我们从头开始.如果它是假的,我们评估第二个条件.如果这是真的,我们从头开始.如果两者都为假,我们退出循环.请注意,仅在第一个条件为假时才评估第二个条件.现在我们来看看条件:

!*p1++
!*p2++
Run Code Online (Sandbox Code Playgroud)

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

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].
Run Code Online (Sandbox Code Playgroud)

t1与&t1 [0]相同.p1-t1 ==&t1 [5] - &t1 [0] == 5.t2与&t2 [0]相同.p2 - t2 ==&t2 [3] - &t2 [0] == 3.

  • 我将第二个@GingerPlusPlus评论.在一个条件下的副作用通常不是一个好主意(虽然有一些使用它们的奉献成语); 有条件的副作用是可怕的.并使用`!`与零进行比较,而不是显式.更多的混淆. (10认同)
  • 除了极少数例外(例如,对于附近有不等式比较的并行结构),IMHO应该*总是*使用`!var`或bare`var`编写比较,*尤其是*当`var`是指针时.基本原理与为什么永远不应该写一个明确的`== true`或`== false`基本相同. (2认同)
  • @ alex.forencich如果你的意思是应该尽可能避免使用原始指针,当然,但是超过数组末尾的指针是非常常见和有用的; 随机访问迭代器通常只是原始指针周围的瘦包装器,在这种情况下,容器的`end()`迭代器实际上与这样的指针相同. (2认同)
  • 像这样的例子的要点与良好的代码无关.这是一个很好的调试和逻辑练习. (2认同)

jxh*_*jxh 14

你在你的评价是正确的t1,t2,p1,和p2.

while (!*p1++ || !*p2++);
Run Code Online (Sandbox Code Playgroud)

我不喜欢这种编码风格,因为很容易假设程序员错误地放置了分号.为了表明空体是真正的意图,应该以某种方式区分空体(例如注释,放在单独的线上,或者使用花括号).

while,只要该条件是进入体内true.由于这是一个逻辑或表达,二者!*p1++!*p2++必须false在之前while循环终止.当两者都变为非零时*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
Run Code Online (Sandbox Code Playgroud)

由于每次迭代都使用后递增,因此p1以值结束&t1[5],并p2以值结束&t2[3].

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

从而:

p1 - t1 => 5
p2 - t2 => 3
Run Code Online (Sandbox Code Playgroud)

  • +"同一数组中的指针减法测量两个指针之间的数组元素数量之间的距离",这在其他答案中并不是很清楚 (3认同)

Ank*_*ain 13

这里要注意的关键是如何(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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

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
Run Code Online (Sandbox Code Playgroud)

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

循环中的表达式被评估5次.

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

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

现在,我们有p1 = t1 + 2 indices,和p2 = t2.

  1. 在第三次迭代中,*p1不再是当前值0.因此,两个p1p2递增.

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

请注意,在前四次迭代中,要么指向p1或者p2指向a 0- 所以not左侧或右侧都是True,因此while循环继续.

  1. 在第五次迭代中,p1和p2都递增,但由于两者都没有指向0值,所以循环退出.

因此p1增加5次,并且p2增加3次.

总结 - p1 - t1将包含1 +在开头t1t2(2 + 2 + 1)连续出现的0的数量.p2 - t2将评估在t2(2 + 1)的开头连续出现的1 + 0的数量.


Gal*_*lik 6

第一:

while (!*p1++ || !*p2++);
Run Code Online (Sandbox Code Playgroud)

这意味着虽然内容 p10保持循环每次添加1,p1直到它成为non-zero.之后,在内容p2就是0不断循环添加1到两个p1p2各一次.如果在任何时候的内容 p1成为0再次逻辑重复(我知道这是令人困惑).

基本上在while(first || second)样式测试中,第二部分在第一部分失败时进行测试.无论测试通过还是失败,指针都会递增.

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

注意#1:如果p1t1char指针然后减去它们将给出它们之间的字符数.如果它们是float指针,那么减去它们会给你浮点数等等......指针算法以它们指向的数据类型为单位添加和减去.

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