Forum: PC-Programmierung verständnisfrage code


von mark (Gast)


Lesenswert?

Hallo,

ich habe in einem code folgende Zeile gefunden
1
if (1 / ((sizeof(*parameter) == 850) ? 1 : 0)) {}

was genau passiert da?
ich teile im worst-case 1/0 ? und es wird eh nichts ausgeführt?
Oder was ist der Sinn hinter dieser zeile?

von Bernd K. (prof7bit)


Lesenswert?

Könnte ein verkappter static assert sein, gibt auch noch ne Variante mit 
nem ungültigen Arrayzugriff. Wenn die Bedingung nicht erfüllt ist knallt 
es bereits beim Kompilieren. Hat man früher so gemacht mangels anderer 
Möglichkeiten.

Neuere C-Varianten (C11) unterstützen das Schlüsselwort _Static_assert

Im obigen Fall hat wahrscheinlich jemand ein liebevoll handgepacktes 
struct gebastelt und will sicherstellen daß auch in ferner Zukunft nicht 
klammheimlich die Alignmentregeln sich ändern ohne daß es beim 
Kompilieren schon lautstark knallt mit direktem Zaunpfahlwink darauf was 
falsch ist (sizeof soll genau 850 sein oder irgendwas stimmt nicht).

Mit _Static_assert kann man auch noch schön ne Meldung ausgeben:
1
typedef struct {
2
    u32 magic;
3
    settings_outtype_t output_type;
4
    bool is_npn;
5
} settings_t;
6
7
_Static_assert(_Alignof(settings_t) >= 4, "settings_t must be aligned by at least 4");
8
_Static_assert(sizeof(settings_t) % 4 == 0, "settings_t size must be multiple of 4");
9
_Static_assert(sizeof(settings_outtype_t) == 1, "enum size must be 1");

In Deinem Beispiel gibts halt eine vollkommen absurde Fehlermeldung aber 
immerhin, es kompiliert nicht klammheimlich. Man hätte vielleicht noch 
einen Kommentar dazuschreiben können warum das da steht und wo 
vermutlich der Fehler zu suchen ist (der Programmierer hatte ein 
schlechtes Gewissen und kannte die wackelige Stelle ganz genau sonst 
hätte er diese Absicherung dort nicht errichtet) als Hinweis für 
zukünftige Leser die diesen alten Trick nicht kennen.

: Bearbeitet durch User
Beitrag #6075577 wurde vom Autor gelöscht.
von Wilhelm M. (wimalopaan)


Lesenswert?

Bernd K. schrieb:
> Neuere C-Varianten (C11) unterstützen das Schlüsselwort _Static_assert

Da nix von C da steht:

in C++ heisst es: static_assert()

(in C11 gibt es ein gleichlautendes Marcro dafür).

: Bearbeitet durch User
Beitrag #6075624 wurde von einem Moderator gelöscht.
von sid (Gast)


Lesenswert?

mark schrieb:
> ich teile im worst-case 1/0 ? und es wird eh nichts ausgeführt?

so schauts aus.. es wird nicht nur nichts ausgeführt,
der Code rennt in einen divisionby0 error
(der hoffentlich irgendwo behandelt wird)

ich halte die Zeile für mindestens unschön ehrlich gesagt..
wenn es um die Ausführung geht
reicht die zweite Hälfte völlig
1
if(sizeof(*parameter) == 850){}

macht NULL Sinn,
ausser dass ein Fehlerfall ausgelöst wird falls sizeof NICHT 850 liefert
beim compilieren.
Aber das lässt sich mMn auch eleganter abfragen ..

ein crash test header zB der sich beim release einfach wieder entfernen 
lässt

Aber naja so macht halt jeder anders :D

von Wilhelm M. (wimalopaan)


Lesenswert?

mark schrieb:
> if (1 / ((sizeof(*parameter) == 850) ? 1 : 0)) {}

Totaler Schwachsinn, denn es gibt bei -Wall -Wextra "nur" eine Warnung, 
und zur Laufzeit keinen Div0-Error, da wegoptimiert (auch ohne -O)

Kann auch nur dort stehen, wo Statements zugelassen sind, also bspw. 
nicht außerhalb von Funktionen. static_assert() dagegen schon.

von Daniel A. (daniel-a)


Lesenswert?

Wilhelm M. schrieb:
> Kann auch nur dort stehen, wo Statements zugelassen sind, also bspw.
> nicht außerhalb von Funktionen. static_assert() dagegen schon.

Dafür hat static_assert das umgekehrte Problem. Wenn man ein Macro hat, 
und das soll ein Resultat liefern, aber man will darin mit static_assert 
was prüfen, das geht nicht...

von Wilhelm M. (wimalopaan)


Lesenswert?

Daniel A. schrieb:
> Wilhelm M. schrieb:
>> Kann auch nur dort stehen, wo Statements zugelassen sind, also bspw.
>> nicht außerhalb von Funktionen. static_assert() dagegen schon.
>
> Dafür hat static_assert das umgekehrte Problem. Wenn man ein Macro hat,
> und das soll ein Resultat liefern, aber man will darin mit static_assert
> was prüfen, das geht nicht...

Mach mal ein Beispiel bitte.

von Bernd K. (prof7bit)


Lesenswert?

So wie folgt ist/war eigentlich die gängige Methode mit alten Compilern, 
so kenn ich das noch von früher:
1
// wirft einen Fehler wenn buffer_descriptor_t nicht genau 8 Bytes lang ist
2
typedef char static_assertion_buffer_size_wrong[(sizeof(buffer_descriptor_t)==8)?1:-1];


Das kann man auch in ein Makro packen:
1
#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1]

Und dann so benutzen:
1
STATIC_ASSERT(sizeof(buffer_descriptor_t)==8, buffer_size_wrong);


Dann kommt ungefähr so ne Meldung:
1
src/usb/usb_device.c:84:46: error: size of array 'static_assertion_buffer_size_wrong' is negative

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Bernd K. schrieb:
> typedef char
> static_assertion_bufer_size_wrong[(sizeof(buffer_descriptor_t)==8)?1:-1] ;

Der "Trick" ist zumindest in der Hinsicht besser, als dass er 
funktioniert, und einen Compile-Zeit Fehler produziert und kein 
Statement ist. Trotzdem ist seine Zeit seit C11 definitiv vorbei, und 
sollte durch static_assert() ersetzt werden.

von Daniel A. (daniel-a)


Lesenswert?

Wilhelm M. schrieb:
> Daniel A. schrieb:
>> Wilhelm M. schrieb:
>>> Kann auch nur dort stehen, wo Statements zugelassen sind, also bspw.
>>> nicht außerhalb von Funktionen. static_assert() dagegen schon.
>>
>> Dafür hat static_assert das umgekehrte Problem. Wenn man ein Macro hat,
>> und das soll ein Resultat liefern, aber man will darin mit static_assert
>> was prüfen, das geht nicht...
>
> Mach mal ein Beispiel bitte.

Sowas geht nicht:
1
#include <assert.h>
2
3
#define DEVIDE_EVEN_NUMBER_BY_TWO(X) \
4
  ( 0 ? static_assert((X)%2 == 0, "Must be an even number!") : X/2 )
5
6
int bla = DEVIDE_EVEN_NUMBER_BY_TWO(42); // Geht nicht: error: expected expression before ‘_Static_assert’

Mein use-case war damals zwar etwas anders, ich wollte einen Typecheck 
für einer generischen linked list machen, aber das ging damals noch aus 
ganz anderen Gründen nicht ohne Erweiterungen (typeof) in C, also hab 
ichs gelassen. Bin seither schon einmal in die Falle getappt, als ich 
bei etwas Refactoring nicht aufpasste...

Der trick mit dem negativen Array type sieht aber interessant aus. 
Scheint auch mit compund literalen zu gehen:
1
#include <stdint.h>
2
3
#define DEVIDE_EVEN_NUMBER_BY_TWO(X) \
4
  (0 ? (int)(intptr_t)(int[(X)%2?-1:1]){0} : X/2)
5
6
int bla = DEVIDE_EVEN_NUMBER_BY_TWO(42); // OK
7
int bla2 = DEVIDE_EVEN_NUMBER_BY_TWO(43); // Fehler

Cool. Nur dass der Komma Operator hier nicht anwendbar ist, ist etwas 
schade. Und jetzt muss ich mir mein Listenmakroproblem wieder anschauen, 
ob das nicht doch irgendwie geht...

von Wilhelm M. (wimalopaan)


Lesenswert?

Daniel A. schrieb:
> Sowas geht nicht:#include <assert.h>
>
> #define DEVIDE_EVEN_NUMBER_BY_TWO(X) \
>   ( 0 ? static_assert((X)%2 == 0, "Must be an even number!") : X/2 )
>
> int bla = DEVIDE_EVEN_NUMBER_BY_TWO(42); // Geht nicht: error: expected
> expression before ‘_Static_assert’

Ja, denn static_assert() ist kein Ausdruck.

Wenn ich Dich richtig verstehe, möchtest Du eine Zusicherung, die

1) in einem compile-time-constant Kontext eine compile-time Assertion 
ist,

2) sonst eine run-time Assertion ist.

Könnte man so machen (in C++)
1
#include <cstdint>
2
#include <cassert>
3
4
constexpr int divEven(int v) {
5
    bool c = (v % 2) == 0;
6
    assert(c);
7
    return c / 2;
8
}
9
10
int main() {
11
    constexpr auto e1 = divEven(43); // compile-time-error
12
    auto e2 = divEven(43); // run-time-error
13
}

Fall 2) ist ja klar.

Fall 1) produziert den gewünschten compile-time Fehler, weil
__assert_fail(...) selbst nicht constexpr ist. Wird tatsächlich 
desöfteren benutzt, finde ich aber auch nur einen Hack.

Für Fall 1) wäre ggf. eine andere Variante machbar:
1
template<auto V>
2
bool divEvenC() {
3
    constexpr bool c = (V % 2) == 0;
4
    static_assert(c);
5
    return c / 2;
6
    
7
}
8
9
int main() {
10
    constexpr auto e3 = divEvenC<43>();
11
    auto e4 = divEvenC<42>();
12
}

Noch eine Variante wäre, divEven() mit als 
divEven(std::integral_constant<T, N>) zu überladen.

von DPA (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Wenn ich Dich richtig verstehe, möchtest Du eine Zusicherung, die
>
> 1) in einem compile-time-constant Kontext eine compile-time Assertion
> ist,
> 2) sonst eine run-time Assertion ist.

Nö, zur compile time zum compile-time-constant kontext reicht mir 
völlig. Aber ich nutze eben C11, da gibts keine Templates und kein 
constexpr, dafür muss ich dann Macros nehmen, und in gewissen 
Situationen brauch ich dann halt ein Macro, das mir einen Wert zurück 
gibt, den ich aber noch prüfen muss, weil man das Macro sonst falsch 
anwenden könnte, und dann muss ich alles in eine Expression reinpacken, 
und dann kann ich static_assert halt dort nicht mehr verwenden.

von A. S. (Gast)


Lesenswert?

sid schrieb:
> so schauts aus.. es wird nicht nur nichts ausgeführt,
> der Code rennt in einen divisionby0 error
> (der hoffentlich irgendwo behandelt wird)
>
> ich halte die Zeile für mindestens unschön ehrlich gesagt..
> wenn es um die Ausführung geht
> reicht die zweite Hälfte völligif(sizeof(*parameter) == 850){}
>
> macht NULL Sinn,

Verstehe ich nicht. Der Compiler sieht 1/0 und soll nicht warnen? 
Vielleicht muss er nicht, vielleicht geht's eleganter, aber es ist doch 
absurd, anzunehmen, dass der dortige Compiler nicht warnt.

Noch absurder ist es, das zu verbessern und dann beklagen dass das 
keinen Sinn mehr macht.

Und das z.b. die Array-Version zuverlässiger und universeller ist: 
geschenkt. Ich kannte die aber auch nicht von Anfang an.

von sid (Gast)


Lesenswert?

A. S. schrieb:
> Verstehe ich nicht. Der Compiler sieht 1/0 und soll nicht warnen?
> Vielleicht muss er nicht, vielleicht geht's eleganter, aber es ist doch
> absurd, anzunehmen, dass der dortige Compiler nicht warnt.

Der compiler schmeisst (hoffentlich) n Fehler aus und warnt nicht nur
(Warnungen könnten ja ignoriert werden)

Ja, das sieht aber in der Tat so aus (im nachhinein) als meinte ich die 
vereinfachte if-kondition.
Hab ich vielleicht doof formuliert..
Stell dir ein
"So wie du sie im Code gefunden hast ..." davor vor :D

Denn was ich meinte ist,
dass es NULL Sinn macht die 'Fehlerquelle' absichtlich
in die if-Kondition zu packen die (wie ich annehme) Laufzeitrelevant 
ist.
also im kompilierten Zustand immer und immer wieder abgefragt wird.
Nur um das kompilieren zu vermeiden.

mMn ist es sinnvoller das in einen header zu stecken den man bei 
erfolgreichem Test auskommentieren kann um sauberere und besser lesbaren 
Quellcode zu haben.

von Writer (Gast)


Lesenswert?

sid schrieb:
> Denn was ich meinte ist,
> dass es NULL Sinn macht die 'Fehlerquelle' absichtlich
> in die if-Kondition zu packen die (wie ich annehme) Laufzeitrelevant
> ist.
> also im kompilierten Zustand immer und immer wieder abgefragt wird.
> Nur um das kompilieren zu vermeiden.

ich würde annahmen, der TO hat den vollständigen Code gepostet. Also 
eine Leere Schleife mit dieser if-Abfrage, die vom Compiler (oder wem 
auch immer) komplett entfernt wird.

Aber klar, ein define, dass dann einfach nur "sAssert(850 == sizeof 
*parameter);" erlaubt, wäre auch vor C11 schöner. Nur wie gesagt, dass 
war vor Jahren nicht Standard. Ich habe es auch erst vor ~15 Jahren 
irgendwo zufällig kennengelernt (mit []), als erster unter uns Kollegen. 
Wenn das jemand selbst erdacht hat: Alle Achtung.

von sid (Gast)


Lesenswert?

Writer schrieb:
> ich würde annahmen, der TO hat den vollständigen Code gepostet. Also
> eine Leere Schleife mit dieser if-Abfrage, die vom Compiler (oder wem
> auch immer) komplett entfernt wird.

Ah siehste.. das hab ich anders gedeutet..
dann natürlich spielt es wirklich keine Rolle (wird ja 
wegrationalisiert)

Aber so ist das halt..
am Ende des Tages erkennt man Programmierer and ihrem Dialekt wieder ;)

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.