Hier ist mein 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);
}
Wenn Sie gcc 8.3.0 oder 8.2.1 mit einer beliebigen Optimierungsstufe verwenden -O0
, wird dies ausgegeben , 0 2
wenn ich es erwartet habe 2 2
. Der Compiler hat entschieden, dass das strlen
an b[0]
den Wert, durch den geteilt wird, gebunden ist und ihn daher niemals erreichen oder überschreiten kann.
Ist das ein Fehler in meinem Code oder ein Fehler im Compiler?
Dies ist im Standard nicht klar formuliert, aber ich dachte, die gängige Interpretation der Zeigerherkunft war, dass X
der Code für jedes Objekt (char *)&X
einen Zeiger generieren sollte , der über die gesamte Iteration iterieren kann X
- dieses Konzept sollte auch dann gelten, wenn X
dies der Fall ist Sub-Arrays als interne Struktur.
(Bonusfrage, gibt es ein gcc-Flag, um diese spezielle Optimierung auszuschalten?)
2 2
unter verschiedenen Optionen.s.b
beschränkt ist, gibtb[0]
es zwei Optionen: (1) Zugriff außerhalb der Grenzen, falls 8 Nicht-Null-Zeichen vorhanden sind, dh UB, (2) gibt es ein Null-Zeichen, in dem Die Länge ist kleiner als 8, daher ergibt das Teilen durch 8 Null.Antworten:
Es gibt einige Probleme, die ich sehen kann und die davon abhängen können, wie sich der Compiler für das Layout des Speichers entscheidet.
Im obigen Code
s.b
befindet sich ein Array mit 23 Einträgen aus einem Array mit 8 Zeichen. Wenn Sie sich nur auf beziehen, erhaltens.b
Sie die Adresse des ersten Eintrags im 23-Byte-Array (und das erste Byte im 8-Zeichen-Array). Wenn der Code sagt&s.b
, werden Sie nach der Adresse der Adresse des Arrays gefragt. Unter dem Deckmantel generiert der Compiler höchstwahrscheinlich einen lokalen Speicher, speichert die Adresse des Arrays darin und liefert die Adresse des lokalen Speichers anstrlen
.Sie haben 2 mögliche Lösungen. Sie sind:
oder
Ich habe auch versucht, Ihr Programm auszuführen und das Problem zu demonstrieren, aber sowohl das Klirren als auch die Version von gcc, die ich mit allen
-O
Optionen habe, funktionierten immer noch wie erwartet. Für das, was es wert ist, verwende ich Clang Version 9.0.0-2 und GCC Version 9.2.1 unter x86_64-pc-linux-gnu.quelle
Der Code enthält Fehler.
ist zum Beispiel riskant, obwohl s mit b beginnt sollte sein:
Das zweite strlen () hat ebenfalls Fehler
Zum Beispiel sollte sein:
Die Zeichenfolge sb sollte bei korrekter Kopie 17 Buchstaben lang sein. Nicht sicher, wie Strukturen im Speicher gespeichert werden, wenn sie ausgerichtet sind. Haben Sie überprüft, ob jdn tatsächlich die 17 kopierten Zeichen enthält?
Ein Strlen (jdn) sollte also 17 zeigen
Der printf zeigt nur Ganzzahlen an, da% d eine Ganzzahl ist und die Variable n als Ganzzahl deklariert ist. sizeof (BUF) sollte 8 sein
Eine 17 geteilt durch 8 (17/8) sollte also 2 ausgeben, da n als Ganzzahl deklariert ist. Da memcpy zum Kopieren von Daten nach s und nicht nach sb verwendet wurde, würde ich vermuten, dass dies mit Speicherausrichtungen zu tun hat. Angenommen, es handelt sich um einen 64-Bit-Computer, kann eine Speicheradresse 8 Zeichen enthalten.
Nehmen wir zum Beispiel an, dass jemand ein Malloc (1) aufgerufen hat, als der nächste "freie Speicherplatz" nicht ausgerichtet ist ...
Der zweite strlen-Aufruf zeigt die korrekte Nummer an, da die Zeichenfolgenkopie in die s-Struktur anstatt in sb durchgeführt wurde
quelle