Forum: Compiler & IDEs Merkwürdige Compileroptimierung, wo liegt (mein) Denkfehler


von Harald P. (haraldp)


Lesenswert?

In einem größeren Projekt stürzte mein Programm an einer bestimmten 
Stelle konsequent ab. Ich konnte die Stelle lokalisieren und eingrenzen. 
Folgender Code zeigt das Problem:
1
uint8_t PufferB[32], PufferB1[32];
2
3
4
void SendZeich(uint8_t Zeich)
5
{
6
 UDR = Zeich;
7
}
8
9
int main(void)
10
{
11
  uint8_t zmax = 25;
12
13
  while (zmax--)
14
  {//  hier absichtlich 64 reingeschrieben, um PufferB1 auch zu bearbeiten
15
   for (uint8_t j=0; j<64; j++) if (PufferB[j]>127) PufferB[j] = '.';
16
   SendZeich(PufferB[zmax]);
17
  }
18
  
19
 return(0);
20
}

Hier das dissaemblierte Ergebnis:
1
void SendZeich(uint8_t Zeich)
2
{
3
 UDR = Zeich;
4
  48:  8c b9         out  0x0c, r24  ; 12
5
  4a:  08 95         ret
6
7
0000004c <main>:
8
}
9
10
int main(void)
11
{
12
  4c:  80 e0         ldi  r24, 0x00  ; 0
13
  uint8_t zmax = 25;
14
15
  while (zmax--)
16
  {//               hier absichtlich 64 reingeschrieben, um PufferB1 auch zu bearbeiten
17
   for (uint8_t j=0; j<64; j++) if (PufferB[j]>127) PufferB[j] = '.';
18
  4e:  9e e2         ldi  r25, 0x2E  ; 46
19
  50:  e8 2f         mov  r30, r24
20
  52:  f0 e0         ldi  r31, 0x00  ; 0
21
  54:  e0 5a         subi  r30, 0xA0  ; 160
22
  56:  ff 4f         sbci  r31, 0xFF  ; 255
23
  58:  20 81         ld  r18, Z
24
  5a:  27 fd         sbrc  r18, 7
25
  5c:  90 83         st  Z, r25
26
  5e:  8f 5f         subi  r24, 0xFF  ; 255
27
  60:  f7 cf         rjmp  .-18       ; 0x50 <main+0x4>
28
29
00000062 <_exit>:
30
  62:  f8 94         cli
Klar, das ist unsauber programmiert. Aber daß der Compiiler hier das 
SendZeich wegoptimiert, ist schon komisch. Laß ich die innere Schleife 
nur bis 32 laufen, wird der Code um 20Byte länger und alles scheint zu 
stimmen. Was ist hier los?

Compiler: AVR-GCC-4.8.0, Optimierung -s, Target ATMEGA8

Harald

von icke (Gast)


Lesenswert?

Du greifst in der for-schleife ausserhalb des Speichers zu!
PufferB[j] wenn j>31 ist, können komische sachen passieren.
Ausserdem wird sendZeichen nicht in der der for-schleife nicht 
aufgreufen, sndern danach. Ich weiß nicht, ob das gewünscht ist. Hab mir 
das nicht weiter angeguckt

von Stefan E. (sternst)


Lesenswert?

Harald P. schrieb:
> Klar, das ist unsauber programmiert.

Unsauber programmiert? Anzunehmen, dass zwei Arrays direkt 
hintereinander in der gewünschten Reihenfolge im Speicher liegen, ist 
nicht "unsauber programmiert", sondern schlicht Unsinn.

Harald P. schrieb:
> Aber daß der Compiiler hier das
> SendZeich wegoptimiert, ist schon komisch.

Du produzierst absichtlich "undefined behavior". Und bei "undefined 
behavior" steht es dem Compiler frei, zu machen was immer er will. Da 
ist es völlig müßig über den dann produzierten Code zu philosophieren.

von Yalu X. (yalu) (Moderator)


Lesenswert?

In

  http://gcc.gnu.org/gcc-4.8/changes.html

steht geschrieben:

  "GCC now uses a more aggressive analysis to derive an upper bound for
  the number of iterations of loops using constraints imposed by
  language standards. This may cause non-conforming programs to no
  longer work as expected, such as SPEC CPU 2006 464.h264ref and
  416.gamess. A new option, -fno-aggressive-loop-optimizations, was
  added to disable this aggressive analysis. In some loops that have
  known constant number of iterations, but undefined behavior is known
  to occur in the loop before reaching or during the last iteration, GCC
  will warn about the undefined behavior in the loop instead of deriving
  lower upper bound of the number of iterations for the loop. The
  warning can be disabled with -Wno-aggressive-loop-optimizations.

GCC 4.8.0 habe ich nicht, aber GCC 4.8.1 gibt folgende Warnung aus:
1
warning: iteration 32u invokes undefined behavior [-Waggressive-loop-optimizations]
2
    for (uint8_t j=0; j<64; j++) if (PufferB[j]>127) PufferB[j] = '.';
3
                                            ^

Der Compiler erkennt also richtigerweise ein "undefined behavior" im 32.
Schleifendurchlauf (von 0 aus gezählt) und denkt sich mit verteufelt
hämischem Grinsen (zu dem sonst nur Dichter Paul in der Lage ist ;-)):
"Der Harald verlangt mit -Os nach möglichst kurzem Code. Den werde ich
ihm gerne liefern."

Er setzt also ganz frech die obere Schleifengrenze auf unendlich, denn
bis zum 32. Schleifendurchlauf entsteht dadurch keinerlei Unterschied
im Verhalten des Programms. Und Ab dem 32. Schleifendurchlauf ist das
Verhalten sowieso undefiniert, d.h. egal, welchen Unsinn das Programm
danach fabriziert: undefinierter als undefiniert kann das Verhalten
nicht werden.

Durch diesen schlauen Gedankengang wird die Abfrage des Schleifenendes
eingespart. Da die Schleife nun aber endlos läuft, wird der Aufruf von
SendZeich nie erreicht, so dass dieser ebenfalls entfernt werden kann.

Für diese geniale Kürzung des Programmcodes (auf die man erst einmal
kommen muss) klopft sich der Compiler zurecht auf die eigene Schulter
und hat dabei keinerlei schlechtes Gewissen, denn das Verhalten des
Programms entspricht immer noch zu 100% den Vorgaben des C-Standards.

Rechter kann's dir der Compiler kaum machen, oder? :)

von Peter D. (peda)


Lesenswert?

Harald P. schrieb:
> {//  hier absichtlich 64 reingeschrieben, um PufferB1 auch zu
> bearbeiten

Nö, ist nicht erlaubt.

Du mußt beide Puffer in einer Struct zusammen fassen und für den 
Vollzugriff eine Union definieren.

von Oliver (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Du mußt beide Puffer in einer Struct zusammen fassen und für den
> Vollzugriff eine Union definieren.

Oder ein Array mit Größe 64 anlegen, und die Verwaltung der Arrayhälften 
selber.

Oliver

von Rolf Magnus (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Harald P. schrieb:
>> {//  hier absichtlich 64 reingeschrieben, um PufferB1 auch zu
>> bearbeiten
>
> Nö, ist nicht erlaubt.
>
> Du mußt beide Puffer in einer Struct zusammen fassen und für den
> Vollzugriff eine Union definieren.

Was allerdings formal genauso falsch ist. Nur die Wahrscheinlichkeit, 
daß es trotzdem funktioniert, ist halt höher.

von Axel S. (a-za-z0-9)


Lesenswert?

Yalu X. schrieb:

[schnipp]

Etwas kürzer formuliert: garbage in - garbage out!

Der Compil^WKoch, der aus Scheiße Kompott machen kann ist ein Mythos.


XL

von Peter D. (peda)


Lesenswert?

Rolf Magnus schrieb:
> Was allerdings formal genauso falsch ist.

Kannst Du das mal näher erläutern?
Unions aus Structs findet man doch wie Sand am Meer.
Z.B. der Klassiker:
1
union b_w {
2
  struct{
3
    uint8_t l;
4
    uint8_t h;
5
  }b;
6
  uint16_t w;
7
};

von Klaus W. (mfgkw)


Lesenswert?

Natürlich ist es legal, sowas zu bauen.
Nur darauf verlassen, daß die Elemente der struct direkt hintereinander 
liegen, ist gewagt - es dürfen ja Füllbyte eingefügt werden.

von Stefan E. (sternst)


Lesenswert?

Peter Dannegger schrieb:
> Kannst Du das mal näher erläutern?

Formal erlaubt der Standard es eigentlich nur, genau den Union-Member zu 
lesen, der auch zuletzt beschrieben wurde.

Peter Dannegger schrieb:
> Unions aus Structs findet man doch wie Sand am Meer.
> Z.B. der Klassiker:

Und weil dieser "Missbrauch" so extrem gebräuchlich ist, wird es 
vermutlich auch kein Compiler jemals wagen, das nicht wie erwartet zu 
übersetzen. Und der immer wieder erhobene Zeigefinger ist in dem Fall 
dann auch eher akademischer Natur.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Stefan Ernst schrieb:
> Formal erlaubt der Standard es eigentlich nur, genau den Union-Member zu
> lesen, der auch zuletzt beschrieben wurde.

Mit einer Ausnahme: sofern alle Union-Mitglieder an ihrem Anfang
die exakt gleiche Sequenz haben, darf dieser gemeinsame Teil in
jeder beliebigen Konstellation gelesen werden.

Das ist aber hier nicht der Fall.

Ich hatte gerade nochmal nachgelesen.  An vielen anderen Stellen ist
der Standard relativ streng und gestattet Füllbytes eigentlich nur,
wenn dies durch die notwendige Ausrichtung des Datentyps im Speicher
erforderlich ist (damit dürften bei “char” nie welche auftauchen,
denn das ist per defintionem die kleinste adressierbare Einheit).

Bei structs steht jedoch:

“There may be unnamed
padding within a structure object, but not at its beginning.”

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Stefan Ernst schrieb:
> Peter Dannegger schrieb:
>> Unions aus Structs findet man doch wie Sand am Meer.
>> Z.B. der Klassiker:
>
> Und weil dieser "Missbrauch" so extrem gebräuchlich ist, wird es
> vermutlich auch kein Compiler jemals wagen, das nicht wie erwartet zu
> übersetzen. Und der immer wieder erhobene Zeigefinger ist in dem Fall
> dann auch eher akademischer Natur.

Bei GCC steht im Kleingedruckten, dass er solche "Überlagerungen" 
behandelt wie erwartet.

Damit kann man es mit GCC zwar verwenden, aber es in nicht portabel. 
Und das Gros der Anwender, die solche Hacks einbauen, machen es wohl 
nicht, weil sie dieses Kleingedruckte kennen, sondern weil sie -- aus 
welchen Gründen auch immer -- glauben, es sei legales C / C++.

von Rolf Magnus (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Rolf Magnus schrieb:
>> Was allerdings formal genauso falsch ist.
>
> Kannst Du das mal näher erläutern?
> Unions aus Structs findet man doch wie Sand am Meer.

C schreibt für unions vor, daß man nur das Element lesen darf, das man 
davor zuletzt geschrieben hat.

von Peter D. (peda)


Lesenswert?

Man kann mit den Unions aus Structs sehr schön und vor allem 
übersichtlich serielle Protokolle implementieren.

Man hat eine Struct, wo jedes Element eine bestimmte Bedeutung hat und 
jagt diese dann als Byte-Stream über ein Interface.
Und die Gegenseite empfängt den Stream, legt ihn in genau eine gleiche 
Struct ab und kann dann alle Elemente bequem zugreifen.

von Peter II (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Man hat eine Struct, wo jedes Element eine bestimmte Bedeutung hat und
> jagt diese dann als Byte-Stream über ein Interface.
> Und die Gegenseite empfängt den Stream, legt ihn in genau eine gleiche
> Struct ab und kann dann alle Elemente bequem zugreifen.

nein kann man nicht immer. Es gibt immer noch das Big/Little Endian 
Problem. Man sollte immer die Protokoll von der Internen Darstellung 
trennen.

Spätestens wenn man mehre Protokollversionen unterstützen muss, wird es 
sehr unpraktisch.

von Oliver (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Man kann mit den Unions aus Structs sehr schön und vor allem
> übersichtlich serielle Protokolle implementieren.

Man macht das auch häufig so.
Das ändert aber nichts daran, daß dieses Vorgehen nicht konform zum 
C-Standard ist.

Und in diesem Fall mit unterschiedlichen Systeme auf beiden Seiten darf 
man sich dann doch Gedanken über alingment und byte order machen. Das 
bekommt eine union nicht automatisch hin.

Oliver

von Harald P. (haraldp)


Lesenswert?

Vielen Dank für die vielen Hinweise. Tatsächlich lagen die Puffer im 
Originalprogramm in einer struct (für ein serielles Protokoll), und mit 
einer alten Compilerversion hat auch alles wie gewünscht funktioniert. 
Na ja, das C-Feature, daß keine Pufferüberläufe vom Compiler abgefangen 
werden - wie z.B. in Pascal möglich -, sollte man tatsächlich nicht 
strapazieren.
Richtig wäre es hier, wenn der Compiler eine Warnung herausgeben würde. 
Das Verhalten im Kleingedruckten zu verstecken, ist meiner Meinung nach 
etwas zu wenig.
Harald

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Harald P. schrieb:
> Richtig wäre es hier, wenn der Compiler eine Warnung herausgeben würde.

Hast du die denn auch alle eingeschaltet?  (-Wall -Wextra)

von Stefan R. (srand)


Lesenswert?

Peter Dannegger schrieb:
> Man hat eine Struct, wo jedes Element eine bestimmte Bedeutung hat und
> jagt diese dann als Byte-Stream über ein Interface.
> Und die Gegenseite empfängt den Stream, legt ihn in genau eine gleiche
> Struct ab und kann dann alle Elemente bequem zugreifen.

Ich sehe in deiner Beschreibung keine Union.

Eher einen Cast auf eine Struktur.

von Harald P. (haraldp)


Lesenswert?

Jörg Wunsch schrieb:
> Hast du die denn auch alle eingeschaltet?  (-Wall -Wextra)

Nein, -Wextra hatte ich nicht. Ändert aber auch nichts (gerade 
getestet).

Stefan Rand schrieb:
> Ich sehe in deiner Beschreibung keine Union.

Im Originalprogramm ist es eine union. Hier für den Beitrag, habe ich 
extrem gekürzt. Aber auch bei union gibt es das beschriebene Verhalten.
Harald

von Klaus F. (kfalser)


Lesenswert?

Harald P. schrieb:
>
1
> uint8_t PufferB[32], PufferB1[32];
2
> 
3
> 
4
> void SendZeich(uint8_t Zeich)
5
> {
6
>  UDR = Zeich;
7
> }
8
> 
9
> int main(void)
10
> {
11
>   uint8_t zmax = 25;
12
> 
13
>   while (zmax--)
14
>   {//  hier absichtlich 64 reingeschrieben, um PufferB1 auch zu 
15
> bearbeiten
16
>    for (uint8_t j=0; j<64; j++) if (PufferB[j]>127) PufferB[j] = '.';
17
>    SendZeich(PufferB[zmax]);
18
>   }
19
> 
20
>  return(0);
21
> }
22
>

Manchmal wundert es mich schon, um welche Dinge man lange 
herumdiskutieren kann.
Das Zusammenfassen von BufferB und BufferB1 zu einem einzigen Array 
hätte das Problem gelöst.
Der Zugriff auf BufferB1 erfolgt einfach um 32 Einträge versetzt, das 
bringt nun wirklich keine Performance Nachteile.
Schlimmstenfalls könnte man immer noch einen Pointer definieren, der auf 
BufferB[32] zeigt.

von Axel S. (a-za-z0-9)


Lesenswert?

Jörg Wunsch schrieb:
> Ich hatte gerade nochmal nachgelesen.  An vielen anderen Stellen ist
> der Standard relativ streng und gestattet Füllbytes eigentlich nur,
> wenn dies durch die notwendige Ausrichtung des Datentyps im Speicher
> erforderlich ist...

> Bei structs steht jedoch:
>
> “There may be unnamed
> padding within a structure object, but not at its beginning.”

Jep. Deswegen gibts seit Äonen
1
#pragma pack
und Freunde. Bei AVR-gcc ist das (erfreulicherweise) der Default.


Rolf Magnus schrieb:
> C schreibt für unions vor, daß man nur das Element lesen darf, das man
> davor zuletzt geschrieben hat.

Nein. Dann wären unions ja sinnlos. Wo soll das stehen?

Die einzige Einschränkung, die ich kenne ist wenn eine union längere und 
kürzere Member hat. Dann ist undefiniert was man beim Lesen des längeren 
Members bekommt, wenn man zuvor ein kürzeres geschrieben hat. Und zwar 
ist genau genommen undefiniert was in den Teilen des längeren Members 
steht, die vom kürzeren nicht belegt werden.

Noch anders ausgedrückt: beim Schreiben einer union ist es dem Compiler 
gestattet, immer die ganze union zu überschreiben, auch wenn der Zugriff 
über ein Member erfolgt, das kleiner ist als die gesamte union. Was 
dabei in den zusätzlichen Platz geschrieben wird, ist nicht definiert. 
Ganz ähnlich wie mit dem Inhalt von Padding.


XL

von Peter II (Gast)


Lesenswert?

Axel Schwenke schrieb:
> Nein. Dann wären unions ja sinnlos. Wo soll das stehen?

es steht in der doku. Und sie sind nicht sinnlos, sie sind einfach für 
etwas andere gedacht. Man kann mit unions 2 verschiede arten von Daten 
speicher ohne den Doppelten Platz zu verbrauchen.

http://msdn.microsoft.com/en-us/library/y9zewe0d.aspx
[...]
If a union of two types is declared and one value is stored, but the 
union is accessed with the other type, the results are unreliable. For 
example, a union of float and int is declared. A float value is stored, 
but the program later accesses the value as an int. In such a situation, 
the value would depend on the internal storage of float values. The 
integer value would not be reliable.
[...]

andere Interpretieren den Text auch wieder anders, für mich ließt es 
sich so das es nicht definiert ist.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Axel Schwenke schrieb:
> Rolf Magnus schrieb:
>> C schreibt für unions vor, daß man nur das Element lesen darf, das man
>> davor zuletzt geschrieben hat.
>
> Nein. Dann wären unions ja sinnlos. Wo soll das stehen?

Im C-Standard.  Das wurde hier schon X mal durchgekaut, inclusive der 
entsprechenden Referenzen zum Standard.  Etwa

Beitrag "Re: Programmierung von Strukturen in C"

von Josef D. (jogedua)


Lesenswert?

Rolf Magnus schrieb:
> C schreibt für unions vor, daß man nur das Element lesen darf, das man
> davor zuletzt geschrieben hat.

Jörg Wunsch schrieb:
> Mit einer Ausnahme: sofern alle Union-Mitglieder an ihrem Anfang
> die exakt gleiche Sequenz haben, darf dieser gemeinsame Teil in
> jeder beliebigen Konstellation gelesen werden.

Und das kann man nutzen, um in der Union zu speichern, was (welche 
Struct) man zuletzt geschrieben hat.

von (prx) A. K. (prx)


Lesenswert?

Axel Schwenke schrieb:
> Nein. Dann wären unions ja sinnlos. Wo soll das stehen?

Die angedachte Rolle von unions ist nicht Zerlegung und Zusammenbau 
von Daten, auch wenn sie oft dafür verwendet werden. Sondern die 
Speicherung alternativer Inhalte, z.B. in Abhängigkeit von einer 
Kennung. Also beispielsweise sinngemäss:
1
struct {
2
   enum { Text, Number, Callback } tag;
3
   union {
4
      char text[10];
5
      long number;
6
      void (*callback)(void);
7
   };
8
};
In diesem Beispiels wird abhängig von <tag> der passende Inhalt 
platzsparend verwendet. Aber es wird nie der falsche Zweig verwendet.

Manche Sprachen verbinden in den entsprechenden Konstrukten die 
Varianten explizit mit dem Tag und kontrollieren das ggf. beim Zugriff.

von Oliver (Gast)


Lesenswert?

Die angedachte Rolle von unions stammt aus der Urzeit der 
Programmierung, und entsprach den Fortran-common-Blöcken. Damals musste 
oder wollte man halt Hauptspeicher sparen, in dem man ihn mehrfach 
verwenden konnte.

Oliver

von Markus (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Kannst Du das mal näher erläutern?
> Unions aus Structs findet man doch wie Sand am Meer.
> Z.B. der Klassiker:union b_w {
>   struct{
>     uint8_t l;
>     uint8_t h;
>   }b;
>   uint16_t w;
> };

und die Reihenfolge (High-Byte/Low-Byte) ist z.B. auch schon wieder 
Architekturabhängig.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Oliver schrieb:
> und entsprach den Fortran-common-Blöcken.

Die natürlich genauso häufig als „typecast von hinten durch die Brust
ins Auge“ missbraucht worden sind, wie die unions in C. ;-)

von Axel S. (a-za-z0-9)


Lesenswert?

Johann L. schrieb:
> Axel Schwenke schrieb:
>> Rolf Magnus schrieb:
>>> C schreibt für unions vor, daß man nur das Element lesen darf, das man
>>> davor zuletzt geschrieben hat.
>>
>> Nein. Dann wären unions ja sinnlos. Wo soll das stehen?
>
> Im C-Standard.  Das wurde hier schon X mal durchgekaut, inclusive der
> entsprechenden Referenzen zum Standard.  Etwa
>
> Beitrag "Re: Programmierung von Strukturen in C"

Nur steht dort nirgends, was Rolf oben behauptet.

Ich copypaste das mal hierher:

> When a value is stored in a member of an object of union type,
> the bytes of the object representation that do not correspond
> to that member but do correspond to other members take unspecified
> values,

das ist genau das, was ich oben geschrieben habe. Und eine wesentlich 
weniger starke Einschränkung als das was Rolf behauptet.

Insbesondere sehe ich da keine Einschränkung, bei dieser
1
union {
2
  double d;
3
  uint64_t i;
4
};
in beliebiger Reihenfolge auf union members zuzugreifen. Mit definierten 
Resultaten.


XL

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Und was ist daran zu deuteln?

Und in 6.7.2.1 steht es auch:

> [...] The value of at most one of the members can
> be stored in a union object at any time.

von Karlo (Gast)


Lesenswert?

Mal angenommen ich würde gerne diesen "Trick" fürs oben angesprochene 
serielle Protokoll benutzen:

Was wäre denn "weniger unelegant": einen Zeiger auf einen Strukturzeiger 
(natürlich gepackt) casten oder die unsachgemäse Verwendung einer Union?

von Εrnst B. (ernst)


Lesenswert?

Karlo schrieb:

> Was wäre denn "weniger unelegant": einen Zeiger auf einen Strukturzeiger
> (natürlich gepackt) casten oder die unsachgemäse Verwendung einer Union?

Die umgecasteten Zeiger werden unter Unix gerne genommen.
Beispiel:
1
struct sockaddr {
2
     sa_family_t sa_family;
3
      char        sa_data[14];
4
};
5
6
7
struct sockaddr_un {
8
    unsigned short sun_family;  /* AF_UNIX */
9
    char sun_path[108];
10
};
11
12
13
struct sockaddr_in {
14
    short            sin_family;   // e.g. AF_INET
15
    unsigned short   sin_port;     // e.g. htons(3490)
16
    struct in_addr   sin_addr;     // see struct in_addr, below
17
    char             sin_zero[8];  // zero this if you want to
18
};
usw.

Gemeinsam haben sie nur den short (*) am Anfang, damit der aufgerufene 
Code weis, wie er den Pointer casten muss, und eine 
16-Byte-Minimallänge...

*) Der Code oben ist aus verschiedenen Quellen zusammenkopiert. Echt 
keine Absicht, dass da drei verschiedene Typen auftauchen :)

von Karl H. (kbuchegg)


Lesenswert?

Karlo schrieb:

> Was wäre denn "weniger unelegant": einen Zeiger auf einen Strukturzeiger
> (natürlich gepackt) casten

Womit du dann auch hier wieder bei undefined behaviour landest :-)

> oder die unsachgemäse Verwendung einer Union?

-> beides ist undefined behaviour. D.h. aus dieser Sicht betrachtet, 
schenken sich diese beiden Möglichkeiten nix.


(Wobei ich mir jetzt nicht sicher bin, ob es undefined oder unspecified 
behaviour ist. Ist aber im Grunde egal)

von (prx) A. K. (prx)


Lesenswert?

Nehmen wir mal an, es gäbe eine Maschine, die jedes Datenwort im 
Speicher vorsorglich mit einem Typencode versieht. Also zwischen Code, 
Ganzzahlen, Fliesskommzahlen und Text unterscheidet. Und bei falschem 
Zugriff einen Alarm auslöst. Das könnte die gängige C Praxis ziemlich 
durcheinander wirbeln. Egal ob man Unions oder gecastete Pointer 
verwendet.

von Karl H. (kbuchegg)


Lesenswert?

A.K.

> Nehmen wir an

Exakt


Im Grunde geht es doch darum:
(Annahme: sizeof(int) == sizeof(float))
1
  int i = 85;
2
  float * ptr = (float*) &i;

Der C-Standard will nicht definieren, was in diesem Falle das Ergebnis 
von
1
  float k = *ptr;
sein soll. Da der C-Standard über Dinge wie Byte-Order bzw. Floating 
Point System keine Aussage macht, kann er das auch gar nicht. Das 
Ergebnis kann eine gültige Floating Point Zahl sein, das Ergebnis kann 
NaN sein, der Versuch des Auslesens dieser Bytesquenz kann aber auch zu 
einem sofortigen Trap in einer Floating Point Hardware führen. All das 
ist ausserhalb des Definitionsbereichs des C-Standards.

Daher wurde sehr darauf geachtet, alle Dinge, die in genau das gleiche 
Horn stossen (eine in einem Datentyp gültige Bytesequenz muss nicht 
notwendigerweise in einem anderen Datentyp ebenfalls eine gültige 
Bytesequenz sein) als unspecified zu brandmarken.

von Rolf Magnus (Gast)


Lesenswert?

Karlo schrieb:
> Was wäre denn "weniger unelegant": einen Zeiger auf einen Strukturzeiger
> (natürlich gepackt) casten oder die unsachgemäse Verwendung einer Union?

Was für mich entscheidend ist, ist, daß Unions im Gegensatz zu Casts 
eigentlich für was völlig anderes gedacht sind. Meisten sind sie auch 
umständlich zu schreiben und schlechter lesbar.
Ich hab keine Ahnung, warum da so oft und gerne Unions verwendet werden.

von (prx) A. K. (prx)


Lesenswert?

Ich habe das übrigens nicht erfunden. Solche Maschinen gab es wirklich, 
die Telefunken-Grossrechner TR4 und TR440: 
http://www.qslnet.de/member/dj4kw/tr4.htm

Obwohl C und die TR440 ungefähr gleichzeitig entstanden bezweifle ich 
allerdings, dass es dafür jemals einen C Compiler gab.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Harald P. schrieb:
> Richtig wäre es hier, wenn der Compiler eine Warnung herausgeben würde.
> Das Verhalten im Kleingedruckten zu verstecken, ist meiner Meinung nach
> etwas zu wenig.

Das sehen die GCC-Macher offensichtlich genauso, deswegen haben sie in 
der aktuellen Version (4.8.1) eine entsprechende Warnung vorgesehen, für 
die man sogar nicht einmal -Wall angeben muss (s. mein Beitrag weiter 
oben). Zudem "optimiert" die neue Version dein Beispiel nicht so krass, 
weswegen die Warnung eigentlich fast schon wieder überflüssig ist.

von Axel S. (a-za-z0-9)


Lesenswert?

Johann L. schrieb:
> Und was ist daran zu deuteln?
>
> Und in 6.7.2.1 steht es auch:
>
>> [...] The value of at most one of the members can
>> be stored in a union object at any time.

Wenn man den Satz davor auch noch zitiert:

> The size of a union is sufficient to contain the largest of
> its members. The value of at most one of the members can be
> stored in a union object at any time.

dann wird klar, daß es hier um die Größe der union geht und daß man da 
zu einer Zeit immer nur ein member drin speichern kann und nicht mehrere 
gleichzeitig (im Gegensatz zu einer struct).

Die Behauptung war aber:

> C schreibt für unions vor, daß man nur das Element lesen darf, das man
> davor zuletzt geschrieben hat.

Und ich hätte gern dafür einen Beleg aus dem Standard. Bis ich den 
bekomme, halte ich das für eine UL.


XL

von Stefan R. (srand)


Lesenswert?

Axel Schwenke schrieb:
>> C schreibt für unions vor, daß man nur das Element lesen darf, das man
>> davor zuletzt geschrieben hat.
>
> Und ich hätte gern dafür einen Beleg aus dem Standard. Bis ich den
> bekomme, halte ich das für eine UL.

Ist in C99 erlaubt, C11 habe ich zuhause nicht zur Hand.

Siehe informativen Annex J, Unspecified behavior:

- The value of a union member other than the last one stored into 
(6.2.6.1)

von Ralf G. (ralg)


Lesenswert?

Axel Schwenke schrieb:
> Und ich hätte gern dafür einen Beleg aus dem Standard.

"The size of a union is sufficient to contain the largest of  its 
members."
hat mit
"The value of at most one of the members can be  stored in a union 
object at any time."
nichts zu tun. (Also im Sinne voneinander abhängig)

Wenn gesagt wird, dass nur ein Element der Union gespeichert werden darf 
(das andere/ die anderen also nicht) und man daraus schlussfolgert, dass 
das andere dafür aber gelesen werden kann...

von (prx) A. K. (prx)


Lesenswert?

Axel Schwenke schrieb:
> Und ich hätte gern dafür einen Beleg aus dem Standard. Bis ich den
> bekomme, halte ich das für eine UL.

Es gibt 2 Bereiche unspezifizierten Verhaltens:
(1) Jene, die ausdrücklich so bezeichnet werden,
(2) und jene, für die keine explizite Spezifikation existiert.

Wobei Punkt (2) dort von Bedeutung ist, wo das zu erwartende Verhalten 
nicht selbstverständlich ist und sich nicht als logische Folgerung aus 
andererer Spezifizikation ergibt.

Kurz: nicht spezifiziert = unspezifiziert.

von Stefan R. (srand)


Lesenswert?

Ralf G. schrieb:
> und man daraus schlussfolgert, dass
> das andere dafür aber gelesen werden kann...

Und diese Folgerung ist falsch, wie ich eine halbe Stunde vor deinem 
Beitrag bereits klar gezeigt habe.

von Stefan R. (srand)


Lesenswert?

A. K. schrieb:
> Es gibt 2 Bereiche unspezifizierten Verhaltens:

Die Behauptung war aber "undefiniert" ("darf nicht"), nicht 
"unspezifiziert".

Das Verhalten beim Zugriff auf den "anderen" Unionmember ist aber eben 
unspezifiziert, nicht undefiniert.

von (prx) A. K. (prx)


Lesenswert?

Stefan Rand schrieb:
> Die Behauptung war aber "undefiniert" ("darf nicht"), nicht
> "unspezifiziert".

Die Unterscheidung zwischen undefiniertem und unspezifiziertem Verhalten 
wird dort müssig, wo es weder definiert noch spezifiziert ist. ;-)

Aber wenns dir so lieber ist: Eine Operation, bei der sich je nach zu 
Grunde liegender Maschine ebendiese in den Crashdump verabschieden kann 
darf wohl als undefiniert gelten.

von Ralf G. (ralg)


Lesenswert?

Stefan Rand schrieb:
> Ralf G. schrieb:
>> und man daraus schlussfolgert, dass
>> das andere dafür aber gelesen werden kann...
>
> Und diese Folgerung ist falsch, wie ich eine halbe Stunde vor deinem
> Beitrag bereits klar gezeigt habe.

Deswegen die drei "Hmm-Ich-wundere-mich-sehr"-Punkte am Ende! ;-)

-----

Dass jetzt "jeder" solche "Spezial-Casts" verwendet, ist eine ganz 
andere Sache. Nur darf er dann nicht rumheulen, falls es irgendwann aus 
irgendwelchen Gründen nicht funktioniert!

Wie war doch gleich die Frage? ;-)

von (prx) A. K. (prx)


Lesenswert?

Mal zwischendurch einige weniger philosophische Gedanken dazu. Auf Daten 
in anderer Form zuzugreifen, als der gespeicherte Datentyp vorgibt, wird 
wohl nur auf eher exotischen Maschinen direkt zum Crash führen, es sei 
denn es sind NaNs dabei. Aber es kann auch auf ganz normalen Maschinen 
unangenehme Folgen haben:

- Im Zusammenhang mit Aliasing eröffnen sich ziemliche Untiefen.

- Es kann auf sehr real existierenden Maschinen grässlich langsam sein. 
Reale Maschinen sind nämlich nicht dafür eingerichtet, so etwas 
effizient auszuführen - ganz im Gegenteil.

von Axel S. (a-za-z0-9)


Lesenswert?

Stefan Rand schrieb:
> Axel Schwenke schrieb:
>> ... ich hätte gern dafür einen Beleg aus dem Standard. Bis ich den
>> bekomme, halte ich das für eine UL.

> Siehe informativen Annex J, Unspecified behavior:
> - The value of a union member other than the last one stored into
> (6.2.6.1)

Danke. Dann habe ich das wohl richtig gesehen.

Zitat:

> If the member used to read the contents of a union object is not
> the same as the member last used to store a value in the object,
> the appropriate part of the object representation of the value is
> reinterpreted as an object representation in the new type ...
> This might be a trap representation.

Der letzte Satz meint, daß das Ergebnis der Re-Interpretation sicherlich 
implementationsabhängig ist (mindestens architekturabhängig bzw. noch 
genauer: abhängig von der internen Repräsentation der verschiedenen 
Datentypen). Das ist aber genau nicht mein Problem. Denn in den Fällen 
wo ich das anwenden will - z.b. um einen längeren Datentyp in eine 
Bytefolge zu zerlegen (Marshalling) ist mir das entweder komplett 
schnuppe oder ich kenne die interne Repräsentation genau genug für den 
intendierten Zweck.

Ich möchte noch hinzufügen, daß der oben zitierte Text in C99 als 
ergänzende Fußnote aufgenommen wurde, eben weil C90 hier eine viel zu 
weit gefaßte Bedeutung von "unspecified" enthielt. Ich gehe davon aus, 
daß das schon immer genau so gemeint, aber unglücklich ausgedrückt 
war.

(http://stackoverflow.com/questions/11639947/is-type-punning-through-a-union-unspecified-in-c99-and-has-it-become-specified)


XL

von Stefan R. (srand)


Lesenswert?

A. K. schrieb:
> Die Unterscheidung zwischen undefiniertem und unspezifiziertem Verhalten
> wird dort müssig, wo es weder definiert noch spezifiziert ist. ;-)
>
> Aber wenns dir so lieber ist: Eine Operation, bei der sich je nach zu
> Grunde liegender Maschine ebendiese in den Crashdump verabschieden kann
> darf wohl als undefiniert gelten.

Wenn du nicht weißt, was undefiniert und unspezifiziert im C-Standard 
bedeuten, dann halt dich doch einfach mal zurück. Man muß nicht zu jedem 
Thema quaken.

Und selbstverständlich "verabschiedet" sich kein C-System bei Zugriff 
auf den "falschen" Union-Member. Der C-Standard verbietet es.

von (prx) A. K. (prx)


Lesenswert?

Stefan Rand schrieb:
> Und selbstverständlich "verabschiedet" sich kein C-System bei Zugriff
> auf den "falschen" Union-Member. Der C-Standard verbietet es.

Es kann Datentypen mit unzulässige Werten/Bitpatterns geben, deren 
Verwendung zu einer Exception führt. Da kommen Fliesskommadaten in 
Frage, aber auch Pointer (vgl. Intel 286 Segmentierung). Auch eine 
Maschine mit getaggten Speicherworten (wie oben mal skizziert) wäre m.E. 
als Zielmaschine von C zulässig.

Und nun würde ich gerne mal sehen, wo der C Standard verbietet, dass 
eine Maschine bei unzulässiger Befehls/Daten-Kombination das Programm 
abwirft. Bei Kombinationen, die im korrekten Ablauf eines Programmes 
überhaupt nicht auftreten können.

von Stefan R. (srand)


Lesenswert?

A. K. schrieb:
> Es kann Datentypen mit unzulässige Werten/Bitpatterns geben, deren
> Verwendung zu einer Exception führt.

Das ist das, was der Standard "trap representation" nennt. Und das ist 
ebenfalls ausdrücklich ausgeschlossen.

Meine Güte, irgendwann reicht es aber wirklich mal. Wenn du mir ans Bein 
pinkeln willst, dann nimm den Standard zur Hand. Axel hat ausdrücklich 
nach dem Standard gefragt, nicht nach deiner Intuition.

von (prx) A. K. (prx)


Lesenswert?

Stefan Rand schrieb:
> Meine Güte, irgendwann reicht es aber wirklich mal. Wenn du mir ans Bein
> pinkeln willst, dann nimm den Standard zur Hand. Axel hat ausdrücklich
> nach dem Standard gefragt, nicht nach deiner Intuition.

Und ich habe geantwortet, dass Dinge, die vom Standard nicht explizit 
ausgeschlossen werden, dennoch ausgeschlossen sein können. Wo habt ihr 
da ein Problem?

Abgesehen davon will ich hier niemandem ans Bein pinkeln.

von Stefan R. (srand)


Lesenswert?

A. K. schrieb:
> Und ich habe geantwortet, dass Dinge, die vom Standard nicht explizit
> ausgeschlossen werden, dennoch ausgeschlossen sein können. Wo habt ihr
> da ein Problem?

Der Standard schließt aber explizit aus, daß es nicht funktioniert.

Er sagt ausdrücklich, wenn du den "falschen" Member liest, ist der Wert 
unspezifiziert.

Und das bedeutet, da kann jeder "funktionierende" (nicht 
exception-auslösende Wert) rauskommen.

Und das schließt deine Fantasien von wegen "stürzt ab" schlicht und 
ergreifend aus.

von (prx) A. K. (prx)


Lesenswert?

Stefan Rand schrieb:
> Und das schließt deine Fantasien von wegen "stürzt ab" schlicht und
> ergreifend aus.

Nochmal zur Klarstellung: Deiner Ansicht darf also keine vom Compiler 
erzeugte Ladeoperation jedweder Art abstürzen, wenn sie Daten aus einer 
Union läd? Egal wie schräg die geladenen Daten sind? Weil das vom 
Standard zwar als unspezifiziert aber nicht als undefiniert bezeichnet 
wird. Na dann...

von (prx) A. K. (prx)


Lesenswert?

Korrektur: Deiner Ansicht darf also keine vom Compiler erzeugte 
Ladeoperation jedweder Art abstürzen, wenn sie Daten aus einer Union 
läd? Egal wie schräg die geladenen Daten sind? Weil das vom Standard an 
der erwähnten Stelle ausdrücklich als unspezifiziert bezeichnet wird. 
Habe ich das so richtig verstanden?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Stefan Rand schrieb:
> A. K. schrieb:
>> Es kann Datentypen mit unzulässige Werten/Bitpatterns geben, deren
>> Verwendung zu einer Exception führt.
>
> Das ist das, was der Standard "trap representation" nennt. Und das ist
> ebenfalls ausdrücklich ausgeschlossen.

Nein, ist es eben nicht.  Schau doch, was Axel Schwenke oben zitiert
hat: “This might be a trap representation.”

Es ist also sicherlich OK, das zu benutzen (auf unportable Weise),
solange man genug über die Interna kennt, dass man sich sicher
sein kann, dass es im konkret vorliegenden Fall eben keine
“trap representation” darstellt.

von (prx) A. K. (prx)


Lesenswert?

Exakt.

Im Annex J steht zwar, dass es unspezifiziert ist, aus einer Union etwas 
anderes zu lesen, als man vorher reingeschrieben hat. Aber das macht es 
nicht in jeder Situation definiert. Es kann sowohl unspezifiziert als 
auch undefiniert sein. Gemäss C99 6.2.6.1.5 kann es Bitpatterns geben, 
die beim Zugriff eine Exception auslösen - besagte trap representations. 
Und dieser Passus gilt universell, Unions machen da keine Ausnahme.

von Karl H. (kbuchegg)


Lesenswert?

Stefan Rand schrieb:

> Er sagt ausdrücklich, wenn du den "falschen" Member liest, ist der Wert
> unspezifiziert.

Genau genommen sagt er, dass die Operation unspezifiziert ist.

> Und das bedeutet, da kann jeder "funktionierende" (nicht
> exception-auslösende Wert) rauskommen.

Das ist nicht die Bedeutung von unspecified.

unspecified bedeutet, dass sich der Standard raushält, was genau 
passiert, dass aber der Compilerhersteller dokumentieren muss, was 
konkret passsiert.

Im Gegensatz zu undefined. Da darf auch alles mögliche passieren, aber 
der Compilerhersteller ist nicht in der Pflicht dafür ein garantiertes 
Verhalten (welches auch immer das ist) zu implementieren oder zu 
dokumentieren.

> Und das schließt deine Fantasien von wegen "stürzt ab" schlicht und
> ergreifend aus.

Wenn der Compilerbauer dokumentiert, dass eine derartige Operation 
abstürzt, dann ist das aus Sicht des Standards ok.

von Stefan R. (srand)


Lesenswert?

Karl Heinz Buchegger schrieb:
> unspecified bedeutet, dass sich der Standard raushält, was genau
> passiert, dass aber der Compilerhersteller dokumentieren muss, was
> konkret passsiert.

Och, Karl-Heinz! Das weißt du aber besser.

Was du meinst ist "implementation-defined".

"unspecified" bedeutet zwei oder mehrere Möglichkeiten, C-System sucht 
sich eine raus, ohne das dokumentieren zu müssen.

von Rolf Magnus (Gast)


Lesenswert?

Stefan Rand schrieb:
> A. K. schrieb:
>> Es gibt 2 Bereiche unspezifizierten Verhaltens:
>
> Die Behauptung war aber "undefiniert" ("darf nicht"), nicht
> "unspezifiziert".

Das hängt nun davon ab, wie du "darf nicht" definierst, was offenbar 
anders ist als meine Definition. Unspezifiziert heißt in dem Kontext, 
daß jeder Compiler es manchen kann, wie er lustig ist, es in jeder neuen 
Version ändern kann und daß das konkrete Verhalten auch nirgends 
dokumentiert sein muss.
Undefiniert heißt zusätzlich noch, daß ab dieser Stelle beliebiges 
passieren darf, bis hin zur Selbstzerstörung des Universums.

"Darf nicht" schließt für mich erstmal beides ein, sofern ich nicht 
einen guten Grund habe und mir darüber klar bin, was meine 
Zielplattformen draus machen. Nun ist ein Cast in der Hinsicht auch 
nicht so viel besser, als eine union, aber warum ich ihn dennoch 
bevorzuge, hab ich schon geschrieben.
Die dritte Möglichkeit, die auch explizit in der ISO-Norm erwähnt wird, 
wurde aber noch gar nicht angesprochen, nämlich memcpy. Jedes beliebige 
Objekt (außer Bitfield-Members) kann als Array aus unsigned char 
interpretiert werden, indem es per memcpy() in ein solches kopiert wird. 
Das ist dann meiner Ansicht nach implementation defined und nicht mehr 
unspecified.

von Stefan R. (srand)


Lesenswert?

Rolf Magnus schrieb:
> Das hängt nun davon ab, wie du "darf nicht" definierst, was offenbar
> anders ist als meine Definition.

Wir sind uns doch wohl einig, daß Programme, die unspecified behavior 
auslösen, konforme Programme sind, während Programme, die undefined 
behavior auslösen, eben gar keine C-Programme sind?

Das ist meine Richtschnur bei "darf nicht".

unspecified ist okay, wenn man weiß, daß man sich das verhalten 
hereinholt (in der praxis programmiert man meistens für eine 
überschaubare Zahl von Zielplattformen). undefined ist nicht okay (auch 
wenn mans manchmal trotzdem macht).

von (prx) A. K. (prx)


Lesenswert?

Es steht in 6.2.6.1.5 auch ausdrücklich drin, dass man mit character 
types an alle Daten lesend ran darf. Aber eben nur mit denen. Wie auch 
beim Aliasing haben chars eine Sonderrolle.

Weshalb
  u.i = 1; ... = u.x[1];
bei
  union { uint32_t i; uint8_t x[4]; } u;
etwas anders zu bewerten ist als
  union { uint32_t i; uint16_t x[2]; } u;

von Karl H. (kbuchegg)


Lesenswert?

Stefan Rand schrieb:
> Karl Heinz Buchegger schrieb:
>> unspecified bedeutet, dass sich der Standard raushält, was genau
>> passiert, dass aber der Compilerhersteller dokumentieren muss, was
>> konkret passsiert.
>
> Och, Karl-Heinz! Das weißt du aber besser.
>
> Was du meinst ist "implementation-defined".

Damn.
Richtig.


Ich seh schon. Ich sollte mich da raushalten. Ist schon lange her, dass 
ich den Standard gelesen habe und vieles davon hab ich vergessen bzw. 
mag sich auch in den Details geändert haben.
1
3.17.3
2
1 unspecified value
3
valid value of the relevant type where this International Standard
4
imposes no requirements on which value is chosen in any instance
5
2 NOTE An unspecified value cannot be a trap representation.

Auch interessant
1
6.2.6.1
2
5 Certain object representations need not represent a value of the
3
object type. If the stored value of an object has such a representation
4
and is read by an lvalue expression that does not have character type,
5
the behavior is undefined.

zusammen mit einigen anderen Aussagen über unions sieht es wohl so aus, 
dass man die Verwendung einer union zum Zwecke der Konvertierung in 
Bytes legalisiert hat.

von (prx) A. K. (prx)


Lesenswert?

Stefan Rand schrieb:
> Wir sind uns doch wohl einig, daß Programme, die unspecified behavior
> auslösen, konforme Programme sind, während Programme, die undefined
> behavior auslösen, eben gar keine C-Programme sind?

Programme mit unspecified behavior produzieren ein Ergebnis, das aber 
nicht notwendigerweise vorhersagbar ist. Programme mit undefined 
behavior sind nicht notwendigerweise übersetzbar und produzieren auch 
nicht notwendigerweise ein Ergebnis.

von (prx) A. K. (prx)


Lesenswert?

Karl Heinz Buchegger schrieb:
> zusammen mit einigen anderen Aussagen über unions sieht es wohl so aus,
> dass man die Verwendung einer union zum Zwecke der Konvertierung in
> Bytes legalisiert hat.

Wobei man dabei wohl nicht ausgerechnet die unions um Auge hatte, als 
vielmehr die generelle Möglichkeit zur Byte-Serialisierung binärer 
Daten. Egal ob mit unions oder mit der verbreiteteren Methode über 
Pointer. Andernfalls liessen sich lowlevel Funktionen aus dem 
I/O-Bereich oder der Speicherverwaltung oft nicht in C formulieren.

In diesem Zusammenhang darf man auch die Ausnahmestellung von 
char-Pointern beim Aliasing sehen.

von Rolf Magnus (Gast)


Lesenswert?

Stefan Rand schrieb:
> Rolf Magnus schrieb:
>> Das hängt nun davon ab, wie du "darf nicht" definierst, was offenbar
>> anders ist als meine Definition.
>
> Wir sind uns doch wohl einig, daß Programme, die unspecified behavior
> auslösen, konforme Programme sind, während Programme, die undefined
> behavior auslösen, eben gar keine C-Programme sind?

Konform ja, aber (nach ISO-C) nicht strikt konform.

> Das ist meine Richtschnur bei "darf nicht".
>
> unspecified ist okay, wenn man weiß, daß man sich das verhalten
> hereinholt

Man weiß halt nicht unbedingt, welches Verhalten man sich reinholt. Es 
muß ja nicht mal dokumentiert sein, was passiert. Manchmal kommt man 
halt nicht drum herum, gerade in der µC-Programmierung. Es ist also 
insofern ok, als daß einem halt nichts anderes übrig bleibt. Also 
ungefähr in dem Maße, wie es auch ok ist, wenn einem auf der Landstraße 
ein Überholer entgegenkommt, die Straße zu verlassen und ins Gemüse zu 
fahren. ;-)

A. K. schrieb:
> Es steht in 6.2.6.1.5 auch ausdrücklich drin, dass man mit character
> types an alle Daten lesend ran darf.

Ich hab jetzt nur C99 da. Dort steht in 6.2.6.1.4, daß man sie in ein 
Array aus unsigned char kopieren darf, nicht aber, daß man sie direkt 
lesen darf. Das Kopieren kann man per memcpy() erledigen. Das wird 
zugegebenermaßen in der Regel auch nichts anderes machen, als die Daten 
byteweise lesen, aber da es eine Standardfunktion ist, kann die 
Implementierung auch beliebige Tricks anwenden, die dem Benutzer nicht 
zur Verfügung stehen.

von (prx) A. K. (prx)


Lesenswert?

Rolf Magnus schrieb:
>> Es steht in 6.2.6.1.5 auch ausdrücklich drin, dass man mit character
>> types an alle Daten lesend ran darf.
>
> Ich hab jetzt nur C99 da.

Ich bezog mich ebenfalls auf C99. Allerdings steht auch in 6.2.6.1.4 
nur, dass man per memcpy lesend in uchars kopieren darf. Davon dass 
dies auch schreibend zulässig sei, steht dort nichts.

C11 wirkt auf den ersten Blick unverändert.

von Mark B. (markbrandis)


Lesenswert?

Harald P. schrieb:
> Im Originalprogramm ist es eine union. Hier für den Beitrag, habe ich
> extrem gekürzt.

Problem exists between keyboard and chair. ;-)

von Harald P. (haraldp)


Lesenswert?

Also, um auf das Ausgangsproblem zurückzukommen. Wünschenswert wäre ein 
Compilerverhalten, bei dem der Übersetzer eine Warnung (oder sogar 
Error) bei erkannten Programmierfehlern (wie hier eine 
Feldgrenzenüberschreitung) ausgibt, anstatt Code zu erzeugen, der ins 
Nirwana führt.

von Mark B. (markbrandis)


Lesenswert?

Harald P. schrieb:
> Also, um auf das Ausgangsproblem zurückzukommen. Wünschenswert wäre ein
> Compilerverhalten, bei dem der Übersetzer eine Warnung (oder sogar
> Error) bei erkannten Programmierfehlern (wie hier eine
> Feldgrenzenüberschreitung) ausgibt, anstatt Code zu erzeugen, der ins
> Nirwana führt.

Dann verwendest Du vielleicht die für Dich falsche Programmiersprache? 
In C darf man gnadenlos über die Grenzen von Arrays hinaus schreiben. 
Niemand hindert Dich daran. In Java z.B. sieht das anders aus.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Harald P. schrieb:
> Also, um auf das Ausgangsproblem zurückzukommen. Wünschenswert wäre ein
> Compilerverhalten, bei dem der Übersetzer eine Warnung (oder sogar
> Error) bei erkannten Programmierfehlern (wie hier eine
> Feldgrenzenüberschreitung) ausgibt, anstatt Code zu erzeugen, der ins
> Nirwana führt.

ACK. -Warray-bounds sollte hier m.E. meckern, tut es aber nicht 
(getestet mit 4.7 und 4.8).

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.