As the previous strcat/strncat/strlcat examples show, C can be very clumsy when it comes to concatening strings (or any kind of string manipulation, actually).
Using asprintf(3) is another, more convenient solution which is considered secure, and automatically allocates as much memory as you need.
The only thing you have to care about is to free the used memory when finished.
The drawback is that asprintf(3) isn't available on all platforms.
The source code below includes its own version of asprintf() if it can't be found in the system library (i.e. in stdio.h) on your operating system.
/* Include unistd.h if not on MS Windows. */
#ifndef_WIN32
#include<unistd.h>
#endif
#include<errno.h>
#include<limits.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"asprintf.c"/* Compile: cc -g -ansi -pedantic -Wall -O2 -o concat-strings-asprintf concat-strings-asprintf.c *//* Run: ./concat-strings-asprintf */intmain(intargc, char *argv[])
{
char **pointer_to_full_name = NULL;
char *full_name = NULL;
intnumber_of_chars_printed = 0;
/* Command line argument check. */if (argc != 3)
{
printf("Usage: %s <first_name> <last_name>\n", argv[0]);
return 1;
}
/* Allocate enough memory for pointer-to-pointer to hold a pointer to char. */if ((pointer_to_full_name = (char **)malloc(sizeof(pointer_to_full_name))) == NULL)
{
printf("malloc() error: %s\n", strerror(errno));
return -1;
}
/* Good practice: always initialize *pointer_to_full_name. */
*pointer_to_full_name = "\0";
/* Concat strings using asprintf(). */if ((number_of_chars_printed = asprintf(pointer_to_full_name, "%s %s", argv[1], argv[2])) < 0)
{
printf("asprintf() error: %s\n", strerror(errno));
free(pointer_to_full_name);
return -1;
}
full_name = *pointer_to_full_name;
printf("Your name is '%s'.\n", full_name);
/* free() allocated memory. */
free(full_name);
free(pointer_to_full_name);
return 0;
}
Copy the file below to the same directory as the file above, and it will be included if not present on your operating system.
asprintf.c:
/*
* Copyright (c) 2004 Darren Tucker.
*
* Based originally on asprintf.c from OpenBSD:
* Copyright (c) 1997 Todd C. Miller <Todd.Miller AT courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/externintvsnprintf();
/* Include vasprintf() if not on your OS. */
#ifndefHAVE_VASPRINTF
#include<errno.h>
#include<limits.h>
#include<stdarg.h>
#include<stdlib.h>
#ifndefVA_COPY
# ifdefHAVE_VA_COPY
# defineVA_COPY(dest, src) va_copy(dest, src)
# else
# ifdefHAVE___VA_COPY
# defineVA_COPY(dest, src) __va_copy(dest, src)
# else
# defineVA_COPY(dest, src) (dest) = (src)
# endif
# endif
#endif
#defineINIT_SZ 128
intvasprintf(char **str, constchar *fmt, va_list ap)
{
intret = -1;
va_list ap2;
char *string, *newstr;
size_tlen;
VA_COPY(ap2, ap);
if ((string = malloc(INIT_SZ)) == NULL)
gotofail;
ret = vsnprintf(string, INIT_SZ, fmt, ap2);
if (ret >= 0 && ret < INIT_SZ) { /* succeeded with initial alloc */
*str = string;
} elseif (ret == INT_MAX || ret < 0) { /* Bad length */gotofail;
} else { /* bigger than initial, realloc allowing for nul */
len = (size_t)ret + 1;
if ((newstr = realloc(string, len)) == NULL) {
free(string);
gotofail;
} else {
va_end(ap2);
VA_COPY(ap2, ap);
ret = vsnprintf(newstr, len, fmt, ap2);
if (ret >= 0 && (size_t)ret < len) {
*str = newstr;
} else { /* failed with realloc'ed string, give up */
free(newstr);
gotofail;
}
}
}
va_end(ap2);
return (ret);
fail:
*str = NULL;
errno = ENOMEM;
va_end(ap2);
return (-1);
}
#endif/* Include asprintf() if not on your OS. */
#ifndefHAVE_ASPRINTFintasprintf(char **str, constchar *fmt, ...)
{
va_list ap;
intret;
*str = NULL;
va_start(ap, fmt);
ret = vasprintf(str, fmt, ap);
va_end(ap);
return ret;
}
#endif