Um for de 0 a 9, com os numero 5 e 6 errados, talvez um problema de alocação;

1. Um for de 0 a 9, com os numero 5 e 6 errados, talvez um problema de alocação;

joao pedro ache virgili
joaovirgili

(usa Ubuntu)

Enviado em 18/09/2016 - 23:38h

Não sei nem explica direito este erro, mas é o seguinte:
Estou fazendo um trabalho onde monto uma pokedex. Aloco dinamicamente um vetor de tPokemon (minha struct pokemon), para armazenar os dados de varios pokemon. Porém, ao imprimi-la, os Pokemon 5 e 6 não aparecem com numero 5 e 6, eles aparecem como lixo.

Impressão da Pokedex:
Pokemon 1 nao consta na Pokedex
Pokemon 2 nao consta na Pokedex
Pokemon 3 consta na Pokedex
Pokemon 4 nao consta na Pokedex
Pokemon 1701539664 consta na Pokedex
Pokemon 757935405 consta na Pokedex
Pokemon 7 nao consta na Pokedex
Pokemon 8 nao consta na Pokedex
Pokemon 9 nao consta na Pokedex
Pokemon 10 nao consta na Pokedex

não sei o que é, será algum problema de memória?
Agora, mudei o nome da variavel de "id" para "valor" e apenas o numero 5 ficou com problema:
Impressão da Pokedex:
Pokemon 1 nao consta na Pokedex
Pokemon 2 nao consta na Pokedex
Pokemon 3 nao consta na Pokedex
Pokemon 4 nao consta na Pokedex
Pokemon 1701539664 consta na Pokedex
Pokemon 6 nao consta na Pokedex
Pokemon 7 nao consta na Pokedex
Pokemon 8 nao consta na Pokedex
Pokemon 9 nao consta na Pokedex
Pokemon 10 nao consta na Pokedex

Mas logo depois fui continuar a testar e o numero 6 voltou a ficar com problema. São sempre estes 2 numeros.

Segue o codigo da função:
/*Função que insere na pokedex. Um vetor alocado dinamicamente, onde se o pokemon ja estiver registrado, terá o valor true*/
bool inserirPokemon(tPokedex* p, int valor) {
if (p->v[valor-1].reg == false) {
p->numElems++;
p->v[valor-1].reg = true;
p->v[valor-1].ID = valor;
p->v[valor-1].candy = p->v[valor-1].candy + 3;
p->v[valor-1].evolve = CandyEvolucao();
printf("Pokemon %d registrado com sucesso na Pokedex\n", valor);
return true;
}
p->v[valor-1].candy = p->v[valor-1].candy + 3;
return false;
}

main.c : http://pastebin.com/xg931FkB
pokemon.c: http://pastebin.com/s7AaZXDz
pokemon.h http://pastebin.com/0HPuBW25
pokedex.c http://pastebin.com/9NLD0jey
pokedex.h http://pastebin.com/kS2dGVpt

há um warning que também não sei porque acontece e ele não aparece 100% das vezes:
Pokedex.c: In function ‘initPokedex’:
Pokedex.c:12:7: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
p->v = (tPokedex*) malloc (MAX*sizeof(tPokedex));





  


2. Re: Um for de 0 a 9, com os numero 5 e 6 errados, talvez um problema de alocação;

Paulo
paulo1205

(usa Ubuntu)

Enviado em 19/09/2016 - 02:02h

joaovirgili escreveu:

há um warning que também não sei porque acontece e ele não aparece 100% das vezes:
Pokedex.c: In function ‘initPokedex’:
Pokedex.c:12:7: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
p->v = (tPokedex*) malloc (MAX*sizeof(tPokedex));


O tipo de p->v é “tPokemon *”, não “tPokedex *”.

Com frequência eu alerto aqui que essa conversão de tipos aplicada sobre o valor retornado por malloc() não precisa -- e não deve -- ser usada num programa em C. Também é inadequado usar o nome do tipo como argumento de sizeof, porque você também pode se confundir da mesma maneira como se confunde com o tipo da conversão (e de fato foi o que aconteceu).

A melhor forma de nunca errar na alocação de dados é usar a seguinte forma.
ptr_tipoX=malloc(num_elementos * sizeof *ptr_tipoX);  // Sem conversão explícita, e deixando o compilador calcular o tamanho em função do tipo do dado apontado. 

No seu caso, o exemplo acima assume a seguinte forma.
p->v=malloc(MAX * sizeof *p->v); 


O erro no tamanho pode ser suficiente para explicar o problema que você viu. Corrija essa parte, e veja se o problema some, porque, a essa hora da madrugada, olhar programa alheio, ainda mais separado em vários arquivos, vai se tornando uma tarefa bem complicada de fazer direito...

Um comentário a mais antes de parar: os nomes das variáveis e campos não ajuda muito a ler o programa. O que significa um campo chamado v? É um vetor? Se o é, que tipo de dado ele guarda? Se você usasse nomes um só pouco mais descritivos, essa perguntas poderiam ser evitadas.


3. Re: Um for de 0 a 9, com os numero 5 e 6 errados, talvez um problema de alocação;

Paulo
paulo1205

(usa Ubuntu)

Enviado em 19/09/2016 - 02:54h

Outro problema que eu vi no seu programa (que não deve influenciar o erro que você, mas que é bom corrigir, até para você se acostumar a programar de modo mais seguro) é o fato de você não usar protótipos das suas funções nos arquivos de cabeçalhos. Veja os seguintes exemplos de extratos de código:

// Declarações em pokedex.h
bool initPokedex();
void limparPokedex();


// Implementação em pokedex.c
bool initPokedex(tPokedex* p){ /* bla, bla, bla, bla... */ }
void limparPokedex (tPokedex* p){ /* ble, ble, ble, ble... */ }


Notou que os argumentos das declarações das funções não batem com os argumentos de suas implementações? Isso é uma prática obsoleta em C, e um erro em C++. Seria muito melhor se você colocasse os argumentos também na declaração.

Em C, a declaração omitindo argumentos significa que a função, ao ser implementada ou quando for invocada, pode receber uma quantidade qualquer de argumentos de quaisquer tipos de dados. Quando você trata a função desse modo, você mata a possibilidade de o compilador fazer qualquer verificação sobre o uso correto dos argumentos da função. Se você, no seu programa, chamar a função com um argumento que não seja um ponteiro para tPokedex (por exemplo, se usar um dado do tipo tPokemon *, ou mesmo um dado inteiro, ou string), o compilador vai deixar passar esse erro crasso sem nenhum aviso ou preocupação.

Já em C++, uma lista de argumentos vazia significa que a função não pode receber argumento nenhum (equivalente, no C, a uma lista de argumentos contendo apenas a palavra-chave “void”). A linguagem aceita declarar e definir várias funções com mesmo nome, desde que elas tenham argumentos diferentes, e as considera como funções distintas (recurso chamado de sobrecarga de funções, ou function overload). Quando o compilador compilar a definição da função que tem uma lista de argumentos diferente da declaração, ele vai considerá-la como outra função. E como a função que você vai usar em main.c (supondo que você não vai errar o tipo do argumento, como teorizado no parágrafo anterior) não é a mesma que está declarada no cabeçalho, então main.c não vai nem mesmo saber que existe uma implementação válida, e vai tomar um erro dizendo que a versão que o compilador conhece não aceita os argumentos que você tentou impor.

Aliás, a diferença de comportamento entre C e C++ é algo que você deve aprender a levar em conta quando fizer seus arquivos de cabeçalho. Você pode hoje projetar uma biblioteca pensando em usá-la apenas com programas em C, e daqui a alguns meses ou anos acaba aparecendo um programa em C++ que requer uma funcionalidade parecida, e você pensa se aquela biblioteca poderia vir a calhar. Até poderia, mas se o arquivo de cabeçalhos apresentar elementos com diferenças de significados (por exemplo: como interpretar listas de argumentos vazias) ou que acomodem recursos numa linguagem que são inexistentes na outra (por exemplo: permitir ou não múltiplas funções com o mesmo nome), esse arquivo terá de ser refeito, a fim de evitar erros de compilação ou de linking.

Assim que puder, aprenda a se proteger contra variações de comportamento entre C e C++. Você já viu sobre include guards, então vai notar que a forma básica de fazer o mesmo cabeçalho valer tanto para C quanto para C++ é quase igual a um include guard (ou talvez seja um tipo mesmo de guarda).

#ifdef __cplusplus  /* Dois sinais “_” antes de “cplusplus”. */
extern "C" {
#endif

/*
Suas declarações vão aqui. Agora elas podem ser usadas pelas
duas linguagens.
*/

#ifdef __cplusplus
} // fecha o bloco “extern "C"”.
#endif



4. Re: Um for de 0 a 9, com os numero 5 e 6 errados, talvez um problema de alocação;

joao pedro ache virgili
joaovirgili

(usa Ubuntu)

Enviado em 19/09/2016 - 04:28h

paulo1205 escreveu:

joaovirgili escreveu:

há um warning que também não sei porque acontece e ele não aparece 100% das vezes:
Pokedex.c: In function ‘initPokedex’:
Pokedex.c:12:7: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
p->v = (tPokedex*) malloc (MAX*sizeof(tPokedex));


O tipo de p->v é “tPokemon *”, não “tPokedex *”.

Com frequência eu alerto aqui que essa conversão de tipos aplicada sobre o valor retornado por malloc() não precisa -- e não deve -- ser usada num programa em C. Também é inadequado usar o nome do tipo como argumento de sizeof, porque você também pode se confundir da mesma maneira como se confunde com o tipo da conversão (e de fato foi o que aconteceu).

A melhor forma de nunca errar na alocação de dados é usar a seguinte forma.
ptr_tipoX=malloc(num_elementos * sizeof *ptr_tipoX);  // Sem conversão explícita, e deixando o compilador calcular o tamanho em função do tipo do dado apontado. 

No seu caso, o exemplo acima assume a seguinte forma.
p->v=malloc(MAX * sizeof *p->v); 


O erro no tamanho pode ser suficiente para explicar o problema que você viu. Corrija essa parte, e veja se o problema some, porque, a essa hora da madrugada, olhar programa alheio, ainda mais separado em vários arquivos, vai se tornando uma tarefa bem complicada de fazer direito...

Um comentário a mais antes de parar: os nomes das variáveis e campos não ajuda muito a ler o programa. O que significa um campo chamado v? É um vetor? Se o é, que tipo de dado ele guarda? Se você usasse nomes um só pouco mais descritivos, essa perguntas poderiam ser evitadas.


Entendi. É que foi desta maneira que o professor havia ensinado, achei que era o certo. Arrumei e vou guardar aqui para as próximas vezes..
v é um vetor de pokemons. A pokedex é constituida por um vetor de pokemons (v), onde cada espaço do vetor é para um tipo de pokemon.
Vou tentar deixar mais claro da proxima vez


5. Re: Um for de 0 a 9, com os numero 5 e 6 errados, talvez um problema de alocação;

joao pedro ache virgili
joaovirgili

(usa Ubuntu)

Enviado em 19/09/2016 - 04:31h

paulo1205 escreveu:

Outro problema que eu vi no seu programa (que não deve influenciar o erro que você, mas que é bom corrigir, até para você se acostumar a programar de modo mais seguro) é o fato de você não usar protótipos das suas funções nos arquivos de cabeçalhos. Veja os seguintes exemplos de extratos de código:

// Declarações em pokedex.h
bool initPokedex();
void limparPokedex();


// Implementação em pokedex.c
bool initPokedex(tPokedex* p){ /* bla, bla, bla, bla... */ }
void limparPokedex (tPokedex* p){ /* ble, ble, ble, ble... */ }


Notou que os argumentos das declarações das funções não batem com os argumentos de suas implementações? Isso é uma prática obsoleta em C, e um erro em C++. Seria muito melhor se você colocasse os argumentos também na declaração.

Em C, a declaração omitindo argumentos significa que a função, ao ser implementada ou quando for invocada, pode receber uma quantidade qualquer de argumentos de quaisquer tipos de dados. Quando você trata a função desse modo, você mata a possibilidade de o compilador fazer qualquer verificação sobre o uso correto dos argumentos da função. Se você, no seu programa, chamar a função com um argumento que não seja um ponteiro para tPokedex (por exemplo, se usar um dado do tipo tPokemon *, ou mesmo um dado inteiro, ou string), o compilador vai deixar passar esse erro crasso sem nenhum aviso ou preocupação.

Já em C++, uma lista de argumentos vazia significa que a função não pode receber argumento nenhum (equivalente, no C, a uma lista de argumentos contendo apenas a palavra-chave “void”). A linguagem aceita declarar e definir várias funções com mesmo nome, desde que elas tenham argumentos diferentes, e as considera como funções distintas (recurso chamado de sobrecarga de funções, ou function overload). Quando o compilador compilar a definição da função que tem uma lista de argumentos diferente da declaração, ele vai considerá-la como outra função. E como a função que você vai usar em main.c (supondo que você não vai errar o tipo do argumento, como teorizado no parágrafo anterior) não é a mesma que está declarada no cabeçalho, então main.c não vai nem mesmo saber que existe uma implementação válida, e vai tomar um erro dizendo que a versão que o compilador conhece não aceita os argumentos que você tentou impor.

Aliás, a diferença de comportamento entre C e C++ é algo que você deve aprender a levar em conta quando fizer seus arquivos de cabeçalho. Você pode hoje projetar uma biblioteca pensando em usá-la apenas com programas em C, e daqui a alguns meses ou anos acaba aparecendo um programa em C++ que requer uma funcionalidade parecida, e você pensa se aquela biblioteca poderia vir a calhar. Até poderia, mas se o arquivo de cabeçalhos apresentar elementos com diferenças de significados (por exemplo: como interpretar listas de argumentos vazias) ou que acomodem recursos numa linguagem que são inexistentes na outra (por exemplo: permitir ou não múltiplas funções com o mesmo nome), esse arquivo terá de ser refeito, a fim de evitar erros de compilação ou de linking.

Assim que puder, aprenda a se proteger contra variações de comportamento entre C e C++. Você já viu sobre include guards, então vai notar que a forma básica de fazer o mesmo cabeçalho valer tanto para C quanto para C++ é quase igual a um include guard (ou talvez seja um tipo mesmo de guarda).

#ifdef __cplusplus  /* Dois sinais “_” antes de “cplusplus”. */
extern "C" {
#endif

/*
Suas declarações vão aqui. Agora elas podem ser usadas pelas
duas linguagens.
*/

#ifdef __cplusplus
} // fecha o bloco “extern "C"”.
#endif


eu não li muito sobre include guards, mas entendi o funcionamento.. Vi algumas maneiras e não soube bem qual usar, a que funcionou eu deixei. O que é o extern "C"? um tipo de variavel globa?
devo modificar para este modo?


6. Re: Um for de 0 a 9, com os numero 5 e 6 errados, talvez um problema de alocação;

Paulo
paulo1205

(usa Ubuntu)

Enviado em 19/09/2016 - 13:19h

Escrever de madrugada é horrível. Além de ter errado no Português, ainda esqueci de falar de um aspecto muito importante na discussão sobre precaver-se contra comportamento inesperado, tanto devido a erros de tipos (como o cast errado aplicado ao retorno de malloc()) quanto por causa de usar verificação de tipos de argumentos em funções.

Configure a invocação do seu compilador para gerar o máximo possível de diagnósticos, e a considerar “warnings” como se fossem erros (i.e. parando a compilação prematuramente). Com o GCC (tanto gcc quanto g++), eu sempre recomendo as opções “-Wall -Werror -O2 -pedantic-errors”.


7. Re: Um for de 0 a 9, com os numero 5 e 6 errados, talvez um problema de alocação;

joao pedro ache virgili
joaovirgili

(usa Ubuntu)

Enviado em 19/09/2016 - 17:28h

paulo1205 escreveu:

Escrever de madrugada é horrível. Além de ter errado no Português, ainda esqueci de falar de um aspecto muito importante na discussão sobre precaver-se contra comportamento inesperado, tanto devido a erros de tipos (como o cast errado aplicado ao retorno de malloc()) quanto por causa de usar verificação de tipos de argumentos em funções.

Configure a invocação do seu compilador para gerar o máximo possível de diagnósticos, e a considerar “warnings” como se fossem erros (i.e. parando a compilação prematuramente). Com o GCC (tanto gcc quanto g++), eu sempre recomendo as opções “-Wall -Werror -O2 -pedantic-errors”.


Certo, farei isso logo depois de terminar o trabalho, ele é pra hoje a noite e ainda preciso finalizar.

Eles está dando warning as vezes, o programa funciona normal, mas esses warnings aparecem as vezes:
Itens.c: In function ‘AdicionarItem’:
Itens.c:38:17: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
s->fim->prox = NovaPoke;
^
Itens.c:39:11: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
s->fim = s->fim->prox;
^
Itens.c:62:15: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
s->f->prox = NovaPot;
^
Itens.c:63:9: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
s->f = s->f->prox;
^
Itens.c: In function ‘ImprimeSacola’:
Itens.c:98:13: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
posAtual1 = posAtual1->prox;
^
Itens.c:115:13: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
posAtual2 = posAtual2->prox;
Itens.c: In function ‘RemoverItem’:
Itens.c:130:26: warning: comparison of distinct pointer types lacks a cast
while (posAtual->prox != s->fim)
^
Itens.c:131:14: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
posAtual = posAtual->prox;
^
Itens.c:132:8: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
aux = posAtual->prox;
^
Itens.c:143:24: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
tPotions* posAtual = s->inicio;
^
Itens.c:146:26: warning: comparison of distinct pointer types lacks a cast
while (posAtual->prox != s->f)
^
Itens.c:147:14: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
posAtual = posAtual->prox;
^
Itens.c:148:8: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
aux = posAtual->prox;


Minhas funções
Adicionar Item:
bool AdicionarItem (Sacola* s, int valor_item) {
/*caso seja pokeball*/
if (valor_item == pokeball || valor_item == greatball || valor_item == ultraball) {

tPokeballs* NovaPoke = malloc(sizeof *NovaPoke);
if (!NovaPoke) {
printf("Não foi possivel adicionar item\n");
return false;
}

NovaPoke->prox = NULL;
NovaPoke->pk_tipo = valor_item;

if (s->inicio == NULL) {
s->inicio = NovaPoke;
s->fim = NovaPoke;
}
else {
s->fim->prox = NovaPoke;
s->fim = s->fim->prox;
}
s->quant_itens[valor_item]++;
s->NumItens++;
return true;
}

/*caso seja potion*/
else {
tPotions* NovaPot = malloc(sizeof *NovaPot);
if (!NovaPot) {
printf("Não foi possivel adicionar item\n");
return false;
}

NovaPot->prox = NULL;
NovaPot->pt_tipo = valor_item;

if (s->ini == NULL) {
s->ini = NovaPot;
s->f = NovaPot;
}
else {
s->f->prox = NovaPot;
s->f = s->f->prox;
}
s->quant_itens[valor_item]++;
s->NumItens++;
return true;
}
}

Remover Item (incompleta, está dando erro):
bool RemoverItem (Sacola* s, int valor_item) {
/*Caso seja pokeball*/
if (valor_item == pokeball || valor_item == greatball || valor_item == ultraball) {
tPokeballs* posAtual = s->inicio;
tPokeballs* aux;
if (!SacolaVazia(s)) {
while (posAtual->prox != s->fim)
posAtual = posAtual->prox;
aux = posAtual->prox;
posAtual->prox = s->fim->prox;
s->fim = posAtual;
free(aux);
return true;
}
printf("Sacola Vazia\n");
return false;
}
/*Caso seja Potion*/
else {
tPotions* posAtual = s->inicio;
tPotions* aux;
if (!SacolaVazia(s)) {
while (posAtual->prox != s->f)
posAtual = posAtual->prox;
aux = posAtual->prox;
posAtual->prox = s->f->prox;
s->f = posAtual;
free(aux);
return true;
}
printf("Sacola Vazia\n");
return false;
}
}


Breve explicação do funcionamento:
/*Os itens disponíveis enumerados*/

enum Itens {pokeball, greatball, ultraball, potion, superpotion, hyperpotion, maxpotion};

/*Fila para pokebolas*/
typedef struct no_pk {
struct item* prox;
int pk_tipo;
}tPokeballs;

/*Fila para Poções*/
typedef struct no_pt {
struct no_po* prox;
int pt_tipo;
}tPotions;

typedef struct {
tPokeballs *inicio;
tPokeballs *fim;
tPotions* ini;
tPotions* f;
int NumItens;
int quant_itens[7];
}Sacola;

Minha estrutura Sacola está possui 2 filas, uma para Potion (tPotion) e outra para Pokeballs (tPokeballs). Assim, ao adicionar um item, verifico se o item a ser adicionado é potion ou pokeball, para adicionar a fila correspondente.
Fiz assim pois o professor exige que o primeiro item de um item a ser pego seja o primeiro a ser usado. Assim, não teria como fazer uma fila para todos os itens.
Muito obrigado.


8. Re: Um for de 0 a 9, com os numero 5 e 6 errados, talvez um problema de alocação;

joao pedro ache virgili
joaovirgili

(usa Ubuntu)

Enviado em 19/09/2016 - 18:04h

Erro meu, dá sim para fazer apenas com uma lista.. havia me confundido com pilha, a fila remove no inicio.


9. Re: Um for de 0 a 9, com os numero 5 e 6 errados, talvez um problema de alocação;

Paulo
paulo1205

(usa Ubuntu)

Enviado em 19/09/2016 - 19:50h

joaovirgili escreveu:

Entendi. É que foi desta maneira que o professor havia ensinado, achei que era o certo. Arrumei e vou guardar aqui para as próximas vezes..
v é um vetor de pokemons. A pokedex é constituida por um vetor de pokemons (v), onde cada espaço do vetor é para um tipo de pokemon.
Vou tentar deixar mais claro da proxima vez


Vamos separar a questão sobre o uso de malloc() em partes:

P.1) É preciso fazer conversão de tipos explícita sobre o valor retornado por malloc()?

R.1) Depende da linguagem.

R.1.a) Em C, não é preciso. malloc() retorna um “ponteiro para void”, e a linguagem automaticamente converte um ponteiro para void em qualquer outro tipo de ponteiro e vice-versa. Então uma conversão explícita de tipos acaba sendo algo redundante e, nessa qualidade, pode e deve ser evitada. Insistir em usá-la acaba criando mais código sobre o qual dar manutenção, e dá margem a acabar convertendo para o tipo errado. Você mesmo acabou incorrendo nesse erro.

R.1.b) Em C++ não existe conversão automática de ponteiro para void para outros tipos de ponteiros (embora exista no sentido inverso). Sendo assim, se você quiser usar malloc() em C++, você teria de fazer a conversão explícita. Mesmo assim, a forma como você fez seria considerada inadequada, porque conversão de tipos ao estilo de C é quase sempre evitada, já que pode encobrir erros que o compilador poderia de outra forma identificar e informar. Eis como essa conversão poderia ser feita em C++.
ptr_tipoX=static_cast<decltype(ptr_tipoX)>(malloc(/*bla, bla, bla*/)); 

Mas mesmo isso seria considerado um último caso. C++ tem os operadores new e delete para fazer alocação e desalocação de memória, em vez de trabalhar com funções. Esses operadores sempre devolvem um tipo de ponteiro compatível com a alocação que está sendo feita, em lugar de confiar na conversões automáticas ou manuais de tipo.
struct schar { char data; schar(): data(0){ } };
struct sint { int data; sint(): data(0){ } };

typedef const schar cchar;
typedef const sint cint;

int *pi=new int; // Aloca espaço para um inteiro.
char *str=new char[50]; // Aloca espaço para 50 caracteres.
double *pd=new int[20]; // ERRO: tipo de ponteiro incompatível com alocação de 20 inteiros.
float *pf=static_cast<float *>(new int[10]); // ERRO também: static_cast só aceita conversões seguras.
long *pl=reinterpret_cast<long *>(new char[sizeof *pl]); // OK: você está dizendo ao compilador que sabe o que está fazendo.
int *pi_constchar=reinterpret_cast<int *>(new cchar[10*sizeof(int)]); // ERRO: mesmo reinterpret_cast não pode descartar o atributo const.
sint *psint_cint=const_cast<sint *>(new cint[5]); // OK: você diz explicitamente que está descartando o const.
int *pi_cchar=reinterpret_cast<int *>(const_cast<schar *>(new cchar[3*sizeof(int)])); // OK: primeiro descarta o const, depois reinterpreta o ponteiro.
float *pf_cint=(float *)new cint[4]; // RUIM: Estilo C, não confere const nem compatibilidade de tipos.

Só que, no fim das contas, você possivelmente não vá usar nada disso no dia-a-dia em C++. A biblioteca padrão do C++ inclui templates de classes para arrays, vetores, listas, conjuntos ordenados ou não e com repetições no não, mapas, filas, pilhas etc. Esses containers resolvem a maioria das necessidades comuns de alocação dinâmica de dados.
std::vector<int> vi;  // Declara um vetor de inteiros (inicialmente vazio).
for(int i=0; i<10; i++)
vi.push_back(i); // Acrescenta dez elementos ao vetor, mudando dinamicamente seu tamanho.

std::list<std::string> lista_nomes{"Athos", "Portos", "Dartagnan"}; // Declara lista duplamente encadeada e insere valores iniciais.
auto terceiro_da_lista=lista_nomes.find("Dartagnan");
lista_nomes.insert(terceiro_da_lista, "Aramis"); // Insere elemento no meio da lista (Dartagnan agora é o quarto).

std::map<std::string, unsigned> nome_matricula;
nome_matricula["Paulo"]=1;
std::cout << "A matricula de Paulo tem o nº " << nome_matricula["Paulo"] << '\n';


P.2) Qual a melhor maneira de informar o tamanho de cada elemento a ser alocado?

R.2) A melhor maneira é deixar o compilador calcular para você o máximo de informações que ele puder, porque isso reduz o esforço de manutenção.

Veja o extrato de código abaixo (em C).
unisgned n_open_fds;  // Número de arquivos/sockets com acessos simultâneos abertos.
struct pollfd *pollable_fds; // Array dinâmico de descritores a serem testados com a função poll().
void *new_ptr; // Um “void *” que eu vou usar para coisas temporárias.

/* ... */

/* Alocação inicial. */
pollable_fds=malloc(n_open_fds * sizeof(struct pollfd)); // Repare que eu não uso cast explícito.

/* ... */

/* Apareceu um novo descritor para ser adicionado ao array. */
n_open_fds++;
new_ptr=realloc(pollable_fds, (n_open_fds+1) * sizeof(struct pollfd);
if(new_prt!=NULL){ // Verifica se realocação foi bem sucedida.
pollable_fds=new_ptr;
pollable_fds[n_open_fds++]=novo_fd;
}

/* ... */

/* Um descritor chegou ao fim dos dados. Tenho de removê-lo do array. */
unsigned n=0;
while(n<n_open_fds && pollable_fds[n].fd!=fd_encerrado)
n++;
if(n!=n_open_fds-1){
/* Se não for o último descritor do array, troco-o de lugar com o último. */
struct pollfd temp_pfd=pollable_fds[n];
pollable_fds[n]=pollable_fds[n_open_fds-1];
pollable_fds[n_open_fds-1]=temp_pfd;
}
new_ptr=realloc((n_open_fds-1) * sizeof(struct pollfd));
if(new_prt!=NULL){ // Verifica se realocação foi bem sucedida.
pollable_fds=new_ptr;
n_open_fds--;
}

Reparou em quantos pontos diferentes do programa eu amarrei manualmente o nome pollable_fds ao tipo struct pollfd? Em cinco (!) lugares diferentes, que são a declaração, a alocação inicial, a realocação quando surge um novo descritor, a troca do n-ésimo elemento com o último, e a realocação quando um descritor tem sua atividade encerrada.

Agora suponha o caso de eu querer trocar o uso de poll() pela interface epoll do Linux. Eu não vou ter mais um array de descritores (struct pollfd), mas terei de ter um array de eventos (struct epoll_event), e esse array de eventos pode ter um ciclo de vida parecido com o mostrado acima, mas com um tipo de dados diferente.

Isso significa que dá para reaproveitar quase todo o código, certo? Bastaria adaptar as chamadas de função e trocar os tipos de alguns dados, não é?

Sim, seria mais ou menos isso. Mas veja que, só de amarrações manuais de tipo, uma única mudança vai implicar dar manutenção em cinco pontos diferentes do programa. Se eu tivesse deixado o compilador inferir os tamanhos dos registros em cada (re)alocação, usando o elemento apontado como operando de sizeof em vez do nome do tipo, possivelmente reduziria o número de alterações a apenas duas, das duas declarações (ou a apenas uma, da primeira declaração, se eu usar memmove() na hora de trocar elementos do array, em lugar de atribuição).


MORAL DA HISTÓRIA

Seu professor ensinou coisa errada?

Não. O que ele mostrou está certo, pelo menos do ponto de vista sintático e funcional. Só que a maneira como eu sugeri a você fazer simplifica a sua vida, no que diz respeito à manutenção de código. E pode acreditar que, se você seguir carreira em programação, dar manutenção em código -- ainda mais com ponteiros -- é possivelmente muito mais trabalhoso do que criar código novo.






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts