Je communique avec une carte qui nécessite que je lui envoie 2 octets signés.
explication du type de données
ce que j'ai besoin d'envoyer
Aurais-je besoin d'une manipulation au niveau du bit ou puis-je simplement envoyer un entier 16 bits comme suit?
int16_t rc_min_angle = -90; int16_t rc_max_angle = 120; write(fd, &rc_min_angle, 2); write(fd, &rc_max_angle, 2);
4 Réponses :
int16_t
a la taille correcte mais peut ou peut ne pas être la bonne endianness. Pour garantir un ordre little endian, utilisez des macros telles que celles de endian.h :
#define _BSD_SOURCE #include <endian.h> ... uint16_t ec_min_angle_le = htole16(ec_min_angle); uint16_t ec_max_angle_le = htole16(ec_max_angle); write(fd, &ec_min_angle_le, 2); write(fd, &ec_max_angle_le, 2);
Ici htole16
signifie "host to little endian 16-bit". Il convertit l'endianness natif de la machine hôte en little endian: si la machine est big endian, il échange les octets; si c'est little endian c'est un no-op.
Notez également que vous devez passer l'adresse des valeurs à write ()
, pas les valeurs elles-mêmes. Malheureusement, nous ne pouvons pas intégrer les appels et écrire write (fd, htole16 (ec_min_angle_le), 2)
.
Pas d'accord avec "Malheureusement, nous ne pouvons pas intégrer les appels". Exemple de compteur write (fd, & (uint16_t) {htole16 (ec_min_angle)}, 2);
De même, d'autres plates-formes ont des fonctions hton () et ntoh () dans leurs bibliothèques C, pour placer l'échange d'octets dans le contexte du remplissage / vidage du tampon réseau stackoverflow.com/q/15863775 . "Host to network", et vice versa.
@vvaltchev Par en ligne , le répondeur voulait probablement dire «écrire l'appel en ligne» (sur une seule ligne), et non «étendre la fonction appelée en ligne».
@ user4815162342 Je suis tout à fait d'accord avec vous, je viens de lire le texte de loin trop rapidement, manquant cela. Je me concentrais sur htole16 ()
, alors que tout le "commentaire en ligne" concernait en fait l'interface de write ()
, pas htole16 ()
. Quoi qu'il en soit, c'est amusant d'observer à quelle vitesse la discussion est sortie du sujet, avec plusieurs commentaires sur l '"inlining" du 2ème argument de write ()
, ce qui est totalement hors de propos, étant donné la question :-)
@vvaltchev Bon point sur le hors-sujet - l'utilisation de write ()
a également généré une discussion similaire sous réponse. Ici c'est un peu mieux car l'expression inline n'est pas complètement hors sujet, car elle affecte l'ergonomie.
Si vous n'avez pas besoin de l'avoir de type générique, vous pouvez simplement faire:
#include <stdint.h> #include <unistd.h> /*most optimizers will turn this into `return 1;`*/ _Bool little_endian_eh() { uint16_t x = 1; return *(char *)&x; } void swap2bytes(void *X) { char *x=X,t; t=x[0]; x[0]=x[1]; x[1]=t; } int main() { int16_t rc_min_angle = -90; int16_t rc_max_angle = 120; //this'll very likely be a noop since most machines //are little-endian if(!little_endian_eh()){ swap2bytes(&rc_min_angle); swap2bytes(&rc_max_angle); } //TODO error checking on write calls int fd =1; write(fd, &rc_min_angle, 2); write(fd, &rc_max_angle, 2); }
Pour envoyer des données little-endian, vous pouvez simplement générer les octets manuellement:
int write_le(int fd, int16_t val) { unsigned char val_le[2] = { val & 0xff, (uint16_t) val >> 8 }; int nwritten = 0, total = 2; while (nwritten < total) { int n = write(fd, val_le + nwritten, total - nwritten); if (n == -1) return nwritten > 0 ? nwritten : -1; nwritten += n; } return nwritten; }
Un bon compilateur reconnaîtra que le code ne fait rien et compilera la manipulation de bits en no-op sur un plateforme little-endian. (Voir par exemple gcc générant le même code pour la variante avec et sans le bit-twiddling.)
Notez également que vous ne devez pas ignorer la valeur de retour de write ( )
- non seulement il peut rencontrer une erreur, mais il peut aussi écrire moins que ce que vous lui avez donné, auquel cas vous devez répéter l'écriture.
OK très bien. Pourtant, si le code examine la valeur de retour de write (fd, val_le, 2)
, alors pourquoi pas aussi la valeur de retour de write (fd, val_le + 1, 1);
? Cela semble assez clair pour simplement coder return write (fd, val_le, 2);
et laisser l'appelant décider de la gestion des erreurs.
@chux Vous avez raison, l'examen du deuxième write ()
ne peut être évité. return write (fd, val_le, 2)
est insuffisant car un valide write ()
peut renvoyer 1, ce n'est tout simplement pas un cas d'erreur.
Si les fonctions endian ne sont pas disponibles, écrivez simplement les octets dans le petit ordre endian.
Peut-être avec un littéral composé .
// v------------- compound literal ---------------v write(fd, &(uint8_t[2]){rc_min_angle%256, ec_min_angle/256}, 2); write(fd, &(uint8_t[2]){rc_max_angle%256, ec_max_angle/256}, 2); // ^-- LS byte ---^ ^-- MS byte ---^ // &
J'ai ajouté le &
en supposant que write ()
est similaire à write (2) - Linux .
@CostantinoGrana Le manuel dit "Unités: 0,02197265625 degré." Je n'ai aucune idée de ce que signifient ces 2 chiffres mais je pense que c'est en degrés?
0.02197265625 est 45/2048. Les unités sont 45 / 2048e d'un degré? Faites attention!
Certains compilateurs ont des extensions pour définir les variables comme big ou little-endian. Ceci est très utile car certaines architectures, comme powerpc, sont capables de lire / écrire des entiers dans les deux modes.
@JohnKugelman est-ce que cette virgule est une faute de frappe dans le manuel? La période aurait plus de sens.
C'est un point décimal de style européen, je suppose.
@JohnKugelman Je ne vois plus mon commentaire original. Bref, je n'étais pas trop loin: ce ne sont pas des radians, mais c'est un point fixe même si je ne comprends pas pourquoi 45 / 2048e de degré au lieu d'un simple 1 / 2048e.
@bakalolo Comme l'a dit John Kugelman, vous devez prendre vos diplômes, les multiplier par 2048, puis les diviser par 45. Donc -90 devient -4096, 120 devient 5461 et ainsi de suite.