0

I'm looking for an example code or how to improve the below code (that it's very slow IMO, but it's that I can write) to the fastest way to convert an 2D-array into a char* and copy a char to it.

char*
join(int c, size_t arrsize, const char* arr[])
{
  char *buf, *tbuf, *val;
  size_t i, vsize, total;

  buf = malloc(1);
  for(i = total = 0; i < arrsize; ++i) {
    val = arr[i];
    vsize = strlen(val);

    if((tbuf = realloc(buf, total + vsize + 2)) == NULL) {
      if(buf != NULL)
        free(buf);
      return NULL;
    }

    buf = tbuf;

    memcpy(buf + total, val, vsize);
    total += vsize;

    buf[total] = c;
    total += 1;
  }

  buf[total] = '\0';
  return buf;
}

calling

const char *foo[] = { "a", "b", "c"};
char *baa = join(' ', 2, foo); //a b c
if(baa) {
  printf("%s\n", baa);
    free(baa);
} else {
    printf("No memory\n");
}

How can this be optimised?

2
  • Your title states you want to append char to the end of the string but in your code you're placing the char in between each string. Which are you trying to achieve? Commented Jul 20, 2012 at 20:07
  • A 2d array is char 2d_array[m][n] and is already layed out as char array[m*n] which would make this simple - it could even be done in place. What you have is an array of pointers to char, which point to the beginning of '\0' terminated char arrays of unspecified length. BTW on 64 bit its cheaper to use an actual 2d array when all strings are under the size of an 8 byte pointer (up to16 if no strings are repeated). If you do use actual 2d arrays you have to pass around dimensions though. Commented Apr 22, 2018 at 1:45

4 Answers 4

1

I agree with the Shawn, a single malloc call is probably more advantageous. I was writing up my own take on your code while he was posting his answer:

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

char* join(char delimiter, size_t arrsize, const char* arr[]) {
    size_t i;
    size_t total;
    char* joined;

    for (i = total = 0; i < arrsize; i++) {
        if (arr[i] != NULL) 
            total += strlen(arr[i]) + 1; // size of c-style string + delimiter
    }

    // Note that last delimiter will actually be null termination
    joined = (char*)malloc(sizeof(char) * total);

    if (joined != NULL) {
            // first character isn't guaranteed to be null termination
            // set it so strcat works as intended, just in case
            joined[0] = '\0';
        for (i = 0; i < arrsize; i++) {
            if (arr[i] != NULL) {
                strcat(joined, arr[i]);

                if ((i + 1) != arrsize) 
                    strncat(joined, &delimiter, 1);
        }
    }

    return joined;
}

int main(int argc, char** argv) {
    const char* foo[] = { "aasdasd", "bgsfsdf", "asdasisc" };

    char* baa = join(' ', 3, foo);

    if (baa != NULL) {
        printf("%s\n", baa);
        free(baa);
    } else {
        printf("No memory\n");
    }

    return 0;
}

I made some changes depending on what I thought you were trying to accomplish, the first argument to join is the character delimiter used to separate combined strings, the second is the number of string in arr, and the third is obviously the array.

The code should compile and run, yeilding "assdasd bgsfsdf asdasisc", that is, what I mashed on my keyboard when populating the array to test :P

Sign up to request clarification or add additional context in comments.

1 Comment

Compiled with gcc, -std=c99 -pedantic -Wall. I did have to transcribe it from another screen though, because I'm lazy :P
0

If you can bound the total size of the strings in arr (e.g., 2048). With this you can remove the overhead of iterations in strcpy and strlen functions:

char*
join(int c, size_t arrsize, const char* arr[])
{
  char *buffer;
  size_t i, vsize;
  char *b;

  buffer = malloc(2048);
  b = buffer;
  vsize = 0;
  for(i = 0; i < arrsize; ++i) {
    char *p = arr[i];
    while (*p) {
      *b = *p; 
      b++; p++;
      vsize++;
    } 
    *b = c; b++;
  }

  buffer[vsize++] = '\0';
  return realloc(buffer, vsize);
}

Comments

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

char* join(char c, size_t arrsize, const char* arr[]){
    size_t i, total, len[arrsize];
    char *buff, *ret;

    for(total=i=0;i<arrsize;++i)
        total+=(len[i]=strlen(arr[i]));
    if(NULL==(ret=buff=(char*)malloc((total + arrsize)*sizeof(char))))
        return NULL;
    for(i=0;i<arrsize;++i){
        memcpy(buff, arr[i], len[i]);
        buff+=len[i];
        *buff++=c;
    }
    *--buff='\0';
    return ret;
}

int main(){
    const char *foo[] = { "a", "b", "c"};
    char *baa = join(' ', sizeof(foo)/sizeof(char*), foo); //a b c
    if(baa) {
        printf("\"%s\"\n", baa);
        free(baa);
    } else {
        printf("No memory\n");
    }
    return 0;
}

Comments

0

Instead of repeated calls to realloc, you could do a first loop across arr to determine the total length then call malloc just the once. After that it's just a matter of looping through arr again and doing calls to memcpy with the correct offsets.

Something like this perhaps (note that this is untested and may contain errors):

/* join strings in arr with c as separator */
char* join(int c, size_t arrsize, const char* arr[]) {
  char *buf;
  size_t i, len, total = 0;

  /* determine total length of all strings */
  for (i = 0; i < arrsize; ++i) {
    total += strlen(arr[i]);
  }

  /* allocate mem */
  buf = malloc(total + arrsize);
  if (!buf) return NULL;

  /* copy in all strings from arr */
  total = 0;
  for (i = 0; i < arrsize; ++i) {
    len = strlen(arr[i]);
    memcpy(buf + total, arr[i], len);
    total += len;

    /* append separator (or NUL if last string) */
    buf[total++] = (i == arrsize-1) ? '\0' : c;
  }

  return buf;
}

2 Comments

Note that you aren't reserving space for \0 of each string. total += strlen(arr[i]); must be total += strlen(arr[i]) + 1; and buf = malloc(total + i + 1); where i is the space for each c that will be stored. Also: c must be appended into last loop, into end of arr[i].
Code edited. I'm assuming OP is trying to join the strings with the char as a separator, so no, there shouldn't be a \0 after each string. I've appended c after each string except the last.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.