Welcher Typ ist ein Funktionsname in C? [duplicate]

Lesezeit: 8 Minuten

Benutzer-Avatar
Baruch

Das habe ich in C immer verstanden, func und &func gleichwertig waren. Ich nehme an, sie sollten beide vom Typ Zeiger sein, was auf meinem Win64-System 8 Bytes sind. Allerdings habe ich das gerade probiert:

#include <stdio.h>

int func(int x, int y)
{
    printf("hello\n");
}

int main()
{
    printf("%d, %d\n", sizeof(&func), sizeof(func));
    return 0;
}

Und erwarten, die Ausgabe zu erhalten 8, 8 war überrascht zu bekommen 8, 1 stattdessen.

Warum ist das? Welche Art genau ist func? Es scheint Typ zu sein char oder ein Äquivalent.
Was geht hier vor sich?

Ich habe das mit zusammengestellt gcc -std=c99 wenn es einen unterschied macht.

  • func ist vom Typ “Funktion” und hat nicht wirklich eine definierte Größe. &func ist vom Typ “Zeiger auf eine Funktion”, also hat seine Größe die Größe eines Zeigers.

    – Schleicher

    15. September 2013 um 20:04 Uhr

  • gcc -Wall -Wextra -pedantic -std=c99 ist dein Freund

    – Matte

    15. September 2013 um 20:05 Uhr

  • @Mat Du hast es vergessen -Werror -pedantic-errors.

    Benutzer529758

    15. September 2013 um 20:08 Uhr

  • verwandt: stackoverflow.com/questions/18626080/…

    – zneak

    15. September 2013 um 20:09 Uhr

  • @mbratch: Es ist wichtig zu beachten, dass Funktionszeiger nicht unbedingt die gleiche Größe wie Objektzeiger haben: c-faq.com/ptrs/generic.html

    – Umarmung

    16. September 2013 um 1:11 Uhr

Benutzer-Avatar
ouah

Welcher Typ ist ein Funktionsname in C?

Ein Funktionsname bzw Funktionsbezeichner hat einen Funktionstyp. Wenn es in einem Ausdruck verwendet wird, außer wenn es der Operand von ist sizeof oder & -Operator, wird er vom Typ “Funktion, die zurückkehrt, konvertiert Typ” zum Typ “Zeiger auf eine Funktion, die den Typ zurückgibt”. (Dies ist in C99, 6.3.2.1p4 spezifiziert).

Jetzt

sizeof(func)

ist nicht gültig C als sizeof ist bei einem Operanden vom Funktionstyp nicht erlaubt. Dies ist in den Einschränkungen des angegeben sizeof Operator:

(C99, 6.5.3.4p1 Constraints) „Der sizeof-Operator darf nicht auf einen Ausdruck angewendet werden, der einen Funktionstyp oder einen unvollständigen Typ hat, auf den Namen eines solchen Typs in Klammern oder auf einen Ausdruck, der ein Bitfeldmitglied bezeichnet. “

Aber

sizeof(func)

ist in GNU C erlaubt.

Es gibt eine GNU-Erweiterung in GNU C, die dies zulässt, und in GNU C sizeof mit einem Operanden vom Funktionstyp ergibt 1:

6.23 Arithmetik auf Void- und Function-Pointern

[…] sizeof ist auch für void und Funktionstypen zulässig und gibt 1 zurück.

http://gcc.gnu.org/onlinedocs/gcc/Pointer-Arith.html

  • Gibt es eine Begründung dafür, warum sie sich entschieden haben, dies zu implementieren? und Rückgabe 1? Es scheint mir, als hätten sie eine unnötige Quelle der Verwirrung geschaffen.

    – Theodoros Chatzigiannakis

    15. September 2013 um 20:06 Uhr


  • @TheodorosChatzigiannakis Weil die GNU-Leute selbst verwirrt sind. Schauen Sie sich einfach den C-Codierungsstil an, den sie vorschlagen. Aaaargh!

    Benutzer529758

    15. September 2013 um 20:07 Uhr

  • @TheodorosChatzigiannakis Schauen Sie sich den Link an, dies geschieht, um die Zeigerarithmetik mit zu ermöglichen void * und Zeiger auf Funktionen in GNU C.

    – au

    15. September 2013 um 20:09 Uhr

  • @ouah: Zeigerarithmetik mit void* kann wohl nützlich sein, weil Sie alle Casts vermeiden char* nur um den Zeiger beim Umgang mit Rohspeicher voranzutreiben. Aber Arithmetik mit Zeigerfunktionen ist einfach nur böse.

    – rodrigo

    16. September 2013 um 0:39 Uhr

  • @TheodorosChatzigiannakis Ich spekuliere nur, es könnte sich um eine GCC-Abwärtskompatibilität handeln, die sie nicht brechen möchten. Gcc ist immerhin älter als C89, also wenn dieses Verhalten im ersten gcc war, hat es noch keinen Standard gebrochen 🙂

    – Hyde

    16. September 2013 um 3:51 Uhr

Benutzer-Avatar
Keith Thompson

Gegeben:

int func(int x, int y) { /* ... */ }

der Ausdruck func ist vom Funktionstyp. Insbesondere ist es vom Typ int(int, int)das ist die C-Syntax für den Typ „Funktion mit zwei int Parameter zurückgeben int. (Sie werden diese spezielle Syntax nicht oft sehen, da es nicht üblich ist, direkt auf Funktionstypen zu verweisen.)

Im die meisten Kontexten wird ein Ausdruck des Funktionstyps implizit in einen Zeiger auf die Funktion konvertiert; In diesem Fall ist der Zeiger vom Typ int(*)(int, int).

Die Kontexte, in denen diese implizite Konvertierung erfolgt nicht auftreten sind:

  1. Wenn der Ausdruck der Operand von unär ist &; In diesem Fall, &func liefert die Adresse der Funktion (genau wie func von selbst normalerweise tut); und

  2. Wenn der Ausdruck der Operand von ist sizeof. Ohne diese Ausnahme sizeof func würde die Größe eines Funktionszeigers ergeben. Stattdessen ist es ein Constraint-Verletzungerfordert eine Diagnose vom Compiler.

(Nebenbemerkung: Diese Konvertierung findet statt, wenn der Funktionsname in einem Funktionsaufruf verwendet wird. Die () “Operator” (der Standard nennt es nicht so) erfordert ein Präfix vom Typ Zeiger auf Funktion.)

gcc hat zufällig eine nicht standardmäßige Erweiterung; es erlaubt Zeigerarithmetik für Funktionszeiger und für Typen void*wirkt wie Zeigerarithmetik auf char* Zeiger (dh es arbeitet in Einheiten von Bytes). Leider hat gcc dies meiner Meinung nach über einen Kludge getan, indem die Größe der Funktionstypen und des Typs festgelegt wurde void zu 1. Deshalb bekommst du sizeof func == 1; wenn Sie einen der standardkonformen Modi aktivieren (z. B. gcc -std=c99 -pedantic), erhalten Sie eine Warnung.

Übrigens nicht verwenden %d um das Ergebnis zu drucken sizeof. sizeof liefert ein Ergebnis vom Typ size_t. Wenn Ihre Implementierung dies unterstützt (C99 oder höher), verwenden Sie %zu; Wenn nicht, müssen Sie eine Umwandlung verwenden, um die explizit zu konvertieren size_t Wert auf etwas, das Sie drucken können. Zum Beispiel:

printf("%lu\n", (unsigned long)sizeof &func);

  • Wo du denkst printf("%lu\n", (unsigned long) sizeof &func) im letzten Absatz?

    – chux – Wiedereinsetzung von Monica

    15. September 2013 um 20:17 Uhr

  • @chux: Ja, danke!

    – Keith Thompson

    15. September 2013 um 20:19 Uhr

  • Ich frage mich, was die Verwendung von Zeigerarithmetik mit einem Funktionszeiger bedeuten soll. Mit einer void * Ich kann es verstehen, aber was ist (funcptr+1) sollte heißen?

    – glglgl

    15. September 2013 um 22:02 Uhr

  • @glglgl: Ich hatte noch nie eine Verwendung dafür, aber vielleicht gibt es einige Low-Level-Anwendungen für die Berechnung der Adresse N Bytes vor oder nach dem Start einer Funktion (wenn Sie wissen, wie der Maschinencode aufgebaut ist) oder Berechnen der Differenz zwischen zwei Funktionszeigern.

    – Keith Thompson

    15. September 2013 um 22:06 Uhr

  • @glglgl Ich denke, das ist eher ein Fall, dass gcc-Leute hoffen, dass C Programmierer weiß was er tut. Was, nehme ich an, etwas im Geiste von C ist.

    – Hyde

    16. September 2013 um 4:03 Uhr

Welcher Typ ist ein Funktionsname in C?

Es ist von a Funktionstyp.

Das habe ich in C immer verstanden, func und &func gleichwertig waren

Nun, sie sind nicht “äquivalent”. Eine Funktion tut jedoch verfallen in ein Zeiger auf eine Funktion.

Ich nehme an, sie sollten beide vom Typ Zeiger sein

Das ist eine falsche Annahme.

Und erwartet, die Ausgabe 8, 8 zu erhalten, war überrascht, stattdessen 8, 1 zu erhalten. Warum ist das?

Denn 1. es ist UB, wenn es kompiliert wird, 2. es sollte gar nicht erst kompiliert werden, und als solches ist Ihr Programm frei, alles zu tun.

  • @Mat Da es sich um eine Einschränkungsverletzung handelt, wird beim Kompilieren UB aufgerufen. Zum einen sollte es nicht kompilieren, aber hier tut es das anscheinend. Es tut nicht meine, dass es muss eine Erweiterung/dokumentiert sein.

    Benutzer529758

    15. September 2013 um 20:16 Uhr


  • @Mat Und ein Programm, das von einer nicht konformen Implementierung erstellt wurde … ruft UB auf! Oder nicht? (Oh, und genau genommen ist GCC nicht konform, es sei denn, Sie tun es -Werror -Wall -Wextra -pedantic -pedantic-errors -ansivielleicht.)

    Benutzer529758

    15. September 2013 um 20:19 Uhr


  • Okay, sorry für das Geräusch. Ich dachte an ein konformes Impl. sollte nicht kompilieren, das ist nicht der Fall, es sollte diagnostizieren. Ich habe keine Ahnung, was Sie mit der resultierenden Binärdatei tun können, wenn eine konforme impl. produziert einen, nachdem er dir gesagt hat, dass es Müll war 🙂

    – Matte

    15. September 2013 um 20:29 Uhr

  • @Mat Wahrscheinlich kann es verwendet werden, um zwei Programmierer zu verwirren, die über sein Verhalten diskutieren 😉

    Benutzer529758

    15. September 2013 um 20:33 Uhr


  • @Mat Ich denke, Warnung bedeutet in diesem Fall: “Dies Binary hat ein gcc-spezifisches wohldefiniertes Verhalten, aber wenn Sie es mit einem anderen standardkonformen Compiler kompilieren, das binär könnte alle Ihre Dateien löschen”. Dies ist nützlich, wenn Sie alten Code so reparieren, dass er standardkonform ist.

    – Hyde

    16. September 2013 um 4:12 Uhr


Namen haben in C keinen Typ. Einige Arten von Namen bezeichnen Entitäten, die einen Typ haben, z. B. Typedef-Namen, Objekte oder Funktionen. Andere Arten von Namen bezeichnen Entitäten, die keinen Typ haben, wie z. B. Präprozessorsymbole oder goto Etiketten. Wieder andere Arten von Namen bezeichnen einfach Typen selbst, nämlich typedef Namen.

Der Name einer Funktion bezeichnet eine Entität, die den Funktionstyp hat. Dieser Funktionstyp enthält den Rückgabetyp und (möglicherweise unvollständige) Informationen zu den Parametern.

Wenn Funktionen als Werte verwendet werden, werden sie immer als Zeiger auf Funktionstypen bearbeitet. Eine Funktion als solche kann in einem portablen C-Programm nicht herumgereicht werden, wohl aber ein Zeiger auf eine Funktion.

Konzeptionell sogar bei einem direkten Aufruf gefallen foo()was passiert, ist das foo, ein Ausdruck, der eine Funktion bezeichnet, wird bei der Auswertung implizit in einen Zeiger-auf-Funktion-Wert umgewandelt. Das () Funktionsaufruf-Postfix-Operator ruft dann die Funktion mittels dieses Zeigers auf.

Es gibt eine Regel, dass ein Ausdruck mit Funktionstyp einen Zeigerwert erzeugt, außer wenn dieser Ausdruck der Operand von ist & (Adresse von) oder von der sizeof Operator. func und &func sind nur in dem Sinne gleichwertig, dass sie den gleichen Wert erzeugen. func erzeugt implizit einen Zeigerwert. &func unterdrückt die implizite Generierung eines Zeigers (func ist der Operand von & und so wird die Konvertierung unterdrückt), aber dann & nimmt die Adresse.

Das sieht man also sizeof &func und sizeof func sind anders. Ersteres nimmt die Größe eines Zeigers an und letzteres versucht, die Größe einer Funktion anzunehmen.

Die Größe einer Funktion zu nehmen, ist eine Einschränkungsverletzung in C: Es erfordert eine Diagnose von einer Implementierung, die dem Standard entspricht. Wenn das Programm immer noch übersetzt und einen Wert von 1 wird erzeugt, wenn es ausgeführt wird, das ist ein “Bonus”-Verhalten, das für Ihre Sprachimplementierung spezifisch ist. Es ist nicht in der Standardsprache.

1385220cookie-checkWelcher Typ ist ein Funktionsname in C? [duplicate]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy