IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C Discussion :

calloc remplit il avec "0"


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2004
    Messages
    21
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juillet 2004
    Messages : 21
    Par défaut calloc remplit il avec "0"
    calloc alloue de la memoire et remplit celle-ci avec la valeur 0

    ceci est il valable sur toutes les machines et avec tous les compilateurs?

    ou vaut-il miex faire l initilisation a zero manuellemnet?

  2. #2
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut Re: calloc remplit il avec "0"
    Citation Envoyé par befb
    calloc alloue de la memoire et remplit celle-ci avec la valeur 0

    ceci est il valable sur toutes les machines et avec tous les compilateurs?

    ou vaut-il miex faire l initilisation a zero manuellemnet?
    Le langage C garanti que calloc() fait pareil que malloc() suivi de memset(0). Ce que font les compilateurs dépend de leur degré de compatibilité (qui est < 100%)

    Le problème de memset(p, 0, n), c'est qu'il met tous les bits des bytes à 0. Or le langage C ne garanti pas que 'tous les bits à zéro' soit la représentation de la valeur zéro pour tous les types. En toute rigueur, ce n'est garanti que pour char, signed char et unsigned char.

    Une façon portable rapide et simple d'initialiser un objet allouer à la valeur 0 est de faire ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
       T *p = malloc (sizeof *p);
     
       if (p != NULL)
       {
          static T const z;
          *p = z;
       }
    Pour un tableau dynamique (1D) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
       T *p = malloc (sizeof *p * n);
     
       if (p != NULL)
       {
          static T const z;
          size_t i;
     
          for (i = 0; i < n; i++)
          {
             p[i] = z;
          }
       }

  3. #3
    Membre éprouvé
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    66
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Mai 2004
    Messages : 66
    Par défaut Re: calloc remplit il avec "0"
    Citation Envoyé par Emmanuel Delahaye
    Une façon portable rapide et simple d'initialiser un objet allouer à la valeur 0 est de faire ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
       T *p = malloc (sizeof *p);
     
       if (p != NULL)
       {
          static T const z;
          *p = z;
       }
    Est-ce que c'est écrit dans la doc qu'une variable static est initialisée à 0 ? Sous QNX4#watcom10.6 , ce n'est vrai qu'en mode de compilation debug, pas en mode optimisé ...

  4. #4
    Membre éprouvé Avatar de kaisse
    Profil pro
    Inscrit en
    Novembre 2003
    Messages
    100
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2003
    Messages : 100
    Par défaut
    Le problème de memset(p, 0, n), c'est qu'il met tous les bits des bytes à 0. Or le langage C ne garanti pas que 'tous les bits à zéro' soit la représentation de la valeur zéro pour tous les types. En toute rigueur, ce n'est garanti que pour char, signed char et unsigned char.
    Y'a-t'il des implémentations qui représentent la valeur 0 des int (ou autre) avec autre chose que les bits à 0 ?

    Sinon, même question que DavG: pourquoi static const T z; initialise z à 0 (et est-ce que ca veut dire quelque chose si z n'est pas un type de base ) ?

  5. #5
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut Re: calloc remplit il avec "0"
    Citation Envoyé par DavG
    Citation Envoyé par Emmanuel Delahaye
    Une façon portable rapide et simple d'initialiser un objet allouer à la valeur 0 est de faire ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
       T *p = malloc (sizeof *p);
     
       if (p != NULL)
       {
          static T const z;
          *p = z;
       }
    Est-ce que c'est écrit dans la doc qu'une variable static est initialisée à 0 ? Sous QNX4#watcom10.6 , ce n'est vrai qu'en mode de compilation debug, pas en mode optimisé ...
    Oui, la norme est claire là dessus. Le comportement que tu décris n'est pas conforme (mais il existe, je l'ai eu avec Code Composer de Texas pour DSP TMS320C54). J'ai dû mettre memset(0) après avoir vérifié que all-bit-to-0 convenait à tous les types simples utilisés (dans l'appli, entiers et pointeurs).

    Que fait le compilateur Watcom avec
    ou
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
          static T const z = {0};
    en mode optimisé ?

    Je reconnais que cette façon de procéder, bien que conforme à la norme, a un inconveniant, c'est de multiplier les zones statiques dans le code, ce qui n'est pas bon en embarqué...

  6. #6
    Membre éprouvé
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    66
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Mai 2004
    Messages : 66
    Par défaut Re: calloc remplit il avec "0"
    Citation Envoyé par Emmanuel Delahaye
    Oui, la norme est claire là dessus. Le comportement que tu décris n'est pas conforme (mais il existe, je l'ai eu avec Code Composer de Texas pour DSP TMS320C54).
    On pourrait tenter un recours collectif

    Que fait le compilateur Watcom avec
    ou
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
          static T const z = {0};
    en mode optimisé ?
    Si la variable est initialisée ça fonctionne en mettant les 0 dans les deux cas .. en fait on a été obligé de tout initialiser sinon le comportement en debug et en optimisé n'était pas le même

  7. #7
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut
    Citation Envoyé par kaisse
    Le problème de memset(p, 0, n), c'est qu'il met tous les bits des bytes à 0. Or le langage C ne garanti pas que 'tous les bits à zéro' soit la représentation de la valeur zéro pour tous les types. En toute rigueur, ce n'est garanti que pour char, signed char et unsigned char.
    Y'a-t'il des implémentations qui représentent la valeur 0 des int (ou autre) avec autre chose que les bits à 0 ?
    La norme C99 prévoit le cas des 'Trap représentation'. On peut imaginer une architecture 32 bit dont la zone adressable utilise 24 bits (68k, par exemple). Les valeurs binaires valides des pointeurs seraient donc
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    xxxx 0000 0000 0000 0000 0000 0000
    xxxx 1111 1111 1111 1111 1111 1111
    représentant les adresses @000000 à @FFFFFF

    On aurait donc le droit de concevoir une machine de façon à ce que les poids forts soient obligatoirement à 1111. Le codage des adresses serait donc
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    1111 0000 0000 0000 0000 0000 0000  : @000000
    1111 1111 1111 1111 1111 1111 1111 : @FFFFFF
    avec, par exemple une valeur spéciale pour NULL de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1110 0000 0000 0000 0000 0000 0000  : NIL
    Sur une telle machine, une init 'all-bits-to-0' (memset(0)) provoquerait une 'TRAP representation' (valeur invalide) et un comportement dépendant de l'implémentation.

    Il y a aussi le cas des flottants. Sur certaines impléméntation, all-bit-to-0 n'est pas la représentation de 0.0. (Autre valeur, NaN...)
    Sinon, même question que DavG: pourquoi static const T z; initialise z à 0 (et est-ce que ca veut dire quelque chose si z n'est pas un type de base ) ?
    Oui. La norme garanti que chaque élément sera initialisé avec la valeur logique 0 (donc NULL pour les pointeurs)

  8. #8
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2004
    Messages
    21
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juillet 2004
    Messages : 21
    Par défaut
    Merci pour toutes ces réponses.
    J'avoue que je me suis un peu perdu dans le débat qui s'en est suivit.
    J'utilise des complilateurs que je califierais de classique donc je ne crois pas que je devrais rencontrer ce genre de problèmes

    Le problème de memset(p, 0, n), c'est qu'il met tous les bits des bytes à 0. Or le langage C ne garanti pas que 'tous les bits à zéro' soit la représentation de la valeur zéro pour tous les types. En toute rigueur, ce n'est garanti que pour char, signed char et unsigned char.
    Pour un int, il ne devrait donc pas y avoir de problème (initialisation à 0).

    quand est il pour les pointeurs ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    		//tableaux dynamique de pointeurs
    struct Schedule  **SchIndex;
     
    if((SchIndex=(Schedule **) calloc((NumA*ScheduledDays), (sizeof **SchIndex)))==NULL)
    	printf( "Insufficient memory available\n" );
    mon tableau dynamique de poineurs est il bien initialiser à NULL ?


    Je me rends compte que j ai pas mal de question de détails de ce genre, notament sur ce que fait réellemnt chaque commande, sur la rapidité d'execution de différente méthodes.

    par exemple est ce qu un calloc prend deux fois plus de temps qu'un malloc étant donné qu il y a initialisation en plus.

    bref auriez vous un bon bouquin sur ce genre de sujet à me conseiller

    Merci d'avance

  9. #9
    Membre éprouvé
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    66
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Mai 2004
    Messages : 66
    Par défaut
    Citation Envoyé par befb
    mon tableau dynamique de poineurs est il bien initialiser à NULL ?
    Fais un printf et tu le sauras :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    int i = 0;
    for ( ; i < NumA * ScheduledDays; i++ )
    {
      printf ( "[%d] -> %p\n", i, SchIndex[i] );
    }

  10. #10
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut
    Citation Envoyé par DavG
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
      printf ( "[%d] -> %p\n", i, SchIndex[i] );
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      printf ( "[%d] -> %p\n", i, (void *) SchIndex[i] );
    "%p" attend un (void*).

  11. #11
    Membre Expert
    Inscrit en
    Décembre 2004
    Messages
    1 478
    Détails du profil
    Informations forums :
    Inscription : Décembre 2004
    Messages : 1 478
    Par défaut
    Citation Envoyé par befb
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    		//tableaux dynamique de pointeurs
    struct Schedule  **SchIndex;
     
    if((SchIndex=(Schedule **) calloc((NumA*ScheduledDays), (sizeof **SchIndex)))==NULL)
    	printf( "Insufficient memory available\n" );
    Un peu compliqué à lire. On va aérer tout cela.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    SchIndex = calloc( NumA * ScheduledDays), sizeof *SchIndex);
    if(SchIndex == NULL) /* ou if (!SchIndex) */
    {
       /* gerer l'erreur */
    }
    mon tableau dynamique de poineurs est il bien initialiser à NULL ?
    Non. Ils sont initialisés à zéro (0x000000). Tu vas me dire que NULL == 0 ou (void *) 0. Certes, mais cela est traduit par le compilateur. Par exemple,
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
      type *ptr;
      ptr = NULL;
    va produire dans le code machine ptr = <adresse interdite> où adresse interdite dépend de la machine (cela peut être 0x000000, mais aussi 0xffffff, ou n'importe quelle autre). Le principal, c'est que l'adresse interdite soit différente de toute adresse autorisée (i.e. qu'un pointeur valide ne soit jamais égal à NULL).
    Lorsque tu fais un calloc de pointeurs, tu empêche le compilateur de faire cette traduction (il ne peut pas deviner, le brave petit).
    Donc tu dois faire un malloc, suivi d'un memset à NULL (là c'est bon) ou d'une simple boucle sur les éléments du tableau de pointeurs, en les mettant chacun à NULL (ce que memset fait, basiquement).

  12. #12
    Membre éprouvé
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    66
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Mai 2004
    Messages : 66
    Par défaut
    Citation Envoyé par Emmanuel Delahaye
    "%p" attend un (void*).
    %p marche très bien pour afficher l'adresse d'un pointeur, le cast est implicite et je n'ai jamais eu de problème avec .. par contre je ne l'ai pas essayé avec gcc

  13. #13
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut
    Citation Envoyé par befb
    J'avoue que je me suis un peu perdu dans le débat qui s'en est suivit.
    C'est pas grave, il faut avouer que c'est assez théorique. Néanmoins ça ne te dispence pas de vérifier dans la doc de ton compilateur quelle est la représentation interne de (void*)0, de 0.0 et de 0.0F, hisoire d'être sûr.

    Pour les entiers, il n'y a pas grand risque, sauf architecture exotique, mais elle t'aurais pété au nez depuis longtemps...
    qu'en est il pour les pointeurs ?
    J'ai répondu dans mon article précédent. Il y a un risque.
    par exemple est ce qu un calloc prend deux fois plus de temps qu'un malloc étant donné qu il y a initialisation en plus.
    Ce qui prend du temps, c'est malloc(). L'init, de toutes façons, il faut lma faire, et celle de calloc() (à base de memset()) est probablement rapide et optimisée.

    Si tu dois allier allocation dynamique et performance, le mieux est de travailler avec des pools de blocks de tailles fixes préalloués et gérés 'à la main'. (avec un peu d'abstraction quand même, ne soyons pas gore...)

  14. #14
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut
    Citation Envoyé par DaZumba
    Lorsque tu fais un calloc de pointeurs, tu empêche le compilateur de faire cette traduction (il ne peut pas deviner, le brave petit).
    Donc tu dois faire un malloc, suivi d'un memset à NULL (là c'est bon)
    Ben non, justement. memset() fait précisément exactement comme calloc(), c'est à dire un positionement des bits à la valeur passée en paramètre. L'abstraction 'pointeur' n'est pas vue par memset().
    ou d'une simple boucle sur les éléments du tableau de pointeurs,
    avec assignation explicite (opérateur =), ok.
    en les mettant chacun à NULL (ce que memset fait, basiquement).
    Non, pas du tout.

  15. #15
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut
    Citation Envoyé par DavG
    Citation Envoyé par Emmanuel Delahaye
    "%p" attend un (void*).
    %p marche très bien pour afficher l'adresse d'un pointeur, le cast est implicite et je n'ai jamais eu de problème avec .. par contre je ne l'ai pas essayé avec gcc
    Le problème n'est pas que ça 'marche' ou pas. Le comportement est indéfini. C'est tout. Relire la norme...

    Le 'cast implicite' est vers (char*), pas (void*). La norme dit que ce n'est pas la même chose.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    #include <stdio.h>
     
    int main (void)
    {
       int x;
     
       printf ("%p\n", &x);
     
       return 0;
    }
    gcc -W -Wall -ansi -pedantic
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    main.c:18: warning: void format, different type arg (arg 2)

  16. #16
    Membre Expert
    Inscrit en
    Décembre 2004
    Messages
    1 478
    Détails du profil
    Informations forums :
    Inscription : Décembre 2004
    Messages : 1 478
    Par défaut
    Citation Envoyé par Emmanuel Delahaye
    Ben non, justement. memset() fait précisément exactement comme calloc(), c'est à dire un positionement des bits à la valeur passée en paramètre. L'abstraction 'pointeur' n'est pas vue par memset().
    Bien sûr, je suis bête... Puisque calloc est équivalent à malloc + memset, memset est la cause du problème. Donc une boucle est la seule solution. Désolé pour cette erreur...

+ Répondre à la discussion
Cette discussion est résolue.

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo