• Revista PROGRAMAR: Já está disponível a edição #53 da revista programar. Faz já o download aqui!

Marfig

Wiki

14 mensagens neste tópico

Hmm... existem alguns problemas com o artigo da secção cpp (artigo "Ponteiros vs. Referências").

Não sei bem onde discutir isto uma vez que o wiki não disponibiliza uma área de discussão para cada artigo...

EDIT: Ah. Burro. Só agora vi o forum para discutir o Wiki. Se algum mod não se importar de mover esta thread... obrigado.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Viva, o fórm está um pouco desorganizado na zona do wiki com tanto quadro e subquadro pelo que muitas vezes algumas mensagens passam despercebidas. Vou entrar em contacto com o skin e falar com os restantes moderadores e ver se há algum impedimento em que se reduza o numero de quadros desta secção.

Seguindo para a discussão sobre o artigo em si, força!

Diz aí de tua justiça.

Aproveito para relembrar que o wiki tem um sistema de controlo de versões ao nível de cada página. O que significa que se encontrarem erros odem corrigi-los podendo as revisões antigas ser ainda consultadas.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Bom... o problema está maioritariamente na parte final do artigo. Não consigo perceber a referência a "array de caracteres".

Lê-se:

Para uma boa regra nada como uma boa excepção. Os arrays de caracteres, típicos char* são causa de alguns problemas se não forem correctamente compreendidos.

char* str1 = "string\0";

Em primeiro lugar os arrays de caracteres são do tipo char[] e não char*. A distinção é importante porque para todos os efeitos um array na realidade é convertido para um apontador para o seu primeiro elemento quando estamos a usar o seu nome. Assim no exemplo acima, estamos a criar um array e str1 aponta para o primeiro elemento. str1 não é um array, mas um apontador.

Não é possível, por exemplo inicializar char* str1 = { 'C', 'h', 'a', 'r', '\0' }

Em segundo lugar arrays do tipo char podem ser criados de 3 formas:

char str1[] = { 'C', 'h', 'a', 'r' }; //cuidado. Válido. Mas não pode ser utilizado como string literal.
char str2[] = { 'C', 'h', 'a', 'r', '\0' };
char str3[] = "Char"; // o null é adicionado automaticamente

Portanto no exemplo dado no wiki, o array conterá na realidade dois \0, uma vez que quando se inicializa um array do tipo char com double-quotes o terminador null é adicionado automaticamente.

Em terceiro lugar não consigo entender como é que isto se aplica à informação que está  ser dada acima. A não ser que surja por causa da confusão entre char[] e char*. Acontece que o que foi criado em memória (no stack) foi um array. Não se faz delete de um array a não ser que ele seja criado no heap. O apontador str1 está a apontar para o stack. A string não foi colocada no ponteiro como é dito no texto. Na realidade a única coisa que qualquer ponteiro "contém" é o endereço de memória para o qual está a apontar.

...

Finalmente...

Confesso-me dividido entre aceitar o restante do artigo como válido ou sugerir que seja completamente reescrito. A informação não está propriamente errada (ao contrário da parte que falei acima). É no entanto bastante insuficiente e confusa a meu ver. Não é realmente nada disto que define as reais diferenças entre Ponteiros e Referências. Ou melhor, também é mas é apenas uma gota de água...

Um exemplo de como me parece que o artigo é insuficiente está aqui:

Alguns programadores referem que retornam sempre ponteiros porque preferem trabalhar com ponteiros, sendo estes com maior potencial em relação a referências. Uma coisa não impede a outra e é péssima prática porque pode muito bem guardar uma referência num ponteiro se tal for necessário.

Bom... mas porquê que eu quero fazer isso? É essa a questão que não está a ser respondida realmente em lado nenhum. E é uma questão fundamental no desenvolvimento em C++. Particularmente sensível porque muitos dos programadores que se iniciam no C++, vêm do C onde as Referências não existem e têm dificuldades em percebê-las.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites
O apontador str1 está a apontar para o stack.

Não sei como é em C++, mas em C as strings constantes costumam ir para a secção .rodata, o que até costuma dar segfault se as tentarmos modificar em certos SOs...

Em C++, se não estiver no .rodata provavelmente é inicializada antes de a main() ser chamada, e nesse caso deve ficar no heap. Sei que o código de inicialização do C++ chama uma série de construtores para variaveis globais, mas não sei se para as strings constantes também faz isso (não me parece).

JJ

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

É mais ou menos a mesma coisa em C++. O standard não específica onde as string literals devem ser colocadas. Podem estar em qualquer lado. Já o tipo passa então a ser const char[] (ou const wchar_t[] se for wide) e não char[] pelo que qualquer tentativa de alteração faz como dizes.  Mas é na realidade também Undefined e o compilador é livre, se quiser, de não delegar a decisão ao SO. Que eu saiba, nenhum o faz e preferem deixar o SO decidir, o que portanto resulta no segfault.

É o problemas das literal strings e porque se deve evitar C-Style strings em C++ e usar em vez disso a class String do STL... Basicamente o código em baixo funciona. Mas é na realidade undefined e uma fonte inesgotável de problemas.

char* str1 = "Olá!";   
str1[2] = 'é'; //oops! a string literal é const char[]

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Marfig, se achares que podes acrescentar algo útil no artigo referido, já o podes fazer, pois agora existe a possibilidade de edição por todos os utilizadores registados, em todos os namespaces, como é anunciado aqui:thumbsup:

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Obrigado Anolsi. Vou fazer isso amanhã. Pelo menos retirar o texto relativo à parte final do artigo. Quanto ao restante, eventualmente reescreverei tudo. Mas isso já fica para o decorrer da semana.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Venho também aqui alertar para um artigo da wiki sobre C: http://wiki.portugal-a-programar.org/c:scanfparastrings que foi tirado de um tutorial aqui do fórum: http://www.portugal-a-programar.pt/index.php?showtopic=10549

É indicado um modo compacto de limpar o buffer que não funciona: scanf("%*[^\n]%*c");

No entanto, já foram sugeridas alternativas pelo Marfig na thread do tutorial.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Epá, tenho me esquecido de alterar o artigo que deu início a esta thread.

Prometo que amanhã faço isso.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites
É mais ou menos a mesma coisa em C++. O standard não específica onde as string literals devem ser colocadas. Podem estar em qualquer lado.

Nem em C. Eu só queria dizer que as implementações costumam meter as strings em .rodata.

Já o tipo passa então a ser const char[] (ou const wchar_t[] se for wide) e não char[] pelo que qualquer tentativa de alteração faz como dizes.

O facto de dar segfault não tem nada a ver com o facto de ser const char[]...

Tem a ver, sim, com o facto de alguns SOs carregarem a secção .rodata numa área de memória não modificável. No entanto, isso não é de forma alguma obrigatório. Tal como acederes a uma zona de memória freeada() [ou deletada, já que estamos a falar de C++] não tem de dar necessariamente segfault (e normalmente não dá), aceder a uma const char[] pode não te dar segfault.

É o problemas das literal strings e porque se deve evitar C-Style strings em C++ e usar em vez disso a class String do STL... Basicamente o código em baixo funciona. Mas é na realidade undefined e uma fonte inesgotável de problemas.

Isso já não sei... A STL não é uma biblioteca de templates? Então porque é que tem lá uma classe para strings?

JJ

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Isso já não sei... A STL não é uma biblioteca de templates? Então porque é que tem lá uma classe para strings?

JJ

Perguntas bem, para dizer a verdade nunca pensei no nome daquilo, de facto tem lá a palavra 'template' no meio.

Porquê não sei, mas aquilo tem basicamente uma implementação para as estruturas de dados típicas todas que praí andam. Pilhas, filas, árvores, listas, strings, e por aí fora.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Isso já não sei... A STL não é uma biblioteca de templates? Então porque é que tem lá uma classe para strings?

Não tem. Foi um lapso. Não estava a pensar no que estava a escrever. Deveria ter dito Standard Library.

O facto de dar segfault não tem nada a ver com o facto de ser const char[]...

Tem a ver, sim, com o facto de alguns SOs carregarem a secção .rodata numa área de memória não modificável.

Hmm... em todo o caso atenção que um segfault não está limitado apenas a memória read-only. Stack overflows, null pointers, buffer overflows e acesso a memória de outros processos, todos podem criar segfaults em sistemas como o Windows e Linux... este último não tenho bem a certeza em todos os casos...

Mas sim, sem dúvida que aqui se trata de memória não modificável. Mas é preciso tomar em conta que um sistema operativo precisa apenas de implementar protecção de memória a um nível mais elevado para também gerar segfaults. Afinal o segfault propriamente dito não é mais do que uma exception do SO, não do CPU. Este é o caso, por exemplo dos null pointers em Windows, onde

int* ptr = 0;

*ptr = 23;

gera um segfault porque o Windows faz mapeamento de null pointers e implementa protecção de memória a um nível mais elevado. Portanto mesmo sem o recurso à arquitectura (rodata e por aí fora) o sistema operativo pode implementar -- e implementa -- segfaults.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites
Hmm... em todo o caso atenção que um segfault não está limitado apenas a memória read-only. Stack overflows, null pointers, buffer overflows e acesso a memória de outros processos, todos podem criar segfaults em sistemas como o Windows e Linux... este último não tenho bem a certeza em todos os casos...

Os stack overflows e os buffer overflows podem não gerar segfaults. Só geram (partindo do princípio que estamos num sistema operativo moderno, com paginação e tal), se por acaso tocarem numa área não mapeada, ou se escreverem numa área só de leitura, ou se levarem o processador a "saltar" (fazer branch) para uma área de memória não executável (isso é possível nos processadores mais recentes).

Em Unix, este tipo de erros são reportados ao programa com o SIGSEGV. Se esse "sinal" não foi capturado (por acaso, não sei se pode ser ou não), o programa é terminado com uma mansagem de erro e faz-se um core dump.

Em windows, ideia é a mesma (o programa é terminado se acede a uma área de memória que não devia).

> e acesso a memória de outros processos

A memória dos processos está isolada nos sistema operativos modernos, e cada um só se "vê" a si e ao kernel.

Mas sim, sem dúvida que aqui se trata de memória não modificável. Mas é preciso tomar em conta que um sistema operativo precisa apenas de implementar protecção de memória a um nível mais elevado para também gerar segfaults. Afinal o segfault propriamente dito não é mais do que uma exception do SO, não do CPU. Este é o caso, por exemplo dos null pointers em Windows, onde

int* ptr = 0;

*ptr = 23;

gera um segfault porque o Windows faz mapeamento de null pointers e implementa protecção de memória a um nível mais elevado. Portanto mesmo sem o recurso à arquitectura (rodata e por aí fora) o sistema operativo pode implementar -- e implementa -- segfaults.

Esse tipo de "monitorização" do programa precisa sempre da ajuda da CPU. Porquê? Porque o programa é deixado a executar, e se um programa faz alguma "asneira", só o CPU pode notificar o kernel.

Mas sim: tens razão quando dizes que os segfaults são de "alto nivel". Quero dizer que é só um nome que o kenrel dá a uma série de erros... No fundo é a versão unixiana do "este programa executou uma operação ilegal e será encerrado".

JJ

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!


Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.


Entrar Agora