Im Zusammenhang mit einer Quellcodeanalyse ergab sich folgende Frage zur
Handhabung (Deref.) von Zeigern auf Funktionen:
Im untenstehenden Programm wird /lfunction ()/ dreimal per
Pointer-Dereferenzierung aufgerufen ( # 1 - 3 ); in verschiedener Schreibweise.
Meines Wissens ist nur Schreibweise #2 ANSI-zulässig.
/PURE C, GNU C und Microtec C/ (letzteres in der Industrie verwendet) verdauen
alle Aufrufe klaglos: Es wird die Funktion gerufen; keine bizarren Effekte.
*Frage:*
Sind Schreibweisen #1 und #3 evtl. /doch/ ANSI und damit /portabel?/
Wer weiß Definitives?????
Gruß Götz
Hier ist das (unglaublich komplexe) Softwarepaket:
typedef long (*funcp) ( int zahl );
long lfunction ( int zahl );
int main ( void )
{
funcp fptr;
long lzahl;
fptr = lfunction;
lzahl = (fptr) ( (int) 1234 ); /* #1 */
lzahl = (* fptr) ( (int) 48 ); /* #2 */
lzahl = fptr ( (int) 33 ); /* #3 */
return 0;
}
long lfunction ( int zahl )
{
return ((long) zahl) * 2L;
}
Sie sind. Zunaechst einmal sind #1 und #3 identisch, da die Klammern
in diesem Fall die Semantik nicht veraendern. Zur Erhoehung der Les-
barkeit erlaubt ANSI explizit, dass statt
(*fkt_ptr)(params,...);
einfach
fkt_ptr(params,...);
geschrieben wird. Am praegnantesten wird das in der "Rationale" zum
Standard (http://wwwwbs.cs.tu-berlin.de/~jutta/c/rat/title.html) in
Abschnitt 3.3.2.2 beschrieben:
Pointers to functions may be used either as (*pf)() or as pf().
Im weiteren wird sogar noch erlaeutert, dass auch beliebig viele De-
referenzierungen zulaessig sind, also z.B. (***pf)().
Im ISO-Standard selbst ist Abschnitt 6.3.2.2 einschlaegig, aber fuer
sich allein nicht so eindeitig (man muss noch zig andere Stellen be-
ruecksichtigen).
Gruss
- Jochen
PS: Die expliziten Casts auf int bei allen drei Aufrufen sind
ueberfluessig, da die Definition des Funktionszeiger-Typs
als Prototyp dient und die erforderlichen Casts implizit
garantiert.
--
Jochen Schoof (http://www-info2.informatik.uni-wuerzburg.de/staff/joscho)
___________________________________________________________________________
\_Address __/ Informatik II, Uni Wuerzburg, Am Hubland, D-97074 Wuerzburg
\_Email ___/ mailto:sch...@informatik.uni-wuerzburg.de
Ja. (fptr) ist äquivalent zu fptr genauso wie (x) zu x und
(((((3))))) zu 3.
*fptr ist äquivalent zu fptr, wenn fptr ein Funktionspointer ist.
Hope it helps,
- Marcus [PGP]
> Meines Wissens ist nur Schreibweise #2 ANSI-zulässig.
es ist die einzige Schreibweise, die bei mir Kopfschmerzen erzeugt. Mir
ist nur #3 bekannt von der ich sicher bin, daß sie strikt ANSI ist.
> lzahl = (fptr) ( (int) 1234 ); /* #1 */
> lzahl = (* fptr) ( (int) 48 ); /* #2 */
> lzahl = fptr ( (int) 33 ); /* #3 */
Und wenn Du die absolut überflüssigen Casts weglassen würdest, wäre dieses
Segment auch lesbar.
Ciao
Sönke
Nur Deppen rappen!
SML>
SML>Und wenn Du die absolut überflüssigen Casts weglassen würdest, wäre
SML>dieses Segment auch lesbar.
--> Kicher...
Sag DAS mal den Jungs/Mädels, die die Programmierrichtlinien in großen
Konzernen verfassen.
Mir ist sowas auch ziemlich lästig, aber mit der Zeit gewöhnt man sich das dann
doch an... und machts dann im Privatleben auch.
Gruß Götz
>> Und wenn Du die absolut überflüssigen Casts weglassen würdest, wäre
>> dieses Segment auch lesbar.
> --> Kicher...
> Sag DAS mal den Jungs/Mädels, die die Programmierrichtlinien in großen
> Konzernen verfassen.
sind das dieselben Leute, die Warnschilder schreiben, daß man einen
Fahrstuhl nicht benutzen sollte, wenn dieser brennt?
SML>Und wenn Du die absolut überflüssigen Casts weglassen würdest, wäre
SML>dieses Segment auch lesbar.
GB>Sag DAS mal den Jungs/Mädels, die die Programmierrichtlinien in großen
GB>Konzernen verfassen.
Wenn Leute Programmierrichtlinien verfassen, die casts vorschreiben, wo keine
notwendig sind, dann gehören die meiner Meinung nach gefeuert.
Gruß JJvB
PS: Ich habe selbst mal welche für unsere Firma verfasst, aber so'n Schrott
kam da nicht rein.
SML>Und wenn Du die absolut überflüssigen Casts weglassen würdest, wäre
SML>dieses Segment auch lesbar.
GB>Sag DAS mal den Jungs/Mädels, die die Programmierrichtlinien in großen
GB>Konzernen verfassen.
Das mit den casts ist gar keine so einfache Sache. Wenn ich grundsätzlich
überall caste, dann sind Datentypen anscheinend egal. Ich versuche von
vorneherein die angemessenen Datentypen zu verwenden, so daß ein int auch immer
einem int zugewiesen wird. Wo das nicht geht (z.B. bestimmte Libraries, die ich
nun mal nicht ändern kann) da ist ein cast ein Muß).
Das (int)33 finde ich aber genauso überflüssig wie ein (long)33L, weil 33 ja
bereits ein int ist. Grundsätzlich alles zu casten finde ich sinnlos. Ein cast
heißt für mich hier hat sich der Programmierer etwas dabei gedacht, daß er z.B.
ein unsigned short auf ein unsigned char casted: er weiß, das er hier einige
bits unter den Tisch fallen läßt.
BTW, welche Programmierrichtlinien - wenn ihr denn danach arbeitet - findet ihr
hilfreich und welche nerven? Ich finde es z.B. ausgesprochen hilfreich, wenn
Makros immer GROSS geschrieben werden. Man weiss sofort, daß MAKRO(a++)
eventuell etwas unerwartetes liefern kann, weil es möglich ist, daß der
Ausdruck a++ mehrfach ausgewertet wird.
Bei großen Projekten ist es sinnvoll alle public Prozeduren eines Moduls
(eventuell mehrere source files) mit den gleichen Anfangsbuchstaben zu
beginnen.
Z.B. XX_init(), YY_init(), YY_send_byte(), YY_xoff(), ...
Sinnvolle Richtlinien machen sich in der Regel bezahlt. Man muß doch hin und
wieder mal Software auf ein anderes Zielsystem portieren - auch wenn das Jahre
vorher nicht geplant war. Oder zwei ähnliche Moduln müssen auf einmal in einem
System laufen. Wie gut, wenn man dann auch bei gleichlautenden Funktionsnamen
diese zwei Präfix-Buchstaben hatte!
Shubhashayam
*** Gerhard
SML>sind das dieselben Leute, die Warnschilder schreiben, daß man einen
SML>Fahrstuhl nicht benutzen sollte, wenn dieser brennt?
Was ist denn daran verwerflich? Es ist unheimlich gefährlich, einen Fahrstuhl
im Brandfall zu benutzen!
Ciao
Uwe H..
Ja, ich denke, so ähnlich kann man's sehen. Eigentlich ist es, meine ich, ein
Abfallprodukt bei der Produktion von Richtlinien, die im Großen und Ganzen
sonst vernünftig sind: Die berühmten Auswüchse und Sumpfblüten. Kann man in
jeder Behörde etc. reihenweise bewundern. Ist ja bekannt, daß Konzerne,
proportional zu ihrer Größe, verstärkte Ähnlichkeit mit Behörden bekommen...
;-)
Gruß Götz
SML>sind das dieselben Leute, die Warnschilder schreiben, daß man einen
SML>Fahrstuhl nicht benutzen sollte, wenn dieser brennt?
...wenn ES brennt...
Shubhashayam
*** Gerhard
> Wie gehe ich mit fremden Headerfiles um, bei denen es Konflikte gibt? (Z.B.
> mit abweichenden Deklarationen, fehlender #ifndef / #define -Klammerung
> etc.) Sollte ich diese Headerfiles anpassen oder soll ich eigene Headerfiles
> schreiben, die die problematischen Headerfiles nachladen und 'fixen' ?
es ist natürlich immer besser, irgendwelche Pakete unberührt zu lassen.
Wie dem auch sei, in beiden Fällen solltest Du gut dokumentieren, was Du
da eigentlich machst.
> Ist es statthaft, Standardfunktionen durch eigene Makros zu ersetzen?
> Ich habe mir zum Beispiel
> #define new( type ) (type *) malloc( sizeof( type ))
> #define cnew( num,type ) (type *) calloc( num,sizeof( type ))
> #define crealloc( blk, num, type ) (type *) realloc( blk, (num) * sizeof(
> type ))
> geschrieben, benutze diese durchgängig und finde es viel praktischer als die
> Standard-Aufrufe.
Also mir fällt augenblicklich nichts ein, was noch gefährlicher für ein
Projekt ist, als das, was Du dort fabrizierst.
Sieht man einmal davon ab, daß "new" C++ und kein C ist, solltest Du
zumindest genau wissen, was deine "Standard-Funktion" macht, bevor Du sie
"verbesserst".
Und ich gebe dir schwarz auf weiß, daß "new" wesentlich mehr tut, als
Speicher für ein Objekt zu reservieren.
Obiges mag bei deinen Geschichten prächtig funktionieren, aber wenn Du
dein Haederfile später in einem echten C++ Programm verwenden willst,
fällst Du mächtig auf die Nase.
Casts: siehe meine letzte mail v. 28.04. ...
Richlinien (etwa zur Schreibweise von Bezeichnern):
Hier finde ich es nicht so wichtig, WIE die Konventionen im einzelnen lauten,
aber sie sollten zweierlei Dinge sicherstellen:
1) Makros, Globale, Modulglobale, Funktionen... sollten möglichst klar
voneinander zu unterscheiden sein.
2) Die Konventionen sollten etwa innerhalb einer Firma, eines Projekts o. ä.
STRIKT von ALLEN Beteiligten eingehalten werden.
So sind in erwähntem Konzern u.a. folgende (sinnvolle!) Konventionen üblich:
- Globale Var. fangen mit Großbuchstaben an und stehen in Headerfile,
- Modulglobale: Auch Gr.-Buchstabe, stehen aber in C-Modul.
- Lokale Variablen: Fangen klein an; falls zusammengesetzte Wörter, dann
fängt ab dem 2. jedes Teilwort groß an.
- Makros: Nur Großbuchst., Unterstriche trennen Teilwörter
- Funktionen: wie Lokalvariablen falls Modul-static, wie Globale falls
exportiert. Tragen Präfix, der das Herkunftsmodul kenntlich macht, wie etwa
"errSomethingWrong (...)" aus dem Modul "errhand.c".
- Namen für Bezeichner sind IMMER englisch.
- Tabweite bei Einrückungen ist 3.
- Kommentare sind ebenfalls englisch.
- Zeiger tragen Prä- oder Suffix "...Ptr" bzw. "ptr...", sofern nicht
SOFORT aus dem Zusammenhang als Zeiger ersichtlich.
Aber wie gesagt, wichtiger ist, daß Konventionen _einheitlich_ und
_durchgängig_ gehandhabt werden...
Gruß Götz
Götz>2) Die Konventionen sollten etwa innerhalb einer Firma, eines Projekts o.
ä.
Götz>STRIKT von ALLEN Beteiligten eingehalten werden.
In dem was Du dann ausführst, stimme ich Dir voll zu.
Wie sieht es aber mit Richtlinen für die Optik aus?
Z.B.
Götz>- Tabweite bei Einrückungen ist 3.
Wo ich arbeite kann man trotz Richtlinien mit geübtem Auge an einem Stück Code
erkennen, wer es verbrochen hat.
Beispiel:
function(arg1, arg2)
function (arg1, arg2)
function ( arg1, arg2 )
(Welche Argumente gibt es überhaupt dafür auf einen einheitlichen Stil im oben
beschriebenen Fall zu achten?)
oder (mir schaudert):
#define EQ ==
und dann
if (a EQ b)
wieder einen anderen erkenne ich am
/* that's it */
return;
wieder ein anderer schreibt auch im default-Zweig sein "break;"
oder
int i,j,k; /* loop counters */
long gnrpf;
Man beachte den Kommentar oben - jeder kann sich denken, was i, j, k ist - bei
gnrpf aber wundert sich der Leser, wofür das wohl gebraucht wird. Es macht dann
Spaß zu verfolgen, daß gnrpf mit gnlpf multipliziert und zur Potenz von grumpf
erhoben wird, als index für das Feld gnlumpf dient, in dem dann die pointer auf
die strings mit dem ...
Meine Erfahrung mit Codierrichtlinen ist die, daß sie für die Art der Anwendung
und die Arbeitsumgebung maßgeschneidert werden müssen. Wer
Echtzeitbetriebssystem produziert hat ganz andere Anforderungen als
Arbeitsgruppen, Compiler auf PC basteln.
Shubhashayam
*** Gerhard
SML>Obiges mag bei deinen Geschichten prächtig funktionieren, aber wenn
SML>Du dein Haederfile später in einem echten C++ Programm verwenden
SML>willst, fällst Du mächtig auf die Nase.
Und wenn ich Großbuchstaben verwende? Ist das Problem hauptsächlich der
Konflikt mit der C++-Funktion new(), von deren Existenz ich nichts wußte? Oder
ist das einfach schlechter Stil? (Mir ging einfach das in meinen Augen doch
recht stupide Wiederholen von (type) malloc( sizeof ( type ) ) auf den Geist.)
ciao
Heiko
> function ( arg1, arg2 )
Eindeutig zu viel Whitespace, läßt das ganze sehr unübersichtlich
werden, insbesondere, wenn hier noch geschachtelt wird:
f ( g ( h, i ), j, k ( l ( m ) ) );
> #define EQ ==
Schick den Kollegen in die Fortran-Abteilung (oder wars Cobol?)
>wieder ein anderer schreibt auch im default-Zweig sein "break;"
Das muß ja nicht verkehrt sein:
switch (state) {
default:
// do something
break;
case 1:
// do something else
break;
}
Wobei man natürlich mit "Special cases first, default last"
argumentierten kann.
>- bei gnrpf aber wundert sich der Leser, wofür das wohl gebraucht wird.
:-)
- Marcus [PGP]
MO>>wieder ein anderer schreibt auch im default-Zweig sein "break;"
MO>Das muß ja nicht verkehrt sein:
MO>switch (state) {
MO>default:
MO> // do something
MO> break;
Wir benutzen X-Tools (einen Struktogramm-Editor, da kommt default automatisch
immer zum Schluß - man merkt ich kann schon gar kein C mehr :-)
Shubhashayam
*** Gerhard
GB> oder
GB> int i,j,k; /* loop counters */
GB> long gnrpf;
GB>
GB> Man beachte den Kommentar oben - jeder kann sich denken, was i, j, k ist -
bei
GB> gnrpf aber wundert sich der Leser, wofür das wohl gebraucht wird. Es macht
dann
GB> Spaß zu verfolgen, daß gnrpf mit gnlpf multipliziert und zur Potenz von
grumpf
GB> erhoben wird, als index für das Feld gnlumpf dient, in dem dann die pointer
auf
GB> die strings mit dem ...
ROTFL!!
Tschö, Heiko (mit Mac Philipp)
HK>ROTFL!!
Das ist bitterer Ernst!
;-)
Shubhashayam
*** Gerhard