Buffer overflow
char array[MAX];
scanf("%s", array);
This can overflow the buffer in array
if the user input exceeds MAX-1
characters. There are multiple ways around this:
Ask scanf
to allocate an appropriately sized buffer (if you have a moderately recent glibc):
char *array = NULL;
scanf("%ms", &array);
You need to release the allocated buffer later:
free(array);
To use free
you need to #include <stdlib.h>
.
Use fgets()
and inform it about the buffer size:
fgets(array, sizeof(array), stdin);
This will read a whole line though and not just a single word. It may also include the newline character at the end that you need to remove yourself:
length = strlen(array) - 1;
if (array[length] == '\n')
array[length--] = '\0';
getline()
works in a similar fashion but also allocates a buffer for you; conveniently it also returns the amount of characters read (which may differ from the length of the allocated buffer stored in array_size
):
char *array = NULL;
size_t array_size;
size_t length = getline(&array, &array_size, stdin);
// ... remove potential newline ...
// ... do stuff with array ...
free(array);
getline
requires glibc's GNU extensions (#define _GNU_SOURCE
) or POSIX.1-2008 (#define _POSIX_C_SOURCE 200809L
).
Use appropriate data types
size_t
for length
since that's the return type of strlen
and is guaranteed to hold all possible string lengths. You need to watch out for the corner cases below though to avoid an integer underflow (which you'd also have with any unsigned
type).
- same for
i
since it depends on length
.
Corner cases
What if strlen(array) == 0
? Then strlen(array) - 1
has a huge positive value since it underflows the unsigned return type of strlen
and wraps around to the largest possible value of that type.
In a similar fashion i >= 0
is always true since unsigned types cannot assume values less than 0.
You'll have to find a way to take care of these corner cases. What about simply doing nothing in that case since the reversion of the empty string is the empty string itself?
length = strlen(array);
if (length > 0) {
for (size_t i = length - 1, n = 0; n < length; i--, n++) {
// ...
}
}
I left out i >= 0
intentionally since it is redundant with n < length
. It had also no effect since the ,
operator in C means that only the right side of it is responsible for the value of the entire (sub-)expression. You probably wanted &&
but, as I said, the two conditions are (nearly) equivalent here.
Unnecessary code and (possibly) better algorithms
You don't need an array2
if you end up printing the characters one by one. You can just print them directly from array
:
printf("%c", array[i]);
Alternatively you can first revert the string and later print the whole string:
for (unsigned short i = length, n = 0; i >= 0, n <= length; i--, n++)
array2[n] = array[i];
fwrite(array2, 1, length, stdout);
Or you can revert the string in-place without an additional buffer/array:
for (unsigned short i = length, n = 0; i > n; i--, n++)
{
char c = array[n];
array[n] = array[i];
array[i] = c;
}
puts(array);
puts
prints the whole string and appends a newline. By convention *nix programs always terminate their lines, including the last (unless they operate on raw bytes).
Better character output function
printf
is very comfortable to use if you want formatted output but it's also pretty heavy-weighted. If you're only printing individual characters you can just use:
putchar(array[i]);
Error checking
You should check the return values of all functions that may fail if it impacts the outcome of the program. For instance:
scanf
, fgets
, or getline
may all return error conditions if an I/O error occurred that would mess up the remainder of the program. You should check for errors and react accordingly (i. e. shut down the program):
if (scanf("%ms", &array) <= 0)
// ...
if (fgets(...) == NULL)
// ...
if (getline(...) == -1)
// ...
If an error occurs I suggest you print an error message and return an error condition yourself (which terminates the program if done from main()
):
if (...) {
perror("Input error");
return 1;
}
Terminating a program with a value other than 0 notifies the caller of an error by convention.
In a similar fashion you can check for errors on printf
, fwrite
, puts
, and putchar
and react to them.
You can read about the error conditions of functions in their manual pages. On most *nices you can access those comfortably with man 3 <FUNCTION_NAME>
.
Use expressive variable names
array
is a buffer for a string: string_buffer
or shorter strbuf
.
i
and n
are the left and right indices of the reversion loop: left
and right
.
Final program
A possible final program could look like this:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
char *strbuf = NULL;
size_t strbuf_size;
ssize_t length = getline(&strbuf, &strbuf_size, stdin);
if (length == -1) {
perror("Input error");
return 1;
}
if (length != 0) {
if (strbuf[length-1] == '\n')
strbuf[--length] = '\0';
if (length != 0) {
for (size_t right = (size_t) length - 1, left = 0; left < right; right--, left++) {
char c = strbuf[left];
strbuf[left] = strbuf[right];
strbuf[right] = c;
}
if (puts(strbuf) == EOF) {
perror("Output error");
return 1;
}
}
}
free(strbuf);
return 0;
}