L'hexadécimal en C

Bonjour à tous

Je me mets enfin sérieusement à l’étude du C à l’aide de l’ouvrage de référence qu’il n’est plus la peine de présenter, “Le langage C” de Ritchie et Kernighan.

Voici un programme que j’ai écris :

[code]#include <stdio.h>
#include <stdlib.h>

int main()
{
printf("%f %o %x", 123.45, 9, 17.3);

return 0;

}[/code]

Lorsque je l’exécute une première fois, j’obtiens :

Et la seconde fois :

Bien évidemment j’obtiens quelque chose d’incohérent pour le 3ème argument. Mais j’aimerais comprendre pourquoi j’obtiens une valeur aléatoire ? D’où provient-elle ? Est-ce que cela pourrait introduire une faille de sécurité ?

Merci à vous

Au hasard ça doit venir de la représentation des flottants, mais chez moi ça fait le même résultat (apparement)

Compile en activant les avertissements et tu comprendras ton erreur

gcc main.c -Wall -Wextra -pedantic main.c: In function ‘main’: main.c:6:5: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 4 has type ‘double’ [-Wformat]

Oui je l’ai fait et je m’attendais avant même de compiler le programme à avoir au moins un avertissement, si ce n’est une erreur.

Ce que j’aimerais comprendre ce n’est pas pourquoi j’ai cette erreur (ça je le sais) mais comment ça fonctionne derrière. D’où vient cette valeur exactement ? Pourquoi j’ai une valeur aléatoire plutôt qu’une valeur fixe par exemple ?

je fait pas du C , mai du C++, mai en principe on initialise une variable et son type

en gros:
int M;
est different de
int M = 0;

car le premier contien ce qu’il y a en memoire, l’autre y a déjà écrit une valeur.
vu que tu écris directement dans le printf je sai pas si la déclaration ce passe de la meme manière

J’ai toujours la même sortie sur mon ordi (gcc 4.7) mais sur un autre ordi (gcc 4.6) j’ai effectivement des valeurs qui changent.

Soit c’est un problème d’arrondi (ça me paraît bizzare) soit c’est un problème de valeur non initialisée (ça me paraît bizzare aussi) soit … ?

Tu peux toujours regarder le code assembleur:
gcc 4.7

$ gdb -q a.out Reading symbols from /media/Unstable/a.out...done. (gdb) disas main Dump of assembler code for function main: 0x000000000040050c <+0>: push %rbp 0x000000000040050d <+1>: mov %rsp,%rbp 0x0000000000400510 <+4>: sub $0x10,%rsp 0x0000000000400514 <+8>: movabs $0x40314ccccccccccd,%rdx 0x000000000040051e <+18>: movabs $0x405edccccccccccd,%rax 0x0000000000400528 <+28>: mov %rdx,-0x8(%rbp) 0x000000000040052c <+32>: movsd -0x8(%rbp),%xmm1 0x0000000000400531 <+37>: mov $0x9,%esi 0x0000000000400536 <+42>: mov %rax,-0x8(%rbp) 0x000000000040053a <+46>: movsd -0x8(%rbp),%xmm0 0x000000000040053f <+51>: mov $0x40060c,%edi 0x0000000000400544 <+56>: mov $0x2,%eax 0x0000000000400549 <+61>: callq 0x4003e0 <printf@plt> 0x000000000040054e <+66>: mov $0x0,%eax 0x0000000000400553 <+71>: leaveq 0x0000000000400554 <+72>: retq End of assembler dump.
gcc 4.6

$ gdb -q a.out Reading symbols from /home/eo/a.out...done. (gdb) disas No frame selected. (gdb) disas main Dump of assembler code for function main: 0x00000000004004e4 <+0>: push %rbp 0x00000000004004e5 <+1>: mov %rsp,%rbp 0x00000000004004e8 <+4>: mov $0x400610,%eax 0x00000000004004ed <+9>: movsd 0x12b(%rip),%xmm1 # 0x400620 0x00000000004004f5 <+17>: movsd 0x12b(%rip),%xmm0 # 0x400628 0x00000000004004fd <+25>: mov $0x9,%esi 0x0000000000400502 <+30>: mov %rax,%rdi 0x0000000000400505 <+33>: mov $0x2,%eax 0x000000000040050a <+38>: callq 0x4003e0 <printf@plt> 0x000000000040050f <+43>: mov $0x0,%eax 0x0000000000400514 <+48>: pop %rbp 0x0000000000400515 <+49>: retq End of assembler dump.

PS: si tu veux une erreur

$ gcc main.c -Wall -Wextra -pedantic -Werror main.c: In function ‘main’: main.c:6:5: error: format ‘%x’ expects argument of type ‘unsigned int’, but argument 4 has type ‘double’ [-Werror=format] cc1: all warnings being treated as errors

[quote]Tu peux toujours regarder le code assembleur

$ gdb -q a.out[/quote]
Voilà qui devrait m’aider pour beaucoup de choses à l’avenir !!
Merci pour cette aide, ça devrait me permettre de comprendre comment ça fonctionne :023

(C n’est pas fortement typé, mais) peut être qu’un cast explicite donne quelque chose de “correct”.

Salut,

Tu as effectué le test sur une machine de la famille amd64?
Si oui, c’est normal, cela est dû à la convention d’appel utilisée sur ce type d’architecture. Sans trop entrer dans les détails, il faut savoir que sous une architecture amd64, les arguments sont majoritairement passés via les registres et non via la pile. Dans le cas d’une fonction à nombre variable d’argument (comme printf), l’ensemble des valeurs des registres utilisés pour passer les arguments (à l’exception de ceux déjà pris pour des arguments non optionnels) sont stockés dans une zone de sauvegarde divisée en deux: les arguments de type entier et pointeur d’un côté et ceux de type flottant de l’autre.

Dans ton cas que se passe-t-il? Tu vas bien chercher 9 et 123.45 dans leurs parties respectives, mais tu vas chercher 17.3 dans la partie entière alors qu’il est dans la partie flottante. Résultat, tu récupères la valeur d’un registre qui n’a pas été utilisé pour stocker un argument optionnel. Étant donné que la valeur d’un registre change très souvent, cela explique que tu obtiennes un résultat différent d’une exécution à l’autre de ton programme.

Pour les téméraires, l’abi de l’architecture amd64 :mrgreen: