L'histoire du portage du code de C vers Go (et vers la spécification du langage)

Contexte

Nous, les programmeurs, portons souvent le code source entre différents systèmes de traitement du langage. En ce qui concerne la syntaxe, le compilateur la garantit, donc je n'y rentre pas vraiment.

Cependant, pour l'évaluation, dans une syntaxe similaire, Les résultats de l'évaluation peuvent différer selon la langue, et je pense que parfois j'en suis accro.

Cette fois, lors du portage de C vers Go, en raison de différences dans les spécifications linguistiques Écrivez une histoire qui crée une dépendance pour obtenir le résultat escompté.

"Gununu ... Je l'ai transplanté de la même manière, peu importe comment je le regardais ..."

Par exemple, ce code C

main.c


// This is main.c

#include <stdio.h>

void main () {
    unsigned char  a;
    unsigned char  b;
    unsigned short c;

    a = 0x12;
    b = 0x34;
    c = 0x0000;

    c |= (unsigned short)(a << 8);
    c |= (unsigned short)(b << 0);

    printf("c is 0x%04X\n", c);
}

Je l'ai porté sur ce code Go.

main.go


package main

import "fmt"

func main() {
    var a uint8
    var b uint8
    var c uint16

    a = 0x12
    b = 0x34
    c = 0x0000;

    c |= uint16(a << 8)
    c |= uint16(b << 0)

    fmt.Printf("c is 0x%04X\n", c)
}

Il s'agit d'une procédure courante pour combiner les bits de deux variables.

"D'accord, j'ai écrit le casting de C de la même manière, Peu importe comment vous le regardez, il devrait fonctionner exactement à partir du même code !! "

Je l'ai couru.

$ gcc -o main.exe main.c
$ main.exe
c is 0x1234
$ go build -o main.exe
$ main.exe
c is 0x0034

"Oh ... le résultat de l'exécution est différent ... ??"

"La source C de la source de portage a le cast correctement écrit, Go devrait se mettre en colère s'il n'a pas de moule ou de plâtre en premier lieu ... Je ne sais pas, je ne sais pas ... "

Quand on y réfléchit plus tard, ce n'est pas surprenant (même si c'était en fait un programme plus compliqué). Je me suis tordu le cou pendant environ 30 minutes avec ça.

"Hmm ... ?? C'est ..."

Quand j'ai tourné la tête pendant 30 minutes (en fait, j'étais désespérément en train de déboguer) ...

"Oh, ce ʻa << 8` en C a été étendu à int." "La raison pour laquelle les programmes C fonctionnent est parce que les opérations entières sont implicitement étendues à int ..."

"Mais il n'y a pas un tel comportement implicite dans Now Go ..."

main.c


    c |= (unsigned short)(a << 8);
    c |= (unsigned short)(b << 0);

C'est vrai. Le langage C est l'arrière-plan de l'époque où la langue est née, Le système de traitement du langage étant un standard dépendant du processeur Faites souvent un «comportement indéfini», un transtypage implicite, une expansion d'entiers, etc.

Après tout, dans Go, le code ci-dessus est un mauvais code, Une petite croyance m'a retardé de comprendre pourquoi le code de Go ne fonctionnait pas.

Le problème est donc que Go a dû écrire explicitement une extension de type comme celle-ci:

main.go


    c |= uint16(a) << 8 //Variable 8 bits`<<`Tapez cast en une variable 16 bits avant que l'opération ne soit évaluée
    c |= uint16(b) << 0

Les deux programmes sont désormais évalués de manière égale.

$ gcc -o main.exe main.c
$ main.exe
c is 0x1234
$ go build -o main.exe
$ main.exe
c is 0x1234

Vérifiez les spécifications linguistiques

Donc, jusqu'à présent, je l'ai traité dans ma mémoire. Quelles sont les spécifications de chaque langue en premier lieu?

Cette fois, les spécifications linguistiques du système de traitement («C», «Go») effectivement utilisées Voyons comment il est défini pour se comporter lorsque ce type de traitement est effectué.

C

Ce qui suit est renvoyé à ISO / CEI 9899 (norme internationale du langage C: C99), shift C'est une spécification de calcul.

#À propos du fonctionnement de Bit shift
6.5.7 Bitwise shift operators
(Omission)
Semantics
3 The integer promotions are performed on each of the operands. The type of the result is
that of the promoted left operand. If the value of the right operand is negative or is
greater than or equal to the width of the promoted left operand, the behavior is undefined.

(Traduit par l'auteur)
Si l'opérande droit dépasse la largeur en bits de l'opérande gauche "étendu"
Si le deuxième opérande a une valeur négative, le comportement n'est pas défini.

#À propos de l'expansion d'entiers
If an int can represent all values of the original type, the value is converted to an int;
otherwise, it is converted to an unsigned int. These are called the integer
promotions.

(Traduit par l'auteur)
Si int peut représenter toutes les valeurs du type d'origine, la valeur est convertie en int.
Sinon, il sera converti en un int non signé. Elles sont"integer promotion" (Extension entière)Est appelé.

Voici la spécification (opération de valeur entière) de l'opération de décalage référencée à partir de la référence Go.

Go

Integer overflow
For unsigned integer values, the operations +, -, *, and << are computed modulo 2n,
where n is the bit width of the unsigned integer's type. Loosely speaking,
these unsigned integer operations discard high bits upon overflow, and programs may rely on "wrap around".

(Traduit par l'auteur)
(Omission)L'arithmétique des entiers non signés tronque les bits de poids fort et «s'enroule» en cas de débordement.
(0xFF pour les entiers 8 bits+ 0x01 =Que ce sera 0x00)

Encore une fois, dans les opérations de décalage, C est implicitement étendu à int, alors que Go n'a pas de comportement implicite et définit clairement le comportement.

Résumé

Go est parfois appelé meilleur C, mais comme dans cet exemple, le comportement réel n'est pas explicite au niveau de la syntaxe. Dans de tels cas, si le document principal est fourni dans un format facile à lire, Je pensais que se référer aux spécifications linguistiques conduirait à une compréhension rapide, fiable et plus approfondie des spécifications linguistiques.

reference open-std.org ISO/IEC 9899:TC3 The Go Programming Language Specification

Recommended Posts

L'histoire du portage du code de C vers Go (et vers la spécification du langage)
Une histoire sur le portage du code de "Essayez de comprendre comment fonctionne Linux" sur Rust
Histoire de passer de Pipenv à la poésie
Aller à la langue pour voir et se souvenir du langage Partie 7 C en langage GO
J'ai essayé d'illustrer le temps et le temps du langage C
L'histoire de la copie de données de S3 vers TeamDrive de Google
Après tout, l'histoire du retour de Linux à Windows
Aller au langage pour voir et se souvenir de la partie 8 Appeler le langage GO à partir de Python
L'histoire de Python et l'histoire de NaN
Une histoire d'essayer d'améliorer le processus de test d'un système vieux de 20 ans écrit en C
De l'introduction de l'API GoogleCloudPlatform Natural Language à son utilisation
Langage C pour voir et se souvenir de la partie 1 Appeler le langage C depuis Python (bonjour le monde)
J'ai essayé d'extraire et d'illustrer l'étape de l'histoire à l'aide de COTOHA
Essayez d'écrire du code python pour générer du code go - Essayez de porter JSON-to-Go et ainsi de suite
Langage C pour voir et se souvenir de la partie 4 Appelez le langage C depuis Python (argument) double
Langage C pour voir et se souvenir de la partie 5 Appel du langage C à partir du tableau Python (argument)
L'histoire d'essayer de reconnecter le client
L'histoire de la mise en place de MeCab dans Ubuntu 16.04
Portage et modification du solveur de doublets de python2 vers python3.
L'histoire d'essayer deep3d et de perdre
L'histoire du changement de pep8 en pycodestyle
Comment sensibiliser VS Code à l'environnement venv et à ses avantages
Essayez de porter le programme «Programmation informatique numérique FORTRAN77» vers C et Python (partie 1)
[Langage C] Faites attention à la combinaison de l'API de mise en mémoire tampon et de l'appel système sans mise en mémoire tampon
Essayez de porter le programme "FORTRAN77 Numerical Computing Programming" vers C et Python (partie 3)
Essayez de porter le programme "FORTRAN77 Numerical Computing Programming" vers C et Python (partie 2)
Langage C pour voir et se souvenir de la partie 3 Appelez le langage C depuis Python (argument) c = a + b
L'histoire du passage du système Web Azure App Service de Windows à Linux
L'histoire d'essayer Sourcetrail × macOS × VS Code
Liste de code Python à déplacer et à mémoriser
Je veux créer du code C ++ à partir de code Python!
De l'introduction de pyethapp à l'exécution du contrat
Comment afficher la date de modification d'un fichier en langage C jusqu'à nanosecondes
L'histoire du serveur Web et du DAG d'Airflow, dont le chargement prend beaucoup de temps
Résumé du début au chapitre 1 de l'introduction aux modèles de conception appris en langage Java
Extraire des images et des tableaux de pdf avec python pour réduire la charge de reporting
L'histoire du passage de WoSign à Let's Encrypt pour un certificat SSL gratuit
L'opérateur and de l'expression d'évaluation de Python semble être évalué à partir de l'expression de gauche
Une histoire sur la tentative de contribuer à l'analyse COVID-19 avec l'offre gratuite d'AWS et l'échec
Comment limiter la publication de l'API dans la bibliothèque partagée en langage C de Linux
L'histoire du lancement d'un serveur Minecraft depuis Discord
Le mur lors du passage du service Django de Python 2.7 à la série Python 3
L'histoire de Python sans opérateurs d'incrémentation et de décrémentation.
Le processus d'installation d'Atom et de l'exécution de Python
[Mis à jour de temps en temps] Examen de Let Code NumPy
Récupérer le code retour d'un script Python depuis bat
Points Python du point de vue d'un programmeur en langage C
[Langage C] [Linux] Récupère la valeur de la variable d'environnement
La première étape pour obtenir Blender disponible à partir de Python
J'ai senti que j'avais porté le code Python en C ++ 98.
L'histoire de la conversion automatique du langage de TypeScript / JavaScript / Python
L'histoire de vouloir acheter une aventure en forme de bague
L'histoire de l'utilisation de Circleci pour construire des roues Manylinux
Je suis à Singapour en ce moment Une histoire sur la création d'un LineBot et la volonté de faire un travail mémorable
[Linux] [C / C ++] Comment obtenir la valeur d'adresse de retour d'une fonction et le nom de fonction de l'appelant
Je veux me débarrasser des avertissements d'importation de Pyright et pylint dans VSCode
Scraping Go To EAT membres magasins dans la préfecture d'Osaka et conversion au format CSV
J'ai considéré la méthode d'apprentissage automatique et son langage d'implémentation à partir des informations de balise de Qiita