Reputation: 111
*char_ptr++ -= 0x20
Why that expression increments pointer just once? This expression can be decomposed like
*char_ptr++ = *char_ptr++ - 0x20
That means pointer must be incremented twice. First time on the right side, and second on the left. But this incremented just by once.
This expression is the part of the function below
void to_lower_case(char *char_ptr)
{
while(*char_ptr)
{
if (*char_ptr < 0x41)
{
printf("\nInvalid login\n");
exit(0);
}
if (*char_ptr > 0x5A)
*char_ptr++ -= 0x20;// *char_ptr++ = *char_ptr++ - 0x20
}
}
Upvotes: 1
Views: 148
Reputation: 48083
You've gotten the answer from the C Standard, but let's also think about why the definition is the way it is, why the expression *char_ptr++ -= 0x20
does not increment char_ptr
twice.
Suppose I had a null-terminated array of characters (as in fact the original code does), and suppose I wanted to subtract 0x20
from each character (which is similar to what the original code is doing). I might write
while(*char_ptr)
*char_ptr++ -= 0x20;
Now, even before we figure out exactly what this code does, certain things jump out at us. In particular, the while(*char_ptr)
part and the char_ptr++
part immediately tell us that this code is looping over the characters until it hits a null (or zero) character -- in other words, it is looping over the characters of a string. This is an extremely common idiom in C code.
And in this case, what it's doing with each character of the string is of course subtracting the value 0x20
from it.
So if the expression *char_ptr++ -= 0x20
did end up incrementing char_ptr
twice, this code wouldn't work! And that would be sad. So it's good that the definition of the -=
operator (and indeed the definition of all the "op=" operators) is that the left-hand side is evaluated only once. And it's no coincidence that they were defined this way, either -- they're defined this way precisely so that code like this works as expected.
While we're at it, let's look at a couple of other aspects of the original code.
What are those magic numbers 0x41
and 0x5A
? Well, in ASCII, 0x41
is capital A, and 0x5A
is capital Z. There's a school of thought that says you can't do C programming without an ASCII table handy, but in fact, laboriously looking up such codes us unnecessary extra work, because the compiler is perfectly willing to do it for us. We can write
if (*char_ptr < 'A')
and
if (*char_ptr > 'Z')
and we'll get the same result, with the added benefits that (a) the code is clearer and easier to read and (b) it's that much more portable to some hypothetical machine that doesn't use ASCII.
The magic number 0x20
that's being subtracted from the lower-case letters is the difference between a lower-case A and a capital A. (In ASCII, lower-case A is 0x61
.) So if you've got a lower-case letter, subtracting 0x20
turns it into the corresponding upper-case letter. So it looks like the code in the question is misnamed: It's actually converting to upper case, not lower case. (It's also going to mistakenly convert certain other characters, since anything greater than Z is converted, which will include punctuation characters like '['
and '|'
.)
Finally, since the expression we've been talking about is the only part of the code that increments char_ptr
, and since it acts only for non-upper-case characters, if the input contains any upper-case letters, char_ptr
won't get incremented even once, and this code will get stuck in an infinite loop.
Upvotes: 0
Reputation: 32727
From one version of the C Language Standard (N1570, section 6.5.16.2 Compound assignment) it says:
A compound assignment of the form E1 op= E2 is equivalent to the simple assignment expression E1 = E1 op (E2), except that the lvalue E1 is evaluated only once, ...
Using the -=
expression, this says that
*char_ptr++ -= 0x20;
is the equivalent of
*char_ptr++ = *char_ptr++ - 0x20;
except that the *char_ptr++
part is evaluated only once (so the pointer increment will only happen once).
Equivalently, it is the same as
*char_ptr = *char_ptr - 0x20;
char_ptr++;
Upvotes: 4
Reputation: 108986
When "we" say a += b;
is the same as a = a + b;
"we" mean that as a 'simplification', not text substitution as in a macro.
What happens with *chr_ptr++ -= 0x20;
is the equivalent of
// *char_ptr++ -= 0x20;
/*1*/ char *tmp = char_ptr;
/*2*/ chr_ptr++; // chr_ptr += 1; // chr_ptr = chr_ptr + 1;
/*3*/ *tmp -= 0x20; // *tmp = *tmp - 0x20;
Note that /*2*/
can happen where I put it or after /*3*/
Upvotes: 3