Kann ich ein Array “übererweitern”, indem ich der umschließenden Struktur mehr Platz zuweise?

Lesezeit: 7 Minuten

Ehrlich gesagt, ist ein solcher Code gültig oder erzeugt er UB?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct __attribute__((__packed__)) weird_struct
{
    int some;
    unsigned char value[1];
};

int main(void)
{
    unsigned char text[] = "Allie has a cat";
    struct weird_struct *ws =
        malloc(sizeof(struct weird_struct) + sizeof(text) - 1);
    ws->some = 5;
    strcpy(ws->value, text);
    printf("some = %d, value = %s\n", ws->some, ws->value);
    free(ws);
    return 0;
}

http://ideone.com/lpByQD

Ich würde nie glauben, dass es für so etwas gültig ist, aber es scheint, dass SystemV-Nachrichtenwarteschlangen genau das tun: siehe Manpage.

Also, wenn SysV-Nachrichtenwarteschlangen das können, kann ich das vielleicht auch? Ich denke, ich würde das nützlich finden, um Daten über das Netzwerk zu senden (daher die __attribute__((__packed__))).

Oder ist dies vielleicht eine bestimmte Garantie für SysV-Nachrichtenwarteschlangen, und ich sollte so etwas nicht anderswo tun? Oder vielleicht kann diese Technik angewendet werden, nur mache ich es falsch? Ich dachte mir, ich frage besser.

Dies - 1 in malloc(sizeof(struct weird_struct) + sizeof(text) - 1) liegt daran, dass ich berücksichtige, dass ein Byte sowieso dank zugewiesen wird unsigned char value[1] damit ich es abziehen kann sizeof(text).

  • @IharobAlAsimi Weil ein Byte mitgezählt wird sizeof(struct weird_struct) Weil unsigned char value[1] hat trotzdem eine Größe von einem Byte? Das habe ich zumindest herausgefunden.

    Benutzer4385532

    2. Mai 2017 um 11:23 Uhr

  • @IharobAlAsimi, es sieht so aus, als wäre das richtig. Obwohl ich Ihrem Vorschlag zur Verwendung zustimme strlen() stattdessen.

    – Anonymous

    2. Mai 2017 um 11:25 Uhr

  • Der Zugriff auf ein Array über seine deklarierten Grenzen hinaus ruft undefiniertes Verhalten hervor. Es funktionierte auf den meisten Compilern, aber mit flexiblen Array-Mitgliedern könnten Compiler dies strenger handhaben; Verlassen Sie sich nicht darauf, dass der obige Code funktioniert.

    – zu ehrlich für diese Seite

    2. Mai 2017 um 11:42 Uhr

  • @Olaf Also würde ich POSIX annehmen muss haben vorgeschrieben ist dies nicht UB, oder wäre die Verwendung von SysV-Nachrichtenwarteschlangen, die von POSIX erwähnt werden, ohne den Aufruf von UB unmöglich? Ist das richtig?

    Benutzer4385532

    2. Mai 2017 um 11:45 Uhr

  • Egal ob Original oder nicht arr[1] struct hack provoziert UB, wenn die Struktur ohne deklarierten Typ zugewiesen wird (zB via malloc), hängt von der genauen Bedeutung des Wortes “Objekt” im C-Standard ab, und das wurde nie zu irgendjemandes Zufriedenheit gelöst; Trotz anhaltender Versuche, den Wortlaut zu korrigieren, widerspricht sich der Text immer noch selbst, und es gibt immer noch mindestens drei Interpretationen, die starke Gründe dafür haben, die “beabsichtigte” Bedeutung zu sein. unter zwei davon ist es UB, aber unter dem dritten nicht.

    – zol

    2. Mai 2017 um 15:57 Uhr


Benutzer-Avatar
Sourav Ghosh

Der Standard-C-Weg (seit C99), um dies zu tun, würde verwenden flexibles Array-Mitglied. Das letzte Mitglied der Struktur muss ein unvollständiges Array sein, und Sie können die erforderliche Speichermenge zur Laufzeit zuweisen.

Etwas wie

struct __attribute__((__packed__)) weird_struct
{
    int some;
    unsigned char value [ ];   //nothing, no 0, no 1, no nothing...
}; 

und später

struct weird_struct *ws =
    malloc(sizeof(struct weird_struct) + strlen("this to be copied") + 1);

oder

struct weird_struct *ws =
    malloc(sizeof(struct weird_struct) + sizeof("this to be copied"));

wird die Arbeit erledigen.

Verwandte, Zitieren der C11 Norm, Kapitel §6.7.2.1

Als Sonderfall kann das letzte Element einer Struktur mit mehr als einem benannten Element einen unvollständigen Array-Typ haben; das nennt man a flexibles Array-Mitglied. In den meisten Situationen wird das flexible Arraymitglied ignoriert. Insbesondere ist die Größe der Struktur so, als ob das flexible Anordnungselement weggelassen worden wäre, außer dass es mehr nachlaufende Polsterung aufweisen kann, als das Weglassen implizieren würde. Wenn jedoch a . (oder ->) Operator einen linken Operanden hat, der (ein Zeiger auf) eine Struktur mit einem flexiblen Array-Mitglied ist, und der rechte Operand dieses Mitglied benennt, verhält er sich so, als ob dieses Mitglied durch das längste Array (mit demselben Elementtyp) ersetzt würde, das dies nicht tun würde die Struktur größer machen als das Objekt, auf das zugegriffen wird; der Versatz des Arrays muss der des flexiblen Array-Elements bleiben, auch wenn dieser von dem des Ersatz-Arrays abweichen würde. Wenn dieses Array keine Elemente hätte, verhält es sich so, als hätte es ein Element, aber das Verhalten ist undefiniert, wenn versucht wird, auf dieses Element zuzugreifen oder einen Zeiger darüber hinaus zu generieren.


Bezogen auf die Verwendung von Arrays mit einem Element, from Online-gcc-Handbuchseite für die Option zur Unterstützung von Arrays der Länge Null

struct line {
  int length;
  char contents[0];
};

struct line *thisline = (struct line *)
  malloc (sizeof (struct line) + this_length);
thisline->length = this_length;

Auf ISO C90 müsste man verzichten contents eine Länge von 1, was bedeutet, dass Sie entweder Platz verschwenden oder das Argument für malloc verkomplizieren.

das beantwortet auch die -1 Teil in der malloc() Argument, wie sizeof(char) ist es garantiert 1 in C.

Der Standard erlaubt es Implementierungen, so zu handeln, wie sie es für richtig halten, wenn Code auf ein Array-Objekt außerhalb seiner angegebenen Grenzen zugreift, selbst wenn der Code den Speicher besitzt, auf den dadurch zugegriffen würde. Soweit ich das beurteilen kann, soll diese Regel einem Compiler ermöglichen, der Folgendes erhält:

struct s1 { char arr[4]; char y; } *p;
int x;
...
p->y = 1;
p->arr[x] = 2;
return p->y;

gleich behandeln mit:

struct s1 { char arr[4]; char y; } *p;
int x;
...
p->arr[x] = 2;
p->y = 1;
return 1;

einen zusätzlichen Lastschritt zu vermeiden, ohne die Möglichkeit pessimistisch in Kauf nehmen zu müssen x könnte gleich 4 sein. Qualitativ hochwertige Compiler sollten in der Lage sein, bestimmte Konstrukte zu erkennen, die den Zugriff auf Arrays über ihre angegebenen Grenzen hinaus beinhalten (z. B. solche, die einen Zeiger auf eine Struktur mit einem Einzelelement-Array als letztes Element beinhalten) und sie vernünftig handhaben, aber nichts in der Der Standard würde dies verlangen, und einige Compiler-Autoren nehmen die Haltung ein, dass die Erlaubnis für Compiler, sich auf unsinnige Weise zu verhalten, als Einladung dazu interpretiert werden sollte. Ich denke, dass Verhalten definiert wäre, auch für die x==4 Fall (was bedeutet, dass der Compiler die Möglichkeit einer Änderung zulassen müsste y), wenn das Schreiben des Arrays über Folgendes gehandhabt würde: (char*)(struct s1*)(p->arr)[x] = 2; aber der Standard ist nicht wirklich klar, ob die Besetzung dazugehört struct s1* ist notwendig.

  • Der Standard erlaubt es Implementierungen, so zu handeln, wie sie es für richtig halten Anders gesagt: undefiniertes Verhalten.

    – 2501

    3. Mai 2017 um 14:30 Uhr


  • @2501: Der Standard bemüht sich nicht, alle Verhaltensweisen zu definieren, die einen Compiler für einen bestimmten Zweck geeignet machen würden. Die Tatsache, dass der Standard erlaubt Implementierungen, sich in einer bestimmten Situation auf unsinnige Weise zu verhalten, bedeutet nicht, dass ein solches Verhalten einen Compiler nicht für viele Zwecke ungeeignet machen würde. Relativ wenige Aufgaben können von allen denkbaren konformen C-Implementierungen ausgeführt werden, und folglich ist zu erwarten, dass sich nur wenige Programme auf allen C-Implementierungen sinnvoll verhalten. Die meisten Programme können nur auf Implementierungen ausgeführt werden, die geeignet sind …

    – Superkatze

    3. Mai 2017 um 15:22 Uhr

  • …auf ihre Bedürfnisse. Die Tatsache, dass ein bestimmtes Programm auf einer bestimmten Implementierung nicht sinnvoll läuft, bedeutet nicht, dass beides fehlerhaft ist, sondern lediglich, dass die Implementierung für die Verwendung mit dem Programm nicht geeignet ist. Ich bin mir nicht sicher, warum einige Compiler-Autoren den Ausdruck “nicht portierbar oder fehlerhaft” in der Definition von UB einfach als “fehlerhaft” interpretieren, aber das scheint die Modereligion zu sein.

    – Superkatze

    3. Mai 2017 um 15:25 Uhr


  • @2501: Um es anders auszudrücken, wenn 90 % der Implementierungen, einschließlich aller Implementierungen, die für einen bestimmten Zweck geeignet sind, das Verhalten einer bestimmten Aktion angeben, dann hat die Aktion bei diesen 90 % der Implementierungen ein definiertes Verhalten, unabhängig davon, ob der Standard dies vorschreibt oder nicht. Die Tatsache, dass der Standard für einige Aktionen kein Verhalten definiert, bedeutet nicht, dass das Verhalten in allen Kontexten undefiniert ist.

    – Superkatze

    3. Mai 2017 um 18:47 Uhr

1334220cookie-checkKann ich ein Array “übererweitern”, indem ich der umschließenden Struktur mehr Platz zuweise?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy