Always specify length of array pointers.
In C, it is not always guaranteed that an array of char
will be null terminated and therefore, the size can be determined by calling strlen
. Always pass along the length of the array when passing a pointer to a function. This is not just for strings, but also for other pointer types which point to a dynamically allocated and static arrays.
Naming
Choose a consistent naming plan. The most commonly found style in most C code is snake_case. But whatever you choose, stick to it and don't deviate.
Const-correctness
If a method is not going to modify it's parameters, make that intention clear by accepting the arguments as const
. This form of programming is what makes code self-documenting because you don't need to tell anyone that the function does not modify it's parameter, the const
specifier tells the whole story.
Malloc + free
Always remember, for every malloc
, there should be an equal and opposite free
. Not freeing memory leads to unexpected memory leakages. This may not apply to you now, but just know this, many man hours have gone into developing tools for detecting memory leaks (valgrind
, leakSanitizer
, etc) when the best programmers are making extra effort not to leak memory, but still fail. Memory leaks are no joke. Free the memory when you are finished with it.
With all that being said, here is my rendition of your code
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h> /* isspace */
#include <stddef.h> /* size_t */
#define SIZE (size_t)80
void inputStr(char*, size_t);
char* upgradeString(const char const*, size_t);
int main() {
char string[SIZE] = {'\0'}, *result;
printf("Enter string: ");
inputStr(string, SIZE);
size_t strLength = strlen(string);
if (strLength > 0 && isspace(string[strLength - 1])) {
string[--strLength] = '\0';
}
result = upgradeString(string, strLength);
puts(result);
free(result);
}
void inputStr(char* str, size_t length) {
fgets(str, length, stdin);
}
char* upgradeString(const char const* oldText, size_t length) {
size_t i, newSize = 0, foundSpace = 0;
for (i = 0; i < length; i++) {
if (isspace(oldText[i])) {
if (foundSpace) {
continue;
}
foundSpace = 1;
} else {
foundSpace = 0;
}
newSize++;
}
char *result = malloc((sizeof *result) * (newSize + 1));
if (result) {
char *temp = result;
foundSpace = 0;
for (i = 0; i < length; i++) {
if (isspace(oldText[i])) {
if (foundSpace) {
continue;
}
foundSpace = 1;
} else {
foundSpace = 0;
}
*temp++ = oldText[i];
}
}
return result;
}
The changes made are based on the following additional considerations:
- What happens if the user enters nothing (standard input is empty)?
- What happens if the user just presses enter?
Conclusion
In conclusion be precise, be constant with naming, be explicit with intent. This will make your code robust and potentially bug free.
P.S.
It is not necessary to cast the result of malloc
strcpy
checking for null terminator when copying. Since the length oftemp
is known in advance amemcpy
would work just as good and likely a little bit faster. \$\endgroup\$