Comment concaténer des chaînes const / littérales en C?
Je travaille en C, et je dois concaténer quelques choses.
En ce moment j'ai ceci:
message = strcat("TEXT ", var);
message2 = strcat(strcat("TEXT ", foo), strcat(" TEXT ", bar));
Maintenant, si vous avez de l'expérience en C, je suis sûr que vous réalisez que cela vous donne un défaut de segmentation lorsque vous essayez de l'exécuter. Alors, comment puis-je contourner cela?
17 réponses
En C ,les "chaînes" sont simplement des tableaux char
. Par conséquent, vous ne pouvez pas les concaténer directement avec d'autres "chaînes".
Vous pouvez utiliser le strcat
function, qui ajoute la chaîne pointée par src
à la fin de la chaîne pointée par dest
:
char *strcat(char *dest, const char *src);
Voici un exemple de cplusplus.com:
char str[80];
strcpy(str, "these ");
strcat(str, "strings ");
strcat(str, "are ");
strcat(str, "concatenated.");
Pour le premier paramètre, vous devez fournir le tampon de destination lui-même. Le tampon de destination doit être un tampon de tableau de caractères. E. g.: char buffer[1024];
Assurez - , que le premier paramètre a suffisamment d'espace pour stocker ce que vous essayez de copier en elle. Si vous êtes à votre disposition, il est plus sûr d'utiliser des fonctions comme: strcpy_s
et strcat_s
où vous devez explicitement spécifier la taille du tampon de destination.
Note : un littéral de chaîne ne peut pas être utilisé comme tampon, car il s'agit d'une constante. Ainsi, vous devez toujours allouer un tableau char pour le tampon.
, La valeur de retour de strcat
peut être simplement ignoré, il renvoie simplement le même pointeur que celui qui a été passé en tant que premier argument. Il est là pour plus de commodité, et vous permet d'enchaîner les appels en une seule ligne de code:
strcat(strcat(str, foo), bar);
Ainsi, votre problème pourrait être résolu comme suit:
char *foo = "foo";
char *bar = "bar";
char str[80];
strcpy(str, "TEXT ");
strcat(str, foo);
strcat(str, bar);
Évitez d'utiliser strcat
dans le code C. Le moyen le plus propre et, surtout, le plus sûr est d'utiliser snprintf
:
char buf[256];
snprintf(buf, sizeof buf, "%s%s%s%s", str1, str2, str3, str4);
Certains commentateurs ont soulevé un problème selon lequel le nombre d'arguments peut ne pas correspondre à la chaîne de format et le code sera toujours compilé, mais la plupart des compilateurs émettent déjà un avertissement si c'est le cas.
Les Gens, utilisez strncpy(), strncat(), ou snprintf().
dépasser votre espace tampon détruira tout ce qui suit en mémoire!
(et n'oubliez pas de laisser de l'espace pour le caractère null final '\ 0'!)
Malloc et realloc sont également utiles si vous ne savez pas à l'avance combien de chaînes sont concaténées.
#include <stdio.h>
#include <string.h>
void example(const char *header, const char **words, size_t num_words)
{
size_t message_len = strlen(header) + 1; /* + 1 for terminating NULL */
char *message = (char*) malloc(message_len);
strncat(message, header, message_len);
for(int i = 0; i < num_words; ++i)
{
message_len += 1 + strlen(words[i]); /* 1 + for separator ';' */
message = (char*) realloc(message, message_len);
strncat(strncat(message, ";", message_len), words[i], message_len);
}
puts(message);
free(message);
}
Les chaînes peuvent également être concaténées au moment de la compilation.
#define SCHEMA "test"
#define TABLE "data"
const char *table = SCHEMA "." TABLE ; // note no + or . or anything
const char *qry = // include comments in a string
" SELECT * " // get all fields
" FROM " SCHEMA "." TABLE /* the table */
" WHERE x = 1 " /* the filter */
;
N'oubliez pas d'initialiser le tampon de sortie. Le premier argument de strcat doit être une chaîne terminée par null avec suffisamment d'espace supplémentaire alloué pour la chaîne résultante:
char out[1024] = ""; // must be initialized
strcat( out, null_terminated_string );
// null_terminated_string has less than 1023 chars
Le premier argument de strcat() doit pouvoir contenir suffisamment d'espace pour la chaîne concaténée. Donc allouer un tampon avec suffisamment d'espace pour recevoir le résultat.
char bigEnough[64] = "";
strcat(bigEnough, "TEXT");
strcat(bigEnough, foo);
/* and so on */
Strcat() concaténera le deuxième argument avec le premier argument, et stockera le résultat dans le premier argument, le char retourné * est simplement ce premier argument, et seulement pour votre commodité.
Vous n'obtenez pas une chaîne nouvellement allouée avec le premier et le deuxième argument concaténés, ce que je suppose vous attendiez basé sur votre code.
Comme les gens l'ont souligné, la gestion des chaînes s'est beaucoup améliorée. Vous pouvez donc apprendre à utiliser la bibliothèque de chaînes c++ au lieu des chaînes de style C. Cependant, voici une solution en C PUR
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
void appendToHello(const char *s) {
const char *const hello = "hello ";
const size_t sLength = strlen(s);
const size_t helloLength = strlen(hello);
const size_t totalLength = sLength + helloLength;
char *const strBuf = malloc(totalLength + 1);
if (strBuf == NULL) {
fprintf(stderr, "malloc failed\n");
exit(EXIT_FAILURE);
}
strcpy(strBuf, hello);
strcpy(strBuf + helloLength, s);
puts(strBuf);
free(strBuf);
}
int main (void) {
appendToHello("blah blah");
return 0;
}
Je ne suis pas sûr que ce soit correct / sûr mais en ce moment je ne pouvais pas trouver une meilleure façon de le faire dans ANSI C.
La meilleure façon de le faire sans avoir une taille de tampon limitée est d'utiliser asprintf ()
char* concat(const char* str1, const char* str2)
{
char* result;
asprintf(&result, "%s%s", str1, str2);
return result;
}
C'est un comportement indéfini pour tenter de modifier les littéraux de chaîne, ce qui est quelque chose comme:
strcat ("Hello, ", name);
Va tenter de le faire. Il va essayer de tack sur la chaîne name
à la fin du littéral de chaîne "Hello, "
, qui n'est pas bien défini.
Essayez quelque chose ceci. Il réalise ce que vous semblez essayer de faire:
char message[1000];
strcpy (message, "TEXT ");
strcat (message, var);
Cela crée une zone tampon qui est autorisé à être modifié, puis copie à la fois le littéral de chaîne et un autre texte. Juste être prudent avec les les dépassements de tampon. Si vous contrôlez les données d'entrée (ou les vérifiez avant), il est bon d'utiliser des tampons de longueur fixe comme je l'ai fait.
Sinon, vous devez utiliser des stratégies d'atténuation telles que l'allocation de suffisamment de mémoire du tas pour vous assurer de pouvoir le gérer. En d'autres termes, quelque chose comme:
const static char TEXT[] = "TEXT ";
// Make *sure* you have enough space.
char *message = malloc (sizeof(TEXT) + strlen(var) + 1);
if (message == NULL)
handleOutOfMemoryIntelligently();
strcpy (message, TEXT);
strcat (message, var);
// Need to free message at some point after you're done with it.
Vous pouvez écrire votre propre fonction qui fait la même chose que strcat()
mais cela ne change rien:
#define MAX_STRING_LENGTH 1000
char *strcat_const(const char *str1,const char *str2){
static char buffer[MAX_STRING_LENGTH];
strncpy(buffer,str1,MAX_STRING_LENGTH);
if(strlen(str1) < MAX_STRING_LENGTH){
strncat(buffer,str2,MAX_STRING_LENGTH - strlen(buffer));
}
buffer[MAX_STRING_LENGTH - 1] = '\0';
return buffer;
}
int main(int argc,char *argv[]){
printf("%s",strcat_const("Hello ","world")); //Prints "Hello world"
return 0;
}
Si les deux chaînes ont plus de 1000 caractères, la chaîne sera coupée à 1000 caractères. Vous pouvez modifier la valeur de MAX_STRING_LENGTH
en fonction de vos besoins.
En supposant que vous avez un char [fixed_size] plutôt qu'un char*, vous pouvez utiliser une seule macro créative pour tout faire en même temps avec un ordre <<cout<<like
("plutôt %s le %S\N disjoint", "than", "format de style printf"). Si vous travaillez avec des systèmes embarqués, cette méthode vous permettra également de laisser de côté malloc et la grande famille de fonctions *printf
comme snprintf()
(Cela empêche dietlibc de se plaindre aussi de *printf)
#include <unistd.h> //for the write example
//note: you should check if offset==sizeof(buf) after use
#define strcpyALL(buf, offset, ...) do{ \
char *bp=(char*)(buf+offset); /*so we can add to the end of a string*/ \
const char *s, \
*a[] = { __VA_ARGS__,NULL}, \
**ss=a; \
while((s=*ss++)) \
while((*s)&&(++offset<(int)sizeof(buf))) \
*bp++=*s++; \
if (offset!=sizeof(buf))*bp=0; \
}while(0)
char buf[256];
int len=0;
strcpyALL(buf,len,
"The config file is in:\n\t",getenv("HOME"),"/.config/",argv[0],"/config.rc\n"
);
if (len<sizeof(buf))
write(1,buf,len); //outputs our message to stdout
else
write(2,"error\n",6);
//but we can keep adding on because we kept track of the length
//this allows printf-like buffering to minimize number of syscalls to write
//set len back to 0 if you don't want this behavior
strcpyALL(buf,len,"Thanks for using ",argv[0],"!\n");
if (len<sizeof(buf))
write(1,buf,len); //outputs both messages
else
write(2,"error\n",6);
- Note 1, vous n'utiliseriez généralement pas argv [0] comme ceci-juste un exemple
- Note 2, Vous pouvez utiliser n'importe quelle fonction qui génère un char*, y compris des fonctions non standard comme itoa() pour convertir des entiers en types de chaîne.
- Note 3, Si vous utilisez déjà printf n'importe où dans votre programme, il n'y a aucune raison de ne pas utiliser snprintf(), puisque le code compilé serait plus grand (mais en ligne et significativement plus rapide)
int main()
{
char input[100];
gets(input);
char str[101];
strcpy(str, " ");
strcat(str, input);
char *p = str;
while(*p) {
if(*p == ' ' && isalpha(*(p+1)) != 0)
printf("%c",*(p+1));
p++;
}
return 0;
}
Vous essayez de copier une chaîne dans une adresse qui est allouée statiquement. Vous devez chat dans un tampon.
Plus précisément:
...snip...
Destination
Pointer to the destination array, which should contain a C string, and be large enough to contain the concatenated resulting string.
...snip...
Http://www.cplusplus.com/reference/clibrary/cstring/strcat.html
Il y a un exemple ici.
Si vous avez de l'expérience en C, vous remarquerez que les chaînes ne sont que des tableaux de caractères où le dernier caractère est un caractère nul.
Maintenant, c'est assez gênant car vous devez trouver le dernier caractère pour ajouter quelque chose. {[3] } fera cela pour vous.
Donc, strcat recherche dans le premier argument un caractère nul. Ensuite, il remplacera cela par le contenu du deuxième argument (jusqu'à ce que cela se termine par un null).
Passons maintenant en revue votre code:
message = strcat("TEXT " + var);
Ici, vous ajoutez quelque chose au pointeur vers le texte "TEXT" (le type de "TEXT" est const char*. Pointeur.).
Cela ne fonctionnera généralement pas. La modification du tableau "TEXT" ne fonctionnera pas car il est généralement placé dans un segment constant.
message2 = strcat(strcat("TEXT ", foo), strcat(" TEXT ", bar));
Cela pourrait mieux fonctionner, sauf que vous essayez à nouveau de modifier des textes statiques. strcat n'alloue pas de nouvelle mémoire pour le résultat.
Je proposerais de faire quelque chose comme ça au lieu de cela:
sprintf(message2, "TEXT %s TEXT %s", foo, bar);
Lisez la documentation de sprintf
pour vérifier ses options.
Et maintenant un point important:
Assurez - vous que le tampon a suffisamment d'espace pour contenir le texte et le caractère null. Il y a quelques fonctions qui peuvent vous aider, par exemple, strncat et les versions spéciales de printf qui allouent le tampon pour vous. Ne pas assurer la taille du tampon entraînera une corruption de la mémoire et des bogues exploitables à distance.
C'était ma solution
#include <stdlib.h>
#include <stdarg.h>
char *strconcat(int num_args, ...) {
int strsize = 0;
va_list ap;
va_start(ap, num_args);
for (int i = 0; i < num_args; i++)
strsize += strlen(va_arg(ap, char*));
char *res = malloc(strsize+1);
strsize = 0;
va_start(ap, num_args);
for (int i = 0; i < num_args; i++) {
char *s = va_arg(ap, char*);
strcpy(res+strsize, s);
strsize += strlen(s);
}
va_end(ap);
res[strsize] = '\0';
return res;
}
Mais vous devez spécifier combien de chaînes vous allez concaténer
char *str = strconcat(3, "testing ", "this ", "thing");
Essayez quelque chose de similaire à ceci:
#include <stdio.h>
#include <string.h>
int main(int argc, const char * argv[])
{
// Insert code here...
char firstname[100], secondname[100];
printf("Enter First Name: ");
fgets(firstname, 100, stdin);
printf("Enter Second Name: ");
fgets(secondname,100,stdin);
firstname[strlen(firstname)-1]= '\0';
printf("fullname is %s %s", firstname, secondname);
return 0;
}