Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper way to return a string in C

Tags:

c

string

pointers

I have the following code:

char* get_address_string(PACKAGE* pkg){
    char *c;
    sprintf(c, "%02x:%02x:%02x:%02x:%02x:%02x", pkg->address[0], pkg->address[1], 
        pkg->address[2], pkg->address[3], pkg->address[4], pkg->address[5]);
    return c;
}

The code works fine. However, I know this is not the proper way to return a string in C. I am receiving the warning "c is used uninitialized in this function".

What is the proper way to write this function in C?

like image 593
mstagg Avatar asked Jan 07 '23 23:01

mstagg


2 Answers

"Proper way to return a string in C" is not truly possible. In C, a string is a character array (up to and including the null character) and arrays, by themselves, cannot be returned from a function.

A function can return pointers. So the usual method of "return a string" it to:

  1. Return a pointer. char *foo1(...) like char *strdup()

  2. Pass in a pointer to a character array and modify its contents. void foo2(char *,...) like int sprintf(char *dest, const char *format, ...)

  3. Combine 1 & 2 char *foo3(char *, ...) like char *strcpy(char *dest, char *src)

  4. Pass the address of a pointer and update that. foo4(char **ptr) like ssize_t getline(char **lineptr, size_t *n, FILE *stream)

The key is that the memory associated with the pointer must be valid after the function is complete. Returning a pointer to a function's non-static memory is undefined behavior. Successful methods include having the calling code pass in the pointer, or the function providing it via memory allocation of pointer to some persistent value like a global variable or string constant.

What is the proper way to write this function in C?

Current design practice encourages functions like #2 & #3 above to also supply a size_t size so the function knowns the limitations of the memory available.

    char *foo2(char *s, size_t size, const pkg_T *pkg) {
      int result = snprintf(s, size, "%02x:%02x:%02x:%02x:%02x:%02x", 
        pkg->address[0], pkg->address[1], pkg->address[2], 
        pkg->address[3], pkg->address[4], pkg->address[5]);
      // encoding error or not enough room
      if (result < 0 || result >= size) return NULL;
      return s;
    }

Another method would allocate memory (I favor the above though). This obliges the calling code to free() the memory.

    #define UINT_MAX_WIDTH (sizeof(unsigned)*CHAR_BIT/3 + 3)

    char *foo2alloc(char *s, size_t size, const pkg_T *pkg) {
      char buf[(UINT_MAX_WIDTH+3)*6 + 1];
      int result = snprintf(buf, sizeof buf, "%02x:%02x:%02x:%02x:%02x:%02x", 
        pkg->address[0], pkg->address[1], pkg->address[2], 
        pkg->address[3], pkg->address[4], pkg->address[5]);
      // encoding error or not enough room
      if (result < 0 || result >= size) return NULL;
      return strdup(buf);
    }
like image 132
chux - Reinstate Monica Avatar answered Jan 18 '23 11:01

chux - Reinstate Monica


c is a pointer, but no memory is allocated. The return value is ok, that's how it can be done in C.

But you need to allocate memory.

like image 22
Ely Avatar answered Jan 18 '23 10:01

Ely