9
votes

Copier un réseau de caractères de 4 éléments dans un entier en C

Un char est 1 octet et un entier est de 4 octets. Je veux copier des octets-octets d'un char [4] dans un entier. J'ai pensé à différentes méthodes, mais je reçois des réponses différentes. xxx

sortie est 6513249 1633837824 6513249

Lequel est correct? Qu'est-ce qui va mal?

c

2 commentaires

Le premier moyen est similaire à faire un «code> union et, car les réponses ci-dessous indiquent que l'endansion des processeurs.


Utilisez printf ("% 08x% 08x% 08x \ n", A, B, C); et remarquez comment tous les mêmes octets sont là, mais dans un ordre différent.


6 Réponses :


1
votes
unsigned int a = *(unsigned int*)str;
This initialization is not correct and invokes undefined behavior. It violates C aliasing rules an potentially violates processor alignment.

0 commentaires

1
votes

Les deux sont corrects d'une manière:

  • Votre première solution Copies dans l'ordre d'octet natif (c'est-à-dire l'ordre d'octets de l'article de la CPU) et peut donc donner des résultats différents en fonction du type de processeur.

  • Votre deuxième solution Copies dans Big Endian Octet Order (c'est-à-dire l'octet le plus important à l'adresse la plus basse), quelle que soit la CPU utilise. Cela donnera la même valeur sur tous les types de processeurs.

    Qu'est-ce qui est correct dépend de la manière dont les données d'origine (matrice de char) sont censées être interprétées.
    Par exemple. Le code Java (fichiers de classe) utilise toujours l'ordre d'octet de Big Endian (peu importe ce que l'utilisation de la CPU utilise). Donc, si vous souhaitez lire int S à partir d'un fichier de classe Java, vous devez utiliser la deuxième manière. Dans d'autres cas, vous voudrez peut-être utiliser le moyen dépendant du processeur (je pense que MATLAB écrit int s dans l'ordre d'octet natif dans les fichiers, cf Cette question ).


4 commentaires

Les deux premiers peuvent provoquer des accidents. Cela devrait être mentionné dans n'importe quelle réponse. Ni est correct.


@Eric Postpischil: 1ère méthode : l'alignement est un problème complètement différent qui n'a rien à faire la question originale des OPS. Dans de très nombreux cas (c'est-à-dire sur de nombreuses plates-formes matérielles), l'alignement n'a pas d'importance du tout et le code comme celui-ci est totalement correct. 2e Way : cela n'entraînera certainement pas un accident en aucune circonstance (peu importe si INT est suffisamment grand pour la valeur déplacée par 24 bits)


L'alignement est important et a à voir avec la question initiale de l'OP: aliasing A char en tant que INT n'est pas garanti pour être conforme aux exigences d'alignement et peut se bloquer dans certaines implément . Le fait qui ne se bloque pas sur de nombreuses plateformes ne le rend pas bien, car il n'efface pas le fait qu'il écrase certains.


La deuxième façon peut déborder dans str [0] << 24 . str [0] est un char , il est donc favorisé à int (sauf éventuellement dans des implémentations perverses cées dans lesquelles un int n'est pas plus large qu'un Char ). Ceci est un entier signé. Ensuite, le transfert de 24 bits peut déborder la plage d'un int . Par exemple, si str [0] est 128, puis str [0] << 24 serait 2147483648, mais la valeur la plus importante représentable par un 32 bits int est 2147483647. Le comportement de débordement avec des entiers signés n'est pas défini par la norme C. Le programme peut planter ou produire des résultats incorrects.



15
votes

C'est un Endansness problème. Lorsque vous interprétez le char * code> en tant que int * code> Le premier octet de la chaîne devient l'octet le moins important de l'entier (parce que vous avez exécuté ce code sur x86 qui est petit endian ), pendant la conversion manuelle, le premier octet devient le plus significatif.

Pour mettre cela dans des images, c'est le tableau source: P>

union {
    char str[4];
    unsigned int ui;
} u;

strcpy(u.str, "abc");
printf("%u\n", u.ui);


1 commentaires

Merci. La photo le rend très clair. La réponse que je voulais était celle avec des octets placés manuellement. BTW, vous avez créé une image TYPO-0X64 dans une image au lieu de 0x63.



1
votes

Vous avez dit que vous souhaitez copier octet-byte.

Cela signifie que la ligne non signée int a = * (non signé int *) str; code> n'est pas autorisé. Cependant, ce que vous faites est un moyen assez courant de lire un tableau comme un type différent (comme lorsque vous lisez un flux à partir d'un disque. P>

Il a juste besoin de peaufiner: p>

void 
changeEndian32(void * data)
{
    uint8_t * cp = (uint8_t *) data;
    union 
    {
        uint32_t word;
        uint8_t bytes[4];
    }temp;

    temp.bytes[0] = cp[3];
    temp.bytes[1] = cp[2];
    temp.bytes[2] = cp[1];
    temp.bytes[3] = cp[0];
    *((uint32_t *)data) = temp.word;
}


2 commentaires

Pour les membres de l'Union, les résultats sont dépendants de la mise en œuvre si quelque chose est stocké comme un type et extrait d'une autre.


@Altermann - Je ne savais pas ça. Je suis intéressé d'apprendre plus. Avez-vous une référence? Mon C est presque toujours "dépendant de la mise en œuvre" alors je suis heureux de faire remarquer ces choses.



6
votes

aucun des deux premiers n'est correct.

La première violente les règles d'aliasing et peut échouer car l'adresse de str code> n'est pas correctement alignée pour un non signé INT code>. Pour réinterpréter les octets d'une chaîne en tant que non signé INT code> avec l'ordre d'octet du système hôte, vous pouvez le copier avec memcpy code>: p> xxx pré >

(présumer la taille d'un non signé INT code> et la taille de str code> sont les mêmes.) p>

La seconde peut échouer avec un débordement entier parce que str [0] code> est favorisé à un int code>, donc str [0] a type int code> int code> , mais la valeur requise par le décalage peut être plus grande que représente dans un int code>. Pour remédier à cela, utilisez: p>

unsigned int b = (unsigned int) str[0] << 24 | …;


0 commentaires

0
votes

Si votre utilisation du compilateur CVI (National Instruments), vous pouvez utiliser la fonction Scan pour le faire:

non signé int a;

Pour Big Endian: Numériser (Str, "% 1i [b4uzi1o3210]>% i", & a);

Pour Little Endian: Numériser (Str, "% 1i [b4uzi1o0123]>% i", & a);

Le modificateur O spécifie l'ordre d'octets. I à l'intérieur des crochets indique où commencer dans le tableau STR.


0 commentaires