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 :

Problème avec malloc


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2010
    Messages
    74
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 74
    Par défaut Problème avec malloc
    Bonjour,

    Je ne parviens pas à allouer un tableau de manière dynamique :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include <stdio.h>
    #include <stdlib.h>
     
    int main(int argc, char *argv[])
    {
    char *tab = NULL;
     
    tab = malloc(5 * sizeof(char));
    printf("%d\n", sizeof(tab));
    free(tab);
     
    return 0;
    }
    le sizeof me renvoie toujours une taille de 4 octets et je ne comprends pas pourquoi. Je compile sous gnu gcc.

    Merci de votre aide.

  2. #2
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 484
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 484
    Par défaut
    Bonjour,

    « sizeof » est un mot-clé réservé du C et pas une fonction dans le sens où on l'entend d'habitude. Ça veut dire aussi que sizeof est évalué à la compilation.

    Ici, tu écris sizeof(tab), donc il va toujours te renvoyer la taille du pointeur, quoi que celui pointe, soit quatre octets sur une machine 32 bits.

  3. #3
    Membre Expert
    Avatar de supersnail
    Homme Profil pro
    Inscrit en
    Novembre 2006
    Messages
    1 719
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 719
    Par défaut
    Bonjour,

    C'est parce que sizeof te retourne la taille de ton pointeur vers la zone mémoire allouée... Or un pointeur a une taille de 4 octets (pour contenir une adresse de 32 bits).

    Donc tu as bien un tableau de 5 caractères qui est créé, mais tu ne peux malheureusement pas connaître sa taille (à part faire confiance à malloc qui renvoie 0 si la zone n'a pas pu être allouée).

  4. #4
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2010
    Messages
    74
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 74
    Par défaut
    Dans ce cas pour quoi quand on fait :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    char tab[6] = "salut";
    printf("%d\n", sizeof(tab));
    celui-ci nous renvoie bien 6 ? Car tab est bien un pointeur sur le 1er élèment donc je devrai avoir 4 octets pour le pointeur.

  5. #5
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 484
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 484
    Par défaut
    Citation Envoyé par Flynet Voir le message
    Dans ce cas pour quoi quand on fait :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    char tab[6] = "salut";
    printf("%d\n", sizeof(tab));
    celui-ci nous renvoie bien 6 ? Car tab est bien un pointeur sur le 1er élèment donc je devrai avoir 4 octets pour le pointeur.
    Eh non ! :-) tab est un tableau, mais invoquer tab seul renvoie l'adresse de ce tableau, donc une valeur de type « char * ». Par contre, si tu écris :

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    const char * tab = "Salut';

    Là, tu te retrouves avec un sizeof(tab) égal à 4.

    C'est facile à retenir car, dans le cas de tab[6], tu alloues 6 caractères, mais tu n'alloues pas de mémoire supplémentaire pour stocker l'adresse du tableau (connue). Donc, à aucun moment, sizeof() ne pourrait te renvoyer 4.

  6. #6
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2010
    Messages
    74
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 74
    Par défaut
    Donc si je comprends bien, le fait d'utiliser la déclaration sous forme de pointeur fait que sizeof renverra la taille du pointeur. Mais dans le cas du tableau, l'adresse de la mémoire étant connue, sizeof ne renverra que la taille du tableau ?

  7. #7
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 484
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 484
    Par défaut
    Citation Envoyé par Flynet Voir le message
    Donc si je comprends bien, le fait d'utiliser la déclaration sous forme de pointeur fait que sizeof renverra la taille du pointeur. Mais dans le cas du tableau, l'adresse de la mémoire étant connue, sizeof ne renverra que la taille du tableau ?
    C'est surtout que sizeof() va naturellement te renvoyer la taille qu'occupe une entité en mémoire. Lorsque tu déclares un pointeur, tu réserves quatre octets dans la pile. Lorsque tu déclares un tableau de six caractères, tu réserves exactement six octets dans la pile et rien d'autre (en considérant que chaque caractère occupe un seul octet et en négligeant les histoires de padding).

    Un tableau n'est pas accompagné d'un pointeur en mémoire qui contient l'adresse des données qu'il contient. Sinon, ça veut dire que tu pourrais changer cette valeur. Essaie par exemple de compiler ceci :

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    int main (void)
    {
        char * pointer     = "Hello";
        char   buffer  [6] = "World";
     
        pointer = (char  *)0x12345678;
        buffer  = (char[6])0x12345678;
     
        return 0;
    }

    Code Shell : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    $ gcc programme.c -o programme
    programme.c: In function ‘main’:
    programme.c:7: erreur: le transtypage spécifie un type de tableau

    Tu peux réaffecter « pointer » mais pas « buffer » car il n'y a pas de pointeur en mémoire associé au tableau.

  8. #8
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 835
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 835
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Flynet Voir le message
    Dans ce cas pour quoi quand on fait :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    char tab[6] = "salut";
    printf("%d\n", sizeof(tab));
    celui-ci nous renvoie bien 6 ? Car tab est bien un pointeur sur le 1er élèment donc je devrai avoir 4 octets pour le pointeur.
    Bien que les pointeurs et les tableaux soient souvent très similaires dans leur manipulation, un pointeur n'est pas un tableau et un tableau n'est pas un pointeur. Donc sizeof(tab) te donnera 6.

    La cause de la confusion vient dans le fait que bien souvent on utilise un pointeur pour manipuler le contenu d'un tableau. D'où l'idée que c'est la même chose.
    Exemple

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    char tab[6] = "salut";
    char *pt=tab;
    for (i=0; i < 6; i++)
        printf("i=%d => tab=(%p) %c et pt=(%p) %c\n", i, &tab[i], tab[i], &pt[i], pt[i])
    Mais la différence se voit ici
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    char tab[6] = "salut";
    char *pt=tab;
    printf("taille tab: %d\n", sizeof(tab));
    printf("taille pt: %d\n", sizeof(pt));
    Et cette différence devient un gouffre quand on commence à travailler avec des tableaux multi-dimensionnels...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  9. #9
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2010
    Messages
    74
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 74
    Par défaut
    Merci de vos réponses, je comprend mieux à présent. Le problème c'est que sur le site où j'ai suivi la programmation, on nous fait comprendre que tableau est un pointeur et qu'il pointe sur le premier élèment du tableau d'où la confusion.

    Ma question maintenant porte plutôt sur ton code obsidian, j'ai bien compris qu'on essayait de réaffecter des adresses mémoires mais que signifient les :
    (char *)
    (char[6])

    C'est la première fois que je vois ce genre de choses.

  10. #10
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 484
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 484
    Par défaut
    Citation Envoyé par Flynet Voir le message
    Ma question maintenant porte plutôt sur ton code obsidian, j'ai bien compris qu'on essayait de réaffecter des adresses mémoires mais que signifient les :
    (char *)
    (char[6])
    Il est sage de choisir tout de suite un document complet et bien construit (par exemple, le K&R), ou les cours de C, sinon tu vas aller d'approximations en approximations, tu vas prendre de mauvaises habitudes, et ce sera difficile de les changer ensuite.

    Un nom de type seul entre parenthèses précédant une expression, c'est un « cast » ou « transtypage » en français. Il s'agit de convertir la valeur concernée dans le type en question. C'est aussi répandu en C que les pointeurs et fait partie des fondamentaux du langage.

    Par exemple, « (int)2.5 » vaut « 2 », puisque « 2.5 » est de type double par défaut, puis converti en int par le biais du cast. Ne subsiste donc que la partie entière.

    Souvent, ces conversions sont implicites quand le compilateur sait les faire (conversions entre types numériques), la plupart du temps, elles sont explicites, et parfois, elles sont impossibles.

    En particulier, les pointeurs sont des types à part entière et peuvent donc être transtypés. C'est très utile mais c'est aussi la porte ouverte à tous les bricolages, même les plus sales. Tu ne peux pas convertir un entier en structure, par exemple, parce que ça n'a pas de sens et, de toutes façons, le compilo ne saurait pas quel genre de résultat ce serait censé donner. Par contre, une adresse mémoire étant une adresse mémoire, tu peux très bien affirmer qu'un « int * », soit « l'adresse en mémoire d'un int » est en fait l'adresse en mémoire de n'importe quoi d'autre. À partir du moment où c'est faisable (l'adresse en elle-même reste inchangée) et que tu affirmes cela explicitement au compilo, il va te croire.

    Dans le cas qui nous intéresse, donc, j'invente une adresse bidon (« 0x12345678 ») qui est, à ce stade, considérée par le compilateur comme un nombre entier et pas un pointeur. Ensuite, je transtype ce nombre vers le type de chacune de mes variables, pour avoir des arguments de même nature de chaque côté du « = ».

    On voit alors qu'on peut affecter une nouvelle valeur à un pointeur, mais pas à un tableau.

  11. #11
    Membre actif
    Profil pro
    Inscrit en
    Février 2010
    Messages
    87
    Détails du profil
    Informations personnelles :
    Localisation : Algérie

    Informations forums :
    Inscription : Février 2010
    Messages : 87
    Par défaut
    Citation Envoyé par Flynet Voir le message
    On nous fait comprendre que tableau est un pointeur et qu'il pointe sur le premier élèment du tableau d'où la confusion.
    oui c'est vrais que un tableau c'est un pointeur qui pointe sur le premier élément et pour les variables aussi et pour un pointeur normale en réalité c'est un pointeur de pointeur je vais t'exprimer ça pas a pas, un pointeur tous simplement une adresse sauvé en mémoire, je vais te donne une exemple imaginer que nous savons l'adresse de pointeur avant de le crée
    #define pointeur 0x956000

    unsigned char data = 0xAA;
    pointeur = data ; //ici on a affecté la donnée 0xAA a l'adresse 0x95600
    c'est la meme chose avec la déclaration de pointeur mais ici on a une adresse de pointeur valide definé par le system
    unsigned char *pointeur = NULL;

    unsigned char data = 0xAA;
    pointeur = data ; //ici on a affecté la donnée 0xAA a une l'adresse valide
    et pour les tableaux, un 'nom' de tableau lui meme est un variable imagine toi exemple on déclare un tableau de dix cases et on laisse les crochets"[9]" a coté voir ce qu'il reste
    unsigned char nomdetableau;//déclaration de tablau de dix cases
    il nous reste qu'une variable / mais que fait cette variable ici?/pour sauvé une adresse /la qu'elle? /c'est le system qui va choisir cette adresse/ pour quoi?/pour mettre les dix cases de ton tableau en mémoire/c'est la meme chose avec un pointeur normale avec seulement un nom différant
    unsigned char pointeur[9]; //un pointeur de dix cases
    meme chose avec les variables et bonne chanse

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

Discussions similaires

  1. problème avec malloc
    Par cyril_sy dans le forum C
    Réponses: 13
    Dernier message: 12/05/2007, 13h49
  2. Problème avec malloc et autre
    Par ego dans le forum C
    Réponses: 5
    Dernier message: 02/05/2007, 18h29
  3. probléme avec malloc
    Par tomasi dans le forum C
    Réponses: 18
    Dernier message: 15/11/2006, 15h15
  4. Problème avec malloc
    Par f56bre dans le forum C
    Réponses: 11
    Dernier message: 13/11/2006, 14h36
  5. Problème avec malloc.
    Par kmitz dans le forum C
    Réponses: 2
    Dernier message: 25/03/2006, 18h05

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