alphasil Posted June 8, 2012 at 09:20 PM Report #461554 Posted June 8, 2012 at 09:20 PM Olá ppl; Depois de uns dias para preparar um exame de SO estou de volta para continuar o meu calvário de C 😛 Estou a seguir o manual online que o Bscara me aconselhou e surgiu-me aqui uma dúvida, tenho de fazer o seguinte: Faça um programa que leia quatro palavras pelo teclado, e armazene cada palavra em uma string. Depois, concatene todas as strings lidas numa única string. Por fim apresente esta como resultado ao final do programa. Fiz assim: #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { int tam; char p1[40], p2 [40], p3[40], p4[40], pfinal[40]; printf("Introduza uma palavra!\n"); gets(p1); printf("Introduza uma palavra!\n"); gets(p2); printf("Introduza uma palavra!\n"); gets(p3); printf("Introduza uma palavra!\n"); gets(p4); strcat(p2, p1); strcat(p3, p2); strcat(p4, p3); strcat(pfinal, p4); tam= strlen(pfinal); printf("\n%s e tem o tamanho de %d bytes", pfinal, tam); return 0; } Funcionar funciona mas antes da string final aparecem-me uns carateres estarnhos, é normal? Obrigado gmc11
HappyHippyHippo Posted June 8, 2012 at 09:25 PM Report #461556 Posted June 8, 2012 at 09:25 PM (edited) é ... tu não inicializas a string pfinal, logo é de supor que tem lixo .. e como nunca atribuis nada a ela .. é esse lixo que aparece é boa prática inicializar todas as variáveis antes de começar a trabalhar com elas no caso dos arrays tens (isto para todos os arrays): memset(p1, 0, 40); ps : não uses o gets ... altera esse tipo de chamada por fgets(p1, 40, stdin); Edited June 8, 2012 at 09:27 PM by HappyHippyHippo IRC : sim, é algo que ainda existe >> #p@p Portugol Plus
alphasil Posted June 8, 2012 at 09:35 PM Author Report #461558 Posted June 8, 2012 at 09:35 PM (edited) Pois, mas o problema é que manda usar o gets A função gets() lê uma string do teclado. Sua forma geral é: gets (nome_da_string) As strings que escrevi aparecem todas juntas, só que antes aparecem esses carateres estranhos, vou modificar como disseste.. Ficou assim #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { int tam; char p1[40], p2 [40], p3[40], p4[40], pfinal[40]; memset(p1, 0, 40); memset(p2, 0, 40); memset(p3, 0, 40); memset(p4, 0, 40); memset(pfinal, 0, 40); printf("Introduza uma palavra!\n"); fgets(p1,40, stdin); printf("Introduza uma palavra!\n"); fgets(p2,40, stdin); printf("Introduza uma palavra!\n"); fgets(p3,40, stdin); printf("Introduza uma palavra!\n"); fgets(p4,40, stdin); strcat(p2, p1); strcat(p3, p2); strcat(p4, p3); strcat(pfinal, p4); tam= strlen(pfinal); printf("\n%s e tem o tamanho de %d bytes", pfinal, tam); return 0; } Mas agora cada uma das strings vem numa linha e o objetivo era concatenar (pelos vistos estão porque está a ler a variável pfinal), certo? Obrigado Edited June 8, 2012 at 09:41 PM by alphasil gmc11
HappyHippyHippo Posted June 8, 2012 at 09:37 PM Report #461560 Posted June 8, 2012 at 09:37 PM a função fgets também lê do teclado e é mais segura se estás a seguir um tutorial, não estás a fazer nenhum trabalho para a escola, por isso não necessitas de seguir as normas de um professor. faz o melhor e usa fgets IRC : sim, é algo que ainda existe >> #p@p Portugol Plus
alphasil Posted June 8, 2012 at 10:11 PM Author Report #461567 Posted June 8, 2012 at 10:11 PM Ok Muito obrigado pelas dicas 🙂 gmc11
HappyHippyHippo Posted June 8, 2012 at 10:18 PM Report #461569 Posted June 8, 2012 at 10:18 PM só uma nota : tem cuidado porque a função strcat supõe que o primeiro argumento tem tamanho suficiente para guardar o resultado eu nunca tentei fazer uma concatenação para dar erro por isso não sei o resultado que pode dar, mas podes sempre verificar se o resultado da função é válido if (strcat(p1, p2) != p1) { // erro !!! } IRC : sim, é algo que ainda existe >> #p@p Portugol Plus
bsccara Posted June 9, 2012 at 01:04 AM Report #461585 Posted June 9, 2012 at 01:04 AM mas podes sempre verificar se o resultado da função é válido if (strcat(p1, p2) != p1) { // erro !!! } Não, não pode. A função strcat retorna sempre o primeiro parâmetro, independentemente do que a função fizer. A má utilização desta função é uma das causas dos famosos 'buffer overflow'. Dependendo da alocação da memória apontada pelo primeiro parâmetro pode alterar outros blocos de memória alocada dinamicamente, alterar o valor de variáveis locais à função ou corromper a pilha do CPU, levando a estouros na saída da função. Deve-se ter extremo cuidado com a utilização desta e de outras funções como a strcpy pois erros na sua utilização causam bugs muito difíceis de localizar.
HappyHippyHippo Posted June 9, 2012 at 08:01 AM Report #461592 Posted June 9, 2012 at 08:01 AM (edited) Não, não pode. A função strcat retorna sempre o primeiro parâmetro, independentemente do que a função fizer. A má utilização desta função é uma das causas dos famosos 'buffer overflow'. Dependendo da alocação da memória apontada pelo primeiro parâmetro pode alterar outros blocos de memória alocada dinamicamente, alterar o valor de variáveis locais à função ou corromper a pilha do CPU, levando a estouros na saída da função. Deve-se ter extremo cuidado com a utilização desta e de outras funções como a strcpy pois erros na sua utilização causam bugs muito difíceis de localizar. desculpa ... é o que dá responder em cima dos joelhos Edited June 9, 2012 at 08:02 AM by HappyHippyHippo IRC : sim, é algo que ainda existe >> #p@p Portugol Plus
mundo Posted June 12, 2012 at 09:34 AM Report #462114 Posted June 12, 2012 at 09:34 AM Pois, mas o problema é que manda usar o gets A função gets() lê uma string do teclado. Sua forma geral é: gets (nome_da_string) As strings que escrevi aparecem todas juntas, só que antes aparecem esses carateres estranhos, vou modificar como disseste.. Ficou assim #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { int tam; char p1[40], p2 [40], p3[40], p4[40], pfinal[40]; memset(p1, 0, 40); memset(p2, 0, 40); memset(p3, 0, 40); memset(p4, 0, 40); memset(pfinal, 0, 40); printf("Introduza uma palavra!\n"); fgets(p1,40, stdin); printf("Introduza uma palavra!\n"); fgets(p2,40, stdin); printf("Introduza uma palavra!\n"); fgets(p3,40, stdin); printf("Introduza uma palavra!\n"); fgets(p4,40, stdin); strcat(p2, p1); strcat(p3, p2); strcat(p4, p3); strcat(pfinal, p4); tam= strlen(pfinal); printf("\n%s e tem o tamanho de %d bytes", pfinal, tam); return 0; } Mas agora cada uma das strings vem numa linha e o objetivo era concatenar (pelos vistos estão porque está a ler a variável pfinal), certo? Obrigado O que acontece é que o fgets, coloca o '\n' no final de cada string, tens que os retirar.
dardevelin Posted June 12, 2012 at 04:44 PM Report #462308 Posted June 12, 2012 at 04:44 PM (edited) Boas, existem vários pontos que quero abordar nesta questão, nomeadamente más praticas, e erros, contudo acho importante responder a questão primeiro e só depois prosseguir com os pontos seguintes. Primeiro o Erro! strcat(pfinal, p4); Vamos analisar a função passo a passo. //prototipo da função char *strcat(char *restrict s1, const char *restrict s2); segundo a sua descrição no standard (numa tradução solta), strcat ira concatenar a string apontada por s2, à string apontada por s1 ( incluindo o "terminador" null byte ou seja '\0' ). O byte inicial da string s2 ira apagar o terminador do final da s1 escrevendo na posição do mesmo o byte inicial apontado por s2. Se a copia ocorrer entre os objetos que se sobrepõem, o comportamento é indefinido. O que isto significa é que antes de concatenar a p4 a pfinal, a função ira fazer uma "pesquisa" pelo terminador, e só nessa altura ira copiar p4. do mesmo. Uma vez que pfinal não foi inicializado, a localização/existência do terminador é indeterminada. Portanto os caracteres "estranhos" que aparecem antes da string concatenada são o resultado de, "lixo" existente antes de um terminador. Agora os outros pontos que gostaria de abordar. Não sei se reparaste mas o teu programa, iria resultar em frases ao contrario. imaginemos o seguinte input, Nós Estamos Em Junho fazendo o mapa do input para as variáveis, p1 = Nós p2 = Estamos p3 = Em p4 = Junho agora vamos ver as concatenações: strcat(p2, p1); // ou seja p2 agora é igual a -> Estamos Nós strcat(p3, p2); //ou seja p3 agora é igual a -> Em Estamos Nós strcat(p4, p3); //ou seja p4 agora é igual a -> Junho Em Estamos Nós //finalmente o pfinal strcat(pfinal, p4); //ou seja pfinal agora é igual a -> lixo existente até um terminador + Junho Em Estamos Nós /* Presumo que o objectivo do programa seria Construir uma string concatenada com base no input mas coerente. */ Próximo ponto, o HappyHippyHippo parece estar muito disponível para ajudar, contudo, na minha visão dizer apenas troca X por Z porque é alegadamente mais segura, não é solução para "ajudar" alguém à aprender. Por isso, descrevo aqui o problema de gets, gets, para começar é uma função que foi removida no ultimo standard, por isso devo desde já dizer que é bom conhecer a sua existência, mas o seu uso deve ser limitado para fins académicos, como sempre o melhor é analisar a função em maior detalhe. char *gets(char *s); Uma tradução "livre" do standard seria: gets () é uma função que lê os bytes da entrada stdin , para a array apontada por s, até que o carácter '\n' seja lido ou uma condição de fim-de-arquivo 'EOF' seja encontrada. Qualquer (nova_linha '\n') deve ser descartada e um (byte nulo '\0') deve ser colocado imediatamente após o último byte lido na array. Como é possível observar não existe nenhuma menção em tamanho desta leitura. Contudo visto que em C as arrays não são "naturalmente" dinâmicas, ou seja não "crescem" conforme necessário automaticamente os programadores têm de ter cuidado para não ler mais dados do que o espaço que têm disponível, visto que se o fizerem o comportamento do programa seria indefinido. Ora como o programador não têm controlo sobre o input a ser feito no programa, não pode precaver e reservar espaço suficiente. Aqui entra a função fgets , esta função, permite fazer uma leitura "limitada" por tamanho invés de uma leitura por localização de uma nova linha ou terminador ou EOF vejamos o prototipo char *fgets(char *restrict s, int n, FILE *restrict stream); sem entrar em muitos detalhes, a função ira ler n bytes do stream, e armazena-los em s Sendo assim, é garantido não ultrapassar a leitura de n bytes, o que permite então com que o programador conte com o tamanho máximo suportado por s; Outros Erros, Agora em respostas fornecidas. fgets não adiciona o carácter identificador de nova linha , fgets limita-se a fazer uma leitura de n bytes que por consequência pode ser maior do que o input fornecido. O input via stdin por norma esta associado ao teclado, o que significa que, quando o input foi introduzido, "Enter" foi pressionado, para que este fosse aceite. Logo sendo este "Enter" à adicionar o carácter identificador de nova linha. nota: fgets adiciona um terminador '\0' no final da leitura Edited June 12, 2012 at 04:54 PM by dardevelin
HappyHippyHippo Posted June 12, 2012 at 04:53 PM Report #462312 Posted June 12, 2012 at 04:53 PM Próximo ponto, o HappyHippyHippo parece estar muito disponível para ajudar, contudo, na minha visão dizer apenas troca X por Z porque é alegadamente mais segura, não é solução para "ajudar" alguém à aprender. sabes quantas vezes já foi descrito aqui no forum o porque do uso do fgets em vez do gets ? fgets não adiciona o carácter identificador de nova linha ... Logo sendo este "Enter" à adicionar o carácter identificador de nova linha. É uma questão de português. Ele realmente adiciona o caracter porque ele está lá no buffer. Quando é referido que o fgets adiciona o caracter, é relativo à diferença entre a função fgets e a gets IRC : sim, é algo que ainda existe >> #p@p Portugol Plus
dardevelin Posted June 12, 2012 at 05:24 PM Report #462322 Posted June 12, 2012 at 05:24 PM (edited) Decidi abordar os restantes pontos relativos a eficiência da aplicação em outro post, visto que o anterior já estava a tornar-se demasiado grande e complicado de gerir a escrita no mesmo. o uso da função memset é bom, contudo para este caso em particular completamente desnecessário, visto que apenas interessa garantir que os dados irão ser escritos desde o inicio da array, e que um terminador seja introduzido no final da string. logo iniciar o primeiro elemento com o terminador seria mais que suficiente neste caso. p1[0]='\0'; p2[0]='\0'; p3[0]='\0'; p4[0]='\0'; Outro Ponto sobre strcat Esta função não toma nenhuma medida de prevenção de overflow( passar o tamanho máximo ) da array. portanto, o teu programa poderá ter um comportamento indefinido com base no input fornecido ao mesmo, visto que se o mesmo for grande o suficiente poderão ser concatenados mais dados do que aqueles que podem armazenados efectivamente. logo um uso correcto da mesma passaria por fazer o seguinte //o máximo que p1, p2, p3, p4 podem armazenar é 40 logo //criamos é uma constante //size_t porque strlen retorna size_t e não int size_t tamanho_s1 = 0; size_t tamanho_tmp = 0; //antes de concatenar as strings tamanho_p1 = strlen(p1); tamanho_tmp = strlen(p2); if( ( tamanho_s1 + tamanho_tmp ) < 40 ) { //quer dizer que há espaço suficiente tamanho_p1 += tamanho_tmp; //o novo tamanho é igual a soma de ambas strings strcat(p1, p2); } else { printf("Erro, p1 não tem espaço suficiente"); exit(1); // sai do programa } tamanho_tmp = strlen(p3); if( (tamanho_s1 + tamanho_tmp) < 40 ) { tamanho_p1 += tamanho_tmp; strcat(p1, p3); } else { printf("Error, p1 não tem espaço suficiente"); exit(1); } // e assim sucessivamente. contudo isto leva nos a outra questão de eficiência. strcat "procura" pelo terminador, contudo se para usar de forma segura strcat temos que saber o tamanho da string em ante mão, já sabemos onde esta o terminador ora, vai estar na posição tamanho_p1 + 1; logo, podemos tentar optimizar um pouco e levar a função strcat "pensar" que esta próxima do fim da array exemplo // com todos os procedimentos de segurança anteriores strcat( &p1[tamanho_p1], p2); // este método faz com que strcat entre já no fim de p1, logo encontrando o terminador // muito mais rápido do que se tivesse que o procurar E por fico aqui por agora. Espero ter ajudado, comprimentos dbs sabes quantas vezes já foi descrito aqui no forum o porque do uso do fgets em vez do gets ? É uma questão de português. Ele realmente adiciona o caracter porque ele está lá no buffer. Quando é referido que o fgets adiciona o caracter, é relativo à diferença entre a função fgets e a gets Boas, se já foi descrita tantas vezes, não seria mais correcto indicar o post invés do método pelo qual optou ? Algo que já me deparei é que existem vários posts sobre os mesmos tópicos e ninguém pareceu importar-se muito em redireccionar para os antigos. Sou novo por aqui, e apenas estou a tentar dar a melhor ajuda que sei, da forma mais informativa possível. Quanto a questão do português tenho de discordar. Dizer que a função faz algo, implica que parte da sua funcionalidade impõe tal feito. Já dizer que a mesma em certa circunstancia faz algo, implica que mesmo não sendo parte da sua raiz (propósito) pode acontecer em certas circunstancias. Apenas digo isto porque mais tarde pode levar a sérios "bugs" no raciocínio e na construção de programas Edited June 12, 2012 at 05:28 PM by dardevelin
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now