0

After reading the code on the top answer here,
I have a couple of questions WHY this answer successfully works as it should be.
I have walked through myself to this code, but still do not know WHY str achieves to get the expected string.
I DO NOT have enough reputation to leave comments on this answer, so I decided to open a new question.

The following is the code provided by @dasblinkenlight. (I changed the input string for testing purposes.)

void remove_all_chars(char* str, char c) {
    char *pr = str, *pw = str;           // line 1
                                         // Inside the while loop
    while (*pr) {                        // line 3
        *pw = *pr++;                     // line 4
        pw += (*pw != c);                // line 5
        printf("str: %s\n", str);       // FYI, this will always print `abcd`, isn't it weird, if `str` is mutated in the end of this program?
    }
    *pw = '\0';
}

int main() {
    char str[] = "abcd";
    remove_all_chars(str, 'c');
    printf("'%s'\n", str);
    return 0;
}

So, this is the walk through of the code, in my understanding.

In line 1, both *pr and *pw are pointing to the first element of the input string
*pr ==> 'a'
*pw ==> 'a'

Inside the while loop.
The result will be separated by | per iteration.
                                        (1st iteration)   (2nd iteration)   (3rd iteration)   (4th iteration)
*pr (line 3) ========>  'a'                   |  'b'                     |  'c'                   |  'd'                     
*pw = *pr++ (line 4) ==> 'a' = 'b'         | 'b' = 'c'           | 'c' = 'd'           | 'c' = '\0'           
(*pw != c) (line 5) ==> 'b' != 'c' (true)| 'c' != 'c' (false)| 'd' != 'c' (true) | '\0' != 'c' (true)
pw(after, pw += (*pw != c)) ==> str[1], 'b' | str[1], 'c' | str[2], 'c' | str[3], 'd'

So now, if my walkthrough is correct, I should have str, with the value of bd.
However, running this on the code editor, it will give me back the expected answer which is abd.

I double-checked my walk through with editor, so I am pretty sure in the changes of values in each variable.
If you can help with understanding why str ends up with the value of abd, please let me know.

FluffyCat
  • 55
  • 2
  • 9
  • 1
    1 - declare/initialize *read pointer* and *write pointer*; 3 loop until end of string (the `'\0'` char has value `0` so the `while` loop ends); 4 set char under write pointer to char under read pointer - advance read pointer 5 `(*pw != c)` just tests if write pointer currently points to `c` (the conditional will either evaluate `1` (true) or `0` (false - don't advance write pointer) - char at that location will be overwritten. That pretty much it. – David C. Rankin May 07 '20 at 07:29
  • 1
    When you process the assignments, you shouldn't think in terms of `'a' = 'b'`, but more in terms of `str[1] = 'b'`, for example. What is there will be overwritten. What's important in where you write. – M Oehm May 07 '20 at 07:30
  • @DavidC.Rankin @MOehm Thank you for your answer! Yes, `*pw = *pr++;` means, advance the read pointer, which is the first iteration, `str[1] ('b')`. So now, what this piece of code is doing is, overwrite `*pw` which is `str[0]('a')` with `str[1] ('b')`, right? I find this very weird. WHY would you overwrite 'a' with 'b' ? This is causing the final `str` to start with `b`, which is not what expected result does have, which is `abd`. – FluffyCat May 07 '20 at 07:40
  • Also, why doe `printf("str: %s\n", str);` in line 6, always return `abcd`, when read and write pointers are directly mutating the value of `str`? I expected that the value of `str` would change after every iteration since it is handled by pointers. – FluffyCat May 07 '20 at 07:42
  • 1
    On the first iteration it doesn't really do anything because both `pw` and `pr` are initialized to `str`. So it overwrites the 1st char with the 1st char then advances `pr` the `++` is applied as a *side-effect* **after** the original pointer value was used. On the next iteration if `(*pw != c)`, `pw` is also advanced. The first time `(*pw != c)` returns `1`, then the character pointed to by `pw` will be overwritten on the next iteration. (there are much more readable ways to write this logic -- but if you digest this it will be a good pointer exercise) – David C. Rankin May 07 '20 at 07:48
  • 1
    If you look closely, you will see that the printed string is different in the fourth pass. This will be more apparent if you remove the `'a'`´. – M Oehm May 07 '20 at 07:50
  • 1
    `printf("str: %s\n", str);` always prints `abcd` (until last iteration where it is `abdd`) because the `'c'` is overwritten wit `'d'` on the last iteration, but the last `'d'` isn't overwritten until `*pw = '\0';` is reached `:)` – David C. Rankin May 07 '20 at 07:52

1 Answers1

2

Whats tipping you off is line 4. You can think of this line as two lines:

new line 4: *pw = *pr;

new line 5: pr++;

This means that when pw is pointing to 'a' it will be overwritten by 'a' which is what pr is currently pointing at, not 'b'. In your walkthrough you have 'a' = 'b'. The correct version would be 'a' = 'a' and then pr advances to 'b'. Do a walkthrough with the two lines of code I provided and you will understand it better.