Forum: PC-Programmierung C Problem mit Funktionspointern, Watcom C


von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Servus,

ich hänge an einem blöden Problem fest.

Ich update gerade ein (laufendes) System auf die aktuelle Version
von Watcom C, ver 1.9. Manche Sachen akzeptiert der Compiler nicht
mehr.

Um die ausufernnden möglichen Aufrufe des Programms aus der
Kommandozeile übersichtlicher zu machen und im Program zu
vereinfachen habe ich die in einer Tabelle abgelegt:
1
typedef struct{ char    *cmd;
2
                int     (*fn)();
3
              } T_FNTBL;
4
5
// Aufrufe aus dem cmdline
6
T_FNTBL cmdl[] = {
7
   "--eeplst"   , EEPList                       ,
8
   "--eepimp"   , EEPImport                     ,
9
   "--zvltemp"  , ZVLLoadTemp                   ,
10
   ... etcpp
11
   NULL, NULL };

Ausgewertet wird das nach dem Aufruf mit:
1
if ( argc > 1){
2
      // Aufruf ber cmdline (s. cmdl.h)
3
      for ( i=0; cmdl[i].cmd != NULL; i++){
4
           if ( strcmp( argv[1], cmdl[i].cmd) == 0)
5
                return cmdl[i].fn( argv[2], argv[3], argv[4], argv[5]);
6
      }
7
      ExitError( "uSQuery", "*** invalid command '%s'", argv[1]);
8
}

Definiert sind als Beispiel die ersten 3 Funktionen als:
1
extern  void    EEPList( char*);
2
extern  void    EEPImport();
3
extern  void    ZVLLoadTemp( char*, char*);

Ich erhalte die Fehlermeldungen um die Ohren gehauen:
1
cmdl.h(8): Warning! W102: Type mismatch (warning)
2
cmdl.h(8): Note! N2003: source conversion type is 'void (*)(char *__p1)'
3
cmdl.h(8): Note! N2004: target conversion type is 'int (*)(char *__p1,... )'
4
cmdl.h(9): Warning! W102: Type mismatch (warning)
5
cmdl.h(9): Note! N2003: source conversion type is 'void (*)(char *__p1)'
6
cmdl.h(9): Note! N2004: target conversion type is 'int (*)(char *__p1,... )'
7
cmdl.h(10): Warning! W102: Type mismatch (warning)
8
cmdl.h(10): Note! N2003: source conversion type is 'int (*)(char *__p1,char *__p2)'
9
cmdl.h(10): Note! N2004: target conversion type is 'int (*)(char *__p1,... )'
10
cmdl.h(11): Warning! W102: Type mismatch (warning)
11
cmdl.h(11): Note! N2003: source conversion type is 'void (*)(char *__p1)'
12
cmdl.h(11): Note! N2004: target conversion type is 'int (*)(char *__p1,... )'

usw. Der alte Watcom hatte da nichts dran auszusetzen, das Programm
lief auch problemlos.

Ich vermute, die Deklaration int (*fn)(); die ich vorher hatte, hat
den Compiler die Anzahl übergebener Argumente ignorieren lassen.
Der neue akzeptiert das nicht mehr oder ich habe das falsch
deklariert. Probiert habe ich schon einige Varianten, es fehlert
munter vor sich hin ...

Im Moment rufe ich die gefundene Funktion mit den möglichen max 4
Parametern auf, die liegen halt auf dem Stack und stören nicht weiter
wenn die aufgerufene Funktion sie nicht verwendet.

Wie sage ich dem Compiler, er soll das zulassen ?

Wäre nett ...

von Peter II (Gast)


Lesenswert?

meiner Meinung kann das auch nicht sauber gehen.

Die Parameter für den Funktionszeiger müssen wenigsten stimmen, du 
verwendest aber unterschiedliche Parameter.

Ich würde alle funktion so seklarieren:

extern  void    EEPList( char* p1, char* p2, char* p3, char* p4 );


dann sollte es mit

typedef struct{ char    *cmd;
                void (*fn)(char*, char*, char*, char* );
              } T_FNTBL;

gehen.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Danke - leider ist die Tabelle erheblich größer wie die 3 Elemente ...

von (prx) A. K. (prx)


Lesenswert?

Joachim Drechsel schrieb:

>                 int     (*fn)();
> extern  void    EEPList( char*);

> Wie sage ich dem Compiler, er soll das zulassen ?

Hoffentlich überhaupt nicht. Du kannst bei K&R-Funktionen zwar bei 
Parametern vielleicht machen was dir Spass macht (worauf ich nicht 
wetten würde), aber der Return-Typ muss auch da passen.

Beklag dich lieber darüber, dass es der alte gefressen hat. Und der neue 
nur eine Warning draus macht.

von Rolf Magnus (Gast)


Lesenswert?

Joachim Drechsel schrieb:
> Im Moment rufe ich die gefundene Funktion mit den möglichen max 4
> Parametern auf, die liegen halt auf dem Stack und stören nicht weiter
> wenn die aufgerufene Funktion sie nicht verwendet.

Wenn das momentan so ist...
Es ist jedenfalls ziemlich unsaubere Programmierung, abgesehen davon, 
daß du nicht mal prüfst, ob so viele Kommandozeilenparameter überhaupt 
übergeben wurden.

> Wie sage ich dem Compiler, er soll das zulassen ?

Sinnvoller wäre es, den schlechten Code zu reparieren, statt nach einer 
Möglichkeit zu suchen, die Fehler unter dem Teppich zu kehren.

von Karl H. (kbuchegg)


Lesenswert?

Um das sauber zu machen.

Du könntest zb

return cmdl[i].fn( argv[2], argv[3], argv[4], argv[5]);

anstelle der 4 Strings aus der Argumentliste, ganz einfach das ganze 
Argumentarray so wie es ist übergeben


  return cmdl[i].fn( argv );

und die Funktionen holen sich selber die Teile raus, die sie brauchen.

Um die bestehenden Funktionen nicht verändern zu müssen, könnte man zb 
Zwischenfunktionen einführen:

int EEPList_Cmd( char* argv )
{
  return EEPList( argv[2] );
}

int EEPImport_Cmd( char* argv)
{
  return EEPImport();
}

...

und die dann stattdessen in die Command-Liste aufnehmen.

Aber was du auch tust: stell sicher, dass beim Aufruf über den 
Funktionspointer ALLE Funktionen dieselbe Schnittstelle haben. Alles 
andere ist russisch Roulett und wird dir irgendwann auf den Kopf fallen.

> leider ist die Tabelle erheblich größer wie die 3 Elemente ...

Und?
Nach deinen ersten 5 Millionen Lines of Code kratzt dich das nicht mehr, 
wenn du mal 50 oder 100 Zeilen nacheditieren musst.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Nach deinen ersten 5 Millionen Lines of Code kratzt dich das nicht mehr,
> wenn du mal 50 oder 100 Zeilen nacheditieren musst.

Sind aktuell ca. 42.000 ;) Von 85 C-Files bin ich bei 35 in
der Nachbearbeitung. Schon den ganzen Tag :-(

Ok. Ich danke allen für die diversen Sichtweisen und Tips.

Mit Anzahl und Art der cmdline-Parameter werden sich mit
Sicherheit keine Probleme geben, das System läuft ausschließlich
inhouse und wird über Batchfiles angeworfen. Läuft das einmal
läuft das immer.

Ist ein Parameter falsch angegben haut's das Programm eh raus,
das fällt aber dann sofort auf und wird behoben. Ein Kunde kommt
damit nie in Berührung. Von daher möge man mir die leicht lockere
Sichtweise nachsehen.

Ich gebe allen Funktionen 4 Argumente, werden sie nicht verwendet
sind es halt Dummies. Wenn der Compiler dann noch meckert nehme
ich nur noch GWBASIC ... grrrrrr Oder ich schalte die
Fehlermeldung ab ...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Joachim Drechsel schrieb:
> typedef struct{ char    *cmd;
>                 int     (*fn)();
>               } T_FNTBL;

Die Funktionen müssen den gleichen Prototyp haben. Eine Lösung ist
1
int (*fn)(const char*, ...);

wobei die "..." wörtlich zu nehmen sind, d.h. es sind 
varargs-Funktionen, die die Argumentauswertung per stdarg.h erledigen.

Weil das erste Argument einer Varags-Funktion nicht anonym sein darf und 
das Argument benamt sein muss, kannst eh die Option als const char* 
übergeben.

Weil die Argumento pro opt-Funktion fix sind, braucht's aber eigentlich 
keine Varargs, zumindest nicht mehrere in der Tabelle.

Ergo: Design-Fehler.

Eigentlich brauch eine opr-Funktion nur

- Die Option als const char*

- Einen Zeiger auf die Position in der Argumentliste

- Gibt zurück, wieviel sie von der Argumentliste konsumiert hat.

Der Prototyp wäre also

int (*fn)(const char *opt, const char ** argp);

Wobei das Hin- und Herschubsen der Infos angemehmer ist über eine eigene 
Opt-Typedef.

Doese Rädchen ist aber schon bestimmt 1000 mal erfunden...

von Karl H. (kbuchegg)


Lesenswert?

Joachim Drechsel schrieb:

> sind es halt Dummies. Wenn der Compiler dann noch meckert nehme
> ich nur noch GWBASIC ... *grrrrrr*

Na na na.
Mit Drohungen kommt man auch nicht weiter :-)

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Seufz. Manchmal erinnern mich aktuelle Compiler an das
deutsche Steuerrecht ... ;)

Johann L. schrieb:
> wobei die "..." wörtlich zu nehmen sind, d.h. es sind
> varargs-Funktionen, die die Argumentauswertung per stdarg.h erledigen.

Es war halt ein Versuch.

Das argv nochmal aufzusplitten halte ich für mit Kanonen auf
Spatzen geschossen.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Joachim Drechsel schrieb:
>
>> sind es halt Dummies. Wenn der Compiler dann noch meckert nehme
>> ich nur noch GWBASIC ... *grrrrrr*
>
> Na na na.
> Mit Drohungen kommt man auch nicht weiter :-)

Wenn Computer nicht willig sind, hat sich das Zeigen eines Lötkolbens 
bewährt :-) Das fällt dann noch nicht mal unter 'Nötigung'.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Wenn Computer nicht willig sind, hat sich das Zeigen eines Lötkolbens
> bewährt :-) Das fällt dann noch nicht mal unter 'Nötigung'.

Da bin ich der letzte der da Hemmungen hat :-)))

Motorsäge ist auch brauchbar.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Joachim Drechsel schrieb:
> Seufz. Manchmal erinnern mich aktuelle Compiler an das
> deutsche Steuerrecht ... ;)
>
> Johann L. schrieb:
>> wobei die "..." wörtlich zu nehmen sind, d.h. es sind
>> varargs-Funktionen, die die Argumentauswertung per stdarg.h erledigen.
>
> Es war halt ein Versuch.
>
> Das argv nochmal aufzusplitten halte ich für mit Kanonen auf
> Spatzen geschossen.

Was willst du denn da "aufsplitten"?

Wenn --foo einen Parameter verlangt, dann werden dadurch 2 Argumente 
verkonsumiert; eines für "--foo" und eines für dessen Parameter.

War vor der Auswertung von --foo die Auswertung bei argv[n], ist sie 
danach bei argv[n+2]. Vorausgesetzt es gibt genug Argumente, und der 
Auswerter von --foo ist zufrieden mit dem, was er vorgesetzt bekommt.

Nirgends werden Funktionen mit variablen Argumentlistn benötigt, noch 
nicht einmal Funktionen mit unterschiedlichen Argumentlisten für die 
Optionen.

Das einzige, was der Auswerter machen muss, ist sagen, wieviel argumente 
er verspeist hat, ob es Fehler gab und sich irgendo das Ergebnis zu 
merken.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Johann L. schrieb:
> Nirgends werden Funktionen mit variablen Argumentlistn benötigt, noch
> nicht einmal Funktionen mit unterschiedlichen Argumentlisten für die
> Optionen.

Doch. Bei mir. Siehe mein erstes Post.

Es werden unterschiedliche Programmfunktionen mit unterschiedlichen
Parametern aufgerufen. Eigentlich ein durchaus übliche Sache.

Es geht nicht um Sinn oder Unsinn dieser Konstrutkion, eher um das
"wie sage ich es meinem Compiler".

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Joachim Drechsel schrieb:
> Eigentlich ein durchaus übliche Sache.

Aber nicht so, wie Du es umgesetzt hast. Das war allenfalls zu Zeiten 
des steinalten K&R-C-Dialekts (also vor C89 resp. "ANSI-C") so 
möglich.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Rufus Τ. Firefly schrieb:
> K&R-C-Dialekts

Eigentlich das Original ...

Diese Pingeligkeit bringt mir nichts außer Arbeit. Im konkreten
Fall sind es 85 C-Quelldateien. 34 habe ich jetzt durch :-(

von Peter II (Gast)


Lesenswert?

Joachim Drechsel schrieb:
> Im konkreten
> Fall sind es 85 C-Quelldateien. 34 habe ich jetzt durch :-(

hättest dir ja ein Programm schreiben können was das macht

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Joachim Drechsel schrieb:
> Diese Pingeligkeit bringt mir nichts außer Arbeit.

Sicher, aber bei solchen Sourcecodekadavern ist das halt mal nötig. 
Hätte man ja in den letzten 20 Jahren schon mal machen können.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Peter II schrieb:
> hättest dir ja ein Programm schreiben können was das macht

Habe ich mir auch überlegt. Es ist aber leider nicht ganz so
trivial. Im Prinzip ginge das.

Einzelne Funktionen die als void in der Tabelle aufgeführt sind
geben halt bei Bedarf (wenn ich sie wo anders verwende) auch mal
einen Wert zurück.

Manchmal auch ganz gut wenn man "manuell" mal 'drüberschaut ;)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Joachim Drechsel schrieb:
> Johann L. schrieb:
>> Nirgends werden Funktionen mit variablen Argumentlistn benötigt, noch
>> nicht einmal Funktionen mit unterschiedlichen Argumentlisten für die
>> Optionen.
>
> Doch. Bei mir. Siehe mein erstes Post.
>
> Es werden unterschiedliche Programmfunktionen mit unterschiedlichen
> Parametern aufgerufen. Eigentlich ein durchaus übliche Sache.

Ja, dann aber nicht mit ein und dem selben Prototyp.

Wenn das "durchaus üblich" ist, würde ich mal ein

$ gcc file.c -fsyntax-only -Werror=strict-prototypes

o.ä. für die QM vorschlagen. Wenn obiger Aufruf schon einen Fehler 
wirft, brauch man erst garnicht weiter zu machen.

> Es geht nicht um Sinn oder Unsinn dieser Konstrutkion, eher um das
> "wie sage ich es meinem Compiler".

Je nach Compiler/ABI überhaupt nicht.

Und delbst wenn der Compiler alles schluckt und stattdessen 
stillschweigend falschen Code erzeugt, will man das auch nicht wirklich.

Merke: Fehler/Warnungen zur Compilezeit sind einfacher zu beheben als 
Fehler zur Laufzeit.

Mit einem avr-gcc zum Beispie würde dir alles um die Ohren fliegen, weil 
eine Funktion wissen muss was einem Callee zu übergeben ist. Dort macht 
es zum Beipiel ein Unterschied zwischeb 16-Bit und 32-Bit Werten. Ohne 
Prototypen kann das der Compiler nicht wissen und dementsprechend auch 
keinen korrekten Code erzeugen.

AVR ist nicht das einzige Beispiel, es gibt auch 32-Bit Plattformen wo 
ohne gscheite Prototypen nix zu wollen ist.

Joachim Drechsel schrieb:

> Diese Pingeligkeit bringt mir nichts außer Arbeit. Im konkreten
> Fall sind es 85 C-Quelldateien. 34 habe ich jetzt durch :-(

Wenn "Pingeligkeit" jetzt vonnöten ist, dann oftmals wegen früherer 
Schlampigkeit...

Aber hilft eh nix, du musst eben durch. Und wenn schon, ist das immer 
ein guter Anlass, es wirklich solide und besser warbar zu machen. Und 
oft findet man auch Lösungen, die viel näher liegen und instruktiver 
sind als der alter Code :-)

von (prx) A. K. (prx)


Lesenswert?

Joachim Drechsel schrieb:

> Einzelne Funktionen die als void in der Tabelle aufgeführt sind
> geben halt bei Bedarf (wenn ich sie wo anders verwende) auch mal
> einen Wert zurück.

Ach herrje. Du solltest nicht in C sondern in B programmieren.

von (prx) A. K. (prx)


Lesenswert?

Johann L. schrieb:

> Ja, dann aber nicht mit ein und dem selben Prototyp.

Wohl aber wenn man konsequent ohne Prototypen arbeitet. Also in 
schönstem K&R-Stil, wenn der Compiler den noch kann ;-). Der Pointer ist 
es ja schon, die darin landenden Funktionen müssten folgen. Dann passt 
auch der Code wieder.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

In diesem Fall sind es ausschließlich Argumente des Typs char* -
kein Compiler würde da falschen Code erzeugen.

Solche Sachen wie qsort( &arr, count, sizeof(char*), strcmp);
schluckt er aus unerfindlichen Gründen auch nicht mehr. Die
Funktion muß neu deklariert werden.

Nichts gegen Tests des Compilers, da ist aber Erbsenzählerei ...

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

So geht's (falls es jemanden interessiert):
1
typedef struct{ char    *cmd;
2
                void    *fp;
3
              } T_FCALL;
4
5
T_FCALL funcs[] = {
6
   "--stldummy" , STLGetDocumentDirectDummy     , 
7
   "--overview" , QueryMyself,                   
8
   ... etcpp
9
   NULL, NULL, NULL };
10
11
typedef void    (*vfunc)();
12
13
   main() ... etc
14
15
   vfunc   fptr;
16
   if ( argc > 1){
17
        // Aufruf ber cmdline (s. cmdl.h)
18
        for ( i=0; funcs[i].cmd != NULL; i++){
19
                if ( strcmp( argv[1], funcs[i].cmd) == 0) {
20
                      fptr = (vfunc) funcs[i].fp;
21
                      fptr( argv[2], argv[3]);
22
                      return 0;
23
                }
24
        }
25
        ExitError( "warnix", "*** invalid command '%s'", argv[1]);

von Karl H. (kbuchegg)


Lesenswert?

Joachim Drechsel schrieb:

>                       fptr = (vfunc) funcs[i].fp;

Da hättest du auch gleich in der Initialisierung das 'Alles plattmach, 
wir überstimmen den Compiler' - Mittel "Cast" einsetzen können.

Na ja. Du musst es ja wissen. Meine persönliche Erfahrung ist eher: 
Sowas fliegt einem irgendwann um die Ohren. Ist nur eine Frage der Zeit. 
Seinen Compiler anlügen und mit einem Cast zu überstimmen war noch nie 
eine gute Idee.

Und manchmal muss man da eben durch und Altlasten beseitigen.

von Karl H. (kbuchegg)


Lesenswert?

Joachim Drechsel schrieb:
> Rufus Τ. Firefly schrieb:
>> K&R-C-Dialekts
>
> Eigentlich das Original ...
>
> Diese Pingeligkeit bringt mir nichts außer Arbeit. Im konkreten
> Fall sind es 85 C-Quelldateien. 34 habe ich jetzt durch :-(

Jetzt mal Butter bei den Fischen.
Wieviele Files das sind interessiert doch nicht.
Wie gross ist die Tabelle? 500 Einträge, 1000, 2000?
Das ist das was interessiert und den Aufwand bestimmt. Bei unter 200 
Einträgen in der Tabelle denk ich mir noch gar nix und mach mir 
Zwischenfunktionen, die die Parameteranpassung an die eigentlichen 
Funktionen machen. Ich denke, da könnte man zb auch den Präprozessor 
ganz gut zur Arbeit einspannen. Wär nicht das erste mal, dass ich mir 
eine Handvoll Funktionen per Makro erzeugen lasse.

DEF_FUNC_1( EEPList, int )
DEF_FUNC_0( EEPImport, int )

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Ich weiß nicht wie groß diese Tabelle noch werden wird.

Diese Konstruktion macht für mich das System übersichtlicher und
erfüllt ihren Zweck. Stabilitätsprobleme kann ich keine erkennen.

Stimmen Anzahl oder ein einzelner Parameter nicht, bricht das
entsprechende Programm eh mit einer Fehlermeldung ab.

Mit den 85 Files bin ich jetzt durch, Programm läuft. Am Wochenende
teste ich alles noch mal durch, am Montag lasse ich es auf die
Allgemeinheit los :)

Nur mal so:

Wenn eine Funktion kein return enthält ist sie vom Typ void.
Warum muß ich das dem Compiler jedesmal sagen ? Die Logik könnte
auch sein: default ist int, sobald ein return auftaucht, erhält
sie dessen Typ (natürlich mit einem Test ob in allen Lebenslagen
überhaupt etwas und der gleiche Typ zurückgegeben wird).
Entsprechende Warnungen könnte auch der Linker von sich geben.

qsort( &arr, n, sizeof( char*), strcmp) geht nicht mehr - ich
muß strcmp umdeklarieren. Eigentlich auch nicht so unbedingt
notwendig ...

von (prx) A. K. (prx)


Lesenswert?

Joachim Drechsel schrieb:

> Wenn eine Funktion kein return enthält ist sie vom Typ void.

Andersrum: Nur beim Typ void kannst du das return weglassen (und als 
hässliche Ausnahme bei main). Hinschreiben darfst du es trotzdem. Als 
"return;".

> Warum muß ich das dem Compiler jedesmal sagen ? Die Logik könnte
> auch sein: default ist int, sobald ein return auftaucht, erhält
> sie dessen Typ (natürlich mit einem Test ob in allen Lebenslagen
> überhaupt etwas und der gleiche Typ zurückgegeben wird).

Weil das nicht funktioniert.

Welchen Return-Typ hätte denn diese Funktion?
1
f(int x)
2
{
3
     if (x) return f(x-1);
4
     return 0;
5
}
Hier wäre formal jeder beliebige Integer-Typ passend, sowie alle 
Pointer.

Denk dran, dass der Compiler nicht den Sinn einer Funktion ermittelt, 
sondern sich nur an die Syntax halten kann. Und eine Funktion auch mal 
aufgerufen wird, bevor sie fertig definiert ist.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

A. K. schrieb:
> Hier wäre formal jeder beliebige Integer-Typ passend, sowie alle
> Pointer.

Richtig. Und ist es nicht meine Sache wie ich diesen Wert
interpretiere ?

A. K. schrieb:
> Und eine Funktion auch mal
> aufgerufen wird, bevor sie definiert ist.

Wie das ? ;)

von (prx) A. K. (prx)


Lesenswert?

Joachim Drechsel schrieb:

>> Hier wäre formal jeder beliebige Integer-Typ passend, sowie alle
>> Pointer.
>
> Richtig. Und ist es nicht meine Sache wie ich diesen Wert
> interpretiere ?

Woher sollte der Compiler hier ohne tiefschürfende Codeanalyse den 
Wertebereich erkennen können? Also wissen, dass es int sein kann und 
nicht long sein muss.

Ausserdem ist zwar
   char *p = 0;
zulässig, nicht aber
   extern int x; // irgendwo anders auf 0 gesetzt
   p = x;

> Wie das ? ;)

Siehe Beispiel. Das einzige return mit zumindest ansatzweise bekanntem 
Typ steht hinter dem Aufruf der Funktion. Der Aufruf erfolgt also bevor 
eine Sprache deiner Prägung den Typ kennt. C wurde so konzipiert, dass 
man es in einem Durchgang von oben nach unten analysieren und übersetzen 
kann.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Als Aufruf habe ich jetzt den konkreten Aufruf zur runtime
verstanden ...

A. K. schrieb:
> Woher sollte der Compiler hier ohne tiefschürfende Codeanalyse den
> Wertebereich erkennen können? Also wissen, dass es int sein kann und
> nicht long sein muss.

int ist ja default für den Funktionstyp. Wenn ich einen anderen
konkret angebe ist / muß das passen. So tiefschürfend ist das nicht.

von (prx) A. K. (prx)


Lesenswert?

Joachim Drechsel schrieb:

> int ist ja default für den Funktionstyp. Wenn ich einen anderen
> konkret angebe ist / muß das passen. So tiefschürfend ist das nicht.

Eine Sprache, wie sie dir vorschwebt, ist nur mit Interpretern wirklich 
machbar. Denn konsequent durchdacht sind Typdeklarationen dann unsinnig, 
jede Variable kann jeden möglichen Wert jeden möglichen Typs enthalten 
und der entscheidet sich zur Laufzeit.

Sowas gibts. Smalltalk wäre so eine. Nachteilig sind allerlei Prüfungen, 
die erst zur Laufzeit stattfinden, und erst dann eine Exception werfen, 
d.h. vor der Nase des Anwenders. Deklaratorische Sprachen prüfen vieles 
davon im Compiler, d.h. vor der Nase des Entwicklers.

Gewöhn dich an den Unterschied in der Systematik. C ist eine 
deklaratorische Sprache. Variablen und Funktionen werden vor der ersten 
Verwendung deklariert und es nicht die Aufgabe des Compilers, eine 
fehlende Deklaration stillschweigend zu erraten.

Der ursprüngliche C Ansatz, den Typ "int" stillschweigend anzunehmen, 
wenn sonst keiner da war, gehörte nicht zu den besten Ideen des 
Sprachschöpfers.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

A. K. schrieb:
> Eine Sprache, wie sie dir vorschwebt, ist nur mit Interpretern wirklich
> machbar. Denn konsequent durchdacht sind Typdeklarationen dann unsinnig,
> jede Variable kann jeden möglichen Wert jeden möglichen Typs enthalten
> und der entscheidet sich zur Laufzeit.

So etwas schwebt mir nicht vor. Das wäre so ein nicht an die
Wand zu nagelnder Pudding wie zB Java-Script.

Dem Compiler ist die Funktion zur während des Kompilierens vollständig
bekannt - er sollte das eigentlich eindeutig bestimmen können.

Deklariere ich eine Funktion als char* geht es ja auch und er kann
prüfen was so mit return zurückgeschickt wird. Eine Prüfung zur
Laufzeit ist da ja auch nicht notwendig.

von Rolf M. (rmagnus)


Lesenswert?

Joachim Drechsel schrieb:
> Dem Compiler ist die Funktion zur während des Kompilierens vollständig
> bekannt

Nur wenn die Implementation in der selben Datei steht wie der Aufruf und 
vor diesem steht. Sonst kennt er an der Stelle nur das, was der Prototyp 
verrät. Und wie soll der Compiler dann erraten, was aus der Funkton für 
ein Typ zurückgegeben wird, wenn ich es nicht explizit hinschreibe?

> Deklariere ich eine Funktion als char* geht es ja auch und er kann
> prüfen was so mit return zurückgeschickt wird. Eine Prüfung zur
> Laufzeit ist da ja auch nicht notwendig.

Da ist der Typ ja auch dem Aufrufer bekannt.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Das mit der "selben Datei" hatte ich jetzt einfach vorrausgesetzt.
Sorry für das Mißverständnis.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

A. K. schrieb:
> Johann L. schrieb:
>
>> Ja, dann aber nicht mit ein und dem selben Prototyp.
>
> Wohl aber wenn man konsequent ohne Prototypen arbeitet. Also in
> schönstem K&R-Stil, wenn der Compiler den noch kann ;-)

Ja, kann er, zB avr-gcc:
1
void caller (fn)
2
int fn();
3
{
4
    fn (1, 2);
5
}

Preisfrage: Wie werden hier die Werte übergeben?

Du weisst nicht, ob das jeweilige fn zum Beispiel einen int erwartet 
oder einen long oder einen float oder eine double.

von Karl H. (kbuchegg)


Lesenswert?

Johann L. schrieb:

> Du weisst nicht, ob das jeweilige fn zum Beispiel einen int erwartet
> oder einen long oder einen float oder eine double.


Jep.
Daran kann ich mich auch noch erinnern.
Was habe ich geflucht


wo steckt der/die Fehler?
1
#include <stdio.h>
2
3
#define DEG_TO_RAD(x)   ((x) * 3.141592654 / 180.0)
4
5
int main()
6
{
7
  double alpha = DEG_TO_RAD( 90.0 );
8
9
  printf( "%f\n", cos( alpha ) );
10
}

Ein Compiler SOLL nichts annehmen. Entweder die notwendigen Angaben 
stehen dort, oder es ist ein Syntax Error.

von (prx) A. K. (prx)


Lesenswert?

Johann L. schrieb:

> Du weisst nicht, ob das jeweilige fn zum Beispiel einen int erwartet
> oder einen long oder einen float oder eine double.

Klar, wobei float freilich nicht in Frage kommt, da die nur an 
Funktionen mit deklarierten Parametern direkt übergeben werden können, 
sonst stets als double.

Von seinem Code wird das offenbar schon so gehandhabt, dass die 
Parameter passen, es also kein Problem darstellt. Natürlich ist das 
grober Unfug, keine Frage.

von (prx) A. K. (prx)


Lesenswert?

Johann L. schrieb:

> Preisfrage: Wie werden hier die Werte übergeben?

Das war damals klar definiert. Schrott rein Schrott raus, man musste 
wissen was man tat. Oder lint bemühen.

von (prx) A. K. (prx)


Lesenswert?

Joachim Drechsel schrieb:

> Dem Compiler ist die Funktion zur während des Kompilierens vollständig
> bekannt

Nicht notwendigerweise. C wurde so definiert, dass man den Code einer 
Funktion inkrementell erzeugen kann, ohne zuerst die gesamte Funktion zu 
parsen. Immerhin war damals auf den dafür zur Verfügung stehenden 
Maschinen Speicher recht knapp.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Es ging um eine Tabelle mit Funktionspointern gleichen Types.
Nur die Anzahl der Argumente variiert, der Typ ist generell char*.
Da wird nichts anbrennen - wie auch ? argv ist ein char*-Array.

Alle Funktionen sind extern und prototypisiert.

Grund der Tabelle:

Anstelle
1
if ( strcmp( argv[1], "--eeplist") == 0){
2
  EEPList( argv[2]);
3
  return 0;
4
}

habe ich nur noch eine Zeile in der Tabelle. Die Tabelle selbst
kann ich wegen des besseren Überblicks sortieren.

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.