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

Bruno_F

Problema com o compilador... I guess

18 mensagens neste tópico

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  :ipool:).

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

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Dá um valor estranho porque estás a tentar mostrar o endereço da posição de memória da variável d.

Tens de meter o printf sem o &.

printf("%d", d);

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

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

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

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Para copiar uma string tens de usar o strcpy.

No teu caso ficaria:

strcpy(ampm, "PM");

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros 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.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros 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...

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

A segurança assenta grande parte das vezes em compromissos de usabilidade e performance. Escolhe as tuas prioridades (Não deixas de ter a tua quota-parte de razão, atenção! ;-)

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros 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. :)

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros 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).

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros 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 :-)

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Só não percebi isto

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

que cena marada

o que é o %08x  :wallbash:

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

faz print do valor alinhado a 8 dígitos hexadecimais, neste caso, faz print de um endereço 32 bit em hexadecimal.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

faz print do valor alinhado a 8 dígitos hexadecimais, neste caso, faz print de um endereço 32 bit em hexadecimal.

Já agora, é mais correcto usar o especificador %p para a escrita de endereços de ponteiros.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros 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. :)

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros 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.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros 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

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros 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 :D

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