J'ai ce tableau
int main(void) { DataStructTypeDef foo; memcpy((void *)&foo, (void *)buffer, sizeof(DataStructTypeDef)); printf("%s", foo.dateOfBirth); // It prints 010119:M:FOO:BAR //printf("%s", foo.dateOfBirth); // Expected value 010119 return 0; }
et je veux le copier champ par champ dans la structure de données
DataStructTypeDef foo; memcpy((void *)&foo, (void *)buffer, sizeof(DataStructTypeDef));
Disons que toutes les longueurs sont fixes (par exemple. firstName
est toujours composé de 4 caractères, lastName
de 3 etc ...)
J'ai utilisé cette approche: p >
typedef struct{ uint8_t firstName[5]; uint8_t pad1; uint8_t lastName[4]; uint8_t pad2; uint8_t dateOfBirth[7]; uint8_t pad3; uint8_t genre; uint8_t pad4; uint8_t car[4]; uint8_t pad5; uint8_t phone[4]; uint8_t pad6; }DataStructTypeDef;
Lorsque j'essaie d'imprimer dateOfBirth
, le tableau entier à partir de 01012019 s'affiche comme ceci
uint8_t *buffer = "JOHN:DOE:010119:M:FOO:BAR";
p >
4 Réponses :
Puisque les membres du char array
que vous copiez ne sont pas terminés par null, printf ("% s",
ne saura pas quand il a rencontré la fin de chaque chaîne.
Ceci peut être contrôlé dans printf
en limitant le nombre de caractères qui s'impriment ...
Par exemple:
printf("%.6s", food.dateOfBirth);
Un équivalent serait:
printf("%.*s", (int)sizeof(foo.dateOfBirth), foo.dateOfBirth);
. *
spécifie la "précision" des caractères que vous souhaitez imprimer. Donc, dans votre cas, dateOfBirth
= précision / taille 6.
%. * s
spécifie le nombre maximum de caractères à afficher. la sortie s'arrêtera à un terminateur nul s'il y en a un avant cette limite.
Avec la structure fixe de
foo.pad1 = foo.pad2 = foo.pad3 = foo.pad4 = foo.pad5 = foo.pad6 = '\0';
Cela fonctionne pour moi:
uint8_t *buffer = "JOHN\000DOE\000010119\000M\000FOO\000BAR";
Le tampon semble horriblement mutilé car si je mets "\ 0" "010119"
comme "\ 0010119"
, il interprète l'échappement dans le mauvais sens. Une meilleure solution serait probablement de le garder comme un seul et d'écrire entièrement la séquence octale comme \000
:
int main(void) { uint8_t *buffer = "JOHN" "\0" "DOE" "\0" "010119" "\0" "M" "\0" "FOO" "\0" "BAR"; DataStructTypeDef foo; memcpy((void *)&foo, (void *)buffer, sizeof(DataStructTypeDef)); printf("%s", foo.dateOfBirth); // Expected value 01012019 }
Ici, chaque \ 000
devient un octet nul et il n'entre pas en conflit avec le 010119
suivant l'une des séquences d'échappement.
Alternativement, cela fonctionne si je prends le tampon d'origine chaîne de "JOHN: DOE: 010119: M: FOO: BAR"
et remplacez simplement tous les :
après la copie, comme ceci:
typedef struct { uint8_t firstName[4]; uint8_t pad1; uint8_t lastName[3]; uint8_t pad2; uint8_t dateOfBirth[6]; uint8_t pad3; uint8_t genre; uint8_t pad4; uint8_t car[3]; uint8_t pad5; uint8_t phone[3]; uint8_t pad6; }DataStructTypeDef;
Essayez-vous vraiment d'initialiser le champ scalaire uint8_t pad1
avec la chaîne "\ 0"?
@AndrewHenle non, ces chaînes sont automatiquement concaténées en une seule chaîne au moment de la compilation. Donc, tous ces "\ 0"
se traduisent en un \ 0
chacun. Je ne l'ai fait que de cette façon car sinon, cela l'interpréterait comme un littéral différent quand il y a un 0
juste après un \ 0
dans le littéral.
Ah. Je l'ai. Je n'ai pas attrapé le ,
manquant entre les champs.
Re «il interprète l'échappement dans le mauvais sens»: En C, une séquence d'échappement octale a un à trois chiffres octaux. Le compilateur interprète correctement ”\ 001"
comme 1. Le zéro peut être désigné par ”\ 000"
.
Je pourrais donc faire fonctionner \ 0
dans la chaîne littérale en transformant toutes ses instances en \ 000
... bon à savoir, merci!
Tous ces "\ 0" se traduisent par '\ 0' '\ 0' et avec cela dépassant le membre à initialiser. UB?
@alk: non, seul un "\ 0" final se traduirait par 2 octets nuls. les chaînes concaténées n'ont pas d'octets nuls incorporés supplémentaires, seulement ceux qui apparaissent explicitement.
@chqrlie: Vous avez raison. Mon esprit a introduit des virgules non existantes. Veuillez excuser.
Après memcpy
ajoutez ceci: foo.pad1 = foo.pad2 = foo.pad3 = foo.pad4 = foo.pad5 = 0;
. Mais j'espère que c'est un exercice, pas une vraie structure pour un vrai travail.
"J'espère que c'est un exercice, pas une véritable structure pour un vrai travail." Qu'est ce qui ne va pas avec ça?
@zerocoldTUN Par exemple: 1) prévoyez-vous d'utiliser uniquement des noms dont le prénom est toujours composé de 4 lettres et le nom de famille toujours 3 lettres? 2) Au lieu de pad1
et pad2
, vous pouvez développer firstName
et lastName
avec 1 octet pour le zéro final. 3) Il est normal d'utiliser le tableau char
pour le nom, pas le tableau d'octets.
En fait, ce n'était qu'un exemple. Je ne traite pas les prénoms et les noms de famille. En fait, j'ai une EEPROM (mémoire) et je l'utilise pour stocker les données de mon produit. ID produit, date de fabrication, numéro de lot etc ... La lecture des données de la mémoire renvoie un tableau de caractères, et c'est moi qui ai défini ces champs. J'utilise des champs de longueur fixe pour chaque donnée par exemple: la date est toujours JJMMAAA et le 1er janvier n'est pas écrit comme ceci: 1119. C'est toujours 010119. Donc oui: je suis sûr à 100% que toutes les tailles de champs sont correctes et c'est pourquoi je voulais le stocker directement dans la structure de données.
@zerocoldTUN: " 100% sûr que toutes les tailles de champ sont correctes " Mais à quoi servent les deux points (:
) à l'intérieur des données?
@alk, vous avez raison. Je peux les supprimer. En fait, au début, j'ai utilisé \ 0. J'ai eu un problème avec les champs qui commencent par un chiffre comme: 010119. Donc j'ai changé le \ 0 avec un: pour tester. J'ai cependant oublié de les supprimer.
@zerocoldTUN: '0'
et \ 0
ne sont pas identiques.
Ajoutez un octet supplémentaire à chaque champ pour accueillir le caractère «\ 0». Par exemple utilisez
uint8_t firstName[4];
au lieu de
uint8_t firstName[5];
Au lieu de copier tout le tampon en une seule fois, copiez les éléments un par un. Puisque la taille de chaque champ est fixe, le décalage depuis le début du tampon est fixe et cela facilite le travail d'analyse.
Essayez de définir manuellement les remplissages sur
'\ 0'
après la copie, commefoo.pad1 = '\ 0';
@Blaze J'ai essayé de changer le remplissage de: à \ 0. Le problème est que dans certains cas, j'ai un champ qui commence par un chiffre comme celui-ci: 01012019:. Si je change le: en \ 0, ce sera \ 001012019.
Attention: "firstName est toujours composé de 4 caractères, lastName de 3 etc ..." est faux, vous avez manqué la place du caractère nul les terminant
@bruno J'utilise un bit de remplissage au lieu du caractère \ 0.
@zerocoldTUN si vous ne terminez pas les chaînes par un caractère nul, vous ne pourrez pas utiliser les fonctions standard en supposant que by est présent, donc pas de printf / strcpy / strdup / ....
uint8_t lastName [3];
signifie que lastName contient 1 ou 2 caractères puis le \ 0, pas 3 caractères@xing J'ai édité le message. Ma faute.
@bruno J'ai ajouté 1 à tous les champs (firstName [4] -> firstName [5] etc.). ont toujours le même problème
@xing Mon but est de remplir la structure pour ne pas imprimer ses valeurs. l'impression est juste à des fins de débogage.
Quelle est ta question? Êtes-vous conscient de la façon dont C imite les "chaînes", qui en fait ne sont pas de type de données en C. code> -arrays .
" utilisant un bit de remplissage " ce que vous appelez un " bit " est en fait un
octet
, ou pour être plus précis ununit8_t
.Remarque: les moulages ne sont pas nécessaires en C pour ce code, suggérez d'utiliser un objet sizeof. Simplification:
memcpy (& foo, buffer, sizeof foo);
Facile à coder correctement, à réviser et à maintenir.En plus de tout ce qui a été mentionné jusqu'à présent, si vous suivez cette route en utilisant une structure, veillez à ce qu'elle soit compressée, sinon des choses étranges pourraient se produire en raison d'un remplissage supplémentaire introduit par le compilateur lui-même.