I have been practicing C and I wanted to reverse each word in a string. I wrote this code and although it works, I am not happy with my solution. It requires twice the length of the original string to store the result
and buffer
respectively and there are too many if statements which makes the code hacky. I am wondering how can I do better?
#include <stdio.h>
#include <string.h>
int main()
{
char* str = "123 456 789";
int i, j = 0, k, l = 0;
int str_len = (int)strlen(str);
char buffer[str_len];
int buffer_size = 0;
char result[str_len + 1];
for (i = 0; i < str_len; i++)
{
char c = str[i];
if (c == ' ' || (i + 1) == str_len)
{
short is_end_of_string = 0;
if ((i + 1) == str_len)
{
buffer_size++;
buffer[l] = c;
is_end_of_string = 1;
}
for (k = 0; k < buffer_size / 2; k++)
{
char temp = buffer[k];
buffer[k] = buffer[buffer_size - k - 1];
buffer[buffer_size - k - 1] = temp;
}
for (k = 0; k < buffer_size; k++)
{
result[j++] = buffer[k];
}
if (!is_end_of_string)
{
result[j++] = c;
buffer_size = 0;
l = 0;
}
}
else
{
buffer_size++;
buffer[l++] = c;
}
}
result[j] = 0;
printf("%s\n", result);
return 0;
}
1 Answer 1
Clarify "word"
OP's "word" seems to be characters separated by a space ' '
. A more common definition would use any white-space (tabs, end-of-line, etc.) , not just ' '
as a separator.
Research isspace()
in <ctype.h>
.
Alternative
It requires twice the length of the original string
No need to find the string or word length anywhere. O(1) extra memory needed, unlike OP's O(length) for an in-place reversal.
Algorithm:
p = string start
forever
while is white-space: p++
if p[0] then
note beginning address
while p[0] and is non-white-space: p++
note one-past-end-of-word address
while one-past-end-of-word > beginning
swap (beginning[0], one-past-end-of-word[-1])
beginning++;
one-past-end-of-word--;
else we are done
For general use: String length may exceed INT_MAX
Use size_t
to handle all possible string lengths. Cast not needed. Note that size_t
is an unsigned type, so other parts of code made need changing too.
// int str_len = (int)strlen(str);
size_t str_len = strlen(str);
For boolean objects, use _Bool
or bool
#include <stdbool.h>
// short is_end_of_string = 0;
bool is_end_of_string = false;
// is_end_of_string = 1;
is_end_of_string = true;
There are times to use maybe unsigned char
for a boolean if we had a large array of booleans, yet this is not the case here.
Testing
Try more challenging strings too.
"123 456 789"
" 123 456 789"
"123 456 789 "
" 123 456 789 "
"123\t 456\n 789"
" "
""
"1 2 3\n"
Advanced
Access characters of a string via unsigned char *
instead of char *
for more proper use of is...()
functions in <ctype.h>
which can fail with negative values.
Pedantic: unsigned char *
access also handles rare non-2's complement with signed char to properly distinguish between +0 and -0.
Print with sentinels
Easier to detect proper handling of leading/trailing white-space.
// printf("%s\n", result);
printf("<%s>\n", result);