Domanda

Sto provando a convertire uno struct in un array di caratteri da inviare in rete. Tuttavia, ottengo alcuni output strani dall'array char quando lo faccio.

#include <stdio.h>

struct x
{
   int x;
} __attribute__((packed));


int main()
{
   struct x a;
   a.x=127;
   char *b = (char *)&a;
   int i;
   for (i=0; i<4; i++)
      printf("%02x ", b[i]);
   printf("\n");
   for (i=0; i<4; i++)
      printf("%d ", b[i]);
   printf("\n");
   return 0;
}

Ecco l'output per vari valori di a.x (su un X86 usando gcc):
127:
7f 00 00 00
127 0 0 0

128:
ffffff80 00 00 00
-128 0 0 0

255:
ffffffff 00 00 00
-1 0 0 0

256:
00 01 00 00
0 1 0 0

Comprendo i valori di 127 e 256, ma perché i numeri cambiano quando vanno a 128? Perché non dovrebbe essere solo: 80 00 00 00 128 0 0 0

Sto dimenticando di fare qualcosa nel processo di conversione o sto dimenticando qualcosa sulla rappresentazione dei numeri interi?

* Nota: questo è solo un piccolo programma di test. In un vero programma ho più nella struttura, nomi di variabili migliori e converto in little-endian.
* Modifica: formattazione

È stato utile?

Soluzione

L'identificatore di formato x da solo afferma che l'argomento è un int e poiché il numero è negativo, printf richiede otto caratteri per mostra tutti e quattro i byte diversi da zero del valore di dimensioni int . Il modificatore 0 dice di riempire l'output con zeri e il modificatore 2 dice che l'output minimo dovrebbe essere lungo due caratteri. Per quanto ne so, printf non fornisce un modo per specificare una larghezza massima , ad eccezione delle stringhe.

Ora, stai solo passando un carattere , quindi nudo x dice alla funzione di usare l'intero int che è stato passato invece - a causa della promozione dell'argomento predefinito per " ... " parametri. Prova il modificatore hh per dire alla funzione di trattare l'argomento come solo un char invece:

printf("%02hhx", b[i]);

Altri suggerimenti

Quello che vedi è il segno che preserva la conversione da char a int. Il comportamento deriva dal fatto che sul tuo sistema, char è firmato ( Nota: char non è firmato su tutti i sistemi). Ciò porterà a valori negativi se un modello di bit restituisce un valore negativo per un carattere. Promuovere un tale carattere a un int preserverà il segno e anche l'int sarà negativo. Nota che anche se non inserisci esplicitamente un (int) , il compilatore promuoverà automaticamente il carattere in un int quando passa a printf. La soluzione è convertire prima il tuo valore in unsigned char :

for (i=0; i<4; i++)
   printf("%02x ", (unsigned char)b[i]);

In alternativa, puoi utilizzare unsigned char * dall'inizio:

unsigned char *b = (unsigned char *)&a;

E quindi non è necessario alcun cast nel momento in cui lo si stampa con printf.

char è un tipo firmato; quindi con il complemento a due, 0x80 è -128 per un numero intero a 8 bit (ovvero un byte)

Trattare la struttura come se fosse un array di caratteri è un comportamento indefinito. Per inviarlo tramite la rete, utilizzare invece la serializzazione corretta. È un dolore in C ++ e ancora di più in C, ma è l'unico modo in cui la tua app funzionerà indipendentemente dalle macchine che leggono e scrivono.

http://en.wikipedia.org/wiki/Serialization#C

La conversione della struttura in caratteri o byte nel modo in cui lo stai facendo porterà a problemi quando provi a renderlo neutro in rete. Perché non affrontare questo problema ora? Esistono diverse tecniche che è possibile utilizzare, tutte probabilmente più "portatili" di quello che stai cercando di fare. Ad esempio:

  • L'invio di dati numerici attraverso la rete in modo indipendente dalla macchina è stato a lungo trattato, nel mondo POSIX / Unix, tramite le funzioni htonl , htons , < code> ntohl e ntohs . Vedi, ad esempio, il pagina di manuale byteorder (3) su un sistema FreeBSD o Linux.
  • Anche la conversione di dati da e verso una rappresentazione completamente neutra come JSON è perfettamente accettabile. La quantità di tempo impiegata dai programmi per convertire i dati tra JSON e moduli nativi è destinata a impallidire rispetto alle latenze di trasmissione della rete.

char è un tipo firmato quindi quello che stai vedendo è la rappresentazione a due complimenti, il casting in (unsigned char *) risolverà ciò (Rowland mi ha appena battuto).

In una nota a margine potresti voler cambiare

for (i=0; i<4; i++) {
//...
}

a

for (i=0; i<sizeof(x); i++) {
//...
}

La firma dell'array char non è la radice del problema! (È -a- problema, ma non l'unico problema.)

Allineamento! Questa è la parola chiave qui. Ecco perché non dovresti MAI provare a trattare le strutture come la memoria grezza. Complier (e varie bandiere di ottimizzazione), sistemi operativi e fasi lunari fanno tutti cose strane ed eccitanti nella posizione effettiva in memoria di "adiacente". campi in una struttura. Ad esempio, se si dispone di una struttura con un carattere seguito da un int, l'intera struttura avrà OTTO byte in memoria: il carattere, 3 byte vuoti, inutili e quindi 4 byte per l'int. Alla macchina piace fare cose del genere in modo che le strutture possano adattarsi perfettamente alle pagine di memoria e simili.

Segui un corso introduttivo sull'architettura delle macchine nel tuo college locale. Nel frattempo, serializza correttamente. Non trattare mai strutture come array di caratteri.

Quando vai a inviarlo, usa semplicemente:

(char *) & amp; CustomPacket

per convertire. Funziona per me.

Potresti voler convertire in un array di caratteri non firmato.

A meno che tu non abbia misure molto convincenti che dimostrino che ogni ottetto è prezioso, non farlo . Utilizza un protocollo ASCII leggibile come SMTP , NNTP o uno dei tanti altri protocolli Internet codificati dall'IETF.

Se devi davvero avere un formato binario, non è ancora sicuro solo eliminare i byte in una struttura, perché l'ordine dei byte, le dimensioni di base o i vincoli di allineamento possono differire da host a host. È necessario progettare la protezione del filo in modo da utilizzare dimensioni ben definite e utilizzare un ordine di byte ben definito. Per la tua implementazione, usa macro come ntohl (3) o usa lo spostamento e il mascheramento per inserire byte nel tuo stream. Qualunque cosa tu faccia, assicurati che il tuo codice produca gli stessi risultati su host sia big-endian che little-endian.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top