C-Frage zu einem Warning in einer if-Anweisung

Gast #4757218
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
    }
Persönliche Seite #4757253
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.
#4757256
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.
Gast #4757287
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.
Moderator #4757426
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.
#4757440
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.
#4757505
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
Gast #4757584
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!
Moderator #4757596
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?
#4757649
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
Gast #4776362
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren