Como crio uma matriz de seqüências de caracteres em C?

263

Eu estou tentando criar uma matriz de seqüências de caracteres em C. Se eu usar este código:

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

O gcc me dá "aviso: atribuição do tipo de ponteiro incompatível". Qual é a maneira correta de fazer isso?

edit: Estou curioso para saber por que isso deve dar um aviso ao compilador, pois, se o fizer printf(a[1]);, ele imprime corretamente "hmm".


fonte
12
Apenas para o registro, char (*a[2])[14]é uma matriz de dois ponteiros para uma matriz de 14 caracteres.
Avakar 06/07/2009
4
Eu pensei que era quatorze ponteiros para matrizes de dois caracteres xD
fortran
74
Os conselhos mais úteis que já li para decifrar os tipos C: "Comece pelo nome, leia à direita quando puder e à esquerda quando for necessário": char (*a[2])[14]- comece por a, mova para a direita: "matriz de dois", mova para a esquerda: "ponteiro para", suporte completo para ler direito: "array de forteen", lidos da esquerda: "char" ... Coloque-o em conjunto e temos "um é matriz de dois ponteiros para matrizes de forteen caracteres"
Mark K Cowan
4
@dotancohen: Essa dica foi o que finalmente me convenceu a escrever indicadores, em char *strvez de char* str. Vindo de uma experiência em Delphi / Pascal, eu estava muito acostumado com essa última maneira, até encontrar tipos mais complexos. A primeira maneira ainda me parece feia, mas torna a notação de tipo mais consistente (IMO).
Mark-Cowan
@MarkKCowan, amazing one! Obrigado! :)
Dr. Essen

Respostas:

232

Se você não quiser alterar as cordas, você pode simplesmente fazer

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

Ao fazer assim, você alocará uma matriz de dois ponteiros para const char. Esses ponteiros serão configurados para os endereços das seqüências estáticas "blah"e "hmm".

Se você deseja alterar o conteúdo real da string, é necessário fazer algo como

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

Isso alocará duas matrizes consecutivas de 14 chars cada, após as quais o conteúdo das seqüências estáticas será copiado para elas.

Mikael Auno
fonte
185

Existem várias maneiras de criar uma matriz de cadeias em C. Se todas as cadeias tiverem o mesmo comprimento (ou pelo menos o mesmo comprimento máximo), você simplesmente declara uma matriz 2-d de caractere e atribui conforme necessário:

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");

Você também pode adicionar uma lista de inicializadores:

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

Isso pressupõe que o tamanho e o número de strings no inicializador correspondam às dimensões da matriz. Nesse caso, o conteúdo de cada literal de cadeia de caracteres (que é uma matriz de caracteres com terminação zero) é copiado para a memória alocada para strs. O problema com essa abordagem é a possibilidade de fragmentação interna; se você tiver 99 cadeias de caracteres com 5 caracteres ou menos, mas uma cadeia com 20 caracteres, 99 terão no mínimo 15 caracteres não utilizados; isso é um desperdício de espaço.

Em vez de usar uma matriz 2-d de char, você pode armazenar uma matriz 1-d de ponteiros em char:

char *strs[NUMBER_OF_STRINGS];

Observe que, nesse caso, você alocou apenas memória para reter os ponteiros nas seqüências de caracteres; a memória para as próprias seqüências de caracteres deve ser alocada em outro lugar (como matrizes estáticas ou usando malloc()ou calloc()). Você pode usar a lista de inicializadores como no exemplo anterior:

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

Em vez de copiar o conteúdo das constantes de seqüência de caracteres, você está simplesmente armazenando os ponteiros para elas. Observe que as constantes de string podem não ser graváveis; você pode reatribuir o ponteiro, assim:

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

Mas você pode não conseguir alterar o conteúdo da string; ou seja,

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

pode não ser permitido.

Você pode usar malloc()para alocar dinamicamente o buffer para cada sequência e copiar para esse buffer:

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

BTW,

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

Declara a como uma matriz de 2 elementos de ponteiros para matrizes de 14 elementos de caracteres.

John Bode
fonte
3
@ Slater: sim, se for o resultado de uma mallocchamada.
John Bode
Obrigado por esta resposta muito detalhada. Isso realmente me ajudou.
Cokedude
1
Por que podemos usar apenas strcpy em matrizes String declaradas como uma matriz 2D? Por que a atribuição padrão falha?
Andrew S
4
@ Andrews: A resposta completa não se encaixa em um comentário, mas basicamente é um artefato de como C trata expressões de matriz; na maioria das circunstâncias, uma expressão do tipo T [N]é convertida em uma expressão do tipo T *e o valor da expressão é o endereço do primeiro elemento. Portanto, se você escreveu str = "foo", tentaria atribuir o endereço do primeiro caractere de "foo"à matriz str, o que não funciona. Veja esta resposta para mais detalhes.
John Bode
@JohnBode, você poderia adicionar um pequeno ajuste? char *strs[NUMBER_OF_STRINGS] = {0}; Isso ajuda a evitar problemas futuros ao inicializar strspara NULL. Um monte de gente ler este post quando o fazem buscas do Google em matriz de strings em C.
cokedude
94

Ack! Cordas constantes:

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

Se eu me lembro bem.

Ah, e você deseja usar strcpy para atribuição, não o operador =. strcpy_s é mais seguro, mas não está nos padrões C89 nem C99.

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

Atualização: Thomas diz que strlcpyé o caminho a percorrer.

mpen
fonte
Isso é C99? Eu não acredito que é possível em ANSI C.
Noldorin
6
É possível nos modelos C89 e C99. Também não importa se é com const ou sem ele, embora o primeiro seja o preferido.
1174 avakar
1
Bem, const é novo, e você costumava especificar o tamanho da matriz externa (3 neste caso), mas, caso contrário, isso é perfeitamente aceitável K&R C. Eu tenho um livro C antigo, com direitos autorais de 1984, que possui uma seção mostrando como faça isso. Eles chamam de "matriz irregular". Claro que não tinha "operadores", e o strcpy_s é novo para mim.
TED
6
strcpy_s é uma função da Microsoft. Ele provavelmente deve ser evitado porque não é em C. padrão
cromulent
5
strcpy_s e outras "funções seguras" são padronizadas como ISO / IEC TR 24731 (é um padrão publicado pela ISO e, como tal, não está disponível online gratuitamente; o rascunho mais recente é open-std.org/jtc1/sc22/wg14/www /docs/n1225.pdf )
Pavel Minaev 07/07/2009
14

Aqui estão algumas de suas opções:

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

A vantagem a2é que você pode fazer o seguinte com literais de string

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

E para a3você, faça o seguinte:

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

Pois a1você terá que usar strcpy()(melhor ainda strncpy()) mesmo ao atribuir literais de string. O motivo é que a2, e a3são matrizes de ponteiros e você pode fazer com que seus elementos (ou seja, ponteiros) apontem para qualquer armazenamento, enquanto a1é uma matriz de 'matriz de caracteres' e, portanto, cada elemento é uma matriz que "possui" seu próprio armazenamento ( o que significa que ele é destruído quando sai do escopo) - você só pode copiar itens para o armazenamento.

Isso também nos leva à desvantagem de usar a2e a3- já que eles apontam para armazenamento estático (onde literais de cadeia de caracteres são armazenados) cujo conteúdo não pode ser alterado com segurança (ou seja, comportamento indefinido), se você deseja atribuir literais não de cadeia de caracteres ao elementos de a2ou a3- você primeiro terá que alocar dinamicamente memória suficiente e, em seguida, seus elementos apontarão para essa memória e, em seguida, copiarão os caracteres para ela - e, em seguida, será necessário desalocar a memória quando terminar.

Bah - já sinto falta do C ++;)

ps Deixe-me saber se você precisar de exemplos.

Faisal Vali
fonte
Eu precisava de matrizes de strings para um projeto do Arduino. No final, usei o estilo a2. Inicialmente, tentei o estilo a1 definindo minha matriz de caracteres como char a1 [] [2] = {"F3", "G3" ... etc. } como ele pretendia armazenar seqüências longas de 2 caracteres. Isso deu uma saída inesperada porque eu esqueci que o terminador nulo significaria que cada sequência deveria ter um tamanho de pelo menos 3 para armazenar os 2 caracteres. Usando o estilo a2, eu não preciso especificar o comprimento da corda, e que poderia acomodar diferentes comprimentos de corda, bem assim eu decidi ficar com isso :-)
Jeromy Adofo
char (* a3 []) [] = {& "blá", & "hmm"}; => não funciona no g ++ Apple LLVM versão 9.1.0, mas funciona no gcc
1234
12

Ou você pode declarar um tipo de estrutura, que contém um caractere arry (1 string), eles criam uma matriz das estruturas e, portanto, uma matriz com vários elementos

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 
 }

Uma das vantagens disso em relação a qualquer outro método é que isso permite que você escaneie diretamente a string sem precisar usar strcpy;

FutureSci
fonte
10

No ANSI C:

char* strings[3];
strings[0] = "foo";
strings[1] = "bar";
strings[2] = "baz";
Noldorin
fonte
8
@Zifre: Eu discordo totalmente. Faz parte do tipo - um "ponteiro de char" nesse caso. O que você diria de qualquer maneira ... faz parte do nome da variável? Eu já vi muitos programadores competentes usarem esse estilo.
Noldorin
14
Apenas para ninguém ler isto, eu gostaria de salientar que Bjarne Stroustrup coloca a * pelo tipo ...
MirroredFate
1
@MirroredFate: Correto. De fato, é prática recomendada em C ++ pelo que eu sei. Semanticamente, não faz sentido para mim colocá-lo pelo identificador, devido à maneira como é usado. : /
Noldorin 29/03
16
@Noldorin, char* foo, bar;qual é o tipo debar ?
MASOUD
10
C foi desenvolvido por Dennis Ritchie em 1972 e em 1988 ele e Brian Kernighan publicaram a segunda edição do K&R - The C Programming Language, um livro que muitos consideram como padrão de fato para C. Eles colocam o * pelo identificador.
Marius Lian
10

Se as strings são estáticas, é melhor você:

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

Embora não faça parte do ANSI C básico, é provável que seu ambiente suporte a sintaxe. Essas cadeias são imutáveis ​​(somente leitura) e, portanto, em muitos ambientes, usam menos sobrecarga do que a construção dinâmica de uma matriz de cadeias.

Por exemplo, em pequenos projetos de microcontroladores, essa sintaxe usa memória de programa em vez de (geralmente) memória ram mais preciosa. O AVR-C é um exemplo de ambiente que suporta essa sintaxe, mas o mesmo ocorre com a maioria dos outros.

Bryce
fonte
10

Se você não deseja acompanhar o número de strings na matriz e deseja iterá-las, basta adicionar uma string NULL no final:

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

int i=0;
while(strings[i]) {
  printf("%s\n", strings[i]);
  //do something
  i++;
};
Sergey
fonte
Eu acredito que isso é válido apenas em C ++. Em C, NULL não é garantido como zero; portanto, o loop pode não quebrar quando deveria. Corrija-me se eu estiver errada.
Palec
2
Não faço ideia :) Você pode comparar com NULL in while, se quiser.
Sergey
9

Os literais de string são const char *s.

E seu uso de parênteses é estranho. Você provavelmente quer dizer

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

que declara uma matriz de dois ponteiros para caracteres constantes e os inicializa para apontar para duas constantes de string codificadas.

dmckee --- gatinho ex-moderador
fonte
3

Seu código está criando uma matriz de ponteiros de função. Experimentar

char* a[size];

ou

char a[size1][size2];

em vez de.

Veja wikibooks para matrizes e ponteiros

Dario
fonte
1
Tiremos o chapéu para sua abordagem diferente ... Pessoas como você faz pilha para estouro ...
Sahu V Kumar
1

Olá, você pode tentar o seguinte:

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

um bom exemplo de uso, matriz de strings em c, se você quiser

#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é
fonte
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");
}

Aqui tente isso !!!

Aditya
fonte
1
você pode explicar por que você precisa do loop for com a variável j, ou seja, para (j = 0; j <1; j ++)?
SouvikMaji
0

Estava faltando uma matriz mais dinâmica de strings, em que a quantidade de strings poderia variar dependendo da seleção em tempo de execução, mas, caso contrário, as strings deveriam ser corrigidas.

Acabei por codificar o snippet de código assim:

#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** evseleciona o ponteiro para as seqüências de caracteres da matriz e count seleciona a quantidade de seqüências de caracteres usando a _countoffunção (Semelhante a sizeof(arr) / sizeof(arr[0])).

E há conversão extra de Ansi para unicode usando A2Tmacro, mas isso pode ser opcional para o seu caso.

TarmoPikaro
fonte
-6

Uma boa maneira é definir uma string para você.

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

É realmente assim tão simples.

IceCodr
fonte
4
Está faltando um ;, e como isso cria uma matriz de seqüências de caracteres ?
25814