C

Un article de Haypo.

(Différences entre les versions)
Version du 4 juillet 2007 à 12:20 (modifier)
Haypo (Discuter | Contributions)
(Articles connexes)
← Différence précédente
Version du 30 octobre 2007 à 10:13 (modifier) (défaire)
Haypo (Discuter | Contributions)
(Pointeurs dangereux)
Différence suivante →
Ligne 50 : Ligne 50 :
L'erreur la plus courante en C est le déreferrencement d'un pointeur nul. Exemple abrégé : « int *x = 0; printf("x=%i\n", *x); ». Il set souvent du au fait que la fonction malloc() peut échouer et que son code de retour n'est pas vérifie. Il vaut mieux utiliser une fonction plus haut niveau qui va générer une erreur dans le cas où malloc() échoue (retourne NULL). La fonction g_new() de la glib stoppe le programme avec abort() par exemple. L'erreur la plus courante en C est le déreferrencement d'un pointeur nul. Exemple abrégé : « int *x = 0; printf("x=%i\n", *x); ». Il set souvent du au fait que la fonction malloc() peut échouer et que son code de retour n'est pas vérifie. Il vaut mieux utiliser une fonction plus haut niveau qui va générer une erreur dans le cas où malloc() échoue (retourne NULL). La fonction g_new() de la glib stoppe le programme avec abort() par exemple.
 +
 +== stdint ==
 +
 + #include <stdint.h>
 +
 +La bibliothèque stdint.h propose de manière standards les types entiers de 8, 16, 32 et 64 bits.
 +
 +Type contenant exactement XX bits :
 +* int8_t / uint8_t
 +* int16_t / uint16_t
 +* int32_t / uint32_t
 +* int64_t / uint64_t
 +
 +Types ayant "au moins XX bits" :
 +* int_least8_t / uint_least8_t
 +* int_least16_t / uint_least16_t
 +* int_least32_t / uint_least32_t
 +* int_least64_t / uint_least64_t
 +
 +Type "rapide" :
 +* int_fast8_t / uint_fast8_t
 +* int_fast16_t / uint_fast16_t
 +* int_fast32_t / uint_fast32_t
 +* int_fast64_t / uint_fast64_t
 +
 +Autres types :
 +* intptr_t / uintptr_t (pour les pointeurs « void * »)
 +* intmax_t / uintmax_t (nombre entier maximum)
 +
 +Macro pour indiquer une valeur entière immédiate à la manière de « 1UL » qui équivaut à « (unsigned long)1 » :
 +* INT8_C(x) / UINT8_C(x)
 +* INT16_C(x) / UINT16_C(x)
 +* INT32_C(x) / UINT32_C(x)
 +* INT64_C(x) / UINT64_C(x)
 +* INTMAX_C(x) / UINTMAX_C(x)
 +
 +Valeurs maximales :
 +* INT8_MIN / UINT8_MIN
 +* INT16_MIN / UINT16_MIN
 +* INT32_MIN / UINT32_MIN
 +* INT64_MIN / UINT64_MIN
 +* INTMAX_MIN / UINTMAX_MIN
 +* INTPTR_MIN / UINTPTR_MIN et PTRDIFF_MIN / PTRDIFF_MAX
 +* SIG_ATOMIC_MIN / SIG_ATOMIC_MAX (limites de « sig_atomic_t »)
 +* Et il existe les même pour LEAST et FAST : INT_LEAST8_MIN, ..., INT_FAST8_MIN, ...
 +
 +== inttypes ==
 +
 + #include <inttypes.h>
 +
 +La bibliothèque inttypes.h propose des macros pour utiliser printf avec les types définis par stdint.h.
 +
 +Il existe une macro pour chaque type stdint et chaque type printf. La convention de nommage est « "PRI", type printf, type stdint ». Exemples :
 +* PRId32 est utilisé pour faire un printf "%i" avec un type uint32_t
 +* PRIu64 est utilisé pour faire un printf "%u" avec un type uint64_t
 +* PRIXFAST16 est utilisé pour faire un printf "%X" avec un type int_fast16_t
 +
 +De manière similaire, il existe pour scanf utilisant la convention de nommage est « "SCN", type scanf, type stdint ». Exemples :
 +* SCNd8 : scanf "%d" pour le type int8_t
 +* SCNx32 : scanf "%x" pour le type int32_t ou uint32_t
== Articles connexes == == Articles connexes ==

Version du 30 octobre 2007 à 10:13

Retour à la page précédente Retour aux langages de programmation

Avertissement

Cet article est en cours de rédaction. Sa qualité est pauvre et son intérêt limité. Revenez un peu plus tard (ça peut être long), et lisez un autre article en attendant ;-) Si vous êtes impatient de lire la suite, secouez un peu son auteur :o)

« Il est dit que les programmeurs Lisp savent que
la gestion de la mémoire est si importante
qu'elle ne peut être laissée aux programmeurs,
et que les programmeurs C savent que
la gestion de la mémoire est si importante
qu'elle ne peut être laissée au système
 » — Bjarne Stroustrup

Le langage C, c'est de l'assembleur avec des fleurs tout autour.


Sommaire

Typage dangereux

Le C est un langage dangereux dans la déclaration des types de donnée et des conversions.

Problèmes :

  • on ne sait pas si le type char est signé ou non
  • la taille en bits d'un type (short, int, long) dépend du compilateur et de l'architecture
  • beaucoup de conversions dangereuses sont passées sous silence :
    • Conversion int <=> pointeur (void*) : pose des problèmes quand les deux types n'ont pas la même taille (ce qui arrive sur des processeurs 64-bit)
    • Mélange entier signé et entier non signé avec conversion silencieuse
    • Passage d'un entier signé à un entier signé plus grand

Illustration :

$ cat a.c
#include <stdio.h>
int main()
{
   char c = 250;
   unsigned int x = c;
   printf("c %%u=%u\n", c);
   printf("c %%i=%i\n", c);
   printf("x %%u=%u\n", x);
   return 0;
}

$ gcc a.c -o a -Wall -Wextra && ./a
c %u=4294967290
c %i=-6
x %u=4294967290

On copie 250 dans x et on obtient 4294967290. C'est plutôt gênant... Notez que gcc (4.1.2) n'affiche aucun avertissement.

Pointeurs dangereux

Autre danger en C : les pointeurs. Le langage autorise de lire et écrire n'importe où en mémoire. Or dans un système d'exploitation offrant une protection de la mémoire (utilisant une MMU), les accès illégaux génèrent un signal SIGSEGV qui est quasiment toujours fatal.

Pourtant, il est difficile voir impossible de se passer des pointeurs en C. Les langages Java et Python cachent les pointeurs mais ils existent toujours. Par contre, en Python il est impossible de faire un accès illégal en mémoire. Il sera sanctionné par une exception (LookupError sur un tableau, un dictionnaire ou une chaîne).

Au mieux, on peut vérifier les accès mémoire avec le programme Valgrind. Mais on ne pourra jamais garantir à 100% que tous les accès sont valides car on n'a pas de moyen formel de vérifier tous les cas d'utilisation d'un programme.

L'erreur la plus courante en C est le déreferrencement d'un pointeur nul. Exemple abrégé : « int *x = 0; printf("x=%i\n", *x); ». Il set souvent du au fait que la fonction malloc() peut échouer et que son code de retour n'est pas vérifie. Il vaut mieux utiliser une fonction plus haut niveau qui va générer une erreur dans le cas où malloc() échoue (retourne NULL). La fonction g_new() de la glib stoppe le programme avec abort() par exemple.

stdint

#include <stdint.h>

La bibliothèque stdint.h propose de manière standards les types entiers de 8, 16, 32 et 64 bits.

Type contenant exactement XX bits :

  • int8_t / uint8_t
  • int16_t / uint16_t
  • int32_t / uint32_t
  • int64_t / uint64_t

Types ayant "au moins XX bits" :

  • int_least8_t / uint_least8_t
  • int_least16_t / uint_least16_t
  • int_least32_t / uint_least32_t
  • int_least64_t / uint_least64_t

Type "rapide" :

  • int_fast8_t / uint_fast8_t
  • int_fast16_t / uint_fast16_t
  • int_fast32_t / uint_fast32_t
  • int_fast64_t / uint_fast64_t

Autres types :

  • intptr_t / uintptr_t (pour les pointeurs « void * »)
  • intmax_t / uintmax_t (nombre entier maximum)

Macro pour indiquer une valeur entière immédiate à la manière de « 1UL » qui équivaut à « (unsigned long)1 » :

  • INT8_C(x) / UINT8_C(x)
  • INT16_C(x) / UINT16_C(x)
  • INT32_C(x) / UINT32_C(x)
  • INT64_C(x) / UINT64_C(x)
  • INTMAX_C(x) / UINTMAX_C(x)

Valeurs maximales :

  • INT8_MIN / UINT8_MIN
  • INT16_MIN / UINT16_MIN
  • INT32_MIN / UINT32_MIN
  • INT64_MIN / UINT64_MIN
  • INTMAX_MIN / UINTMAX_MIN
  • INTPTR_MIN / UINTPTR_MIN et PTRDIFF_MIN / PTRDIFF_MAX
  • SIG_ATOMIC_MIN / SIG_ATOMIC_MAX (limites de « sig_atomic_t »)
  • Et il existe les même pour LEAST et FAST : INT_LEAST8_MIN, ..., INT_FAST8_MIN, ...

inttypes

#include <inttypes.h>

La bibliothèque inttypes.h propose des macros pour utiliser printf avec les types définis par stdint.h.

Il existe une macro pour chaque type stdint et chaque type printf. La convention de nommage est « "PRI", type printf, type stdint ». Exemples :

  • PRId32 est utilisé pour faire un printf "%i" avec un type uint32_t
  • PRIu64 est utilisé pour faire un printf "%u" avec un type uint64_t
  • PRIXFAST16 est utilisé pour faire un printf "%X" avec un type int_fast16_t

De manière similaire, il existe pour scanf utilisant la convention de nommage est « "SCN", type scanf, type stdint ». Exemples :

  • SCNd8 : scanf "%d" pour le type int8_t
  • SCNx32 : scanf "%x" pour le type int32_t ou uint32_t

Articles connexes

Liens externes

Récupérée de « http://haypo.alwaysdata.net/wiki/C »