We are missing includes of headers <stdlib.h>
(for malloc()
) and <string.h>
(for strlen()
). Without these, compilation will assume they both return int
, which may cause Undefined Behaviour.
Although bool
is now part of the core language, we should consider including <stdbool.h>
for compatibility with compilers that don't yet support C23.
The function needs a good comment to tell its users that it returns a pointer to memory that they need to free()
. Unlike many other languages, memory management in C is very manual, and it's vitally important to know who owns memory resources such as this at any given point.
We don't intend to modify the contents of the strings that are passed as arguments, so we should declare them const char*
(or equivalently, char const*
- the order isn't significant).
Also, memory allocation with malloc()
can fail - a sensible way to deal with that is to return null, and pass the handling on to the caller. Note also that the sizeof
operator measures in units of char
- so sizeof (char)
is tautologically 1
.
A better allocation would look like this:
size_t const len = strlen(word1) + strlen(word2); char *const result = malloc(len + 1); if (!result) { return result; }
As an aside, note that char *const result
means we won't change result
to point anywhere else. But we're free to modify the char
s that it points to.
The skip_p1
and skip_p2
variables are clear in their purpose. Since they are of bool
type, it's clearer to assign true
rather than 1
:
if (*p1 == '\0') { skip_p1 = true; } if (*p2 == '\0') { skip_p2 = true; }
We could replace these by declaring the variables within the for
loop:
for (int i;;) { const bool skip_p1 = (*p1 == '\0'); const bool skip_p2 = (*p2 == '\0');
Although this code shouldn't be reachable, it's problematic on two counts:
return "SHOULD NEVER REACH HERE";
Since this string wasn't allocated by malloc()
, it's an error for the caller to free()
it. And because string literals may be stored in read-only memory, we shouldn't use them to initialise char*
- only char const*
.
I might include <assert.h>
and replace with
assert(!"unreachable"); free(result); return NULL;
That said, I'm happy to trust my compiler's warnings and omit that part entirely. If there's a way it can be reached, I'll get a warning that we fail to return a value in that path.
As to the algorithm, it's reasonably clear. One thing we might want to consider is that when one of the strings ends, then we can just strcpy()
the other into the output (even if the other string has also ended, as that's a good way to add the terminating null character).
Modified code
Taking all the above into account, we get something that looks very different from the original. But I hope it's educational.
#include <stdlib.h> #include <string.h> /* * Create a new string formed by alternating the letters of s1 and s2. * The returned string comes from malloc(); caller must call free(). * Returns a null pointer if allocation fails. */ char *mergeAlternately(char const *s1, char const *s2) { size_t const len = strlen(s1) + strlen(s2); char *const result = malloc(len + 1); if (!result) { return result; } char *out = result; for (;;) { if (*s1 == '\0') { strcpy(out, s2); return result; } if (*s2 == '\0') { strcpy(out, s1); return result; } *out++ = *s1++; *out++ = *s2++; } }
It's always helpful to have a test program that illustrates how to use the function correctly:
#include <stdio.h> int main() { char *merged = mergeAlternately("AAA", "bbbbb"); if (!merged) { fprintf(stderr, "Allocation failure\n"); return EXIT_FAILURE; } printf("Merged result: %s\n", merged); free(merged); }
i
is not initialised, so it's something of a stroke of good luck, I guess, that this even runs... Not a good idea to use uninitialised variables...\$\endgroup\$