Forum: Compiler & IDEs C-Frage zu einem Warning in einer if-Anweisung


von Gerhard (Gast)


Lesenswert?

Was muss an der unten stehenden if-Anweidung geändert werde, damit diese 
Meldung (der Cursor steht in der Zeile nach dem if) verschwindet:
1
Warning    dereferencing type-punned pointer will break strict-aliasing rules

1
if ( (dest_ip & (*((unsigned long *)&netmask[0])))==
2
       ((*((unsigned long *)&myip[0]))&(*((unsigned long *)&netmask[0]))) )
3
    {
4
        DEBUG("MY NETWORK!\r\n");
5
    }
6
    else
7
    {
8
        DEBUG("ROUTING!\r\n");
9
        dest_ip = (*((unsigned long *)&router_ip[0]));
10
    }

von Peter II (Gast)


Lesenswert?

was ist denn netmask und myip für ein Datentype?

von (prx) A. K. (prx)


Lesenswert?

Wird wohl verschwinden, wenn man striktes Aliasing abschaltet.
Ansonsten: https://en.wikipedia.org/wiki/Pointer_aliasing

Man sollte sich dabei natürlich nicht bloss die Warnung abschalten (das 
ginge u.U. auch direkt), sondern sicher sein, dass durch das gewarnte 
Aliasing kein Problem dräut.

: Bearbeitet durch User
von Gerhard (Gast)


Lesenswert?

So sind myip und netmask angelegt:

1
unsigned char myip[4];
2
unsigned char netmask[4];

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Ein Problem, das Du Dir da einhandelst, kann der nichtalignte 
32-Bit-Zugriff sein. Es gibt Prozessorarchitekturen, bei denen das 
einfach nur etwas langsamer wird (x86), aber es gibt auch 
Prozessorarchitekturen, bei denen so etwas gar nicht erst vorgesehen 
ist.

Wenn Deine Arrays nicht auf einer auf 32-Bit-Zugriffe ausgerichteten 
Adresse anfangen, tritt dieses Problem auf.

Es ist natürlich bequem, durch diese Typecast-Konstruktion einen 
32-Bit-Wert aus einem 4-Byte-Array zu generieren, aber "sauber" ist das 
nicht. Insbesondere, weil es sich nicht um die Byte-Order (little oder 
big-Endian) kümmert.

von Vincent H. (vinci)


Lesenswert?

Normalerweise bieten Compiler für so lustige Casts Macros an, die meist 
irgendwie mit "SIMD" benannt werden. SIMD steht in diesem Fall für 
"Single Instruction Multiple Data", da solche "Späße" bei DSP 
Anwendungen recht häufig vorkommen. Sprich auf irgendwelchen Werten, die 
eigentlich keine 32bit haben, werden Rechenoperationen für 32bit 
angewandt...

Im Fall von ARM und GCC wäre das Macro etwa
__SIMD32(x)


Den Datentyp von dest_ip hast du uns noch verheimlicht?
Is der unsigned long?


Für Leute, die Macros ungern nutzen (wenn auch Compiler interne...) 
würden sich hier Unions anbieten.

von Gerhard (Gast)


Lesenswert?

Der Compiler ist der GCC für die AVR-Familie.


[/C]
unsigned long dest_ip
[C]

von tictactoe (Gast)


Lesenswert?

Du musst die Variablen umkopieren:
1
unsigned long nm, ip;
2
memcpy(&nm, netmask, sizeof(netmask));
3
memcpy(&ip, myip, sizeof(myip));
4
if ((dest_ip & nm) == (ip & nm))
5
{
6
         DEBUG("MY NETWORK!\r\n");
7
}
8
else
9
{
10
         DEBUG("ROUTING!\r\n");
11
         memcpy(&dest_ip, router_ip, sizeof(router_ip));
12
}
Keine Sorge, der Compiler kennt memcpy und merkt schon, dass er gar 
nichts kopieren muss. Die memcpy werden wegoptimiert.

von Dirk B. (dirkb2)


Lesenswert?

ES gibt auch noch memcmp, dass dafür gemacht wurde.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Warum ist myip und ein Byte-Array, dest_ip aber ein 32-Bit-Integer? Das
sind doch beides IP-Adressen, wäre es deswegen nicht sinnvoll, für
beiden den gleichen Datentyp zu verwenden?

Ich würde beide vom Typ unsigned long, oder noch besser uint32_t machen,
ebenso netmask. Dann kannst du die Adressen nach Belieben maskieren (&),
vergleichen (==) und zuweisen (=), ohne dafür eine Cast-Orgie
veranstalten zu müssen. Du brauchst dann auch kein memcpy oder memcmp.
Das macht das Programm ganz nebenbei um ein Vielfaches übersichtlicher.

Wenn du woanders im Programm tatsächlich auf die einzelnen Bytes der
Adresse zugreifen möchtest, um sie von der oder in die human-readable
Darstellung zu konvertieren, kannst du das dort über einen Cast in einen
unsigned-char- bzw. uint8_t-Pointer machen. In diese Richtung ist der
Cast nämlich erlaubt und führt zu keiner Warnung. Alternativ kannst du
dafür auch eine Union verwenden.

: Bearbeitet durch Moderator
von Markus F. (mfro)


Lesenswert?

Rufus Τ. F. schrieb:
> Wenn Deine Arrays nicht auf einer auf 32-Bit-Zugriffe ausgerichteten
> Adresse anfangen, tritt dieses Problem auf.

die Pointer-Aliasing Problematik hat nichts (oder höchstens am Rande) 
mit unaligned-Zugriffen zu tun.

Die Problematik ist (in diesem konkreten Beispiel nicht unbedingt 
offensichtlich), daß sich Zeiger überlappen können.

Das verhindert bessere Optimierungen, weil der Compiler dereferenzierte 
Werte nicht in Registern halten, sondern immer aufs Neue lesen muß.

Leider sind nicht alle gcc Versionen mit einer gleich guten Erkennung 
gesegnet, wann das tatsächlich ein Problem sein könnte und wann nicht.

-fno-strict-aliasing als Antwort auf die Warnung ist nicht unbedingt 
eine gute Lösung. gcc wird sich mit schlechter Registernutzung bedanken 
und paranoid alle Zeiger unnötig oft dereferenzieren.

Wenn man sicher ist, daß Überlappungen ausgeschlossen sind, kann man die 
Zeiger als _restrict_ (oder nur restrict bei C99) deklarieren und 
erklärt gcc, daß der dahinterliegende Speicher ausschließlich über 
diesen Zeiger angefaßt wird.

Ähnlich wie const ist das eine Zusicherung an den Compiler. Bricht man 
sie, kracht's u.U.

von Mikro 7. (mikro77)


Lesenswert?

A. K. schrieb:
> Wird wohl verschwinden, wenn man striktes Aliasing abschaltet.

Aliasing wird bei mir grundsätzlich abgeschaltet.

Das Risiko, ein Aliasing zu übersehen (insbesondere bei Code für 
performante De/Serialisierung) ist hoch. Und GCC warnt quasi nur bei den 
(offensichtlichen) Fällen, wo es keine Probleme gibt. Die wirklichen 
Probleme sieht er aber nicht, wie auch.

Mein Empfehlung: Bei Optimierungen (-O2 und mehr) immer 
-fno-strict-aliasing hinzufügen, es sei denn man weiß wirklich was man 
da tut.

Hier noch ein alter (veralteter?) Link, der recht witzig ist. Linus 
halt. ;-)

http://lkml.org/lkml/2003/2/26/158

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

Gerhard schrieb:
> Der Compiler ist der GCC für die AVR-Familie.

Da sollte es aber mit Aliasing keine Probleme geben.

Oliver

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Markus F. schrieb:
> die Pointer-Aliasing Problematik hat nichts (oder höchstens am Rande)
> mit unaligned-Zugriffen zu tun.

Das wollte ich damit auch nicht zum Ausdruck bringen, das ist ein 
zusätzliches Problem. Wenn auch nicht im konkreten Fall, dem 8-Bit-AVR 
ist Alignment herzlich Wurscht.

von C-Mann (Gast)


Lesenswert?

DRY = Don't Repeat Yourself!
1
#include <stdio.h>
2
3
4
unsigned char myip[4];
5
unsigned char netmask[4];
6
unsigned char router_ip[4];
7
8
9
inline unsigned long bytes2ulong(void *p)
10
{
11
  if (!p)
12
    return 0;
13
14
  return *((unsigned long *)p);
15
}
16
17
18
unsigned long test1(unsigned long dest_ip)
19
{
20
  unsigned long mask = bytes2ulong(netmask);
21
  unsigned long mynet = bytes2ulong(myip) & mask;
22
  
23
  if ( (dest_ip & mask) == mynet )
24
  {
25
    puts("MY NETWORK!");
26
    return dest_ip;
27
  }
28
  else
29
  {
30
    puts("ROUTING!");
31
    return bytes2ulong(router_ip);
32
  }
33
}

Compiler glücklich! Leser glücklich!

von Yalu X. (yalu) (Moderator)


Lesenswert?

Mikro 7. schrieb:
> Aliasing wird bei mir grundsätzlich abgeschaltet.

Was machst du, wenn dein Code irgendwann einmal mit einem anderen
Compiler übersetzt werden soll, der diese Abschaltmöglichkeit nicht
bitetet?

> Und GCC warnt quasi nur bei den (offensichtlichen) Fällen, wo es keine
> Probleme gibt. Die wirklichen Probleme sieht er aber nicht, wie auch.

Bist du sicher?

Ich würde sagen, er warnt viel zu oft, um sicher zu sein, auch die
tatsächlich relevanten, aber schwer erkennbaren Fälle abzudecken.

Oder hast du ein Beispiel für einen Fehler, der mit -fno-strict-aliasing
vermieden worden wäre und wo der GCC trotzdem nicht gewarnt hat?

von Mikro 7. (mikro77)


Lesenswert?

Yalu X. schrieb:

> Was machst du, wenn dein Code irgendwann einmal mit einem anderen
> Compiler übersetzt werden soll, der diese Abschaltmöglichkeit nicht
> bitetet?

Hoffen. Wie jeder andere auch. ;-)

> Ich würde sagen, er warnt viel zu oft, um sicher zu sein, auch die
> tatsächlich relevanten, aber schwer erkennbaren Fälle abzudecken.

Er probiert es.

Durch die vielen False Positives sind die Warnings aber praktisch kaum 
brauchbar. -.-

> Oder hast du ein Beispiel für einen Fehler, der mit -fno-strict-aliasing
> vermieden worden wäre und wo der GCC trotzdem nicht gewarnt hat?

GCC says so:

-Wstrict-aliasing
    This option is only active when -fstrict-aliasing is active. It 
warns about code which might break the strict aliasing rules that the 
compiler is using for optimization. The warning does not catch all 
cases, but does attempt to catch the more common pitfalls. It is 
included in -Wall. It is equivalent to -Wstrict-aliasing=3

von Nop (Gast)


Lesenswert?

Markus F. schrieb:

> -fno-strict-aliasing als Antwort auf die Warnung ist nicht unbedingt
> eine gute Lösung. gcc wird sich mit schlechter Registernutzung bedanken
> und paranoid alle Zeiger unnötig oft dereferenzieren.

Ich habe das mal mit GCC bei einem ziemlich Pointer-intensiven Programm 
nachgemessen.

Ergebnis: strict-aliasing brachte zwar dieselben Ergebnisse wie 
no-strict-aliasing (schonmal gut), aber keinerlei meßbaren 
Performancegewinn. Also lieber abschalten.

Je nach Anwendung mag das natürlich anders ausfallen, aber man sollte 
das schon konkret mal nachmessen, ob es den potentiellen Ärger überhaupt 
wert ist.

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.