paulo1205
(usa Ubuntu)
Enviado em 22/11/2017 - 17:04h
Prezado,
Ao longo desta resposta, vou utilizar uma expressão que não é muito comum no mundo do C, embora o seja em linguagens como Pascal e Perl. Trata-se da expressão “tipo escalar”. E eu a uso para designar tipos de dados que não são dados compostos. Nesse contexto, um
char ou
int são um escalares, ao passo que um
array (vetor, matriz, cubo, hipercubo etc.) são tipos compostos.
Eu acho essa terminologia particularmente útil para o caso em questão por causa da analogia com a Matemática: um escalar não tem dimensão, mas é apenas um valor numérico. Um
char , do modo semelhante, também é apenas um valor numérico em C.
Sendo assim, o título da postagem tem um problema quando fala em “
char de três dimensões”, uma vez que escalares por definição são adimensionais. A questão ficou mais bem colocada no corpo da mensagem, quando você explicitou que gostaria de trabalhar com uma matriz de
strings .
Em parte, a coisa fica um pouco complicada de expressar naturalmente porque o C não possui nem matrizes, como forma de estruturar dados, nem
strings como tipo escalar.
Na verdade, o C não possui
arrays multidimensionais de tipo nenhum. Todos os
arrays em C são apenas unidimensionais. Na analogia com a Matemática, o C só pode criar vetores, não
arrays de mais alta ordem.
Entretanto — e aqui reside uma outra diferença entre o C e nossa Álgebra tradicional —, o C não exige que os componentes de um vetor sejam tipos escalares. A única exigência é que todos os elementos sejam do mesmo tipo, mas tal tipo pode ser qualquer tipo, incluindo um tipo que designe um
array .
Desse modo, uma maneira de simular
arrays com
N dimensões é usar um array unidimensional cujos elementos sejam
arrays com
N-1 dimensões. Como caso particular, uma matriz, que pode ser entendida como um
array bidimensional, pode ser representada em C como um
array de
arrays .
A respeito da falta de um tipo escalar para representar
strings , a abordagem adotada pelo C foi se valer da forma como
strings são tipicamente armazenadas na memória, que é pela disposição dos caracteres que compõem a
string em posições adjacentes de memória. Ora, o que é uma disposição de dados relacionados em posições contíguas de memória, senão um
array ? Assim, para o C, a forma de tratar
strings num programa foi expor sua representação interna. Dados que representem
strings têm de ser declarados como
arrays de caracteres.
Essa escolha, que simplifica a implementação da linguagem, tem algumas consequências um pouco desagradáveis para quem
usa a linguagem. Se
char é um valor numérico (e o é em C), pode haver a necessidade de usar
arrays de
char que não funcionem como
strings , mas apenas como agregados de valores numéricos de pouca magnitude. Como evitar a confusão entre ambos? Como impedir a tentativa de tratar como
string algo que
string não é?
Outra dificuldade é que
arrays em C expõem muito claramente sua natureza composta ao impedir que eles sejam alvos de atribuições. Por conta disso, mesmo que os nomes
a e
b designem
arrays de mesmo tipo num programa, a instrução “
b=a; ” é ilegal. Para copiar o conteúdo de
a para
b , é necessário explicitar a cópia de elemento a elemento, ou realizar tal cópia por meio de uma função, mas isso apenas esconde a complexidade de quem chama a função, que internamente continua tendo de tratar a cópia elemento a elemento. (Pelo lado positivo, expor os detalhes ajuda o programador a estar ciente de que operações com
arrays são custosas. Linguagens que
aparentemente tratam
strings como escalares podem induzir os programadores a pensar que é trivial copiar strings de um lado para o outro ou realizar operações tais como comparações de valores, concatenações, extrações de partes ou manipulação de conteúdo. Mas isso é irreal.)
Na prática, portanto, você queria ter uma matriz de
strings , e acabou tendo de ficar com um
array de
arrays de
strings , que se desdobra ainda mais em
array de
arrays (matriz) de
arrays de caracteres (
string ).
É claro que você sabia disso desde o começo, mas eu espero que as explicações acima tenham ajudado a entender
por que é assim.
Quanto a questão de como inicializar valores para o
array no momento da declaração, penso que também é interessante pensar de um modo
bottom-up .
O caso mais simples é quando você tem um
array de escalares. Nesse caso, a forma mais geral da declaração seria mais ou menos a seguinte.
nome_do_tipo nome_do_array[N ]={valor_0 , valor_1 , valor_2 , ..., valor_(N-1) };
No caso,
N é um valor inteiro e positivo, e a lista de valores pode até
N elementos válidos para o tipo
nome_do_tipo , e cujos índices variam de
0 a
N-1 . Se a lista de valores estiver presente, o valor de
N pode omitido de entre os colchetes, de modo a ser calculado automaticamente pelo compilador, por meio de uma contagem da quantidade de elementos na lista. Se o valor de
N estiver presente entre os colchetes, a lista não precisa conter exatamente
N elementos: os primeiros elementos do
array serão ocupados pelos elementos da lista, e os demais valores fora da lista serão ocupados com o valor
default do tipo
nome_do_tipo (geralmente zero).
void f(void){
int arr1[3]={1, 2, 3}; // OK: arr1[0]==1, arr1[1]==2, arr1[3]==2.
int arr2[3]={1, 2, 3, 4}; // Erro: lista tem mais elementos do que o tamanho do array.
int arr3[]={1, 2, 3}; // OK: tamanho (3) calculado a partir da quantidade de elementos.
int arr4[3]={1}; // OK: arr4[0]==1, arr4[1]==0, arr4[2]==0.
int arr5[3]={}; // OK: arr5[0]==0, arr5[1]==0, arr5[2]==0.
int arr6[3]; // OK, mas os valores iniciais de arr6[0], arr6[1] e arr6[2] são indefinidos.
static int arr7[3]; // OK: arr7[0]==0, arr7[1]==0, arr7[3]==0.
}
No caso particular de
arrays de
char , o C permite outra sintaxe, para favorecer o uso de
strings , que é especificando o conteúdo da
string entre aspas. As quatro declarações abaixo declaram
strings com conteúdo idêntico.
char str1[6]="Paulo";
char str2[6]={'P', 'a', 'u', 'l', 'o'}; // Elemento omitido recebe valor zero automaticamente.
char str3[]="Paulo";
char str4[]={'P', 'a', 'u', 'l', 'o', '\0'}; // Não posso omitir o '\0', senão não será string, apenas array de chars.
Além disso, no caso de
arrays de
arrays , a atribuição de valores iniciais leva em consideração que cada elemento do
array de nível mais externo (i.e. aquele cujo índica fica mais à esquerda) é também um
array . A declaração abaixo traz alguns exemplos.
int mat1[3][4]={ {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }; // Ou seja: mat1[0]={1, 2, 3, 4}, mat1[1]={5, 6, 7, 8}, mat1[2]={9, 10, 11, 12}.
int mat2[][2]={ {0, 1}, {2}, {}, {-1, -1} }; // Dimensão mais externa pode ser calculada pelo compilador (neste caso, 4).
// mat2[0]={0, 1}, mat2[1]={2, 0}, mat2[2]={0, 0}, mat2[3]={-1, -1}.
int mat3[2][]={ {1, 2}, {3, 4} }; // Erro: o tamanho de cada elemento de array interno tem de ser dado explicitamente.
int mat4[][]={ {1, 2}, {3, 4} }; // Erro: o tamanho de cada elemento de array interno tem de ser dado explicitamente.
int cubo[3][3][3]={
{0, 1, 2}, {3, 4, 5}, {6, 7, 8},
{9, 10, 11}, {12, 13, 14}, {15, 16, 17},
{18, 19, 20}, {21, 22, 23}, {24, 25, 26}
};
// Array de nome: cada nome pode ter no máximo 9 caracteres (dando um espaço para o byte nulo).
char arr_nomes[][10]={
"Paulo",
"Ana",
"Miguel"
};
// Matriz de strings (i.e. array de arrays de string, que é array de arrays de arrays de char).
char nomes_celulas[4][5][10]={
{"A1", "A2", "A3", "A4", "A5"},
{"B1", "B2", "B3", "B4", "B5"},
{"C1", "C2", "C3", "C4", "C5"},
{"D1", "D2", "D3", "D4", "D5"}
};