I have developed a reverse-string program. I am wondering if there is a better way to do this, and if my code has any potential problems. I am looking to practice some advanced features of C.
char* reverse_string(char *str)
{
char temp;
size_t len = strlen(str) - 1;
size_t i;
size_t k = len;
for(i = 0; i < len; i++)
{
temp = str[k];
str[k] = str[i];
str[i] = temp;
k--;
/* As 2 characters are changing place for each cycle of the loop
only traverse half the array of characters */
if(k == (len / 2))
{
break;
}
}
}
If you want to practice advanced features of C, how about pointers?
We can toss in macros and xor-swap for fun too!
A pointer (e.g.
char *, read from right-to-left as a pointer to achar) is a data type in C that is usedto refer to location in memory of another value. In this case,
the location where a
charis stored. We can dereferencepointers by prefixing them with an
*, which gives us the valuestored at that location. So the value stored at
stris*str.We can do simple arithmetic with pointers. When we increment (or decrement)
a pointer, we simply move it to refer to the next (or previous)
memory location for that type of value. Incrementing pointers of
different types may move the pointer by a different number of
bytes because different values have different byte sizes in C.
Here, we use one pointer to refer to the first unprocessed
charof the string (str) and another to refer to the last (end).We swap their values (
*strand*end), and move the pointersinwards to the middle of the string. Once
str >= end, eitherthey both point to the same
char, which means our original string had anodd length (and the middle
chardoesn’t need to be reversed), orwe’ve processed everything.
To do the swapping, I’ve defined a macro. Macros are text substitution
done by the C preprocessor. They are very different from functions,
and it’s important to know the difference. When you call a function,
the function operates on a copy of the values you give it. When you call
a macro, it simply does a textual substitution – so the arguments you give
it are used directly.
Since I only used the
XOR_SWAPmacro once, it was probably overkill to define it,but it made more clear what I was doing. After the C preprocessor expands the macro,
the while loop looks like this:
Note that the macro arguments show up once for each time they’re used in the
macro definition. This can be very useful – but can also break your code
if used incorrectly. For example, if I had compressed the increment/decrement
instructions and the macro call into a single line, like
Then this would expand to
Which has triple the increment/decrement operations, and doesn’t actually
do the swap it’s supposed to do.
While we’re on the subject, you should know what xor (
^) means. It’s a basicarithmetic operation – like addition, subtraction, multiplication, division, except
it’s not usually taught in elementary school. It combines two integers bit by bit
– like addition, but we don’t care about the carry-overs.
1^1 = 0,1^0 = 1,0^1 = 1,0^0 = 0.A well known trick is to use xor to swap two values. This works because of three basic
properties of xor:
x ^ 0 = x,x ^ x = 0andx ^ y = y ^ xfor all valuesxandy. So say we have twovariables
aandbthat are initially storing two valuesvaandvb.So the values are swapped. This does have one bug – when
aandbare the same variable:Since we
str < end, this never happens in the above code, so we’re okay.While we’re concerned about correctness we should check our edge cases. The
if (str)line should make sure we weren’t given aNULLpointer for string. What about the empty string""? Wellstrlen("") == 0, so we’ll initializeendasstr - 1, which means that thewhile (str < end)condition is never true, so we don’t do anything. Which is correct.There’s a bunch of C to explore. Have fun with it!
Update: mmw brings up a good point, which is you do have to be slightly careful how you invoke this, as it does operate in-place.
This works fine, since
stack_stringis an array, whose contents are initialized to the given string constant. HoweverWill cause your code to flame and die at runtime. That’s because
string_literalmerely points to the string that is stored as part of your executable – which is normally memory that you are not allowed to edit by the OS. In a happier world, your compiler would know this, and cough an error when you tried to compile, telling you thatstring_literalneeds to be of typechar const *since you can’t modify the contents. However, this is not the world my compiler lives in.There are some hacks you could try to make sure that some memory is on the stack or in the heap (and is therefore editable), but they’re not necessarily portable, and it could be pretty ugly. However, I’m more than happy to throw responsibility for this to the function invoker. I’ve told them that this function does in place memory manipulation, it’s their responsibility to give me an argument that allows that.