bytesculptor
bytesculptor

Reputation: 603

Copy string from const char *const array to string (in C)

EDIT: I added the full code.

I get an array with names and need them to create a string, depending of how many names exist this string looks different. I tried to concatenate the strings with strcpy and strcat but it doesn't work.

char* function1 (size_t n, const char *const names[n]) {
    char *res;

    switch (n) {
    case 0:
        res = "no one likes this";
        break;

    case 1:
        strcpy(res, names[0]);
        strcat(res, " likes this");
        break;

    case 2:
        strcpy(res, names[0]);
        strcat(res, " and ");
        strcat(res, names[1]);
        strcat(res, " like this");
        break;

    case 3:
        strcpy(res, names[0]);
        strcat(res, ", ");
        strcat(res, names[1]);
        strcat(res, " and ");
        strcat(res, names[2]);
        strcat(res, " like this");
        break;

    default:
        strcpy(res, names[0]);
        strcat(res, ", ");
        strcat(res, names[1]);
        strcat(res, " and ");
        strcat(res, (char*) (n - 2));
        strcat(res, " others like this");
        break; 
   }
   return res;
}

How can I get the names from the array? Do I have to get the length of each name and then copy each char by char?

The function is called as follow:

int main(void) {    
   const char *const names[3] = {"John", "Peter", "Paul"};
   char* res = function1(3, names);
   printf("%s", res);

   return 0;
}

The result should be as follows:

I know how to concat the strings, but struggling to copy the names out of the array. This is from a C exercise in a tutorial.

Upvotes: 0

Views: 1829

Answers (4)

KamilCuk
KamilCuk

Reputation: 141165

char *res = "";
strcpy(res,

Such code is invalid and it is undefined behavior. String literals like "string" but also just "" are immutable. You can't write to them. And because language is very old, it allows you to do char *res = "", while it should only allow to do const char *res = "" like in c++.

To copy the strings somewhere, you have have that "somewhere" - a block of free memory that will have enough space to hold the result.

How can I get the names from the array?

Iterate over the array and "get the names".

void function1 (size_t n, const char *const names[n]) {
    for (size_t i = 0; i < n; ++i) {
      printf("Getting names[%zu]=%s\n", i, names[i]);
    }
}

Do I have to get the length of each name and then copy each char by char?

If you want to concatenate those strings together, then you have to copy them char by char. Also include space for a separator, for example a space.

#include <string.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>

char *function1(size_t n, const char * const names[n]) {
    // Calculate how much space we need
    // for zero sentinel value and...
    size_t len = 1;
    // ...and for all the names and...
    for (size_t i = 0; i < n; ++i) {
        len += strlen(names[i]);
    }
    // ...and for space in between words.
    if (n != 0) {
        len += n - 1;
    }
    // Allocate memory. sizeof(*res) = sizeof(char) = 1
    char *res = malloc(len * sizeof(*res));
    if (res == NULL) {
        // always handle errors
        return NULL;
    }
    // Initialize with empty string.
    res[0] = '\0';
    for (size_t i = 0; i < n; ++i) {
        // Concatenate the names.
        strcat(res, names[i]);
        if (i + 1 != n) {
            // And remember about separating them.
            strcat(res, " ");
        }
    }
    return res;
}

int main(void) {
   const char * const names[3] = {"John", "Peter", "Paul"};
   char* res = function1(3, names);
   if (res == NULL) {
        return EXIT_FAILURE;
   }
   printf("Names concatenated with space as separator: %s\n",
        res);
   free(res);
}

Upvotes: 1

chux
chux

Reputation: 153547

Code fails various cases as res is not initialized before use.

char *res;
...
    // res value is indeterminate.  Result: undefined behavior.
    //      v
    strcpy(res, names[0]); // bad code

Instead code needs to set res to point to valid memory area before strcpy(res, ...).

Usual idea would be to calculate memory needs, allocate memory and then assign.


To avoid repetitive work, code could use a variable length array (VLA) to save the lengths of the names.

Find all the lengths, add them up, allocate and concatenate.

OP is also looking for some special text between names. Leave that for OP to add.

// void function1 (size_t n, const char *const names[n]) {
char *function1 (size_t n, const char *const names[n]) {
  size_t length[n]; // VLA
  size_t total = 0;
  for (size_t i=0; i<n; i++) {
    length[i] = strlen(names[i])
    total += length[i];
  }

  char *all = malloc(total + 1);

  if (all) {
    size_t offset = 0;
    for (size_t i=0; i<n; i++) {
      memcpy(&all[offset], names[i], length[i]);
      offset += length[i];
    }
    all[offset] = '\0';
  }

  return all;
}

Upvotes: 1

0___________
0___________

Reputation: 67546

It has to be done like this

char *function1 (size_t pos, const char ** names) {

   char *res = malloc(strlen(names[pos])+1);
   strcpy(res, names[0]);

   /*....*/
   return res;
}

Your code has so many issues in two lines of code ...\

  1. You assign string literal to the pointer and then try to write it. UB
  2. Even if it was not the string literal it does not have enough space to accommodate the data. UB (char res[] = ""; <- wrong)
  3. If not malloc-ed data or static storage variable (abstracting from the string literal) - it returns pointer to local data. UB (char res[strlen(name[pos])+1]; <- wrong)
void function1 (size_t pos, const char ** names, char **res) {

   *res = malloc(strlen(names[pos])+1);
   strcpy(*res, names[0]);

   /*....*/
}

Upvotes: 0

yayg
yayg

Reputation: 319

From man strcpy:

The  strcpy() function copies the string pointed to by src, including the
terminating null byte ('\0'), to the buffer pointed to by dest.  The strings may
not overlap, and the destination string dest must be large enough to receive the
copy

The last line suggests you to allocate enough space for the string to be copied so you will inevitably have to calculate it's length.

Upvotes: 0

Related Questions