There are two main problems with your (original) code:
- it does not provide for your
find_replace()
function to return the allocated destination buffer to the caller, and
- it does not reliably allocate enough space for the destination buffer.
In principle, issue (1) could be resolved in two ways. Either the space could be allocated by the caller and a pointer to it passed to the function, or the space could be allocated by the function and a pointer returned to the caller. Your original code allocates space in the function, but does not return a pointer to it to the caller.
It is preferable for the function to perform the allocation, because satisfying issue (2) requires a more thorough analysis of the inputs than makes sense to insist the caller perform. Consider what happens in your revised code when you do this:
char dest[4];
int canary = 0;
find_replace("aaa", "a", "longer string", dest);
assert(canary == 0);
Most likely you get a segfault, possibly you get an assertion failure, and perhaps you get who-knows-what, because find_replace()
cannot perform its job without writing past the end of dest
, the result of which is undefined.
Although you've said the exercise requires your function to have no return value (i.e. void
), it can still return a pointer to the destination string via the argument list. You simply pass a pointer to the dest
pointer instead of its value, so that the function can update that value. The signature would look like this:
void find_replace(const char *src, const char *from, const char *to,
char **dest_p);
(Note the const
qualifers for src
, from
, and to
, which are appropriate, if not necessary, if the function is meant to accept string literals.)
The amount of space needed for dest
is the length of src
plus one for the terminator plus, if to
is longer than from
, the difference between the lengths of the to
and from
strings times the number of appearances of the to
string. You could, however, compute an upper bound on that length, and later shrink the allocation (if needed) after you find out how much space is actually used. For example:
void find_replace(const char *src, const char *from, const char *to,
char **dest_p) {
ssize_t src_size = strlen(src);
ssize_t from_size = strlen(from);
ssize_t to_size = strlen(to);
char *temp;
if (!from_size) {
/* special case: the 'from' string is empty ... */
/* whatever you do, set temp to something valid or NULL */
} else {
ssize_t size_diff = to_size - from_size;
if (size_diff < 0) size_diff = 0;
temp = malloc(1 + src_size + (src_size / from_size) * size_diff);
if (temp) {
/* use next to track the next unused position in temp */
char *next = temp;
/*
* perform the substitution, updating 'next' appropriately as
* you go along (INSERT CODE AFTER THIS COMMENT) ...
*/
/* be sure to terminate: */
*(next++) = '\0';
/* shrink the string to the actual space used (optional): */
next = realloc(temp, next - temp);
/*
* On (re)allocation error, next will be NULL and temp will still
* be a valid pointer. Otherwise, next will be a pointer to the
* space, not necessarily equal to temp, and temp might no longer
* be a valid pointer.
*
* An OK error recovery strategy is to just return a pointer
* to the full-size space.
*/
if (next) {
temp = next;
}
} /* else allocation failure; return NULL */
}
/*
* The caller gets a pointer to the allocated space (if any). It is his
* responsibility to free it when it is no longer needed.
*/
*dest = temp;
}
The actual substitution code is left as an exercise, since this is homework, after all.