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é.
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.
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
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.
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