1

I have a problem with the following apperently trivial sample code: (on visual studio 2015)

Please ignore the part with pointing to a literal constant, possible warnings or erros on the newwer compiler, that is not what I don't understand.

My problem is why it prints '0' and how the while loop works, tried using both debugger and printf. My understanding of the problem is this:

  1. moves ptr to point at 'e'

  2. checks content of ptr which is 'e' it is not 0 so it enters while loop

  3. back to condition line, moves ptr to 'l'
  4. checks *ptr, it is not 0, enters... blah blah for the letters l, o

Then it increases ptr after 'o' and gets '\0', at which point by my logic it should NOT enter the loop, but i does, and no longer enters after one more step when it is pointing over the terminator at junk?!?

I looked over 2 other topics, this topic about operator precedence and this one about the while(*ptr) case going over the terminator, but I don't understand from the second WHY it enters the loop and thy it increases the pointer value afterwards? for what i understand the order is first increase pointer, then get the value with *

#include <cstdio>
#include<stdlib.h>

int main(void) {
    char* str = "hello";
    char* ptr = str;
    char least = 127;
    while (*ptr++) {
        printf("%c-", *ptr);
        least = ((*ptr) < (least)) ? (*ptr) : (least);
    }
    printf("%d\n", least);
}
Deduplicator
  • 44,692
  • 7
  • 66
  • 118
mihnea
  • 33
  • 4
  • 2
    `*ptr++` is first dereferencing and then incrementing. It is a *post*-increment operator. – Eugene Sh. Jan 24 '20 at 19:49
  • 4
    Here is a hint -- does it print the 'h'? Why or why not? – mevets Jan 24 '20 at 19:50
  • 1
    It enters the loop when the expression `*ptr++` does not convey false. The question you need to ask yourself, once inside the loop what does `*ptr` convey, and how did it get there. That `h` is *not* printed should be a massive clue as to what is really going on. – WhozCraig Jan 24 '20 at 19:50
  • 1
    Is this C or C++? `` is a C++ header, `` is the C header. – Barmar Jan 24 '20 at 19:51
  • 2
    The point is that you're not printing the same character that you tested. – Barmar Jan 24 '20 at 19:54
  • But in my first link on stack overflow it says *ptr++ is *(ptr++), I don't understand operator precedence table that good as ++ is both put at level 1 and level 2 where the * operator is. But is says postfix is at priority 1 and prefix is at level 2. https://en.cppreference.com/w/c/language/operator_precedence – mihnea Jan 24 '20 at 19:56
  • @Barmar ignore my copy paste into visual studio, I have seen i have one header cpp style and one C style, makes no difference, the code as I found it only had the main(). – mihnea Jan 24 '20 at 19:57
  • It is not related to precedence but to what *post*-increment does. It is first "returning" the previous value, and only then incrementing. – Eugene Sh. Jan 24 '20 at 19:58
  • @mevets : thought about the precedence being the issue and it not entering at 0, but some topic said (*ptr++) si *(ptr++), the first in my links. And h should not be printed by any priority version, it increments the pointer in both cases. – mihnea Jan 24 '20 at 20:00
  • @Eugene Sh. just like for while(a[i++]) or a = b++; of f(a++) etc. But that means in my first link there is a mistake, right? – mihnea Jan 24 '20 at 20:01
  • No mistake. As you said, `*p++` is the same as `*(p++)`. But in both cases, the "updated" value of `p` will be "available" after the next sequence point. – Eugene Sh. Jan 24 '20 at 20:03

2 Answers2

2

Inside the loop you're not using the same character that you tested in the while condition.

Since ptr++ is a post-increment, it returns the current value of ptr and then increments it. So when ptr points to the o character, and you do

while (*ptr++)

it tests 'o', which is not zero, so it will enter the loop. But after the test it increments ptr to point to the next character, so now it points to '\0'. Then it prints this character and sets least to it.

You should increment the pointer after processing it. You can do this by moving ptr++ to the end of the loop body. Or you can use a for loop instead of while:

for (ptr = str; *ptr; ptr++)
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Had this feeling, didn't want o believe I am missmatching the precedence. I misstook *ptr++ being the same as *(ptr++) meaning ptr++ gets evaluated first. – mihnea Jan 24 '20 at 20:07
  • It *is* the same! It is not a question of precedence. `p++` is *always* evaluated to the original value of `p`. Increment is an additional *side effect* of it, which is seen after the next sequence point. – Eugene Sh. Jan 24 '20 at 20:07
  • 1
    @mihnea, it's the same thing, the problem here is that you increment the pointer in the wrong place, you should increment it after you use it like you can infer from Barnar's answer, a for loop does just that. – anastaciu Jan 24 '20 at 20:13
  • @mihnea `ptr++` is roughly equivalent to `temp = ptr, ptr = ptr+1, temp`. So `*ptr++` is like `*(temp = ptr, ptr = ptr+1, temp)`. Does that help? – Barmar Jan 24 '20 at 20:15
  • @Barmar And that perfectly captures why `while (*ptr++)` is **horrible** hard-to-understand code. `*(temp = ptr, ptr = ptr+1, temp)` is hard enough to understand on its own - it takes some mental parsing to get through the steps, especially given that they don't proceed linearly. Throwing that into a `while` loop condition and also compressing it to a mere handful of characters makes it into an effectively intractable mess with four separate operations that occur out-of-order all crammed into that limited space. – Andrew Henle Jan 24 '20 at 20:25
  • 1
    @AndrewHenle Indeed, it's rarely useful with a non-empty body. It's useful with an empty body as a simple way to loop to the end of the string. You can also see it in the popular idiom `while (*dest++ = *src++){}` – Barmar Jan 24 '20 at 20:28
  • @EugeneSh. The posted usage isn't idiomatic at all as it controls a non-empty loop. How many questions are posted here every day that are the result of incomplete understanding of `++` and `--` operators? Using those operators to cram too much into too small a space is nothing more than a source of buggy, hard-to-maintain code. IMO anyone who deliberately writes code like that in the question hasn't put any thought into the effort it takes to properly maintain code and needs to grow professionally. – Andrew Henle Jan 24 '20 at 20:36
0

The last printf call outputs the terminating zero of the string literal as an integer.

To make it clear consider a string literal with one actual character as for example "A".

Before the first iteration of the while loop

while (*ptr++)
{
    printf("%c-", *ptr);
    least = ((*ptr) < (least)) ? (*ptr) : (least);
}

the pointer ptr points to the character 'A' of the string literal. This expression

*ptr++

can be imagined like

char c = *ptr;
++ptr;

So the control passes to the body of the loop because 'A' is not equal to zero. Meantime the pointer ptr was increased after evaluation the condition. So now it points to the terminating zero '\0' of the string literal.

As 0 is less than 123 then the variable least gets the value 0.

    least = ((*ptr) < (least)) ? (*ptr) : (least);

In the next iteration of the loop ptr still points to the terminating zero. So the control bypasses the body of the loop and the zero is outputted in the next statement after the loop.

From the C Standard (6.5.2.4 Postfix increment and decrement operators)

2 The result of the postfix ++ operator is the value of the operand. As a side effect, the value of the operand object is incremented (that is, the value 1 of the appropriate type is added to it).

Barmar
  • 741,623
  • 53
  • 500
  • 612
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335