tags:

views:

328

answers:

8

OK, sorry about the bad pun :P

I've coded the old trick of HAL => IBM in C. I've just read the first few pages in K&R reguarding them, and I thought it would be a good first play with them.

char evil[] = "HAL";
char *ptr = evil;   
for (int i = 0; i < strlen(evil); ++i, ++ptr) {             
    (*ptr)++;           
}   
printf("%s\n", evil); // IBM

My problem is, I have two variables incrementing, i and ptr, and something is telling me one of them is redundant (perhaps I'm still not thinking C well enough).

The only reason I use i is to determine if we have read to the end of the string. Is there any way to check the pointer to see if it has arrived at the end of the string?

Update

Sorry for any confusion of the actual question. By have I missed the point I basically meant, why would I use a pointer when I needed an incrementing index to check the length as well. I could just use that index to subscript the right char from the array.

+6  A: 

I typed the question, re-read it, and then realised something very obvious I had overlooked!

char evil[] = "HAL" 
char *ptr = evil;   
for (; *ptr != '\0'; ++ptr) {                       
    (*ptr)++;           
}   
printf("%s\n", evil);

That seemed to do the trick.

alex
You can make the for loop a little clearer by initializing ptr in it: `for (ptr = evil; *ptr != '\0'; ptr++)`. Be careful though. A bad string (without a null terminator) will have your program happily stomping its way across your stack.
Nathon
@Nathon Thanks for the tip.
alex
+2  A: 

The character pointed to will be '\0'. But be careful, misplacing the NUL byte can be a source of program crashes or security holes.

Ignacio Vazquez-Abrams
Ah, yes, after typing the question all out and going over it a few times, it clicked! Happens to me all the time here... Thanks for your answer, do you have any recommended further reading as to why it may be a security hole? I'm still very fresh to C.
alex
http://en.wikipedia.org/wiki/Buffer_overflow
Ignacio Vazquez-Abrams
@Ignacio Many thanks!
alex
+10  A: 

You could as well write

for (char *ptr = evil; *ptr != '\0' ; ++ptr)
teukkam
In C99, it is good to remember. Some compilers require an additional switch to go enable C99 mode.
Juliano
Oh well, I'm not a C programmer, I'm a C++ programmer...
teukkam
Using `NULL` for comparison here, looks weird to me. I am even not sure that this is correct to compare a `null pointer` to a `char`. Probably you meant `'\0'` or just `0`.
Jens Gustedt
You are right, slight mishap there... aren't `'\0'` and `0` pretty much equivalent?
teukkam
`*ptr!='\0'` is more correct. NUL and NULL are very different beasts though NULL degenerates to int whose value is 0 if it needs to and NUL (which is simply a char whose value is 0) gets promoted to int if it needs to. The comparison works by accident which is why I prefer the more explicit `'\0'`.
slebetman
Yes I agree that '\0' is the most readable form as it tells exactly what is being compared, chars in this case.
teukkam
Personally I prefer `0`. In C, character literals are ints, so it's not even technically true that chars are being compared. Even worse: what `'\0'` means is, "the integer value of the character whose integer value is 0". Bit round-the-houses for my taste. But then, I usually use `0` rather than `NULL`, as well. As far as the standard is concerned they are both null pointer constants, and all I need is a null pointer constant. If someone doesn't know whether the variable I'm comparing to is int/pointer/char they're beyond my help, they need to to check the definition ;-)
Steve Jessop
+3  A: 

In C strings end with '\0'. You can use that fact to stop the loop.

for (; *ptr != '\0' ; ++ptr) {
    /* ... */
}
pmg
True, but that doesn't seem to answer the question.
Billy ONeal
Well ... depends on what you think the question is: `Have I missed the point of pointers?` or `Is there any way to check the pointer to see if it has arrived at the end of the string?`
pmg
+2  A: 

Calling strlen(evil) is inefficient. Instead of relying on strlen, use the same thing that makes strlen stop: the null terminator:

for (char *ptr = evil; *ptr; ++ptr) {             
    (*ptr)++;           
}
Marcelo Cantos
+5  A: 

You've figured out the correct implementation, but I would still like to make one point about your original code, which may give you a different way to think about such problems.

Your original solution may be even worse than you think, because its complexity is actually O(n^2), where n is the length of the string. strlen() has a complexity of O(n), because it has to iterate over all the characters in the string until it finds '\0'. So you have a loop that performs n iterations, and calls strlen() each time, resulting in the complexity of O(n^2).

At the very least you should call strlen() once before the loop, and cache the length of the string. And, of course, you have the correct solution that does not call strlen() at all.

Dima
+1 thanks, I also realised I should probably cache the length of the string as well.
alex
+1  A: 

This would do the trick:

char evil[] = "HAL";
*(int*) evil += 65793;
printf("%s\n", evil);
tia
Well, it's certainly more evil. Try it on a 16-bit micro, see how far you get ;-p
Steve Jessop
+1  A: 

Since we are talking about evil:

#include <stdio.h>

int main()
{
    char evil[] = "HAL";
    char *ptr;

    // Increment the pointer and its content in the same expression
    // Just to be really evil

    for (ptr = evil; *ptr != '\0'; ++*(ptr++)) { }

    printf("%s\n", evil);
}
Martin York
I thought I could do that, but I reckoned readability might have suffered! Still, good to know I can so +1
alex
Yes readability does suffer and you should NOT do this. It is an example of being **EVIL**
Martin York
Evil-wise, I was thinking: `while ((*ptr++)++); --*--ptr;`. Or something like that, I haven't tested it for the obvious reason that if I did, I would have to kill myself.
Steve Jessop