¿Cómo concateno cadenas const/literal en C?

9 minutos de lectura

avatar de usuario
El.Anti.9

Estoy trabajando en C y tengo que concatenar algunas cosas.

Ahora mismo tengo esto:

message = strcat("TEXT ", var);

message2 = strcat(strcat("TEXT ", foo), strcat(" TEXT ", bar));

Ahora, si tiene experiencia en C, estoy seguro de que se da cuenta de que esto le da una falla de segmentación cuando intenta ejecutarlo. Entonces, ¿cómo trabajo alrededor de eso?

  • ¡Me gustaría sugerir que use strlcat en lugar de strcat! gratisoft.us/todd/papers/strlcpy.html

    – activoout.se

    21 de noviembre de 2008 a las 13:37

  • Me gustaría repetir esa sugerencia. Strcat causa vulnerabilidad a las explotaciones de desbordamiento de búfer. Alguien puede darle a su programa datos que hagan que ejecute código arbitrario.

    – Brian

    21 de noviembre de 2008 a las 14:10

avatar de usuario
alex b

Evitar el uso de strcat en código C. La forma más limpia y, sobre todo, la más segura es utilizar snprintf:

char buf[256];
snprintf(buf, sizeof(buf), "%s%s%s%s", str1, str2, str3, str4);

Algunos comentaristas plantearon el problema de que la cantidad de argumentos puede no coincidir con la cadena de formato y el código aún se compilará, pero la mayoría de los compiladores ya emiten una advertencia si este es el caso.

  • Damas, estaba hablando de los paréntesis alrededor de “buf” del tamaño del argumento. no son necesarios si el argumento es una expresión. Pero no entiendo por qué te votan negativo. Creo que tu respuesta es la mejor de todas, aunque es c99. (¡tal vez por eso no están de acuerdo! ¡lamers!) +1

    – Johannes Schaub – litb

    21 de noviembre de 2008 a las 13:27

  • sizeof() solo funciona aquí para char buf[…]. NO para char * buf = malloc(…). No hay muchas diferencias entre matrices y punteros, ¡pero esta es una de ellas!

    – Sr. Ree

    21 de noviembre de 2008 a las 13:28

  • Además, está tratando de realizar la concatenación. concatenar usando snprintf() es un GRAN no no.

    –Leonardo Herrera

    21 de noviembre de 2008 a las 15:05

  • @MrRee: ¡Las diferencias entre punteros y matrices son amplias y completas! Está en cómo tú usar ellos que no siempre difieren. Además, los punteros y la asignación dinámica son conceptos realmente ortogonales.

    – Carreras de ligereza en órbita

    7 dic 2011 a las 20:11


  • Uno de mis motivos favoritos es la gente como @unwind, que insiste en la distinción sin sentido entre sizeof(x) y sizeof x. La notación entre paréntesis siempre funciona y la notación sin paréntesis solo funciona a veces, así que siempre use la notación entre paréntesis; es una regla simple de recordar y es segura. Esto se convierte en un argumento religioso: he estado involucrado en discusiones con quienes se oponen antes, pero la simplicidad de ‘siempre usar paréntesis’ supera cualquier mérito de no usarlos (IMNSHO, por supuesto). Esto se presenta para el equilibrio.

    –Jonathan Leffler

    31 de marzo de 2014 a las 0:22

Las cadenas también se pueden concatenar en tiempo de compilación.

#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 */ 
                ;

avatar de usuario
Brian R. Bondy

En C, las “cadenas” son simplemente simples char arreglos Por lo tanto, no puede concatenarlas directamente con otras “cadenas”.

Puedes usar el strcat función, que añade la cadena apuntada por src al final de la cadena apuntada por dest:

char *strcat(char *dest, const char *src);

Aquí hay un ejemplo de cplusplus.com:

char str[80];
strcpy(str, "these ");
strcat(str, "strings ");
strcat(str, "are ");
strcat(str, "concatenated.");

Para el primer parámetro, debe proporcionar el propio búfer de destino. El búfer de destino debe ser un búfer de matriz de caracteres. P.ej: char buffer[1024];

Asegurarse que el primer parámetro tiene suficiente espacio para almacenar lo que está tratando de copiar en él. Si está disponible para usted, es más seguro usar funciones como: strcpy_s y strcat_s donde tiene que especificar explícitamente el tamaño del búfer de destino.

Nota: un literal de cadena no se puede usar como un búfer, ya que es una constante. Por lo tanto, siempre debe asignar una matriz de caracteres para el búfer.

El valor de retorno de strcat simplemente se puede ignorar, simplemente devuelve el mismo puntero que se pasó como primer argumento. Está ahí para su comodidad y le permite encadenar las llamadas en una sola línea de código:

strcat(strcat(str, foo), bar);

Entonces su problema podría resolverse de la siguiente manera:

char *foo = "foo";
char *bar = "bar";
char str[80];
strcpy(str, "TEXT ");
strcat(str, foo);
strcat(str, bar);

  • ¿Podrías poner “Ten mucho cuidado con que…” en negrita, por favor? Esto no se puede enfatizar lo suficiente. El mal uso de strcat, strcpy y sprintf son el corazón del software inestable/inseguro.

    – pedestal

    21 de noviembre de 2008 a las 13:33

  • Advertencia: Tal como está escrito, este código dejará un enorme agujero en su código para explotar el desbordamiento del búfer.

    – Brian

    21 de noviembre de 2008 a las 14:09

  • No es posible explotar el desbordamiento del búfer en el ejemplo anterior. Y sí, estoy de acuerdo, en general, no usaría el ejemplo anterior para longitudes de cadena indeterminadas de foo y bar.

    – Brian R. Bondy

    21 de noviembre de 2008 a las 14:14

  • no usar strcat ¡varias veces! strcat tiene que comprobar todos los bytes anteriores (buscando '\0') ya concatenó. Este es un procesamiento inútil.

    – dolmen

    7 sep 2010 a las 16:13

  • Al segundo @dolmen, ha escrito Joel Spolsky artículo bastante elaborado sobre el tema. Debería ser una lectura obligatoria. 😉

    – peter.slizik

    14 de agosto de 2012 a las 14:53

avatar de usuario
Nico Cvitak

La mejor manera de hacerlo sin tener un tamaño de búfer limitado es usando asprintf()

char* concat(const char* str1, const char* str2)
{
    char* result;
    asprintf(&result, "%s%s", str1, str2);
    return result;
}

avatar de usuario
ceros

Como la gente señaló, el manejo de cadenas mejoró mucho. Por lo tanto, es posible que desee aprender a usar la biblioteca de cadenas de C++ en lugar de las cadenas de estilo C. Sin embargo, aquí hay una solución en C puro.

#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;
}

No estoy seguro de si es correcto/seguro, pero en este momento no pude encontrar una mejor manera de hacerlo en ANSI C.

  • <string.h> es estilo C++. Usted quiere "string.h". tu tambien calculas strlen(s1) dos veces, lo cual no es necesario. s3 debiera ser totalLenght+1 largo.

    – Pato mugido

    7 de diciembre de 2011 a las 20:08

  • @MooingDuck: "string.h" es una tontería

    – sbi

    7 dic 2011 a las 20:15

  • No he usado cadenas de estilo C por un tiempo. Siéntase libre de publicar una versión fija.

    – ceros

    7 de diciembre de 2011 a las 20:16

  • @MooingDuck: Eso es incorrecto. #include <string.h> es correcta C. Utilice corchetes angulares para los encabezados estándar y del sistema (incluidos <string.h>), comillas para los encabezados que forman parte de su programa. (#include "string.h" funcionará si no tiene su propio archivo de encabezado con ese nombre, pero use <string.h> de todas formas.)

    –Keith Thompson

    7 dic 2011 a las 20:33

  • Tenga en cuenta que esto depende de las características específicas de C99: mezcla de declaraciones e instrucciones, y matrices de longitud variable (VLA). Tenga en cuenta también que los VLA no proporcionan ningún mecanismo para detectar o manejar las fallas de asignación; si no hay espacio suficiente para asignar un VLA, el comportamiento de su programa no está definido.

    –Keith Thompson

    07/12/2011 a las 20:35

avatar de usuario
keith thompson

Amigos, usen strnortecpy(), cadenanortegato(), o snorteimprimirf().

¡Exceder el espacio del búfer destruirá cualquier otra cosa que siga en la memoria!

(¡Y recuerde dejar espacio para el carácter final nulo ‘\0’!)

  • <string.h> es estilo C++. Usted quiere "string.h". tu tambien calculas strlen(s1) dos veces, lo cual no es necesario. s3 debiera ser totalLenght+1 largo.

    – Pato mugido

    7 de diciembre de 2011 a las 20:08

  • @MooingDuck: "string.h" es una tontería

    – sbi

    7 dic 2011 a las 20:15

  • No he usado cadenas de estilo C por un tiempo. Siéntase libre de publicar una versión fija.

    – ceros

    7 de diciembre de 2011 a las 20:16

  • @MooingDuck: Eso es incorrecto. #include <string.h> es correcta C. Utilice corchetes angulares para los encabezados estándar y del sistema (incluidos <string.h>), comillas para los encabezados que forman parte de su programa. (#include "string.h" funcionará si no tiene su propio archivo de encabezado con ese nombre, pero use <string.h> de todas formas.)

    –Keith Thompson

    7 dic 2011 a las 20:33

  • Tenga en cuenta que esto depende de las características específicas de C99: mezcla de declaraciones e instrucciones, y matrices de longitud variable (VLA). Tenga en cuenta también que los VLA no proporcionan ningún mecanismo para detectar o manejar las fallas de asignación; si no hay espacio suficiente para asignar un VLA, el comportamiento de su programa no está definido.

    –Keith Thompson

    07/12/2011 a las 20:35

avatar de usuario
Pedro Mortensen

Si tiene experiencia en C, notará que las cadenas son solo matrices de caracteres donde el último carácter es un carácter nulo.

Ahora eso es bastante inconveniente ya que tienes que encontrar el último carácter para agregar algo. strcat hará eso por ti.

Así que strcat busca en el primer argumento un carácter nulo. Luego lo reemplazará con el contenido del segundo argumento (hasta que termine en nulo).

Ahora revisemos su código:

message = strcat("TEXT " + var);

Aquí está agregando algo al puntero del texto “TEXTO” (el tipo de “TEXTO” es const char*. Un puntero).

Eso generalmente no funcionará. Además, la modificación de la matriz “TEXTO” no funcionará, ya que generalmente se coloca en un segmento constante.

message2 = strcat(strcat("TEXT ", foo), strcat(" TEXT ", bar));

Eso podría funcionar mejor, excepto que nuevamente está intentando modificar textos estáticos. strcat no asigna memoria nueva para el resultado.

Yo propondría hacer algo como esto en su lugar:

sprintf(message2, "TEXT %s TEXT %s", foo, bar);

Lea la documentación de sprintf para comprobar sus opciones.

Y ahora un punto importante:

Asegúrese de que el búfer tenga suficiente espacio para contener el texto Y el carácter nulo. Hay un par de funciones que pueden ayudarlo, por ejemplo, strncat y versiones especiales de printf que asignan el búfer por usted. No garantizar el tamaño del búfer conducirá a la corrupción de la memoria y errores explotables de forma remota.

  • El tipo de "TEXT" es char[5], no const char*. Se descompone a char* en la mayoría de los contextos. Por razones de compatibilidad con versiones anteriores, los literales de cadena no son const, pero intentar modificarlos da como resultado un comportamiento indefinido. (En C++, los literales de cadena son const.)

    –Keith Thompson

    7 dic 2011 a las 20:41

¿Ha sido útil esta solución?