¿Cómo creo una matriz de cadenas en C?

263

Estoy tratando de crear una matriz de cadenas en C. Si uso este código:

char (*a[2])[14];
a[0]="blah";
a[1]="hmm";

gcc me da "advertencia: asignación de tipo de puntero incompatible". ¿Cuál es la forma correcta de hacer esto?

editar: Tengo curiosidad por qué esto debería dar una advertencia del compilador ya que si lo hago printf(a[1]);, imprime correctamente "hmm".


fuente
12
Solo para el registro, char (*a[2])[14]es una matriz de dos punteros a una matriz de 14 caracteres.
avakar
44
Pensé que era catorce punteros a las matrices de dos caracteres xD
FORTRAN
74
El consejo más útil que he leído para descifrar los tipos C: "Comience por el nombre, lea a la derecha cuando pueda, a la izquierda cuando deba": char (*a[2])[14]- comience en a, mueva a la derecha: "matriz de dos", mueva a la izquierda: "puntero a", soporte completo, así que lea a la derecha: "conjunto de forteen", lea a la izquierda: "char" ... Júntelo y tenemos "un conjunto de dos punteros a conjuntos de caracteres forteen"
Mark K Cowan
44
@dotancohen: Ese consejo es lo que finalmente me convenció de escribir punteros en char *strlugar de hacerlo char* str. Viniendo de un fondo de Delphi / Pascal, estaba muy acostumbrado a esto último hasta que encontré tipos más complejos. La primera forma todavía me parece fea, pero hace que la notación de tipo sea más consistente (IMO).
Mark K Cowan
@ MarkKCowan, ¡increíble! ¡Gracias! :)
Dr. Essen

Respuestas:

232

Si no desea cambiar las cadenas, simplemente puede hacer

const char *a[2];
a[0] = "blah";
a[1] = "hmm";

Cuando lo haga así, asignará una matriz de dos punteros a const char. Estos punteros se establecerán en las direcciones de las cadenas estáticas "blah"y "hmm".

Si desea poder cambiar el contenido real de la cadena, debe hacer algo como

char a[2][14];
strcpy(a[0], "blah");
strcpy(a[1], "hmm");

Esto asignará dos matrices consecutivas de 14 chars cada una, después de lo cual el contenido de las cadenas estáticas se copiará en ellas.

Mikael Auno
fuente
185

Hay varias formas de crear una matriz de cadenas en C. Si todas las cadenas van a tener la misma longitud (o al menos tendrán la misma longitud máxima), simplemente declare una matriz 2D de caracteres y asigne según sea necesario:

char strs[NUMBER_OF_STRINGS][STRING_LENGTH+1];
...
strcpy(strs[0], aString); // where aString is either an array or pointer to char
strcpy(strs[1], "foo");

También puede agregar una lista de inicializadores:

char strs[NUMBER_OF_STRINGS][STRING_LENGTH+1] = {"foo", "bar", "bletch", ...};

Esto supone que el tamaño y el número de cadenas en el inicializador coinciden con las dimensiones de su matriz. En este caso, el contenido de cada literal de cadena (que en sí mismo es una matriz de caracteres terminados en cero) se copia en la memoria asignada a las cadenas. El problema con este enfoque es la posibilidad de fragmentación interna; si tiene 99 cadenas de 5 caracteres o menos, pero 1 cadena de 20 caracteres de longitud, 99 cadenas tendrán al menos 15 caracteres no utilizados; Eso es una pérdida de espacio.

En lugar de usar una matriz 2-d de char, puede almacenar una matriz 1-d de punteros para char:

char *strs[NUMBER_OF_STRINGS];

Tenga en cuenta que en este caso, solo ha asignado memoria para mantener los punteros a las cadenas; la memoria para las propias cadenas debe asignarse a otra parte (ya sea como matrices estáticas o usando malloc()o calloc()). Puede usar la lista de inicializadores como en el ejemplo anterior:

char *strs[NUMBER_OF_STRINGS] = {"foo", "bar", "bletch", ...};

En lugar de copiar el contenido de las constantes de cadena, simplemente está almacenando los punteros a ellas. Tenga en cuenta que las constantes de cadena pueden no ser grabables; puede reasignar el puntero, así:

strs[i] = "bar";
strs[i] = "foo"; 

Pero es posible que no pueda cambiar el contenido de la cadena; es decir,

strs[i] = "bar";
strcpy(strs[i], "foo");

puede no estar permitido

Puede usar malloc()para asignar dinámicamente el búfer para cada cadena y copiar a ese búfer:

strs[i] = malloc(strlen("foo") + 1);
strcpy(strs[i], "foo");

Por cierto,

char (*a[2])[14];

Declara a como una matriz de punteros de 2 elementos a matrices de 14 elementos de char.

John Bode
fuente
3
@Slater: sí, si es el resultado de una mallocllamada.
John Bode
Gracias por esta respuesta muy detallada. Esto realmente me ayudó.
cokedude
1
¿Por qué solo podemos usar strcpy en matrices de cadenas declaradas como matrices 2D? ¿Por qué falla la asignación estándar?
Andrew S
44
@AndrewS: La respuesta completa no cabe en un comentario, pero básicamente es un artefacto de cómo C trata las expresiones de matriz; En la mayoría de los casos, una expresión de tipo T [N]se convierte en una expresión de tipo T *, y el valor de la expresión es la dirección del primer elemento. Entonces, si escribió str = "foo", intentaría asignar la dirección del primer carácter "foo"a la matriz str, lo que no funciona. Vea esta respuesta para más detalles.
John Bode
@ JohnBode, ¿podría agregar el pequeño ajuste? char *strs[NUMBER_OF_STRINGS] = {0}; Esto ayuda a prevenir futuros problemas inicializando strsa NULL. Mucha gente lee esta publicación cuando Google busca en una variedad de cadenas en C.
cokedude
94

Ack! Cuerdas constantes:

const char *strings[] = {"one","two","three"};

Si recuerdo correctamente.

Ah, y desea utilizar strcpy para la asignación, no el operador =.strcpy_s es más seguro, pero no está en los estándares C89 ni C99.

char arr[MAX_NUMBER_STRINGS][MAX_STRING_SIZE]; 
strcpy(arr[0], "blah");

Actualización: Thomas dice que strlcpyes el camino a seguir.

mpen
fuente
¿Eso es C99? No creo que sea posible en ANSI C.
Noldorin
66
Es posible tanto en C89 como en C99. Tampoco importa si es con const o sin él, aunque se prefiere el primero.
avakar
1
Bueno, const es nuevo, y solía tener que especificar el tamaño de la matriz externa (3 en este caso), pero de lo contrario, esto es perfectamente aceptable K&R C. Tengo un viejo libro C con copyright 1984 que tiene una sección que muestra cómo hacer esto. Lo llaman un "conjunto irregular". Por supuesto, no tenía "operadores", y strcpy_s es nuevo para mí.
TED
66
strcpy_s es una función de Microsoft. Probablemente debería evitarse porque no está en el estándar C.
Cromulent
55
strcpy_s y otras "funciones seguras" están estandarizadas como ISO / IEC TR 24731 (es un estándar publicado por ISO y, como tal, no está disponible en línea de forma gratuita; el borrador más reciente es open-std.org/jtc1/sc22/wg14/www /docs/n1225.pdf )
Pavel Minaev el
14

Aquí tienes algunas de tus opciones:

char a1[][14] = { "blah", "hmm" };
char* a2[] = { "blah", "hmm" };
char (*a3[])[] = { &"blah", &"hmm" };  // only since you brought up the syntax -

printf(a1[0]); // prints blah
printf(a2[0]); // prints blah
printf(*a3[0]); // prints blah

La ventaja de esto a2es que puede hacer lo siguiente con literales de cadena

a2[0] = "hmm";
a2[1] = "blah";

Y para a3usted puede hacer lo siguiente:

a3[0] = &"hmm";
a3[1] = &"blah";

Para a1que tendrá que utilizar strcpy()(mejor aún strncpy()), incluso cuando la asignación de los literales de cadena. La razón es eso a2, y a3son conjuntos de punteros y puede hacer que sus elementos (es decir, punteros) apunten a cualquier almacenamiento, mientras que a1es un conjunto de 'conjunto de caracteres' y, por lo tanto, cada elemento es un conjunto que "posee" su propio almacenamiento ( lo que significa que se destruye cuando sale del alcance): solo puede copiar cosas en su almacenamiento.

Esto también nos lleva a la desventaja de usar a2y a3, dado que apuntan al almacenamiento estático (donde se almacenan los literales de cadena) cuyo contenido no se puede cambiar de manera confiable (es decir, comportamiento indefinido), si desea asignar literales no de cadena al elementos de a2o a3- primero tendrá que asignar dinámicamente suficiente memoria y luego hacer que sus elementos apunten a esta memoria, y luego copiar los caracteres en ella - y luego debe asegurarse de desasignar la memoria cuando haya terminado.

Bah: ya extraño C ++;)

PD Avísame si necesitas ejemplos.

Faisal Vali
fuente
Necesitaba matrices de cadenas para un proyecto Arduino. Al final usé el estilo a2. Inicialmente probé el estilo a1 definiendo mi conjunto de cadenas como char a1 [] [2] = {"F3", "G3" ... etc. } ya que estaba destinado a almacenar cadenas largas de 2 caracteres. Esto dio un resultado inesperado porque olvidé que el terminador nulo significaría que cada cadena debería tener un tamaño de al menos 3 para almacenar los 2 caracteres. Usando el estilo a2, no necesitaba especificar la longitud de la cuerda, y también podía acomodar diferentes longitudes de cuerda, así que decidí seguir con eso :-)
Jeromy Adofo
char (* a3 []) [] = {& "blah", & "hmm"}; => no funciona en g ++ Apple LLVM versión 9.1.0, pero funciona en gcc
1234
12

O puede declarar un tipo de estructura, que contiene un conjunto de caracteres (1 cadena), crear una matriz de estructuras y, por lo tanto, una matriz de elementos múltiples

typedef struct name
{
   char name[100]; // 100 character array
}name;

main()
{
   name yourString[10]; // 10 strings
   printf("Enter something\n:);
   scanf("%s",yourString[0].name);
   scanf("%s",yourString[1].name);
   // maybe put a for loop and a few print ststements to simplify code
   // this is just for example 
 }

Una de las ventajas de esto sobre cualquier otro método es que le permite escanear directamente en la cadena sin tener que usarla strcpy;

FutureSci
fuente
10

En ANSI C:

char* strings[3];
strings[0] = "foo";
strings[1] = "bar";
strings[2] = "baz";
Noldorin
fuente
8
@Zifre: Estoy totalmente en desacuerdo. En gran parte es parte del tipo: un "puntero de caracteres" en este caso. ¿Qué dirías de todos modos ... es parte del nombre de la variable? He visto a muchos programadores competentes usar este estilo.
Noldorin
14
Solo para cualquiera que lea esto, me gustaría señalar que Bjarne Stroustrup pone el * por el tipo ...
MirroredFate
1
@MirroredFate: Correcto. De hecho, se recomienda practicar en C ++ por lo que sé. Semánticamente no tiene sentido para mí ponerlo por el identificador, debido a la forma en que se usa. : /
Noldorin
16
@Noldorin char* foo, bar;¿de qué tipo es bar?
mASOUD
10
C fue desarrollado por Dennis Ritchie en 1972 y en 1988 él y Brian Kernighan publicaron la segunda edición de K&R - The C Programming Language, un libro que muchos sostienen como estándar de facto para C. Pusieron el * por el identificador.
Marius Lian
10

Si las cadenas son estáticas, es mejor que:

const char *my_array[] = {"eenie","meenie","miney"};

Si bien no forma parte del ANSI C básico, es probable que su entorno admita la sintaxis. Estas cadenas son inmutables (solo lectura) y, por lo tanto, en muchos entornos usan menos sobrecarga que construir dinámicamente una matriz de cadenas.

Por ejemplo, en pequeños proyectos de microcontroladores, esta sintaxis usa memoria de programa en lugar de (usualmente) memoria RAM más preciada. AVR-C es un entorno de ejemplo que admite esta sintaxis, pero también lo hacen la mayoría de los otros.

Bryce
fuente
10

Si no desea realizar un seguimiento del número de cadenas en la matriz y desea iterar sobre ellas, simplemente agregue una cadena NULL al final:

char *strings[]={ "one", "two", "three", NULL };

int i=0;
while(strings[i]) {
  printf("%s\n", strings[i]);
  //do something
  i++;
};
Sergey
fuente
Creo que esto es válido solo en C ++. En C, no se garantiza que NULL sea cero, por lo tanto, el ciclo puede no romperse cuando debería. Corrígeme si estoy equivocado.
Palec
2
No tengo idea :) Puede comparar con NULL en la declaración while si lo desea.
Sergey
9

Los literales de cadena son const char * s.

Y su uso de paréntesis es extraño. Probablemente quieras decir

const char *a[2] = {"blah", "hmm"};

que declara una matriz de dos punteros a caracteres constantes, y los inicializa para apuntar a dos constantes de cadena codificadas.

dmckee --- gatito ex moderador
fuente
3

Su código está creando una matriz de punteros de función. Tratar

char* a[size];

o

char a[size1][size2];

en lugar.

Ver wikilibros a matrices y punteros

Darío
fuente
1
Me quito el sombrero por su enfoque diferente ... La gente como usted hace que la pila se desborde ...
Sahu V Kumar
1

hola puedes probar esto a continuación:

 char arr[nb_of_string][max_string_length]; 
 strcpy(arr[0], "word");

un buen ejemplo de uso, matriz de cadenas en c si lo desea

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


int main(int argc, char *argv[]){

int i, j, k;

// to set you array
//const arr[nb_of_string][max_string_length]
char array[3][100];

char temp[100];
char word[100];

for (i = 0; i < 3; i++){
    printf("type word %d : ",i+1);
    scanf("%s", word);
    strcpy(array[i], word);
}

for (k=0; k<3-1; k++){
    for (i=0; i<3-1; i++)
    {
        for (j=0; j<strlen(array[i]); j++)
        {
            // if a letter ascii code is bigger we swap values
            if (array[i][j] > array[i+1][j])
            {
                strcpy(temp, array[i+1]);
                strcpy(array[i+1], array[i]);
                strcpy(array[i], temp);

                j = 999;
            }

            // if a letter ascii code is smaller we stop
            if (array[i][j] < array[i+1][j])
            {
                    j = 999;
            }

        }
    }
}

for (i=0; i<3; i++)
{
    printf("%s\n",array[i]);
}

return 0;
}
Aominé
fuente
0
char name[10][10]
int i,j,n;//here "n" is number of enteries
printf("\nEnter size of array = ");
scanf("%d",&n);
for(i=0;i<n;i++)
{
    for(j=0;j<1;j++)
    {
        printf("\nEnter name = ");
        scanf("%s",&name[i]);
    }
}
//printing the data
for(i=0;i<n;i++)
{
    for(j=0;j<1;j++)
    {
        printf("%d\t|\t%s\t|\t%s",rollno[i][j],name[i],sex[i]);
    }
    printf("\n");
}

Aquí intenta esto !!!

Aditya
fuente
1
¿Puede explicar por qué necesita el bucle for con la variable j, es decir, for (j = 0; j <1; j ++)?
SouvikMaji
0

Me faltaba de alguna manera una matriz más dinámica de cadenas, donde la cantidad de cadenas podría variar dependiendo de la selección del tiempo de ejecución, pero de lo contrario las cadenas deberían ser reparadas.

Terminé codificando fragmentos de código como este:

#define INIT_STRING_ARRAY(...)          \
    {                                   \
        char* args[] = __VA_ARGS__;     \
        ev = args;                      \
        count = _countof(args);         \
    }

void InitEnumIfAny(String& key, CMFCPropertyGridProperty* item)
{
    USES_CONVERSION;
    char** ev = nullptr;
    int count = 0;

    if( key.Compare("horizontal_alignment") )
        INIT_STRING_ARRAY( { "top", "bottom" } )

    if (key.Compare("boolean"))
        INIT_STRING_ARRAY( { "yes", "no" } )

    if( ev == nullptr )
        return;

    for( int i = 0; i < count; i++)
        item->AddOption(A2T(ev[i]));

    item->AllowEdit(FALSE);
}

char** evrecoge el puntero a las cadenas de matriz, y el conteo recoge la cantidad de cadenas usando la _countoffunción. (Similar a sizeof(arr) / sizeof(arr[0]))

Y hay una conversión adicional de Ansi a unicode usando A2Tmacro, pero eso podría ser opcional para su caso.

TarmoPikaro
fuente
-6

Una buena manera es definir una cadena usted mismo.

#include <stdio.h>
typedef char string[]
int main() {
    string test = "string";
    return 0;
}

Es realmente así de simple.

IceCodr
fuente
44
Te estás perdiendo un ;, y ¿cómo crea esto una serie de cadenas ?
keyser