[C/C++] const auf integrale typen in einer Funktion sinnvoll?



  • EDIT: arr, einen satz überlesen, und schon die ganze antwort sinnlos 🙂
    also nochmal:

    eigentlich alle, die eine low-level bzw. maschinennahe programmiersprache benutzen, die dieses keyword kennt, weil diese sprache für maschinennahe programmierung entwickelt wurde und daher auch dafür verwendet wird.

    dieser satz enthält schlüsse, die ich so nicht anerkenne.

    1. dass eine sprache das keyword volatile kennt, bedeutet nicht dass sie für maschinnennahe programmierung entwickelt wurde (das Beispiel Java hast du ja selbst schon gebracht. C++ sehe ich selbst auch in dieser kategorie)
    2. dass jmd. eine sprache einsetzt, die für maschinnennahe programmierung gemacht wurde, bedeutet nicht, dass er selbst maschinnennah programmiert

    angenommen ne firma macht ein mittleres bis grösseres projekt in C. ohne grosses mächtiges OS daruner. wieviel code schätzt du wird bzw. sollte deiner meinung nach davon "maschinnennah" sein?

    ich sage: so wenig wie möglich. d.h. es werden auch wenig entwickler daran arbeiten. der grossteil wird einfach nur deren library/schicht/funktionen einsetzen. und genau dieser grossteil braucht volatile genauso wenig, wie ein python programmierer.

    wer nicht maschinennah programmiert muss sich natürlich nicht mit 'volatile' auseinandersetzen.

    hm. was inetwa dem gleichkommt, was ich geschrieben habe, oder?

    nur dass ich den schluss "lowlevel geeignete sprache == alles nur lowlevel code" nicht akzeptiere.



  • hustbaer schrieb:

    ;fricky schrieb:

    DrGreenthumb schrieb:

    machst du einfach immer erstmal eine Kopie von deinem Buffer bevor du eine Fremdfunktion aufrufst? Oder verfolgst du den Weg den der Buffer so beschreitet?

    ich vergewissere mich zumindest, ob meine daten verändert werden können oder nicht. ein 'const' im funktionskopf ist schon mal ein guter hinweis, aber keine garantie.
    🙂

    sorry, aber das ist IMO total praxisfremd. würde ich immer und überall nachprüfen, ob irgendwas modifiziert wird, was ich an eine funktion als "const" übergebe, dann würde meine produktivität vermutlich auf 1-5% sinken.
    in grossen projekten ist das einfach komplett undurchführbar.

    naja, dann hab' ich wohl glück, dass keiner meine produktivität an der menge des codes bemessen kann, den ich schreibe, sondern eher an dingen, die funktionieren, also was am ende dabei rumkommt. dazu gehört z.b. dass man sich mit tools und libraries und deren bugs intensiv auseinandersetzt, um nicht hinterher wie der 'ochs vorm berge' zu stehen. langfristig lohnt es sich, je mehr man von dem zeug versteht, dass man verwendet, als es einfach nur blind anzuwenden. aber das weisst du sicher selbst.

    hustbaer schrieb:

    wenn man dagegen zwei einfach regeln in die coding-guidelines aufnimmt, kann man wunderbar mit const arbeiten:

    1) const_cast ist genehmigungspflichtig
    2) C-style casts sind ein entlassungsgrund
    

    nö, das mögen vielleicht c++ mässige code monkey regeln sein. besser sind solche:
    1. du kennst das ziel, du weisst was du tust, ich vertraue dir.
    2. mangelhafte kreativität ist ein entlassungsgrund.

    hustbaer schrieb:

    ich weiss nicht was du unter gross verstehst. ich verstehe unter gross etwas ab vielleicht 50 KLOC

    das grösste projekt an dem ich manchmal arbeite hat etwa 140.000 LOC. sonst sinds eher kleinere.

    DrGreenthumb schrieb:

    "const-correctness" ergibt sich aber gezwungenermaßen sobald man irgendwo const schreibt.

    nein, du kannst jederzeit gefahrlos zwischen const und nicht-const wechseln, sofern du damit kein undefiniertes verhalten provozierst.

    DrGreenthumb schrieb:

    Also was nu? const gut oder schlecht?

    kommt drauf an, es hat vor- und nachteile. so genau kann man das nicht sagen.

    DrGreenthumb schrieb:

    Ich fluche jedes mal über c++-const-correctness wenn ich mal wieder eine Funktion doppelt überladen muss.

    code-duplizierung ist mist. in dem fall ist 'const' von nachteil.

    hustbaer schrieb:

    1. dass eine sprache das keyword volatile kennt, bedeutet nicht dass sie für maschinnennahe programmierung entwickelt wurde (das Beispiel Java hast du ja selbst schon gebracht. C++ sehe ich selbst auch in dieser kategorie)

    in Java hat 'volatile' eine andere bedeutung als in C. c++ hat volatile einfach von C übernommen (wie ich glaube). der grund ist wohl, damit man möglichst einfach bestehenden C code mit einem C++ compiler übersetzen kann.

    hustbaer schrieb:

    1. dass jmd. eine sprache einsetzt, die für maschinnennahe programmierung gemacht wurde, bedeutet nicht, dass er selbst maschinnennah programmiert

    dann hat er vielleicht die falsche sprache gewählt.

    hustbaer schrieb:

    angenommen ne firma macht ein mittleres bis grösseres projekt in C. ohne grosses mächtiges OS daruner. wieviel code schätzt du wird bzw. sollte deiner meinung nach davon "maschinnennah" sein?

    ziemlich viel, denn es fängt bei maschinennahen datentypen wie 'int' an und hört irgendwo beim benutzen von systemspezifischen APIs auf. dazwischen kann man natürlich 'relativ plattformunabhängig' programmieren, aber man erreicht nie den grad der plattformunabhängigkeit wie z.b. bei einer VM- oder skriptsprache.
    🙂



  • ok, fricky, sorry. mein fehler.
    kreativen const-schummel code zu schreiben ist natürlich produktiver als sauber zu programmieren. wie konnte ich nur was anderes glauben.



  • hustbaer schrieb:

    1) const_cast ist genehmigungspflichtig
    2) C-style casts sind ein entlassungsgrund
    

    Noob.



  • volkard schrieb:

    hustbaer schrieb:

    1) const_cast ist genehmigungspflichtig
    2) C-style casts sind ein entlassungsgrund
    

    Noob.

    Träumer.



  • hustbaer schrieb:

    ok, fricky, sorry. mein fehler.
    kreativen const-schummel code zu schreiben ist natürlich produktiver als sauber zu programmieren. wie konnte ich nur was anderes glauben.

    wieso 'schummel'? schnittstellen müssen sowieso gut dokumentiert sein, wenn andere damit arbeiten sollen. 'const' hilft dabei nur bedingt.
    aussagekräftiger als 'const' finde ich z.b. die info zur datenrichtung bei den prototypen in einigen windows-headern, die schreiben __in, __out und __inout vor ihre pointer (wahrscheinlich in anlehnung an VHDL, wo in,out,inout bzw 'buffer' als port-modes auch vom compiler gecheckt werden).
    🙂



  • ;fricky schrieb:

    aussagekräftiger als 'const' finde ich z.b. die info zur datenrichtung bei den prototypen in einigen windows-headern, die schreiben __in, __out und __inout vor ihre pointer (wahrscheinlich in anlehnung an VHDL, wo in,out,inout bzw 'buffer' als port-modes auch vom compiler gecheckt werden).

    Da ich zufällig in VHDL "programmieren" kann, würde ich sagen, es hat nichts mit VHDL zu tun. VHDL ist "zu anders" und man kann es weder mit C noch C++, noch mit Assembler vergleichen, denke ich, abgesehen von der Syntax vielleicht.
    Interessant wäre, wie sind diese __in, __out und __inout definiert? __in bestimmt als const, oder? 🙂



  • abc.w schrieb:

    Interessant wäre, wie sind diese __in, __out und __inout definiert? __in bestimmt als const, oder?

    nö, ich glaube das sind einfach nur leere #defines, damit der benutzer am funktionskopf sehen kann, in welcher richtung die pointer arbeiten. 'const' könnte man ja für 'in' nehmen, 'ohne const' für 'inout', aber für eine reine 'out'-datenrichtung sieht C nix vor (sowas wie ein 'inverses const', das wäre ein pointer, über den man schreibzugriffe machen, aber nicht lesen darf).
    🙂



  • abc.w schrieb:

    Da ich zufällig in VHDL "programmieren" kann, würde ich sagen, es hat nichts mit VHDL zu tun. VHDL ist "zu anders" und man kann es weder mit C noch C++, noch mit Assembler vergleichen, denke ich, abgesehen von der Syntax vielleicht.

    nachtrag: die syntax ist an ADA angeleht, aber dieses konzept der port-modi könnte man auch in 'normalen' programmiersprachen verwenden.
    🙂



  • ;fricky schrieb:

    nö, ich glaube das sind einfach nur leere #defines, damit der benutzer am funktionskopf sehen kann, in welcher richtung die pointer arbeiten...

    Oder hat Microsoft vielleicht eine "Spezialversion" ihres Compilers, der diese Schlüsselwörter kennt und vielleicht die eine oder andere Warnung ausgibt, wenn z.B. etwas als __in definiert wurde und in der Funktion doch geändert wird, oder etwas als __out nichts zugewiesen bekommt, sondern fälschlicherweise gelesen wird... Wäre denkbar? 🙂



  • abc.w schrieb:

    ;fricky schrieb:

    nö, ich glaube das sind einfach nur leere #defines, damit der benutzer am funktionskopf sehen kann, in welcher richtung die pointer arbeiten...

    Oder hat Microsoft vielleicht eine "Spezialversion" ihres Compilers, der diese Schlüsselwörter kennt und vielleicht die eine oder andere Warnung ausgibt, wenn z.B. etwas als __in definiert wurde und in der Funktion doch geändert wird, oder etwas als __out nichts zugewiesen bekommt, sondern fälschlicherweise gelesen wird... Wäre denkbar?

    zuzutrauen ist es ihnen jedenfalls. kannst ja mal im winapi-forum fragen, da tummeln sich die m$-compiler freaks.
    🙂



  • Einfach mit MSVC++ mit /analyze compilieren.

    http://msdn.microsoft.com/en-us/library/ms173498.aspx

    Bzw. früher hiess das Teil PREfast:
    http://msdn.microsoft.com/en-us/library/ms933794.aspx



  • ;fricky schrieb:

    hustbaer schrieb:

    ok, fricky, sorry. mein fehler.
    kreativen const-schummel code zu schreiben ist natürlich produktiver als sauber zu programmieren. wie konnte ich nur was anderes glauben.

    wieso 'schummel'?

    const wegcasten, und dann etwas modifizieren, ist schummeln. du behauptest ja hier, dass man sich nicht auf const verlassen kann. das impliziert, dass es OK ist, code zu schreiben, der genau das macht: const wegcasten, und dann modifizieren. und das halte ich nunmal für völlig daneben.

    schnittstellen müssen sowieso gut dokumentiert sein, wenn andere damit arbeiten sollen. 'const' hilft dabei nur bedingt.

    was heisst bedingt? bedingt wie "hilft viel, aber ist nicht 100% ausreichend"? oder bedingt wie "ist so-gut-wie egal"? sag mal konkret was du meinst.

    aussagekräftiger als 'const' finde ich z.b. die info zur datenrichtung bei den prototypen in einigen windows-headern, die schreiben __in, __out und __inout vor ihre pointer (wahrscheinlich in anlehnung an VHDL, wo in,out,inout bzw 'buffer' als port-modes auch vom compiler gecheckt werden).

    __in macht eigentlich nur sinn, wenn ein zeiger nicht const ist, der const sein sollte.
    einzig die unterscheidung zwischen __inout und __out ist etwas, was sich über const nicht abbilden lässt. im idealfall impliziert der methodenname, der parametername oder einfach die semantik der schnittstelle was gemeint ist.

    bei einer funktion "apply" einer klasse "inplace_blur_filter" brauche ich nicht dazuschreiben dass es "in/out" ist, da es logisch ist.

    sollte es nicht logisch sein, dann gehört es dokumentiert. habe nie was anderes behauptet.

    ich behaupte nur, dass const einen nicht unwesentlichen teil dessen was dokumentiert gehört abdeckt, und dass der compiler die checks übernimmt, ob man sich auch an das hält, was man verspricht. genau das ist ja der grosse vorteil dabei.

    eine doku die irgendwo steht, kann der compiler nicht checken. und dass doku sehr schnell veraltet, ist etwas was ich nicht nur glaube, sondern aus der praxis 100% bestätigen kann.



  • hustbaer schrieb:

    ;fricky schrieb:

    hustbaer schrieb:

    ok, fricky, sorry. mein fehler.
    kreativen const-schummel code zu schreiben ist natürlich produktiver als sauber zu programmieren. wie konnte ich nur was anderes glauben.

    wieso 'schummel'?

    const wegcasten, und dann etwas modifizieren, ist schummeln.

    nö, wenn du z.b. 'ne funktion aufrufen willst, bei der das 'const' fehlt, die aber definitiv keine schreibzugriffe über diesen pointer macht, musstes wegcasten. das ist nichts schlimmes und funktioniert auch.

    hustbaer schrieb:

    schnittstellen müssen sowieso gut dokumentiert sein, wenn andere damit arbeiten sollen. 'const' hilft dabei nur bedingt.

    was heisst bedingt? bedingt wie "hilft viel, aber ist nicht 100% ausreichend"? oder bedingt wie "ist so-gut-wie egal"? sag mal konkret was du meinst.

    das kommt drauf an. wenn 'const' logisch passt, unterstützt es die lesbarkeit. wenn aber z.b. da steht: 'void funktion (const void *p)', dann ist klar, dass das interface irgendwie mist ist, weil mit ziemlicher sicherheit im innern gecastet wird.

    btw, nochwas zu deinen casting-verbotsregeln 'wer const wegcastet wird gefeuert', eine kleine quizfrage: wie bekommt man 'const' weg, ohne irgendeinen cast zu benutzen.?
    🙂



  • Gar nicht(!), sondern indem man 100-prozentige 'const-correctness' verwendet (halbe oder dreiviertel const-correctness ist Frickelei -)



  • Th69 schrieb:

    Gar nicht(!)

    doch, 'entconsten' geht ohne cast. wer errät den trick (ist garnicht so schwer)?
    🙂



  • ;fricky schrieb:

    nö, wenn du z.b. 'ne funktion aufrufen willst, bei der das 'const' fehlt, die aber definitiv keine schreibzugriffe über diesen pointer macht, musstes wegcasten. das ist nichts schlimmes und funktioniert auch.

    Doch, das ist ein schlimmer Designfehler, der bei durchdachter Programmierung nicht auftritt. Dass gewisse Bibliotheken schlecht programmiert sind, rechtfertigt const_cast nicht per se.

    ;fricky schrieb:

    das kommt drauf an. wenn 'const' logisch passt, unterstützt es die lesbarkeit. wenn aber z.b. da steht: 'void funktion (const void *p)', dann ist klar, dass das interface irgendwie mist ist, weil mit ziemlicher sicherheit im innern gecastet wird.

    Wieso, es kann ja auch sein, dass nur lesend zugegriffen wird, auch wenn p in einen Typzeiger gecastet werden muss. Ich finde Funktionen, die bei ihrer Schnittstelle heucheln, als würden sie Argumente nicht verändern, völlig daneben.

    ;fricky schrieb:

    doch, 'entconsten' geht ohne cast. wer errät den trick (ist garnicht so schwer)?

    Das wäre eine Möglichkeit:

    union conster
    {
    	const int* m;
    	int* n;
    };
    
    int main()
    {
    	const int a = rand() >= 0 ? 5 : 2; // damit kein konstanter Ausdruck
    
    	conster c;
    	c.m = &a;
    	(*c.n) = 7;
    }
    


  • Nexus schrieb:

    ;fricky schrieb:

    nö, wenn du z.b. 'ne funktion aufrufen willst, bei der das 'const' fehlt, die aber definitiv keine schreibzugriffe über diesen pointer macht, musstes wegcasten. das ist nichts schlimmes und funktioniert auch.

    Doch, das ist ein schlimmer Designfehler, der bei durchdachter Programmierung nicht auftritt. Dass gewisse Bibliotheken schlecht programmiert sind, rechtfertigt const_cast nicht per se.

    ich dachte eigentlich, der const_cast wäre genau für sowas gut, nämlich ein nachträglich ge-constetes objekt mal wieder zu ent-consten. doof ist natürlich, dass man in einer funktion selbst nicht erkennen kann, ob es sich bei dem vom aufrufer erhaltenen pointer nur um eine 'verkleidung', oder um ein tatsächlich konstantes objekt handelt, denn im zweiten fall wäre ein const-wegcast ja undefiniert.

    Nexus schrieb:

    ;fricky schrieb:

    doch, 'entconsten' geht ohne cast. wer errät den trick (ist garnicht so schwer)?

    Das wäre eine Möglichkeit:

    union conster
    {
    	const int* m;
    	int* n;
    };
    ...
    

    genau das meinte ich. weitere möglichkeiten gibts wohl nicht, oder kennt noch jemand eine?
    jedenfalls sollte husti auch unions in seine verbotsliste aufnehmen.
    🙂



  • ;fricky schrieb:

    ich dachte eigentlich, der const_cast wäre genau für sowas gut, nämlich ein nachträglich ge-constetes objekt mal wieder zu ent-consten.

    Der Const-Cast ist mehr ein Workaround für bereits schlechtes Design als ein wirklich oft sinnvoll eingesetztes Sprachmittel. Was hat Const-Correctness für einen Sinn, wenn man sich nicht daran hält? Wenn man ein Objekt als const deklariert, geht man davon aus, dass es nicht geändert wird. Daran kann man sich gefälligst halten...

    ;fricky schrieb:

    genau das meinte ich. weitere möglichkeiten gibts wohl nicht, oder kennt noch jemand eine?

    Gibt noch ein paar Möglichkeiten, besonders mit Strukturen (obwohl diese sehr ähnlich sind). Du hast zu wenig Fantasie! 😉
    Dass da recht schnell undefiniertes Verhalten dazu kommt, sollte klar sein.

    // 1)
    int n = 1;
    const int m = 5*n;
    *(&n + (&m - &n)) = 42;
    
    // 2)
    const int o = 5*n;
    struct
    {
    	int* n;
    	const int* m;
    } z = {&n, &o};
    **(&z.n + 1) = 42;
    
    // 3)
    struct
    {
    	int n;
    	const int m;
    } a = {1, 5};
    *(&a.n + 1) = 42;
    
    // 4)
    struct tmp
    {
    	const int p;
    };
    tmp b = {5};
    tmp h = {42};
    new (&b) tmp(h);
    
    // 5)
    struct hacker
    {
    	int q;
    };
    tmp c = {5};
    reinterpret_cast<hacker*>(&c)->q = 42;
    
    // 6)
    tmp d = {5};
    memcpy(&d, &h, sizeof(h));
    
    std::cout <<   m << " " <<   o << " " << a.m << std::endl;
    std::cout << b.p << " " << c.p << " " << d.p << std::endl;
    


  • Nexus schrieb:

    Der Const-Cast ist mehr ein Workaround für bereits schlechtes Design als ein wirklich oft sinnvoll eingesetztes Sprachmittel. Was hat Const-Correctness für einen Sinn, wenn man sich nicht daran hält? Wenn man ein Objekt als const deklariert, geht man davon aus, dass es nicht geändert wird. Daran kann man sich gefälligst halten...

    stimmt, aber in const-korrektem code darfste nie einen const-pointer an eine nicht-const funktion übergeben, obwohl du 100% weisst, dass sie deine daten nicht anrührt. das ist eine grosse einschränkung. dann vielleicht besser ganz auf const verzichten. ich z.b. verwende 'const' nur, um compiler und linker unter die arme zu greifen, um (auf 'nem embedded system mit wenig RAM) dafür zu sorgen, das unveränderliche daten im Flash und nicht im RAM landen.

    Nexus schrieb:

    Gibt noch ein paar Möglichkeiten, besonders mit Strukturen (obwohl diese sehr ähnlich sind).

    1,2 und 3 kann ich nicht gelten lassen, weil die auch daneben zielen können. 5 hat 'nen cast drin, aber kein 'const-cast', d.h. hb's verbotsliste könnte jetzt so aussehen:
    kündigungsgründe für uneinsichtige c++ hacker sind:
    1. alle casts
    2. memcpy
    3. unions
    4. placement new
    5. anlegen eigener strukturen
    ^^ und da wir das ja nicht wollen, schlagen wir am besten den GCC-entwicklern vor, dieses feature aufzunehmen. der switch könnte heissen: '-enable_coughbear_blacklist' *fg*
    🙂


Anmelden zum Antworten