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 :

Décalage de bits et adresse de la variable


Sujet :

C

  1. #1
    Membre éclairé
    Profil pro
    Inscrit en
    Avril 2011
    Messages
    447
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2011
    Messages : 447
    Par défaut Décalage de bits et adresse de la variable
    Bonjour, j'aimerai comprendre dans le code suivant à la ligne 4 quand ont fais les décalages, par exemple
    (nTmp & 0xFF) << 8) est-ce que l'on travail toujours sur la variable nTmp à son adresse ou bien (nTmp & 0xFF) se trouve tel à un autre endroit de la mémoire même si cela me semble improbable car sa obligerai le compilateur à alloué implicitement de la mémoire à un autre endroit.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    uint8_t *pPtr = malloc(sizeof(uint8_t)*2);
    pPtr[0] = 0; pPtr[1] = 1;
    uint32_t  nTmp = *(uint16_t *)pPtr;
    nTmp = (((nTmp & 0xFF) << 8) | ((nTmp & 0xFF00) >> 8));

  2. #2
    Membre émérite
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Juillet 2020
    Messages
    352
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Chef de projet NTIC

    Informations forums :
    Inscription : Juillet 2020
    Messages : 352
    Par défaut
    Bonjour,
    alors déjà il faut se rendre compte qu'un compilateur n'est plus un simple traducteur «mot à mot» de C en code machine. Un compilo c'est bien plus intelligent que ça de nos jours. Pour t'en rendre compte tu peux essayer tes codes sur godbolt, l'explorateur de compilateurs.

    Tu remarqueras que les compilos récents ne feront rien avec ton à moins d'utiliser le résultat ; tu pourras remarquer que si le compilateur peut au moment de la compilation déterminer la valeur du résultat il le fera et au runtime tu n'auras qu'un affichage de la valeur (genre option -O3).
    Il se peut aussi (genre option -O0) qu'il reconnaisse que tu veux échanger les octets de poids forts et faibles d'un short … et qu'il le fasse sans faire aucun décalage …

    Bref, ta question est bonne mais la réponse dépend de tellement de paramètres qu'il t'est bien plus simple de tester différents compilos en différentes versions pour différentes plateformes avec différentes options pour en voir le résultat

    Bonne exploration

  3. #3
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 153
    Billets dans le blog
    4
    Par défaut
    Est-ce que quand tu écris nTmp + 3 tu travailles sur nTmp ?
    Non. Et les opérateurs de bit ne sont pas plus magiques et opèrent de la même manière.

    https://c.developpez.com/faq/?page=L...-operateurs-gt
    https://c.developpez.com/faq/?page=L...signifie-E1-E2
    https://c.developpez.com/faq/?page=L...signifie-E1-E2
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  4. #4
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 838
    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 838
    Billets dans le blog
    1
    Par défaut
    Bonjour
    Citation Envoyé par hbx360 Voir le message
    Bonjour, j'aimerai comprendre dans le code suivant à la ligne 4 quand ont fais les décalages, par exemple
    (nTmp & 0xFF) << 8) est-ce que l'on travail toujours sur la variable nTmp à son adresse ou bien (nTmp & 0xFF) se trouve tel à un autre endroit de la mémoire même si cela me semble improbable car sa obligerai le compilateur à alloué implicitement de la mémoire à un autre endroit.
    Tu ne comprends pas ce qui se passe, du coups ta question n'a aucun sens. nTmp & 0x FF ne se trouve pas "ailleurs" dans la mémoire, nTmp c'est une variable, donc qui ne se trouve qu'à un endroit dans la mémoire, rien d'autre.

    Il se trouve maintenant que nTmp récupère la valeur de ce qu'il y a à l'adresse "pPtr", ok. Et alors? Qu'est-ce que cela change au point précédent? Rien. nTmp reste une variable qui vient de récupérer une valeur comme elle aurait pu en récupérer tant d'autres.

    Et ensuite tu modifies la valeur de nTmp, ok tu en as le droit. Et donc qu'est-ce que ça change? Rien. Qu'il y ait une valeur ou une autre...

    Les variables ce sont des cases et tu y mets les valeurs que tu veux. Si ces valeurs ont un sens tant mieux sinon tant pis, c'est juste toi que ça concerne.
    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]

  5. #5
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Il se trouve maintenant que nTmp contient l'adresse d'une autre variable de ton code, ok.
    Si je lis bien le code, nTmp contient pas une adresse, car la partie droite commence par une étoile de déréférencement (et en plus il n'est pas déclaré comme pointeur) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    uint32_t  nTmp = *(uint16_t *)pPtr;
                     ^

    Citation Envoyé par hbx360 Voir le message
    Bonjour, j'aimerai comprendre dans le code suivant à la ligne 4 quand ont fais les décalages
    As-tu déjà bien compris les 3 lignes d'avant ?

    1. tu alloues de l'espace pour 2 entiers de 8 bits, qui seront côté à côté quelque part dans la mémoire
    2. tu affectes des valeurs à ces 2 entiers (à 0 et 1)
    3. tu castes le pointeur pour obtenir un pointeur vers un entier de 16 bits et tu mets la valeur pointée dans un entier de 32 bits


    Ainsi, nTmp contient ces 2 octets en poids faible, l'un à 1 et l'autre à 0. Les 16 bits de poids fort sont en tout logique à 0 aussi.

    Enfin, ligne 4, tu prends les bits de poids faibles et tu les décales de 8, les bits de poids et tu les décales de 8 dans l'autre sens, tu les assembles avec | et tu les remets dans nTmp. En gros, tu as inversé les 2 octets de poids faibles.

    Citation Envoyé par hbx360 Voir le message
    (nTmp & 0xFF) << 8) est-ce que l'on travail toujours sur la variable nTmp à son adresse ou bien (nTmp & 0xFF) se trouve tel à un autre endroit de la mémoire même si cela me semble improbable car sa obligerai le compilateur à alloué implicitement de la mémoire à un autre endroit.
    Je ne pense pas que tu puisses le deviner sans regarder le code assembleur généré par ton compilateur (ça dépend des options, du CPU, de la version du compilateur, etc). On peut s'en douter avec l'expérience et la logique, mais je ne crois que ça soit normé par le langage. En toute logique, malloc() va allouer de la mémoire sur le tas. Les autres variables vont être créées sur la pile, mais il est aussi possible qu'elles ne vivent que dans des registres du processeur, sans utiliser la pile.

  6. #6
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 838
    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 838
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Bktero Voir le message
    Si je lis bien le code, nTmp contient pas une adresse, car la partie droite commence par une étoile de déréférencement (et en plus il n'est pas déclaré comme pointeur) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    uint32_t  nTmp = *(uint16_t *)pPtr;
                     ^
    Ah oui. J'ai lu trop vite. Je pensais que nTmp contenait une adresse mais en fait c'est encore plus simple, il contient une bête valeur.
    Ok, j'ai corrigé.
    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]

  7. #7
    Membre Expert

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 644
    Par défaut Du code et des codes
    Bonjour,

    Hors les éléments très explicites de WhiteCrow, si nous sommes dans un cas où le compilateur doit générer un code (non pré-calculable et dont le résultat est utilisé), il n'utilisera pas de décalage mais plutôt un code assembleur de type :
    Code ASM : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    mov ah,        [ptr]
    mov al,        [ptr+1]
    mov word[ptr], ax

    Il n'y a pas de gain à faire une seule lecture (genre mov dx, [ptr]) car les caches font leur travail. Le compilateur peut aussi utiliser les instructions de brassage d'octets du SSE mais cela ne semble pas valable ici.

    Il est toujours intéressant de regarder le code assembleur généré. On y trouve des choses très futées et d'autres beaucoup moins.

    Entre compilateur et CPU, c'est l'escalade du glaive et du bouclier. Les compilateurs savent très bien optimiser l'usage des instructions élémentaires mais les CPU se dotent d'instructions nouvelles qui sont de moins en moins élémentaires. Aussi, le principe du simple code pour de nombreuses instructions assembleur à optimiser se trouve mis à mal quand une seule instruction assembleur correspond à (relativement) de nombreuses lignes de code.

    Salutations

  8. #8
    Membre éclairé
    Profil pro
    Inscrit en
    Avril 2011
    Messages
    447
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2011
    Messages : 447
    Par défaut
    Merci pour tous vos explications et renseignements.

    @Bousk merci pour les liens ; le 1 je l'ai vue mais ne mas pas apporté la réponse que je cherchais.
    Pour le 2 il est dis : "On dit qu'on a masqué c1 avec c2. On appelle ça (c2) un masque car il permet de cacher (masquer) certains bits de c1 (cacher signifie mettre à zéro)."

    Est-ce que cela veut dire qu'on masque juste les bits de la variable sans les mettre définitivement à 0 tant qu'on ne fait pas une assignation sur la même variable ?
    Donc faire c1 = c1 & c2

    @Sve@r ça m'intéresse de savoir comment ça fonctionne.

    @Bktero oui je pense avoir bien compris les décalages et les lignes que je vous ai fourni, après c'est vraiment dans la subtilité/détail que je cherche à comprendre.

    Après sans doute que c'est dans l'optimisation fait par le compilateur que les choses se passe comme l'explique WhiteCrow.

    Dans cette exemple :

    L3 : Je pense que ce résultat doit être stocké temporairement dans la mémoire quelque part pour garder l'intégrité de la variable (nTmp) de départ.

    L4 : ici aussi je pense que c'est pareille le compilo sauvegarde le résultat temporairement.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
        // Je suis en Little Endian.
        L1         0100 0000 1000 0000 0000 0000 0000 0000 
        L2 & 0xFF  1111 1111 0000 0000 0000 0000 0000 0000
        ---------------------------------------------------
        L3 << 8    0100 0000 0000 0000 0000 0000 0000 0000 
        ---------------------------------------------------
        L4         0000 0000 0100 0000 0000 0000 0000 0000

  9. #9
    Membre Expert

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 644
    Par défaut Ou bien...
    Bonjour,

    Pour le fun (associativité et commutativité du ou exclusif à l'ouvrage).

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    uint8_t *p = malloc(sizeof(uint8_t)*2);
    p[0] = 0; p[1] = 1;;   // Valeurs initiales nommées p[0]_i et p[1]_i dans les commentaires
     
    *p     ^= *(p+1);   // p[0] = p[0]_i xor p[1]_i
    *(p+1) ^= *p;       // p[1] = p[1] xor p[0] =  p[1]_i xor (p[0]_i  xor p[1]_i) = p[0]_i = 0
    *p     ^= *(p+1);   // p[0] = p[0] xor p[1] = (p[0]_i xor  p[1]_i) xor p[0]_i  = p[1]_i = 1
    Pas drôle ? Tant pis .

    Salutations

  10. #10
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 838
    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 838
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par hbx360 Voir le message
    @Sve@r ça m'intéresse de savoir comment ça fonctionne.
    Nan mais parce que j'avais mal lu, je pensais que nTmp était un pointeur. Bktero m'a repris.
    Mais tu aurais écrit un code un peu plus simple, style int nTmp=123 ça n'aurait rien changé à la question et aurait été moins confusant. Surtout que "adresse de la variable" de ton titre n'entre absolument pas en ligne de compte dans ta question.

    Citation Envoyé par hbx360 Voir le message
    Pour le 2 il est dis : "On dit qu'on a masqué c1 avec c2. On appelle ça (c2) un masque car il permet de cacher (masquer) certains bits de c1 (cacher signifie mettre à zéro)."
    Oui. C'est à dire qu'on les supprime de la valeur initiale.
    Si par exemple t'as une valeur xyz ("x", "y" et "z" représentant chacun un bit pouvant valoir 0 ou 1) et que tu la filtres via un masque de valeur 5 (101 en binaire) cela donnera au final x0z (la valeur "y" a été "masquée").
    Cela permet de regrouper plusieurs booléens sur un entier. Exemple les droits Unix sont séparés en R (read=100), W (write=010) et X (exécute=001). Si on prend un fichier qui a le droit 6 (110), alors étant donné que 110 masqué par 100 (droit R) donne 100 et que cette valeur n'est pas 0, cela veut dire que R est présent. Pareil pour W (110 & 010 donne 010 donc pas 0 => W est présent). Mais pour X cela fait 110 & 001 = 000 => X n'est pas présent. Au final le fichier possède le droit R et W mais pas X => on peut le lire et le modifier mais pas l'exécuter.

    Citation Envoyé par hbx360 Voir le message
    Est-ce que cela veut dire qu'on masque juste les bits de la variable sans les mettre définitivement à 0 tant qu'on ne fait pas une assignation sur la même variable ?
    Donc faire c1 = c1 & c2
    Oui. Tout calcul est fait en RAM et sera perdu si on ne l'affecte pas quelque part. Si j'écris i+1 cela n'aura aucun intérêt si je ne récupère pas le résultat d'une façon (exemple j=i+1) ou d'une autre (exemple printf("%d\n", i+1)).
    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]

  11. #11
    Membre émérite
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Juillet 2020
    Messages
    352
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Chef de projet NTIC

    Informations forums :
    Inscription : Juillet 2020
    Messages : 352
    Par défaut
    Citation Envoyé par hbx360 Voir le message
    [...]

    Après sans doute que c'est dans l'optimisation fait par le compilateur que les choses se passe comme l'explique WhiteCrow.

    Dans cette exemple :

    L3 : Je pense que …

    [...]

    Alors il est bien plus simple pour avoir une réponse de la demander plutôt que de se perdre en conjectures et de «penser que …».

    Si tu suis le lien que je t'ai donné tu pourras remarquer que si tu prends comme compilateur clang, que tu choisis une vieux i386 comme processeur cible en compilant en mode 32bit sans optimisations ton code nTmp = (((nTmp & 0xFF) << 8) | ((nTmp & 0xFF00) >> 8)); produira :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
            mov     eax, dword ptr [ebp - 8]
            and     eax, 255
            shl     eax, 8
            mov     ecx, dword ptr [ebp - 8]
            and     ecx, 65280
            shr     ecx, 8
            or      eax, ecx
            mov     dword ptr [ebp - 8], eax
    La variable nTmp est sur le stack en ebp-8. On charge le registre eax avec la valeur de la variable (l1), on fait un and $FF (l2) puis on décale à gauche de 8 positions (l3). Ensuite on recharge la valeur de la variable ntmp dans ecx (l4) on la and avec $FF00 (l5) et on la décale à droite de 8 positions (l6). On fait un or sur les deux registres en l7 et on replace la valeur obtenue dans l'emplacement de la variable nTmp en l8.

    On peut constater que c'est une traduction mot à mot de C vers l'asm (utilisation des flags -O0 -march=i386 -m32).

    Si on reprend ton code, en gardant le même processeur cible et la même option d'optimisation mais qu'on change le compilo de clang vers gcc le code asm produit est légèrement différent :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
            mov     eax, DWORD PTR [ebp-16]
            sal     eax, 8
            movzx   edx, ax
            mov     eax, DWORD PTR [ebp-16]
            shr     eax, 8
            movzx   eax, al
            or      eax, edx
            mov     DWORD PTR [ebp-16], eax
    Si en revanche tu demandes de compiler pour un processeur plus récent (rocketlake) en 64 bits le code est légèrement différent (réordonnancé pour prendre avantage du pipeline) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
            mov     eax, DWORD PTR [rsp+12]
            mov     edx, DWORD PTR [rsp+12]
            sal     eax, 8
            movzx   edx, dh
            movzx   eax, ax
            or      eax, edx
            mov     DWORD PTR [rsp+12], eax
    etc.

    Explore godbolt, tu en apprendras beaucoup … et oublie de penser que l'on peut savoir aisément ce qu'un compilo fait … ou ne fait pas ; quoiqu'il en soit, un compilo bien renseigné donnera toujours un code meilleur que ce que 99% des gens peuvent produire.

  12. #12
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 772
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 772
    Par défaut
    Citation Envoyé par hbx360 Voir le message
    Est-ce que cela veut dire qu'on masque juste les bits de la variable sans les mettre définitivement à 0 tant qu'on ne fait pas une assignation sur la même variable ?
    Reprends tes tables de Karnaugh

    Avec 1 opération AND, tu vas extraire 1 ou plusieurs bits. Dans ton cas, ton masque 0x00FF permet de prendre l'octet de poids faible (sur 16 bits) et le masque 0xFF00 permet de prendre l'octet de poids fort (sur 2 octets).
    Avec 1 opération OR, on veut tester les drapeaux ("flags" en anglais). 1 exemple, ce sont les valeurs énumérées (avec donc, des valeurs en puissance de 2 : 1, 2, 4, 8, 16, 32, ...)


    Citation Envoyé par hbx360 Voir le message
    @Bktero oui je pense avoir bien compris les décalages et les lignes que je vous ai fourni, après c'est vraiment dans la subtilité/détail que je cherche à comprendre.
    Ton code est ultra trivial

    Tu crées 1 variable de 16 bits avec 1 tableau de 2 octets (que tu ne désalloues pas ) ... c'est comme le type short.
    Tu mets la valeur 256, 0x100 en hexadecimal.
    Et avec les décalages et les opérations, tu inverses les octets.
    • ((nTmp & 0xFF) << 8) 1 : tu prends l'octet de poids faible et tu mets en poids fort (tu le déplaces d'1 octet vers la gauche)
    • ((nTmp & 0xFF00) >> 8) 2 : tu prends l'octet de poids fort et tu mets en poids faible (tu le déplaces d'1 octet vers la droite)
    • (1 | 2) : tu "assembles" le tout

    Tes masques ne sont pas forcément utiles, mais ils évitent tous les problèmes de bits qui trainent.
    Édit : les masques sont essentiels

    Citation Envoyé par hbx360 Voir le message
    @Sve@r ça m'intéresse de savoir comment ça fonctionne.
    Je ne vois pas la question mais les opérations logiques (AND, OR, XOR, ...) ne sont que sur des entiers. Sinon le compilateur te le dit.
    De toute manière ton code est foireux : les adresses sont en 64 bits et/ ou dépend de la plateforme et les adresses sont "virtuelles" ne peuvent pas être générées "sur la mouche" (on the fly pour la réf.)

  13. #13
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par WhiteCrow Voir le message
    Alors il est bien plus simple pour avoir une réponse de la demander plutôt que de se perdre en conjectures et de «penser que …».

    Si tu suis le lien que je t'ai donné tu pourras remarquer que si tu prends comme compilateur clang, que tu choisis une vieux i386 comme processeur cible en compilant en mode 32bit sans optimisations ton code
    Ca ne sert effectivement à rien deviner ou conjecturer : il faut regarder ce que fait le compilateur !

    En revanche, regarder et analyser de l'assembleur sans optimisation, ça ne sert à rien. Le compilateur n'utilise pas vraiment les fonctionnalités du CPU et de son jeu d'instructions. Il faut donc écrire un petit code qui permet de ne pas se faire totalement optimiser et de regarder ce que ça donne au moins en -O1 : https://godbolt.org/z/591z3MTx8 C'est marrant de passer en O2 et de voir que c'est encore différent.

  14. #14
    Membre éclairé
    Profil pro
    Inscrit en
    Avril 2011
    Messages
    447
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2011
    Messages : 447
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Nan mais parce que j'avais mal lu
    Pas de soucie.

    Citation Envoyé par Sve@r Voir le message
    Surtout que "adresse de la variable" de ton titre n'entre absolument pas en ligne de compte dans ta question
    Bin en fait je ne savais pas comment exprimé ma demande donc mon titre est foireux.

    Citation Envoyé par WhiteCrow Voir le message
    Alors il est bien plus simple pour avoir une réponse de la demander plutôt que de se perdre en conjectures et de «penser que …».
    C'est ce que j'ai fait en ouvrant cette discussion, j'ai posé la question. Après j'ai bien conscience que la façon don je veux exprimé ma demande est pas ouf, j'ai un peu de mal à me faire comprendre.

    Citation Envoyé par WhiteCrow Voir le message
    Si tu suis le lien que je t'ai donné
    Je l'ai fait j'ai rentré mon code j'ai eu les informations en assembleur pareille que toi avec GCC mais je ne comprend pas l'assembleur. Donc merci pour les explications.

    Citation Envoyé par WhiteCrow Voir le message
    La variable nTmp est sur le stack en ebp-8. On charge le registre eax avec la valeur de la variable (l1), on fait un
    and $FF (l2) puis on décale à gauche de 8 positions (l3)
    Si on prend cette opération ci-dessus, quand le calcul avec AND est fini le résultat est stocké où ? C'est cela que je cherche à savoir, car si on met le résultat du AND dans la variable nTmp qui est sur la stack en ebp-8 on perd la valeur initial de nTmp donc il faut bien mettre ce résultat quelque part pour évité de modifier la valeur de nTmp puisque :
    Citation Envoyé par WhiteCrow Voir le message
    Ensuite on recharge la valeur de la variable ntmp dans ecx (l4) ...
    Donc comme on réutilise nTmp pour faire le 2e AND on doit garder la valeur initial elle ne doit pas être modifié avant l'assignation.

    Citation Envoyé par foetus Voir le message
    Ton code est ultra trivial
    En fait quand je parle de subtilité/détail c'est juste la question que je pose ci-dessus, pour l'opération nTmp & 0xFF le résultat est mit où ? Si c'est dans la variable nTmp alors on perdra sa valeur de départ, dans un autre endroit de la mémoire ?
    Citation Envoyé par foetus Voir le message
    Tes masques ne sont pas forcément utiles, mais ils évitent tous les problèmes de bits qui trainent.
    Se que j'ai compris des masques c'est que si on en met pas on ne pourra pas avoir le résultat voulu parce que si on fait juste nTmp << 8 on perdra l'information de la variable nTmp puisque la modification sera mis en dure dans nTmp et quand on réutilisera nTmp pour décaler à droite on aura plus la valeur de départ de nTmp.

    Citation Envoyé par Bktero Voir le message
    ...il faut regarder ce que fait le compilateur !
    Pour moi le compilateur ne répond pas à ma question posé plus haut i.e où sont mis les résultats des opérations intermédiaire ?

    Je modifie mon message pour me répondre en reprenant ce qu'a dit WhiteCrow: "On charge le registre eax avec la valeur de la variable (l1)" Donc si je comprends bien les calcules se font sur le registre eax si qui permet d'avoir les résultats intermédiaire AND et décalage.

  15. #15
    Membre émérite
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Juillet 2020
    Messages
    352
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Chef de projet NTIC

    Informations forums :
    Inscription : Juillet 2020
    Messages : 352
    Par défaut
    Bon, si tu cherches un tout petit peu les opcodes x86 tu verras que l'opcode AND fonctionne ainsi :
    L'opcode attend deux opérande, la destination et la source, avec la syntaxe utilisée (intel en l'occurence) on écrit AND destination, source. Cette instruction fait un ET bit à bit entre destination et source et stocke le résultat dans destination ⇒ destination = destination AND source.
    En gros, le «résultat est stocké dans un registre». Pour «stocker le résultat d'un AND dans un registre» il faut commencer par placer la valeur de nTmp dans ce registre puis de dire avec quelle valeur tu veux faire un AND bit à bit.

    Sais-tu ce qu'est un registre ? Comment fonctionne un x86 (en très gros) ? quel est le modèle mémoire de C ? …

    Maintenant c'est une façon de faire, il y en a d'autres. On pourrait imaginer un compilateur qui ferait un AND entre deux valeurs dont on donne les adresses. Le choix d'utiliser des registres vient du fait que c'est plus performant et qu'ils sont disponibles à ce moment donné dans le code produit.

    Maintenant il y a aussi d'autre architecture où cela sera fait autrement … tout simplement parce que d'autres architectures pourraient proposer un opcode qui fait d'un coup ce que ton code source demande (un peu à la BSWAP du x86).

  16. #16
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 153
    Billets dans le blog
    4
    Par défaut
    Avant de se soucier de ce que sont les registres etc, tu devrais te concentrer sur comprendre ce qu'est un opérateur binaire, unaire et quand une simple variable est modifiée.
    Et c'est largement suffisant.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  17. #17
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 838
    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 838
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par hbx360 Voir le message
    En fait quand je parle de subtilité/détail c'est juste la question que je pose ci-dessus, pour l'opération nTmp & 0xFF le résultat est mit où ?
    L'unité de calcul s'en occupe

    Citation Envoyé par hbx360 Voir le message
    Si c'est dans la variable nTmp alors on perdra sa valeur de départ
    Bien évidemment. Tant que tu n'écris pas spécifiquement nTmp=... nTmp ne sera jamais modifié même s'il est utilisé dans un calcul

    Citation Envoyé par hbx360 Voir le message
    Se que j'ai compris des masques c'est que si on en met pas on ne pourra pas avoir le résultat voulu parce que si on fait juste nTmp << 8 on perdra l'information de la variable nTmp
    Exact (et c'est vrai quel que soit le calcul)

    Citation Envoyé par hbx360 Voir le message
    puisque la modification sera mis en dure dans nTmp et quand on réutilisera nTmp pour décaler à droite on aura plus la valeur de départ de nTmp.
    Il ne faut pas penser en terme "valeur de départ" mais juste en terme de "valeurs à l'instant T". Quand tu écris ntmp=X tu lui mets une valeur X. Si tu écris ensuite ntmp=Y ben tu lui mets une valeur Y et bien évidemment tu perds la valeur X.

    Citation Envoyé par hbx360 Voir le message
    Pour moi le compilateur ne répond pas à ma question posé plus haut i.e où sont mis les résultats des opérations intermédiaire ?
    Dans l'UAL (Unité Arithmétique et Logique). Elle a ses propres zones de travail lui permettant de calculer X operateur Y. Et puis au bout du compte, est-ce vraiment important ???
    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]

  18. #18
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par hbx360 Voir le message
    Pour moi le compilateur ne répond pas à ma question posé plus haut i.e où sont mis les résultats des opérations intermédiaire ?
    la variable nTmp à son adresse ou bien (nTmp & 0xFF) se trouve tel à un autre endroit de la mémoire même si cela me semble improbable car sa obligerai le compilateur à alloué implicitement de la mémoire à un autre endroit
    Euh.... Si tu te demandes si le compilateur alloue de la mémoire, pour après dire que le compilateur ne répond pas à ta question, il va falloir faire le point dans ta tête sur ce que tu cherches à savoir

    "où sont mis les résultats des opérations intermédiaire ?" = et bien dans le registre que le compilateur aura choisi d'utiliser (ou sur la pile ou là où le compilateur aura décidé)

  19. #19
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 772
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 772
    Par défaut
    Citation Envoyé par hbx360 Voir le message
    Se que j'ai compris des masques c'est que si on en met pas on ne pourra pas avoir le résultat voulu parce que si on fait juste nTmp << 8 on perdra l'information de la variable nTmp puisque la modification sera mis en dure dans nTmp et quand on réutilisera nTmp pour décaler à droite on aura plus la valeur de départ de nTmp.
    Après test, avec la valeur 256
    • (((val & 0xFF) << 8) | ((val & 0xFF00) >> 8)) vaut 1
    • ((val << 8) | (val >> 8)) vaut 65537

    Justement parce que tes décalages ne sont plus sur 1 octet, mais sur la valeur entière (ton cas 2 octets)
    Comme je l'ai dit, tes masques servent juste à extraire 1 seul octet (octet poids fort et octet poids faible)
    Et effectivement dans ton cas, ils servent.

  20. #20
    Membre éclairé
    Profil pro
    Inscrit en
    Avril 2011
    Messages
    447
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2011
    Messages : 447
    Par défaut
    Citation Envoyé par WhiteCrow Voir le message
    Cette instruction fait un ET bit à bit entre destination et source et stocke le résultat dans destination ⇒ destination = destination AND source.
    En gros, le «résultat est stocké dans un registre». Pour «stocker le résultat d'un AND dans un registre» il faut commencer par placer la valeur de nTmp dans ce registre puis de dire avec quelle valeur tu veux faire un AND bit à bit.
    Citation Envoyé par Sve@r Voir le message
    Dans l'UAL (Unité Arithmétique et Logique). Elle a ses propres zones de travail lui permettant de calculer X operateur Y.
    Citation Envoyé par Bktero Voir le message
    "où sont mis les résultats des opérations intermédiaire ?" = et bien dans le registre que le compilateur aura choisi d'utiliser (ou sur la pile ou là où le compilateur aura décidé)
    C'est cette réponse/confirmation que je recherchais quand j'ai posé ma question.

    Citation Envoyé par WhiteCrow Voir le message
    Sais-tu ce qu'est un registre ? Comment fonctionne un x86 (en très gros) ? quel est le modèle mémoire de C ? …
    Non je ne connais pas.

    Citation Envoyé par Sve@r Voir le message
    Et puis au bout du compte, est-ce vraiment important ???
    Oui pour moi sa compte.

    Merci à vous tous pour votre aide et les informations que vous m'avez donné, les explications avec l'assembleur son vraiment intéressante.

    Je met en résolu.

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Décalage de bits
    Par Kraz dans le forum VB 6 et antérieur
    Réponses: 10
    Dernier message: 21/10/2006, 18h09
  2. décalages de bits
    Par seb95 dans le forum Java ME
    Réponses: 4
    Dernier message: 05/03/2006, 04h03
  3. décalage de bits
    Par cedre22 dans le forum Langage
    Réponses: 13
    Dernier message: 17/01/2006, 09h33
  4. Multiplication par décalage de bits
    Par tekman54000 dans le forum Assembleur
    Réponses: 2
    Dernier message: 25/10/2005, 11h35
  5. Décalage de bit sur unsigned char [8]
    Par dboulange dans le forum C++
    Réponses: 14
    Dernier message: 26/07/2005, 14h10

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