3

Is there a way to concatenate strings without pre-allocating a buffer?

Consider the following:

int main()
{
    char buf1[] = "world!";
    char buf2[100] = "hello ";
    char * p = "hello ";
    // printf("%s", strcat(p, buf1)); // UB
    printf("%s", strcat(buf2, buf1)); // correct way to use strcat
    return 0;
}

If I have a pointer that I want as prefix for a string, must I first copy it to a buffer and then strcat() it? Is there any function that does that implicitly?

The only options I thought of were strcat(), and sprintf(), and both require a buffer.

  • I need this for a function that expects one string, and I hoped for something close to the Python str1 + str2 syntax
roschach
  • 8,390
  • 14
  • 74
  • 124
CIsForCookies
  • 12,097
  • 11
  • 59
  • 124
  • 4
    If you need to store the concatenated string for later processing, you'll need a buffer. If you just want to print the concatenated string `"%s%s", p, buf1` should do. – Support Ukraine Mar 08 '18 at 08:13
  • Something in between... I need to pass it along as one string to a non-`printf` function – CIsForCookies Mar 08 '18 at 08:16
  • 6
    ...then you need an intermediate buffer. – Jabberwocky Mar 08 '18 at 08:17
  • 4
    keep in mind that there is no "string" type in C like there is in Python or in C++ or other languages – Jabberwocky Mar 08 '18 at 08:18
  • 1
    Python most likely starts generating a tonne of code for that line, most likely using a string class and heap allocation. So are you asking for how to do this most effectively (in terms of speed or memory?) or how to do this in a way that makes the source code look pretty but the machine code look bad? – Lundin Mar 08 '18 at 09:21
  • I hoped for both. I hoped for function `char * f(char* a, char* b)` which concatenates them somehow, but I don't think it's possible in runtime in the way I wanted – CIsForCookies Mar 08 '18 at 09:26

3 Answers3

6

Well, it's not standard C so not very portable, but on GNU libc systems you can use asprintf().

int main(void)
{
    const char buf1[] = "world!";
    const char * const p = "hello ";
    char *s;
    if (asprintf(&s, "%s%s", p, buf1) > 0 && s != NULL)
    {
      puts(s);
      free(s);
    }
    return 0;
}

When compiled and run on a compliant system, this prints

hello world!
unwind
  • 391,730
  • 64
  • 469
  • 606
4

I you really want to use C, then you need to use buffers. In C++ or Python you can use constructors and operators that will hide the work for you, but in C you have to do things for yourself. You might create a little helper function if you want to make your code easier, but watch out for memory leaks and threading:

#include "stdlib.h"
#include "string.h"
#include "stdio.h"

const char * strconcat(const char *p1, const char *p2)
{
    static __thread char buffer[2048];
    snprintf(buffer, 2048, "%s%s", p1, p2);
    return buffer;
}

int main(int argc, char** argv)
{
    const char *p1 = "hello ";
    const char *p2 = "world!";
    printf("%s\n", strconcat(p1, p2));
}

This example will work as long as the resulting string does not exceed 2048 characters; otherwise the resulting string will be truncated.

This is of course only one example of helper functions you could create.

Note that the above example returns a const char * which you cannot edit. If you want to further edit the string, you need to create a helper function which allocates a buffer, but then you need to manage your memory!

machine_1
  • 4,266
  • 2
  • 21
  • 42
Chris Maes
  • 35,025
  • 12
  • 111
  • 136
  • 1
    This isn't a very good idea for real-world applications, since you can only call the function once per set of strings. If I would introduce p3 and p4 and concat them too, then the first string will change. – Lundin Mar 08 '18 at 12:05
  • 1
    Example: `const char* s1 = strconcat("hello ", "world"); const char* s2 = strconcat("not buggy ", "at all"); puts(s1);` – Lundin Mar 08 '18 at 12:30
3

The most effective way to do this in run-time would be to call malloc and allocate room for a new string, then copy everything there. This is most likely what Python does behind the lines - though it will have a string type.

The disadvantage of doing this outside an ADT/class, is that you have to do the clean-up manually.

Using malloc + copy is however far faster than any *printf, it is thread safe and the same function can be re-used again. Example:

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

char* strdog (const char* s1, const char* s2)
{
  size_t size1 = strlen(s1);
  size_t size2 = strlen(s2);
  char* result = malloc(size1 + size2 + 1);
  if(result == NULL) 
  {
    exit(EXIT_FAILURE);
  }

  memcpy(result, s1, size1);
  memcpy(result+size1, s2, size2);
  result[size1 + size2] = '\0';
  return result;
}

inline void cleandog (char* s)
{
  free(s);
}

int main (void)
{
  char s1[] = "hello ";
  char s2[] = "world";

  char* str = strdog(s1, s2);
  puts(str);
  cleandog(str);
}

Note however that C supports compile-time concatenation. So if you are only using string literals, you should do something like this:

#define HELLO "hello "
#define WORLD "world"

...

const char* str = HELLO WORLD;
puts(str);

This is of course the superior version of them all, but only works with compile-time string literals.

Lundin
  • 195,001
  • 40
  • 254
  • 396