0

I am working with a function that takes a pointer to a string as an argument and returns it in lower-case:

char* strLower(char* s) {
    for(char *p=s; *p; p++) *p=lower(*p);
    return s;
}

As of right now, it does return the lower-case string correctly, but it also modifies the original string (the one taken as an argument) outside of the function itself. How can I avoid this?

vvvvv
  • 9
  • 4
  • 1
    You'll need to make a copy of the original string (using POSIX [`strdup()`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/strdup.html) — soon to be part of C23), modify the copy and return it. The calling code is then responsible for freeing the copy. – Jonathan Leffler Apr 26 '23 at 05:19
  • `lower(*p);` is technical UB when `*p < 0` (and not `EOF`). Better as `for(unsigned char *p=s; *p; p++) *p=lower(*p);` – chux - Reinstate Monica Apr 26 '23 at 13:30

2 Answers2

1

For doing this, the function will need some allocated memory to store the result. There are at least three strategies:

  1. You can provide this memory as a result buffer to the function:
char* strLower(const char* src, char *dest) { ...

In this case, it is the callers responsibility that the size of the dest buffer is sufficient.

  1. The function can allocate the memory itself:
char* strLower(const char* src) {
   char *dest = malloc(1+strlen(src));
   ...

In this case, it is the callers responsibility to free the allocated memory when it is no longer used (otherwise there will be a memory leak). Unless there is a good reason for this option, I do not recommend it because dynamic memory allocation should usually have clearly symmetric calls to malloc and free to make sure that memory is freed. In this case it is not obvious that a call to strLower() will allocate memory.

  1. The function can have a statically allocated buffer:
#define MAX_STRING_SIZE 80+1
char* strLower(const char* src) {
   static char dest[MAX_STRING_SIZE];
   ...

In this case, the caller must be aware that the returned string will be overwritten next time strLower() is called.

None of the above possibilities are ideal so unless there is a reason against it, I would recommend to keep the function as it is. The caller can then copy the original string (e.g. using strdup()) in case both versions of the string are needed. This will avoid overhead of copying/memory allocation if it is not needed and make it clear that the caller has the responsibility to free any allocated memory.

nielsen
  • 5,641
  • 10
  • 27
  • 4th approach (similar 1st), `char* strLower(const char* src, size_t dest_size. char *dest) { ...` and have `strLower()` validate sufficient size. – chux - Reinstate Monica Apr 26 '23 at 13:27
  • Thank you very much for you answer. It is concise, yet accessible and offers plenty of solutions. Copying the original string before the function call was the right decision, as it made memory allocation way more flexible. – vvvvv Apr 26 '23 at 17:34
0

Duplicate the string with strdup()/strndup()¹ or malloc() and strcpy().

char *strLower (const char *s) 
{
    char *cp = strdup (s);
    /* Now work with it. */
}

Don't forget to free() the returned string after using it.

Footnote:

1

strdup() and strndup() will be included in the upcoming C2X standard.

Harith
  • 4,663
  • 1
  • 5
  • 20