Voici mon code:
#include <string.h>
#include <stdio.h>
typedef char BUF[8];
typedef struct
{
BUF b[23];
} S;
S s;
int main()
{
int n;
memcpy(&s, "1234567812345678", 17);
n = strlen((char *)&s.b) / sizeof(BUF);
printf("%d\n", n);
n = strlen((char *)&s) / sizeof(BUF);
printf("%d\n", n);
}
Utiliser gcc 8.3.0 ou 8.2.1 avec n'importe quel niveau d'optimisation sauf -O0
, cela sort 0 2
quand je m'y attendais 2 2
. Le compilateur a décidé que le strlen
est limité à b[0]
et ne peut donc jamais égaler ou dépasser la valeur divisée par.
Est-ce un bug dans mon code ou un bug dans le compilateur?
Ce n'est pas clairement énoncé dans la norme, mais je pensais que l'interprétation générale de la provenance du pointeur était que pour tout objet X
, le code (char *)&X
devrait générer un pointeur qui peut itérer sur l'ensemble de X
- ce concept devrait tenir même s'il X
se trouve sous-tableaux comme structure interne.
(Question bonus, existe-t-il un indicateur gcc pour désactiver cette optimisation spécifique?)
2 2
sous diverses options.s.b
est limité,b[0]
il est limité à 8 caractères, et donc deux options: (1) accès hors limite dans le cas où il y a 8 caractères non nuls, qui est UB, (2) il y a un caractère nul, dans lequel le len est inférieur à 8, donc la division par 8 donne zéro. Ainsi, la compilation du compilateur (1) + (2) peut utiliser l'UB pour donner le même résultat aux deux casRéponses:
Il y a certains problèmes que je peux voir et ils peuvent être affectés par la façon dont le compilateur décide de disposer la mémoire.
Dans le code ci-dessus
s.b
est un tableau de 23 entrées d'un tableau de 8 caractères. Lorsque vous vous référez simplement às.b
vous obtenez l'adresse de la première entrée dans le tableau de 23 octets (et le premier octet dans le tableau de 8 caractères). Lorsque le code dit&s.b
, cela demande l'adresse de l'adresse du tableau. Sous les couvertures, le compilateur génère plus que probablement un stockage local, stockant l'adresse de la baie là-dedans et fournissant l'adresse du stockage local àstrlen
.Vous avez 2 solutions possibles. Elles sont:
ou
J'ai également essayé d'exécuter votre programme et de démontrer le problème, mais clang et la version de gcc que j'ai avec toutes les
-O
options fonctionnaient toujours comme prévu. Pour ce que ça vaut, j'utilise la version clang 9.0.0-2 et la version gcc 9.2.1 sur x86_64-pc-linux-gnu).la source
Il y a des erreurs dans le code.
par exemple, est risqué, même si s commence par b devrait être:
Le deuxième strlen () contient également des erreurs
par exemple, devrait être:
La chaîne sb, si copiée correctement, doit comporter 17 lettres. Vous ne savez pas comment les structures sont stockées en mémoire, si elles sont alignées. Avez-vous vérifié que sb contient bien les 17 caractères copiés?
Un strlen (sb) devrait donc afficher 17
Le printf n'affiche que des nombres entiers, car% d est un entier et la variable n est déclarée comme un entier. sizeof (BUF), devrait être 8
Ainsi, un 17 divisé par 8 (17/8) devrait afficher 2 car n est déclaré entier. Comme memcpy était utilisé pour copier des données vers s et non vers sb, je suppose que cela a à voir avec les alignements de mémoire; en supposant qu'il s'agit d'un ordinateur 64 bits, il peut y avoir 8 caractères sur une adresse mémoire.
Par exemple, supposons que quelqu'un a appelé un malloc (1), que le prochain "espace libre" ne soit pas aligné ...
Le deuxième appel strlen, affiche le numéro correct, car la copie de la chaîne a été effectuée dans la structure s au lieu de sb
la source