Dúvida Iniciante com Struct em C Puro [RESOLVIDO]

1. Dúvida Iniciante com Struct em C Puro [RESOLVIDO]

Nick Us
Nick-us

(usa Slackware)

Enviado em 12/08/2019 - 00:38h

Meu problema inicial começa quando percebi que não sei como identificar quantos registros tem em uma Struct.
Se alguém souber me responder como consigo saber isso agradeceria!
Um Exemplo simples seria uma Struct de Nomes. Ela tem 10 nomes escritos nela. Como identifico isso?
A única forma que consegui imaginar seria varrer ela inteira contando registro por registro, mas achei isso MUITO Deselegante!

O Outro problema passou a existir justamente quando tentei contar os registros de uma Struct, fiquei ainda mais confuso! O que vou mostrar no exemplo abaixo.
#include <stdio.h>

struct DbNames {
char Names[5];
};

int main() {
char Text[] = "ABC";
struct DbNames List[] = { {"A"}, {"B"}, {"C"} };

/// ESTES 2 exemplos iniciais funcionam, eu consigo identificar o Caracter Terminador.
if(Text[3] == 0)
puts("OK 1"); // WORK

if(Text[3] == '\0')
puts("OK 2"); // WORK

/// Porque aqui e nos exemplos abaixo eu não consigo identificar o Caracter Terminador?
if(List[3].Names == 0)
puts("OK 3"); // NO WORK | Because it does not work?

/// PORQUE List[3].Names é um ponteiro? Como o gcc reclama abaixo?
// if(List[3].Names == '\0') // Don't accept it | warning: comparison between pointer and zero character constant
// puts("OK 4");

if(List[3].Names == "\0")
puts("OK 5");

// if(List[3].Names == '0') // Don't accept it | warning: comparison between pointer and integer
// puts("OK 6");

if(List[3].Names == NULL)
puts("OK 7");

if(List[3].Names == "")
puts("OK 8");
}

RESUMO DE MINHAS DÚVIDAS:
1) Como conto a Qtd de Registros que tem em uma Struct
2) Porque eu não consigo no código exemplo acima, identificar o Caracter Terminador, NULO da Struct


  


2. MELHOR RESPOSTA

Paulo
paulo1205

(usa Ubuntu)

Enviado em 15/08/2019 - 03:04h

Prezado, boa noite.

Não sei se não consegui entender direito sua dúvida, mas fiquei com a impressão que você misturou “struct” com array.

Em C, existe uma palavra-chave da linguagem chamada struct, que lhe permite definir um tipo de dados novo, composto por um ou mais campos de dados semanticamente relacionados entre si, e cada campo com um tipo de dados próprio. Internamente, cada campo se comporta como se fosse uma variável independente dos demais campos, mas a visão de fora é de que esses campos formam uma estrutura única (daí o nome “struct”, um contração do Inglês structure), de uma informação que se comporta como uma unidade, ainda que composta de partes. Por exemplo, você pode pensar, com razão, que cada pessoa que consta num caderninho de endereços (ou numa lista de contatos, para usar termos mais modernos, e não os da época em que eu estudei isso na faculdade) é um só registro, mas esse registro é composto de partes. A modelagem de um registro assim, em C, poderia se parecer com o que vai abaixo.

struct contato {
char nome[50];
char endereco[60];
unsigned short cod_pais, cod_area;
char telefone[15];
char email[70];
time_t data_nascimento;
};


Com essa declaração de tipo, você poderia criar um dado só para representar meu contato (dados fictícios).

struct contato paulo1205={
"Paulo Pires", // atribuído ao campo nome
"Rua Fulana, 25, ap. 101", // atribuído ao campo endereco
55, 21, // atribuídos aos campos cod_pais e cod_area
"98765-4321", // atribuído ao campo telefone
"paulo1205@provedor.com.br", // atribuído ao campo email
155456100 // atribuído ao campo data_nascimento
};


Com a declaração de variável com definição de valor inicial acima, a variável paulo1205 passa a conter informações que identificam uma única pessoa. Mas, apesar de ser uma pessoa só, há vários atributos meus que podem mudar ao longo do tempo (meu endereço, por exemplo, é algo que eu gostaria muito, de verdade, que mudasse em breve).

Os atributos de uma estrutura podem se comportar como se fossem variáveis comuns, com a única diferença de que a forma de ter acesso a um campo da estrutura requer o uso não apenas do nome do campo, mas também do nome da variável que contém o registro inteiro. Nesse caso, se você quisesse alterar apenas meu endereço, que é um array de caracteres, teria de fazê-lo do mesmo modo como faria com um array comum (isto é: copiando elemento por elemento, explicitamente ou através de uma função, como mostrado abaixo):

strcpy(paulo1205.endereco, "Estrada das Cachoeiras, 3500"); 


Do mesmo modo, outras operações, exemplificadas abaixo.

size_t tamanho_nome=strlen(paulo1205.nome);
assert(paulo1205.nome[len]=='\0');
struct tm *data_decomposta=localtime(paulo1205.data_nascimento);
struct hostent *addr_mailhost=gethostbyname(strchr(paulo1205.email, '@')+1);


Não sei se o que confundiu você foi o fato de que você usou também um array de estruturas. No seu caso, a variável List tem três elementos, e cada elemento (i.e.: List[0], List[1] e List[2]) é um estrutura distinta das demais. Sendo assim, cada uma delas tem o seu próprio campo Names, de modo que, se você quiser acesso à terceira letra do campo Names do segundo de List, tem de usar “List[1].Names[2]” (lembrando que o primeiro índice de um array é 0, não 1). De modo semelhante, se você quiser saber o comprimento do nome do primeiro elemento de List, tem de fazer “strlen(List[0].Names)”.

Com relação ao seu programa, permita-me fazer comentários ao longo da listagem, pedaço a pedaço.

#include <stdio.h>

struct DbNames {
char Names[5];


O nome “Names”, no plural, me parece inadequado, pois você provavelmente tem um nome só, mas que pode acomodar 5 caracteres (ou 4 e mais um byte nulo, se você quiser que ele possa ser manipulado como string).

};

int main() {
char Text[] = "ABC";
struct DbNames List[] = { {"A"}, {"B"}, {"C"} };

/// ESTES 2 exemplos iniciais funcionam, eu consigo identificar o Caracter Terminador.
if(Text[3] == 0)
puts("OK 1"); // WORK

if(Text[3] == '\0')
puts("OK 2"); // WORK


Os dois exemplos são funcionalmente idênticos (não sei se você sabia, mas em C, o tipo de '\0' não é char, mas sim int — experimente fazer “printf("%zu", sizeof '\0')” e veja que não dá 1; em C++, por outro lado, constantes literais de caracteres têm tipo char, e essa é uma das diferenças que causam surpresas a quem está acostumado com uma das linguagens e tem de usar a outra). Em ambos os casos você tem uma comparação entre um caráter (o quanto elemento de Text) e um inteiro, o que é natural para o C, já que char é um tipo aritmético inteiro; o único cuidado que o compilador toma de modo transparente é que ele converte o dado de tipo char em int antes de fazer a comparação (porque o padrão do C determina que isso seja feito, a saber: quando você tem inteiros de capacidades diferentes sendo comparados ou operados aritmeticamente, o de menor capacidade é automaticamente convertido para o tipo do que tem capacidade maior).


/// Porque aqui e nos exemplos abaixo eu não consigo identificar o Caracter Terminador?
if(List[3].Names == 0)
puts("OK 3"); // NO WORK | Because it does not work?


Aqui você está tentando acesso ao quarto elemento de List, que no entanto só tem três elementos. O compilador não viu esse erro, pois não é um erro de sintaxe, mas um erro lógico e semântico.

Fora isso, você está fazendo referência ao campo Names, que é um array. Sendo array, acontece aqui a mesma coisa que aconteceria com qualquer outro array: ele é tratado como um ponteiro para o primeiro elemento. Se você quiser acesso a um elemento específico, você deveria colocar o índice entre colchetes (por exemplo: para que sua comparação faça sentido, seria List[2].Names[1]).

O mesmo problema ocorre no exemplo seguinte, bem como no 6 (que está comentado).


/// PORQUE List[3].Names é um ponteiro? Como o gcc reclama abaixo?
// if(List[3].Names == '\0') // Don't accept it | warning: comparison between pointer and zero character constant
// puts("OK 4");

if(List[3].Names == "\0")
puts("OK 5");


Aqui você compara dois ponteiros entre si (um que é produzido pelo decaimento para ponteiro do array do campo Names e outro da constante literal string, que, no fundo, também acarreta um decaimento de array para ponteiro). Você não está comparando conteúdos dos dados apontados por tais ponteiros, mas tão-somente os valores do endereços apontados. Provavelmente não é o que você quer.

O caso 8 é a mesma coisa, só que com uma constante literal de string diferente. E o caso 7 também é uma comparação entre ponteiros (endereços), só que com um dos ponteiros com o endereço inválido (i.e.: não pode ser usado para se referir a um dado válido) NULL.


// if(List[3].Names == '0') // Don't accept it | warning: comparison between pointer and integer
// puts("OK 6");

if(List[3].Names == NULL)
puts("OK 7");

if(List[3].Names == "")
puts("OK 8");
}



... “Principium sapientiae timor Domini, et scientia sanctorum prudentia.” (Proverbia 9:10)

3. Re: Dúvida Iniciante com Struct em C Puro

Nick Us
Nick-us

(usa Slackware)

Enviado em 15/08/2019 - 05:12h

paulo1205 escreveu: Prezado, boa noite.

Primeiramente obrigado por responder... Eu procurei simplificar ao máximo o exemplo, mas talvez tenha simplificado demais, Então vou tentar explicar melhor.

Quando eu criei a Struct e o Vetor meu objetivo é que eles fossem exatamente parecidos para comparar em minha dúvida
struct DbNames {
char Names[5];
};
struct DbNames List[] = { {"A"}, {"B"}, {"C"} };
char Text[] = "ABC";

O que entendo: Nos 2 Casos, Eu teria 1 vetor contendo 3 caracteres. Tipo:
Text[0] = A
Text[1] = B
Text[2] = C
Text[3] = 0 ou /0 (O Caracter Terminador do vetor)

Motivo pelo qual mostro que ao comparar o CARACTER que não foi preenchido, a comparação ENCONTRA o caracter terminador.
if(Text[3] == 0)
puts("OK 1"); // WORK
if(Text[3] == '\0')
puts("OK 2"); // WORK

Porém no Caso da Struct List que também possue 3 Caracteres que podem ser identificados da mesma forma como:
List[0] = A
List[1] = B
List[2] = C

Ainda mais estranho se eu colocar:
puts(List[3].Names); // O Resultado é ABC Como isso é possível? Pois não deveria ter valor dentro da lógica, pois esse campo não foi preenchido 

A Struct em si seria por exemplo:
List[0] = A, 0,0,0,0 porque ela tem espaço para 4 Caracteres + Terminador em Cada Linha
List[1] = B, 0,0,0,0 porque ela tem espaço para 4 Caracteres + Terminador em Cada Linha
List[2] = C, 0,0,0,0 porque ela tem espaço para 4 Caracteres + Terminador em Cada Linha

Então pensei que esse seria o valor de List[3]
List[3] = 0,0,0,0,0 porque ela tem espaço para 4 Caracteres + Terminador em Cada Linha 

Nesse meio tempo, vi que uma forma de saber qts registros possue uma Struct seria esse abaixo, FUNCIONOU mas não sei se é assim mesmo que se verifica.
size_t size = sizeof List / sizeof List[0];
printf("%d\n", size);

Uma idéia que me deram seria preencher um valor nulo como um registro, assim eu encontraria o 0 no final dela ao varrer a Struct.
struct DbNames List[] = { {"A"}, {"B"}, {"C"}, {"0"} }; 

Porém também achei isso deselegante, pq fiquei pensando, será que não existe uma forma de identificar o final de uma Struct?

OBS: Eu sei que se usa struct com mais membros declarados, eu apenas coloquei 1 membro para simplificar o exemplo, pq achei e ainda não sei, que a Struct funcionaria exatamente como uma Lista, já que ela é uma Lista com 3 nomes cadastrados, sendo sua única diferença é que ela possue espaço para cadastrar 4 letras, mas para o meu exemplo ela poderia também ser declarada assim já que uso apenas 1 caracter no exemplo
struct DbNames {
char Names[2];
};

Entendeu a confusão que estou fazendo? Alguma coisa ai eu não compreendi direito!


4. Re: Dúvida Iniciante com Struct em C Puro [RESOLVIDO]

Nick Us
Nick-us

(usa Slackware)

Enviado em 15/08/2019 - 05:30h

paulo1205 escreveu: (por exemplo: para que sua comparação faça sentido, seria List[2].Names[1]).

Atento a sua explicação que sim, me passou despercebido esse detalhe, onde esqueci que o A estaria em Names[0]. Ainda não consegui fazer essa comparação, não compreendi o que ele identifica como INTEGER, pois para mim seria tudo CHAR eu tentei:
if(List[1].Names[0] == "B") // Apresenta o erro: warning: comparison between pointer and integer
puts("OK 20");



5. Re: Dúvida Iniciante com Struct em C Puro [RESOLVIDO]

Nick Us
Nick-us

(usa Slackware)

Enviado em 15/08/2019 - 05:52h

O que me deixa ainda mais confuso, não faço a mínima idéia do que está errado! Tentando apenas comparar o Texto que está gravado na Struct, tanto que o comando puts IMPRIME ele corretamente, mas o IF não encontra o texto, sendo que quando estou comparando um INTEGER ele se comporta corretamente o IF
#include <stdio.h>
struct {
char Names[5];
int Num;
} List[] = { {"AAAA", 7}, {"BBB", 9}, {"C", 10} };

int main() {
if(List[0].Num == 7) // FUNCIONA
puts("OK 7");

puts(List[0].Names); // Imprime AAAA

if(List[0].Names == "AAAA") // NÃO FUNCIONA Não encontra
puts("OK A");
}




6. Re: Dúvida Iniciante com Struct em C Puro [RESOLVIDO]

Jorge Diego de Sousa Silva
jorgediego

(usa Arch Linux)

Enviado em 15/08/2019 - 10:33h

Uma das maneiras de fazer isso é percorrer o vetor de caracteres até o final dele, o que delimita o final de um vetor é o caractere '\0', então ficaria assim:


int quantidadeRegistros = 0;
for(int i = 0; names[i] != '\0'; i++) {
quantidadeRegistros++;
}



7. Re: Dúvida Iniciante com Struct em C Puro [RESOLVIDO]

Nick Us
Nick-us

(usa Slackware)

Enviado em 15/08/2019 - 14:24h

jorgediego escreveu:Uma das maneiras de fazer isso é percorrer o vetor de caracteres até o final dele, o que delimita o final de um vetor é o caractere '\0', então ficaria assim:

int quantidadeRegistros = 0;
for(int i = 0; names[i] != '\0'; i++) {
quantidadeRegistros++;
}

Jorge, esse é o ponto! Um Vetor normal como citei no meu exemplo isso é possível. Mas também informo que isso na Struct não funciona. NEM o compilador aceita
   if(List[3].Names == '0') // NÃO ACEITA | warning: comparison between pointer and integer
puts("OK 6");

// É mais que óbvio é indiferente mudar a comparação, pq dá no mesmo
if(List[3].Names != '0') // NÃO ACEITA | warning: comparison between pointer and integer
puts("OK 60");

O Meu problema justamente começou porque foi o que tentei fazer para saber qts registros existiam na Struct!


8. Re: Dúvida Iniciante com Struct em C Puro [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 15/08/2019 - 15:15h

Você está misturando coisas distintas.

Em minha mensagem anterior eu praticamente desenhei para você cada um dos pontos em que você tem dúvidas. Eu não sei dizer se você não entendeu porque não leu direito minha mensagem ou se porque lhe falta mais conhecimento de base.

Coisas que você parece estar confundindo:

• caráter com strings de caracteres (como o C não possui um tipo nativo para strings, isso implica ter de usar arrays de caracteres na hora de declarar e alocar espaço, e ponteiros para caracteres na hora de operar com elas);

arrays com estruturas;

• estruturas contendo campos que são arrays com um array de estruturas contendo campos simples (não-arrays).

Não insista em comparar caracteres comuns com ponteiros para caracteres. Os tipos são distintos, e o compilador está avisando sobre isso porque a comparação não faz sentido.

Não insista em comparar ponteiros (endereços) entre si se o que você quer é comparar os dados para onde eles apontam. Se quer comparar dados, tem de fazer referências explícitas a eles, ou usar funções que façam internamente tais referências (por exemplo, a função strcmp(), que serve para comparar o conteúdo de duas strings).

Não insista em aplicar um índice sobre um array de estruturas e querer que ele se aplique também a um dos campos da estrutura. Cada um dos elementos de um array é independente dos demais elementos, e a única relação que eles guardam entre si é o fato de estarem dispostos em posições da memória próximas umas das outras.

Você precisa estudar melhor esses assuntos para parar de se confundir, e até para entender as mensagens claras (se você souber cada conceito) que o compilador está lhe dando.


... “Principium sapientiae timor Domini, et scientia sanctorum prudentia.” (Proverbia 9:10)


9. Re: Dúvida Iniciante com Struct em C Puro [RESOLVIDO]

Nick Us
Nick-us

(usa Slackware)

Enviado em 15/08/2019 - 16:27h

paulo1205 escreveu:Você está misturando coisas distintas.
Em minha mensagem anterior eu praticamente desenhei para você cada um dos pontos em que você tem dúvidas. Eu não sei dizer se você não entendeu porque não leu direito minha mensagem ou se porque lhe falta mais conhecimento de base.

Pode ser falta de conhecimento base como você diz. Vou tentar explicar o que até agora entendi.
Quando declaro:
char Text[] = "ABC"; 

Entendo que eu tenho um vetor/array de caracteres que é o mesmo que: A,B,C ou seja, entendo que o C armazena apenas 1 Caracter então é como se ele fizesse uma lista para esse armazenamento. Por isso ele: Armazena:
Text[0] = A
Text[1] = B
Text[2] = C
Text[3] = Caracter Terminador 0 ou \0

Entendo que ele guardou no Caso não uma String ABC. E sim cada Letra separadamente como se fosse numa variavel diferente, se posso chamar Text[0]... e etc... como variáveis diferentes.
Acontece que quando apenas declaro um vetor de char como char Text[] = "ABC"; que seria o mesmo que char Text[4] = "ABC";
Eu tenho 4 CAMPOS Cada campo desse com 1 Caracter armazenado.

Então pensei. Uma Struct embora ela possa ter elementos dentro dela. Mas até então não estou falando desses elementos, e sim do VETOR.
Por exemplo:
#include <stdio.h>
struct {
char Names[5];
int Num;
} List[] = { {"AAAA", 7}, {"BBB", 9}, {"C", 10} };

int main() {
if(List[0].Num == 7) // FUNCIONA
puts("OK 7");

puts(List[0].Names); // Imprime AAAA

if(List[0].Names == "AAAA") // NÃO FUNCIONA Não encontra
puts("OK A");
}

No Caso acima, eu entendo que minha Struct declarada List[] = { {"AAAA", 7}, {"BBB", 9}, {"C", 10} };
É o mesmo que List[4] = { {"AAAA", 7}, {"BBB", 9}, {"C", 10} };
OU List[3] = { {"AAAA", 7}, {"BBB", 9}, {"C", 10} };

Nessa declaração do exemplo acima List[] = { {"AAAA", 7}, {"BBB", 9}, {"C", 10} };
Entendo que minha Struct não seus elementos internos, mas SIM Apenas a Struct LIST externamente falando
List seria para mim um VETOR com 3 POSIÇÕES
Tipo:
List[0].Names = AAAA
List[1].Names = BBB
List[2].Names = C

Ou seja, entendi que LIST embora uma Struct é um VETOR com 3 posições nesse caso! Entendo claro também que List[0] E etc... Também guarda SIM a outra variável no caso Num, e entendo sim que na mesma posição, porém ele coloca o (.Names ou .Num) para separar isso... Mas nem é disso que estou falando...
Eu pensei apenas no quesito List[0]

Então como em um Vetor no caso que eu informo acima com apenas 3 posições ocupadas, sua última posição teria um 0 ou \0 Informando um terminador.
Da mesma forma pensei que List[0] mesmo sendo uma Struct, ela também não seria um VETOR? Por ter List[0],1,2.... Então pensei que ela teria também em uma posição não usada um termindor 0 ou \0

Quando declarei inicialmente no exemplo uma Struct com apenas 1 ELEMENTO, foi para evitar confusão com outros possíveis elementos que podem ser declarados em uma Struct. Pq não estou falando em si desses elementos. E sim da Struct como Inteira.
TIPO, se declaro assim abaixo:
struct {
char Names[5];
} List[10];

Entendo que List seria um Vetor com 10 posições que posso colocar os meus dados. Cada posição dessa List é representando como abaixo
List[0].Names
List[1].Names

Então o que pensei. O que está gravado em List[11] ou List[11].Names
Como não estaria gravado NADA, pensei que teria um caracter terminador, tipo 0 ou \0

RESUMO. Estou dando explicação baseado no que entendi sobre vetor/array ou Struct.
Entendo que vetor e array são as mesmas coisas. E que vetor é um conjunto de caracteres, representados tipo: 1,2,3,4,5 cada um separado em seu próprio campo.
Não sei se está certo isso, mas foi o que consegui entender até aqui!


10. Re: Dúvida Iniciante com Struct em C Puro [RESOLVIDO]

Nick Us
Nick-us

(usa Slackware)

Enviado em 15/08/2019 - 16:44h

paulo1205 escreveu:Você está misturando coisas distintas. Coisas que você parece estar confundindo:
Não insista em comparar caracteres comuns com ponteiros para caracteres. Os tipos são distintos, e o compilador está avisando sobre isso porque a comparação não faz sentido.

SIM, não entendi isso. Estou tratando todo o assunto falando de caracteres comuns. Mas SIM o compilador fala de ponteiros. Isso está confuso pra mim. Pq Entendo que eu declaro um ponteiro usando ASTERÍSTICO *
Exemplo: Se eu tivesse declarado char *Names[5];
Eu teria declarado um Ponteiro para Names. Mas eu não fiz em nenhum momento isso. Não usei asterísticos em nada então não sei do que o compilador está reclamando quando fala de ponteiros, pq não vejo ponteiro nenhum nesse caso! E obviamente não vejo porque não sei ver QUEM é o ponteiro ou o que é um PONTEIRO neste meu caso!


Não insista em comparar ponteiros (endereços) entre si se o que você quer é comparar os dados para onde eles apontam. Se quer comparar dados, tem de fazer referências explícitas a eles, ou usar funções que façam internamente tais referências (por exemplo, a função strcmp(), que serve para comparar o conteúdo de duas strings).

Sim, eu conheço a função strcmp, pelo que estou entendo sobre o que quer dizer é que não se pode comparar STRINGS da forma como eu estou fazendo, e que eu deveria usar uma função como strcmp para comparar elas.
Mas ainda não compreendi quando vc diz que eu comparei ponteiros (endereços). Eu gostaria que se possível me aponta-se ONDE eu estou tentando fazer essa comparação, para eu entender onde estou errando.


Não insista em aplicar um índice sobre um array de estruturas e querer que ele se aplique também a um dos campos da estrutura. Cada um dos elementos de um array é independente dos demais elementos, e a única relação que eles guardam entre si é o fato de estarem dispostos em posições da memória próximas umas das outras.

Aqui eu gostaria de entender melhor. Será que vc está dizendo que array dos campos tem o terminador e um array de Struct por ser diferente não usa um terminador?
Uma das dificuldades que tenho é justamente a informação do DEBUG, pq ele dá informações que confudem...
Exemplo: Quando declaro da forma abaixo
struct {
char Names[5];
int Num;
} List[] = { {"AAAA", 7}, {"BBB", 9}, {"C", 10} };

O DEBUG informa no caso a posição List[3] que não foi preenchida claro, com os caracteres terminadores
(gdb) p List[3]
$1 = {Names = "\000\000\000\000", Num = 0}

Isso tem me confundido, por isso pensei que eles existissem como em um array comum de caracter...
Exemplo:
char Text[] = "Teste";

(gdb) p Text[5]
$2 = 0 '\000'

No caso acima, o que me parece que são respostas iguais tanto da Struct quanto do vetor comum. Ainda estou sem entender.
Espero que vc tenha entendido o que não estou conseguindo entender!
Obrigado!


11. Re: Dúvida Iniciante com Struct em C Puro

Paulo
paulo1205

(usa Ubuntu)

Enviado em 16/08/2019 - 03:39h

Nick-us escreveu:

Pode ser falta de conhecimento base como você diz. Vou tentar explicar o que até agora entendi.
Quando declaro:
char Text[] = "ABC"; 

Entendo que eu tenho um vetor/array de caracteres que é o mesmo que: A,B,C ou seja, entendo que o C armazena apenas 1 Caracter então é como se ele fizesse uma lista para esse armazenamento. Por isso ele: Armazena:
Text[0] = A
Text[1] = B
Text[2] = C
Text[3] = Caracter Terminador 0 ou \0


Permita-me perguntar: qual material você está usando para lhe apresentar o C? A pergunta é porque parece que há algumas lacunas importantes no conhecimento que você demonstra, e também alguma mistura de conceitos e terminologia.

Discorrendo rapidamente sobre arrays. Arrays (também chamados de vetores) são conjuntos com uma quantidade finita de elementos de dados de um mesmo tipo, dispostos em posições adjacentes na memória. A forma de declarar um array é informando o tipo de cada elemento, seguido pelo nome da variável que vai designar o array e pela contagem do número de elementos do array, expressa entre colchetes. O trecho abaixo mostra alguns exemplos de declarações de arrays, com comentários que explicam o sentido de cada declaração.

int i_arr[50];  // declara ‘i_arr’ como um array contendo 50 elementos do tipo int.
double d_arr[20]; // declara ‘d_arr’ como um array contendo 20 elementos do tipo double.
char c_arr[100]; // declara ‘c_arr’ como um array contendo 100 elementos do tipo char.


Os tipos de cada elemento do array não são restritos a tipos nativos, mas podem também ser de tipos derivados ou de tipos compostos.

char *pc_arr[10];  // declara ‘pc_arr’ como um array contendo 10 elementos do tipo “ponteiro para char”.
struct tm time_table[15]; // declara ‘time_table’ como um array contendo 15 elementos do tipo “struct tm”.
float f_mat[20][10]; // declara ‘f_mat’ como um array contendo 20 elementos do tipo “array contendo 10 elementos do tipo float”.


Declarados dessa maneira, os valores inciais dos elementos do array podem ser ou zerados, se o array tiver alocação estática (variáveis globais ou as declaradas dentro de blocos, mas com o modificador static), ou podem ser desconhecidos/indeterminados, se o array tiver alocação automática (dentro de blocos e sem a especificação static).

Muitas vezes, no entanto, é interessante fazer com que os arrays possuam valores iniciais específicos atribuídos a seus elementos desde o momento de sua declaração. Nesses casos, é possível colocar, após a declaração do array, um sinal de igual (=) seguido de uma lista de valores entre chaves, separados entre si por vírgulas, como no exemplo abaixo.

unsigned primeiros_naturais[10]={0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 


A quantidade de valores iniciais não precisa ser necessariamente igual ao tamanho do array, mas pode ser menor. Nesse caso, os valores contidos na lista serão gravados nas primeiras posições do array, e as posições seguintes serão forçosamente preenchidas com zeros.

char nome[50]={'P', 'a', 'u', 'l', 'o'};  // Ocupa as cinco primeiras posições, e as 45 seguintes são preenchidas com zeros (caracteres nulos). 


Outra possibilidade que a lista de valores iniciais oferece é fazer com que o compilador conte a quantidade de elementos na lista de inicialização e ajuste o tamanho do array para se acomodar exatamente essa quantidade de elementos. Para tanto, deve-se omitir a quantidade de elementos entre os colchetes (deixando-a realmente vazia), e a lista de valores iniciais tem de estar presente.

unsigned algarismos_pares[]={0, 2, 4, 6, 8};  // Tamanho calculado automaticamente para abrigar os 5 elementos listados.
char bilabiais[]={'b', 'm', 'p'}; // Tamanho calculado automaticamente para abrigar os 3 elementos listados. NOTE QUE esta declaração NÃO DECLARA UMA STRING, mas apenas um array com três caracteres, pois não existe o byte terminador '\0' que caracterizaria uma string.

double d_arr[]; // PROBLEMA: compilador não tem como calcular tamanho; no mínimo vai produzir uma mensagem de alerta, se não de erro, durante a compilação.


Como caso particular, uma forma alternativa de especificar listas de elementos inicias, válida apenas para arrays com elementos do tipo char (e suas variantes signed char e unsigned char) é colocar uma sequência de caracteres entre aspas. Essa forma de lista de inicialização, projetada para facilitar o uso de strings, produz um efeito equivalente ao de ter os mesmos caracteres como elementos individuais numa lista de inicialização usando chaves, mas acrescentando ao final um caráter a mais, com o valor nulo, que serve como indicador do fim de string, conforme a convenção de representação usada pelas funções de manipulação de strings da biblioteca padrão do C. Efeito semelhante foi estendido para arrays de caracteres largos (wchar_t).

char nome[]="Paulo";  // equivalente a “char nome[]={'P', 'a', 'u', 'l', 'o', '\0'};”, que, por sua vez é equivalente a “char nome[6]={'P', 'a', 'u', 'l', 'o', '\0'};”.
unsigned char objeto[]="bola"; // equivalente a “unsigned char objeto[5]={'b', 'o', 'l', 'a', '\0'}.”.

wchar_t login[]=L"paulo1205"; // equivalente a “wchar_t login[10]={L'p', L'a', L'u', L'l', L'o', L'1', L'2', L'0', L'5', L'\0'};”.


Declarações de arrays reservam um espaço na memória e, eventualmente (no caso de alocação estática ou no da presença de uma lista de inicialização) atribuem valores iniciais nessas posições alocadas pela declaração. Dali para frente, a variável que designa o array como um todo não pode mais ser alterada pois, caso contrário, ela não indicaria mais aquele mesmo array, naquela região de memória, alocada desde a compilação. Elementos individuais podem ser alterados, mas o array como um todo, não.

int a[5]={0, 1, 2, 3, 4}, b[5]={1, 3, 5, 7, 9}, c[10];
int i;

for(n=0; n<5; n++){
a[n]*=2; // OK: Elemento, por elemento, altera os valores contidos em a, fazendo com que cada um valha o dobro do que valia inicialmente.
c[n*2]=a[n]; // OK: Altera os valores dos elementos de c, usando os valores dos elementos de a.
c[n*2+1]=b[n]; // OK: Altera os valores dos elementos de c, usando os valores dos elementos de b.
}
// Agora a contém {0, 2, 4, 6, 8}, e c contém {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}.

a=b; // ERRO: não posso manipular o array inteiro de uma vez (até porque aqui, ‘a’ não indica mais o array inteiro, mas é apenas um ponteiro constante para o primeiro elemento).
b=c; // ERRO: pelo mesmo motivo.


Entendo que ele guardou no Caso não uma String ABC. E sim cada Letra separadamente como se fosse numa variavel diferente, se posso chamar Text[0]... e etc... como variáveis diferentes.


As duas coisas são verdadeiras. Isto é: você tem os elementos individuais 'A', 'B', 'C' e '\0' em Text[0] até Text[3], mas também tem uma string, indicada por Text sem aplicar índices, o que resulta num ponteiro para o primeiro elemento.


Isso é mesmo um pouco confuso no início (ainda mais se você já usou outras linguagens de programação que tratem strings de uma forma mais amigável).

O problema é que C não tem um tipo de dados dedicado para representar strings. Em lugar disso, a linguagem e sua biblioteca de funções se valem do fato de que strings tipicamente indicam sequências de caracteres na memória, e então usam aquilo que a linguagem tem de mais parecido, que são os arrays de caracteres, para armazenar aquilo que a aplicação — não a linguagem em si — vai tratar como se fossem strings.

Para que isso funcione em nível de aplicação, todas as operações de strings têm de respeitar algumas convenções:

  • a string fica armazenada em um array de caracteres (que pode ter sido declarado como array no código fonte do programa ou residir em memória alocada dinamicamente, através de ponteiros sob rígido controle do programador);
  • o conteúdo da string é considerado a partir de um ponteiro para seu primeiro elemento;
  • o final da string é indicado por um caráter nulo, que funciona como um terminador da string;
  • o terminador não conta como parte da string (por exemplo, a função strlen() não inclui o terminador como parte do comprimento da string), mas mesmo assim ele tem de ser armazenado no mesmo array que contém a string (caso contrário, não se pode mais dizer que o array contém uma string.

Com base nessas convenções, se você quer que sua string tenha, por exemplo, 50 caracteres úteis, terá de declarar (ou alocar dinamicamente) um array com pelo menos 51 caracteres de tamanho, e garantir que sempre haverá um caráter nulo no 51º elemento desse array).

Acontece que quando apenas declaro um vetor de char como char Text[] = "ABC"; que seria o mesmo que char Text[4] = "ABC";
Eu tenho 4 CAMPOS Cada campo desse com 1 Caracter armazenado.


Cuidado com a terminologia. É melhor você dizer que tem quatro elementos nesse array.

A ideia de campo tem mais a ver com estruturas do que com arrays. Campos distintos podem ter tipos de dados distintos, qualificadores de acesso (const, volatile etc.) distintos e atributos distintos, ao passo que elementos de um mesmo array têm necessariamente um só tipo de dados, qualificadores e atributos.

Então pensei. Uma Struct embora ela possa ter elementos dentro dela. Mas até então não estou falando desses elementos, e sim do VETOR.


Não compreendi o que você quis dizer. E, de novo, atenção com a terminologia: a estrutura tem campos. Um dos campos pode até ser um array, e esse campo array pode ter os seus elementos, mas por mais que tais elementos estejam dentro do bloco maior chamado estrutura, o acesso a eles está num nível hierárquico diferente: antes de chegar ao elemento, você tem de passar pelo campo.

Por exemplo:
#include <stdio.h>
struct {
char Names[5];
int Num;
} List[] = { {"AAAA", 7}, {"BBB", 9}, {"C", 10} };

int main() {
if(List[0].Num == 7) // FUNCIONA
puts("OK 7");

puts(List[0].Names); // Imprime AAAA

if(List[0].Names == "AAAA") // NÃO FUNCIONA Não encontra
puts("OK A");
}


Funciona, sim. Só que não faz o que você possivelmente gostaria que fizesse.

O operador de comparação == compara diretamente os valores que aparecem de cada um dos seus lados. No seu caso, você tem um ponteiro para caracteres de um lado e um ponteiro para caracteres do outro lado (ambos gerados a partir do decaimento automático de arrays para ponteiros, algo que é previsto e prescrito pela linguagem), e o que o operador de comparação faz é simplesmente testar se esses dois ponteiros designam o mesmo endereço de memória, o que seguramente nunca será o caso, porque um deles indica um campo da estrutura, e o outro, algo que está necessariamente fora da estrutura.

No Caso acima, eu entendo que minha Struct declarada List[] = { {"AAAA", 7}, {"BBB", 9}, {"C", 10} };
É o mesmo que List[4] = { {"AAAA", 7}, {"BBB", 9}, {"C", 10} };
OU List[3] = { {"AAAA", 7}, {"BBB", 9}, {"C", 10} };


Como eu disse acima, a aparente extensão de tamanho se aplica apenas a array de caracteres com inicialização a partir de constantes literais strings. Nem o seu vetor tem elementos do tipo char, nem sua lista de inicialização esta na forma de string entre aspas.


... “Principium sapientiae timor Domini, et scientia sanctorum prudentia.” (Proverbia 9:10)


12. Re: Dúvida Iniciante com Struct em C Puro [RESOLVIDO]

Nick Us
Nick-us

(usa Slackware)

Enviado em 16/08/2019 - 08:00h

paulo1205 escreveu:Permita-me perguntar: qual material você está usando para lhe apresentar o C? A pergunta é porque parece que há algumas lacunas importantes no conhecimento que você demonstra, e também alguma mistura de conceitos e terminologia.

Sim, existem talvez não lacunas, mas grandes crateras. Eu estou estudando C com o que eu encontro na Internet. Minha dificuldade se dá no pouco conhecimento da linguagem inglesa, ao ler alguns artigos consigo entender parte dele, mas não todo como uma pessoa nativa dessa linguagem. Então como sabemos que uma linguagem de programação aos poucos se complica, e onde começa minha confusão. Outro problema que enfrento são as informações em português da linguagem, Sites de Universidade, entre outros que se propõe a explicar os conceitos de cada coisa, não dão exatamente informações concretas, muita dessas informações me parecem erradas ou desconexas, e claro por EU não saber o TODO, isso cria uma confusão.

No momento por exemplo eu estava estudando sobre typedef na declaração de Struct, dei uma pausa por causa do problema que apresentei aqui. Um site que eu estava lendo por exemplo AFIRMA que toda struct tem que ser declarada usado typedef. Isso não me parece verdade, já que não estou declarando dessa forma. Outro site disse que typedef era para você poder dar apelidos para sua Struct. O que até pouco tempo acreditei, até perceber que eu também não precisava dele para dar um nome ou apelido para minha struct. No fim até aqui, ainda não compreendi direito para que serve isso, ainda estou buscando informações mais verdadeiras.

O Conhecimento que apresento sobre C, é da seguinte forma: Eu leio sobre um assunto, e coloco em prática testando sobre aquilo que li. Testo para saber se é verdadeiro e com o intuito de dominar o que aprendi. E anoto em pequenos textos o que aprendi até então para não esqueçer, e ainda nesse meio tempo, meu objetivo que é o que estou fazendo aos poucos é desenvolver um pequeno banco de dados para cadastrar essas informações.

Porém o PONTO é que quero fazer isso direito, da forma mais correta possível, limpinha, mas claro vão surgindo dúvidas ao longo disso, dúvidas essas que são difíceis as vezes para encontrar respostas e ou explicações que a preencham.

Existem até dúvidas ainda mais bobas em minha agenda de pesquisas a procurar, como por exemplo: PORQUE programadores colocam o asteristico do ponteiro do lado errado? Exemplos: O Codeblocks+wxSmith coloca o asterístico sempre na palavra anterior ao ponteiro como por exemplo:
FrmTips::FrmTips(wxWindow* parent, wxWindowID id) { 

Eu entendo que ponteiro seria parent e não wxWindow.

Muitas pessoas declaram ponteiros da seguinte forma:
char* Nome[15]; 

Para mim isso parece muito errado, já que char não é um ponteiro. Entendo que Nome seria o apontamento desse Ponteiro. o que para mim o correto seria:
char *Nome[15]; 

E o problema não para por ai, ainda o Codeblocks também coloca do lado contrário o & como no exemplo abaixo
void FrmTips::OnBtAddClick(wxCommandEvent& event) { 

Eu costumo corrigir colocando onde eu acho que seria o certo, mas claro não tenho conhecimento completo sobre isso
void FrmTips::OnBtAddClick(wxCommandEvent &event) { 

Muito de minhas dúvidas nunca encontrei um material que explica-se o porque daquele tipo de declaração, isso começou claro com o main (). Que após perguntar e claro após ler muitos artigos que vc respondeu aqui sobre o porque declarar corretamente o main eu consegui compreender muito melhor, foi graças a seus artigos e respostas que pude entender o int o void na frente do main o porque, e etc... como int main(void) suas explicações sobre isso no Viva Linux foram de grande valia. Muitas pessoas não se importam, fazem um programa e pronto. Não é o meu caso!

Eu gosto de saber o que estou escrevendo e o porque tenho que escrever daquele jeito. Não me sinto a vontade escrever um código pq disseram que tem que ser daquele jeito. Quero saber porque!

Pra vc ter uma idéia já vi pessoas escrevendo assim:
(void) printf("Bom dia"); 

Nestes mesmos sources eu removi a palavra (void) da frente de todos printf que encontrei. E ainda não sei o porque essa pessoa declarou dessa forma!

Voltando ao caso de arrays, sempre achei que eles tivessem um terminador, porque lí dessa forma.
Os tipos de cada elemento do array não são restritos a tipos nativos, mas podem também ser de tipos derivados ou de tipos compostos.
char *pc_arr[10];  // declara ‘pc_arr’ como um array contendo 10 elementos do tipo “ponteiro para char”.
struct tm time_table[15]; // declara ‘time_table’ como um rray contendo 15 elementos do tipo “struct tm”.
float f_mat[20][10]; // declara ‘f_mat’ como um array contendo 20 elementos do tipo “array contendo 10 elementos do tipo float”.

Declarados dessa maneira, os valores inciais dos elementos do array podem ser ou zerados, se o array tiver alocação estática (variáveis globais ou as declaradas dentro de blocos, mas com o modificador static), ou podem ser desconhecidos/indeterminados, se o array tiver alocação automática (dentro de blocos e sem a especificação static.

A parte sobre alocação estática ou automática ainda não estudei porque não cheguei até esse ponto.
Eu sei o que é uma variável global, local e etc... como elas funcionam em cada bloco onde são escritas, e PARTE sobre como são descartadas, deixei para estudar depois esse descarte da memória quando eu for estudar e testar sobre a otimização do uso de memória do meu programa, tenho vontade depois de estudar como isso é guardado ou descartado. Mas ainda estou na parte mais básica da coisa a entender primeiro.
Até porque HOJE é pra mim difícil pra compreender pq alguém criaria uma variável const ou static e porque alguém deu o nome de variável para algo que não pode variar se é estático. Mas deixei isso pra depois, pq imagino que tenha a ver com alocação de recursos e coisas do tipo.

Muitas vezes, no entanto, é interessante fazer com que os arrays possuam valores iniciais específicos atribuídos a seus elementos desde o momento de sua declaração. Nesses casos, é possível colocar, após a declaração do array um sinal de igual (=) seguidos de uma lista de valores entre chaves, e separados entre si por vírgulas, como no exemplo abaixo.
unsigned primeiros_naturais[10]={0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 

Essa parte acima eu realmente não entendi, vc declarou 10 espaços e usou os 10 espaços ficando sem espaço para o caracter terminador. Não sei se foi um pequena falta de atenção de sua parte, OU se o caso desse array com essa palavra unsigned que ainda não estudei por nunca precisar usar ou não chegar a ver um exemplo prático de seu uso, ela não precise de um caracter terminador. FICOU a dúvida!

ESSA DECLARAÇÃO ME CONFUNDIU Se puder responder ficarei agradecido!
char bilabiais[]={'b', 'm', 'p'};  // Tamanho calculado automaticamente para abrigar os 3 elementos listados.  NOTE QUE esta declaração NÃO DECLARA UMA STRING, mas apenas um array com três caracteres, pois não existe o byte terminador '\0' que caracterizaria uma string. 

Na Msg acima a forma como vc declarou me deixou SUPER CONFUSO e isso quebra o que até então eu entendia.
Vamos lá. Vc afirma que não tem um terminador. POIS É. Não entendi o porque não tem! Então para entender sua declaração montei o algoritmo abaixo.
#include <stdio.h>
int main() {
char Text[] = "Teste";
char bilabiais[]={'b', 'm', 'p'};
puts("FIM");
}

E óbvio fui buscar entendimento no GDB.
Na minha declaração char Text[] = "Teste"; Onde eu uso 4 Elementos, o 5 Elemento é o Caracter terminador como o GDB informa abaixo!
(gdb) p Text[5]
$8 = 0 '\000'

Agora é que vem a questão: PARA MIM, Text[] ou bilabiais[] são char. E entendo que char armazena 1 caracter. Posso mesmo até compreender que caracteres podem ser decimais pela tabela ASCII no caso Text[4] = 101 'e' mas não vamos complicar...

A única diferença em sua declaração é que vc usou {} e ' ' mas para MIM entedi que isso é porque vc declarou 1 elemento de cada vez ao invés de TIPO fazer uma declaração automática como eu fiz.

MAS... Ao verificar isso pelo GDB vem a SURPRESA:
(gdb) p bilabiais[3]
$5 = 84 'T'

O GDB embora tenha informado um valor, imagino que seja LIXO. Pq para mim nessa posição 3 deveria estar o terminador 0 ou \0
Mas ainda fica mais confuso do porque o GDB informar LIXO com tanta precisão, pq ele continua informando em posições 4,5,6,7... e etc...
O que não acontece exatamente com Text[] são comportamentos diferentes.

Dúvida: PORQUE essas declarações que para mim seriam iguais, SÃO na verdade COMPLETAMENTE DIFERENTES? Porque não existe um caracter Terminador na sua declaração char bilabiais[]={'b', 'm', 'p'};
EU PENSEI QUE:
char Text[] = "Teste"; = char Text[]={'T', 'e', 's', 't', 'e', '\0'};
char bilabiais[]={'b', 'm', 'p'}; = char bilabiais[]={'b', 'm', 'p', '\0'}; ONDE O SISTEMA iria colocar o \0 da mesma forma OU a não ser que declarar dessa forma seja ERRADO ou INCORRETO o que daria erro no programa, mas não deu e nem o compilador informa estar errado essa declaração!
Porém seguindo orientações anteriores suas para compilar usando as msg de informação do compilador, compilei assim:
gcc -Wall -O2 -pedantic -ansi Test.c

E a única msg era sobre as variáveis não estarem sendo usadas. Ou seja, ele não diz que essa declaração estaria errada! UAU que Dúvida!
Curiosamente isso me chamou atenção para esse array int a[3] = {5,6,7}; que também não tem um terminador, mas nesse caso é porque ele não é um char. Que seria diferente do caso acima.
Mas isso também me chamou atenção para o MEU PROBLEMA, no caso o array da minha Struct da minha pergunta...
struct DbNames List[] = { {"A"}, {"B"}, {"C"} }; 

Eu estava procurando um Caracter Terminador nela, então estou percebendo que ELE NÃO VAI EXISTIR CERTO? Assim como em um array de INTEIROS não tem CERTO?
Eu nem havia percebido esse detalhe se for isso, durante todo esse tempo!
Ao que entendi até esse momento aqui é que apenas Textos ou Strings teriam o caracter terminador. PORÉM sua declaração char, que eu consideraria um Texto não possue! Não entendo o porque!

Então pensei. Uma Struct embora ela possa ter elementos dentro dela. Mas até então não estou falando desses elementos, e sim do VETOR.

Não compreendi o que você quis dizer. E, de novo com terminologia, a estrutura tem campos. Um dos campos pode até ser um array, e esse campo array ter os seus elementos, mas por mais que tais elementos estejam dentro do bloco maior chamado estrutura, o acesso a eles está num nível hierárquico diferente: antes de chega ao elemento, você tem de passar pelo campo.

O que eu quis dizer neste caso acima, era do array inicial da Struct List[] = { {"AAAA", 7}, {"BBB", 9}, {"C", 10} };
Que Imaginei que seriam 3 Elementos que usei em List[] E que o 4º Elemento seria um caracter terminador colocado automáticamente. Mas pelos exemplos acima anteriores no caso do array de INTEIROS que não tem terminador, penso agora que é esse o caso acima, ela não tem um Terminador, porque é um array diferente de um array char representando uma String.

Funciona, sim. Só que não faz o que você possivelmente gostaria que fizesse.
O operador de comparação == compara diretamente os valores que aparecem de cada um dos seus lados. No seu caso, você tem um ponteiro para caracteres de um lado e um ponteiro para caracteres do outro lado (ambos gerados a partir do decaimento automático de arrays para ponteiros, algo que é previsto e prescrito pela linguagem), e o que o operador de comparação faz é simplesmente testar se esses dois ponteiros designam o mesmo endereço de memória, o que seguramente nunca será o caso, porque um deles indica um campo da estrutura, e o outro, algo que está necessariamente fora da estrutura.

Aqui acho que consegui entender, o porque ele não encontra o valor, vou assimilar melhor esse decaimento de array para ponteiro.

No Caso acima, eu entendo que minha Struct declarada List[] = { {"AAAA", 7}, {"BBB", 9}, {"C", 10} };
É o mesmo que List[4] = { {"AAAA", 7}, {"BBB", 9}, {"C", 10} };
OU List[3] = { {"AAAA", 7}, {"BBB", 9}, {"C", 10} };

Como eu disse acima, a aparente extensão de tamanho se aplica apenas a array de caracteres com inicialização a partir de constantes literais strings. Nem o seu vetor tem elementos do tipo char, nem sua lista de inicialização esta na forma de string entre aspas.

Acho que entendi sua resposta aqui. Após ver os problemas e dúvidas que cito acima. ENTENDI que minha List não é uma cadeia de caracteres motivo pelo qual não existiria um terminador para caracteres.



01 02



Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts