Jump to content

Problema com o compilador... I guess


Bruno_F

Recommended Posts

Estou agora a começar a tirar a minha licenciatura em Eng. Informática.. e consequentemente estou a começar a aprender C (já tinha umas noções  ?).

O que se passa é que ao fazer os códigos no meu computador, usando o Dev-C++ 4, acontece que ao ler uma variável (p.e um numero inteiro) através de um scanf, ao imprimi-la no ecrã ela dá um numero completamente diferente.

Um exemplo:

#include <stdio.h>
#include <stdlib.h>

int main()
{
      int d;

      scanf("%d", &d);
      printf("%d", &d);
      system("PAUSE");
      return 0;
}

Neste (muito) simples programa, é suposto introduzir-se um num, e o programa reproduzi-lo de seguida. Só que ao corre-lo o numero que eu insiro não corresponde ao numero imprimido. Por exemplo ponho um 4 e aparece um 32894850.

Se me pudessem ajudar agradecia. Cumprimentos

Link to comment
Share on other sites

ok já dá... obrigado pela ajuda. Isto de ser noob a programação tem que se lhe diga.  ?

Já agora uma outra duvida diferente:

Num exercício tenho que fazer um programa que leia um horario em formato 24horas e o transforme num 12horas. P.e 21.40 fica 9.40 PM.

A solução que eu encontrei foi:

#include <stdio.h>
#include <stdlib.h>

int main()
{

      int hora;
      int hora2;
      int min;

      scanf("%d", &hora);
      scanf("%d", &min);
      if (hora>12)
      {
          hora2=hora-12;
          printf("São %d horas e %d minutos PM", hora2, min);
      }
      else if (hora==12)
      {
          hora2=hora;
          printf("São %d horas e %d minutos PM", hora2, min);
      }
      else if (hora==0)
      {
          hora2=12;
          printf("São %d horas e %d minutos AM", hora2, min);
      }
      else
      {
          hora2=hora;
          printf("São %d horas e %d minutos AM", hora2, min);
      }


      system("PAUSE");
      return 0;
}

O programa funciona... mas a minha ideia inicial era:

int main()
{

      int hora, hora2, min;
      char ampm[2];


      scanf("%d", &hora);
      scanf("%d", &min);
      if (hora>12)
      {
          hora2=hora-12;
          ampm= "PM"
      }
      else if (hora==12)
      {
          hora2=hora;
          ampm= "PM"
      }
      else if (hora==0)
      {
          hora2=12;
          ampm= "AM"
      }
      else
      {
          hora2=hora;
          ampm= "AM"
      }
      printf("São %d horas e %d minutos, %c", hora2, min, ampm);
  
      system("PAUSE");
      return 0;
}

Só que tive de abandonar esta ideia visto que ao compilar o programa dava erro. Depois de pesquisar percebi que era por causa dos ampm= "AM", já que não se pode atribuir assim valores a strigs. A minha dúvida é: haverá alguma maneira de atribuir um valor a uma string sem ser lida pelo teclado? Ou seja dizer que a string A é igual a "ABC", por exemplo?

Mais uma vez obrigado pela resposta, e desculpem as perguntas de noob  ?

PS: Ainda não dei vectores, e consequentemente não dei strings

Link to comment
Share on other sites

Para copiar uma string tens de usar o strcpy.

No teu caso ficaria:

strcpy(ampm, "PM");

Deve ser realçado que não é boa prática, utilizar o strcpy num array. Quanto muito deve-se utilizar o strncpy (ou strlcpy) para evitar falhas de segurança.

Neste caso, o uso do strcpy vai causar um problema, uma vez que a variável tem apenas 2 bytes e o strcpy termina sempre a string destino com um null ('\0'), o que significa que o array ampm[] vai passar a ter este tipo de dados em memória no caso de um strcpy(ampm, "PM"):

ampm[0] = 'P'

ampm[1] = 'M'

ampm[2] = '\0' 

Só que, o índice 2, não foi assegurado na definição do array porque esta foi definida com o tamanho de 2 bytes. O que acontece é que, a variável min do programa do bruno_f, vai ser subscrita no byte mais à direita (arquitecturas little endian) pelo \0 do strcpy, alterando o valor da mesma. No caso acima, não causa propriamente uma falha de segurança, uma vez que o valor "PM" está hard-coded, mas se o 2º argumento do strcpy fosse manipulável por um utilizador, este poderia ganhar controlo sobre o fluxo do programa! No entanto, pode decorrer inconsistência dos dados (especialmente os que estiverem relativos à variável min) e umas boas dores de cabeça quando o programa estoirar sem motivo aparente.

1 exemplo simples para ilustrar o cenário corrente:

int main()
{
        int n = 1;
        char foo[2];

        strcpy(foo, "PM");

        printf("%d %s\n", n, foo);


        printf("%08x (foo[2]) == %08x (n)\n", &foo[2], &n);

}

0 PM

bff22934 (foo[2]) == bff22934 (n)

Era esperado que a variável n fosse mostrada com o valor 1, mas o strcpy, ao escrever na variável foo (análoga à ampm do programa do bruno_f) guarda o \0 na memória subjacente, neste caso, como estou (e assumo que a maioria das pessoas neste forum também está) numa arquitectura little endian, em que a memória stack é gerida de cima para baixo (em termos de endereçamento) o \0 é escrito no endereço inferior, neste caso, na var seguinte (ou anterior, por definição -- para quem está a olhar para o código em top-down).

Se repararem, no fim, eu faço o print do endereço da variavel 'n' e do índice 2 do array foo. Alguns devem-se questionar como tou a aceder ao índice 2, se a variavel está definida com apenas 2 elementos no array.  Como a memória stack funciona como uma sequencia de bytes, aquele indice, aponta para o byte no endereço de memória inferior, neste caso, aponta para o 1º byte dos 4 bytes alocados pelo inteiro 'n'.

Espero que tenha sido útil, e espero não ter dito muita gralha... a estas horas já devia estar a dormir :-)

EDIT: Para melhor informação sobre como a memória stack funciona, sugere-se a leitura do artigo sobre o desenvolvimento de exploits na edição 6# da Revista Programar que, embora não tenha lido na totalidade, faz uma introdução à forma de como a memória é gerida nas arquitecturas x86 e mostra como se pode tirar partido de certos erros na gestão da mesma.

Link to comment
Share on other sites

Não é boa prática, não. O strncpy introduz uma perda de performance em relação à strcpy por causa de (pelo menos) mais um decremento e mais uma verificação. Pode parecer pouco, mas em programas em que a velocidade é crítica e a função utilizada repetidamente pode provocar lentidão na execução.

Se aconselhas a sua utilização, tudo bem, apoiado. Mas dizeres que não é boa prática penso que é exagerado.

Ena rimei...

Desaparecido.

Link to comment
Share on other sites

Não é boa prática quando o programador não aloca espaço suficiente para copiar a string para o array.

Eu uso sempre o strcpy, claro que ao primeiro cheguei a cometer erros como os que referiste mas acho que um bom programador de C tem de prevenir essas coisas, e até há coisas bem piores de gerir como deves saber. 🙂

"Nunca discutas com um idiota. Eles arrastam-te até ao seu nível e depois ganham-te em experiência"

Link to comment
Share on other sites

Isto está a entrar um pouco em off-topic, mas a questão aqui não é se se deve ou não usar o strcpy, é saber se o programador sabe o que está a fazer ou nem por isso.

Neste caso, aumentar o tamanho do array era suficiente, uma vez que o utilizador não tem qualquer hipótese de adulterar o que vai ser colocado lá.

Contudo, e fundamentalmente em leituras, o cuidado que temos que ter é enorme. Nós não sabemos quantos caracteres o leitor vai inserir. Nunca.

Logo, usar "scanf("%s",s);" é um erro enorme. Depois de ler a string de modo "correcto" (com fgets) o strcpy deixa de ser tão perigoso. Basta ter o cuidado de a string de entrada e a string de saída terem o mesmo tamanho (pode ser particularmente difícil se alocarmos dinamicamente a memória, mas isso é um caso especial).

Link to comment
Share on other sites

Não é boa prática quando o programador não aloca espaço suficiente para copiar a string para o array.

Eu uso sempre o strcpy, claro que ao primeiro cheguei a cometer erros como os que referiste mas acho que um bom programador de C tem de prevenir essas coisas, e até há coisas bem piores de gerir como deves saber. 🙂

Só fiz a nota porque, ao indicares a um utilizador para utilizar a função strcpy no corrente cenário, estavas a incitar más práticas de programação. Acredito que tenham sido causadas por falta de atenção porém, ainda assim, serviu-me como exercício de memória, pra ver até que ponto ainda me lembrava de C :-)

Link to comment
Share on other sites

Que conclusões conseguem tirar vendo o valor em hexadecimal ? :X

Os endereços de memória costumam-se representar em notação hexadecimal. E consegues perceber que &foo[2] == &n. 🙂

Atenção que isto não se verifica em todas as situações, por exemplo nesta máquina os valores não coincidem.

1 PM

0xbf9ed754 (foo[2]) == 0xbf9ed74c (n)

Aborted (core dumped)

Mas o que o garmg disse continua a estar correcto, é preciso ter cuidado com o que se faz. 🙂

<3 life

Link to comment
Share on other sites

Que conclusões conseguem tirar vendo o valor em hexadecimal ? :X

Os endereços de memória costumam-se representar em notação hexadecimal. E consegues perceber que &foo[2] == &n. 🙂

Atenção que isto não se verifica em todas as situações, por exemplo nesta máquina os valores não coincidem.

1 PM

0xbf9ed754 (foo[2]) == 0xbf9ed74c (n)

Aborted (core dumped)

Mas o que o garmg, é preciso ter cuidado com o que se faz. 🙂

Isso deve-se à forma como o gcc 4.x ou eventuais patches de segurança que alteram o alinhamento das variaveis na stack na compilação. Eu testei aquele exemplo acima no gcc do RHEL 4. No RHEL5 os valores diferem da mesma forma.

Aqui têm mais alguma informação que ajuda a entender a parte do alinhamento da stack e como funcionam as protecções contra stack overflows que  compiladores adicionam ao binário final:

http://www.phrack.org/issues.html?issue=56&id=5

EDIT: Quanto às conclusões que se podem tirar: identificar se é um endereço de stack ou heap/bss.

Link to comment
Share on other sites

Mais uma vez obrigado pelas ajudas e esclarecimentos adicionais... o programa já funciona da maneira que eu quero.  🙂 :)

Quanto ao problema levantado pelo garmg, também me deparei com o facto de os minutos estarem a dar valores indevidos, por isso declarei a variável ampm como uma array char[3], não sei se é a solução mais indicada mas para já tem funcionado.

Cumps

Link to comment
Share on other sites

É exactamente o indicado 😉

Queres guardar 2 caracteres -> alocas 3 (ou mais) posições no array.

Mas não esqueças toda a conversa sobre segurança do código que o teu post originou! Quando não tiveres a certeza da dimensão da string que vais copiar, utiliza o strncpy. Claro que, como bom programador, o teu trabalho é descobrir o tamanho 😄

Desaparecido.

Link to comment
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.