Leere verwenden

Lesezeit: 8 Minuten

() Zeiger für andere Funktionen
Benutzer-Avatar

dmuir void (*f)() Ist es legal, auf Zeiger auf Funktionen mit unterschiedlichen Argumentlisten in über a zuzugreifen

#include    <stdio.h>
#include    <stdlib.h>

typedef void    funp();

static  void    funcall( funp* F, int args, double x)
{
    switch( args)
    {
        case    0:  F();    break;
        case    1:  F(x);   break;
    }
}

static  void    fun0( void)
{
    printf( "zero\n");
}

static  void    fun1( double x)
{
    printf( "one\t%f\n", x);
}

int main( )
{
    funcall( (funp*)fun0, 0, 17.0);
    funcall( (funp*)fun1, 1, 17.0);
    return EXIT_SUCCESS;
}

Zeiger? Das folgende Programm wird ohne Warnungen mit gcc kompiliert und scheint korrekt zu laufen, aber ist es legales C?

gcc -Wpedantic -Wall -Wextra -std=gnu11 -O2 -o ./funp funp.c

Ich habe das mit zusammengestellt nargs Es wäre undefiniertes Verhalten, wenn die

  • Der Parameter stimmte nicht mit der Anzahl der Argumente überein, die die Funktion verwendet hat, aber ist es zulässig, wenn es eine Übereinstimmung gibt? void* Nicht die Antwort auf Ihre spezifische Frage, aber der übliche Ansatz besteht darin, nur einen Funktionszeiger zu haben, der a akzeptiert NULL -Parameter und interpretieren ihn dann je nach tatsächlicher Implementierung unterschiedlich. Dieser Parameter kann dann beliebig sein

    zu einem Zeiger auf eine Struktur mit einem Dutzend Parametern. Wenn der Aufrufer die Anzahl, den Typ und die Reihenfolge der Parameter kennen muss, wird die ganze Idee in der Praxis weniger nützlich.

    – Groo


  • 21. Mai 2020 um 12:25 Uhr

    Der Abstand in dem Code, den Sie gepostet haben, macht mich irrational wütend.

    – Konrad Rudolf


  • 21. Mai 2020 um 21:14 Uhr mainEs wäre eine gute Praxis, die Abgüsse zu entfernen

    . Im Allgemeinen können solche Umwandlungen dazu führen, dass der Compiler die Diagnose für fehlerhafte Konvertierungen unterdrückt.

    – MM


  • 21. Mai 2020 um 23:01 Uhr Unabhängig davon, ob diese Art der Konstruktion streng istlegal fun0 würde ich argumentieren, dass es sauberere Möglichkeiten gibt, dasselbe zu erreichen (zum Beispiel have

    nimm ein Argument an und ignoriere es einfach).

    – bta


  • 22. Mai 2020 um 1:18 Uhr mnemonic op1, op2@KonradRudolph: Mein erster Eindruck in der ersten Sekunde, als ich den Codeblock sah, war, nur basierend auf dem Abstand, dass es wie Assemblersprache aussah (basierend auf mehreren tabulatorgestoppten Spalten, wie sie asm verwenden würde

    ). Ich sagte: “Das war von HNQ, warum habe ich das nicht in der Liste der aktiven Asm-Fragen gesehen?” Ich mag diesen Abstand für C auch nicht!

    – Peter Cordes


22. Mai 2020 um 2:05 Uhr
Benutzer-Avatar

dbusch

In diesem speziellen Fall sind die Anrufe legal. Abschnitt 6.7.6.3p15 des C-Standard

buchstabiert, was zwei Funktionstypen kompatibel macht (relevanter Teil in Fettdruck): Damit zwei Funktionstypen kompatibel sind, müssen beide kompatible Rückgabetypen angeben. Darüber hinaus müssen die Parametertyplisten, falls beide vorhanden sind, in der Anzahl der Parameter und in der Verwendung des Auslassungszeichens übereinstimmen; entsprechende Parameter müssen kompatible Typen haben. Wenn ein Typ eine Parametertypliste hat und der andere Typ durch einen Funktionsdeklarator angegeben wird, der nicht Teil einer Funktionsdefinition ist und eine leere Bezeichnerliste enthält, darf die Parameterliste kein Auslassungszeichen haben und der Typ jedes Parameters muss mit dem Typ kompatibel sein, der sich aus der Anwendung der Standardargument-Promotions ergibt.

Wenn ein Typ eine Parametertypliste hat und der andere Typ durch eine Funktionsdefinition spezifiziert wird, die eine (möglicherweise leere) Bezeichnerliste enthält, müssen beide in der Anzahl der Parameter übereinstimmen, und der Typ jedes Prototypparameters muss mit dem Typ kompatibel sein das ergibt sich aus der Anwendung der Default-Argument-Promotions auf den Typ des entsprechenden Bezeichners. (Bei der Bestimmung der Typkompatibilität und eines zusammengesetzten Typs wird angenommen, dass jeder mit einem Funktions- oder Array-Typ deklarierte Parameter den angepassten Typ hat, und jeder mit einem qualifizierten Typ deklarierte Parameter wird so behandelt, als hätte er die nicht qualifizierte Version seines deklarierten Typs.) typedef Sie haben also eine

void()

mit Typ:

void(void)
void(double)

Und Funktionen mit Typ:...Die beiden Funktionsdefinitionen verwenden keine Auslassungspunkte ( ), so dass die erste Bedingung erfüllt ist. Schauen wir uns für die zweite Bedingung an, was die Standardargument-Promotions

sind. Diese sind in Abschnitt 6.5.2.2p6 spezifiziert:
float Wenn der Ausdruck, der die aufgerufene Funktion bezeichnet, einen Typ hat, der keinen Prototyp enthält, werden die ganzzahligen Heraufstufungen für jedes Argument und für Argumente, die einen Typ haben, durchgeführt doubledazu befördert werden . Diese heißen dieStandardargument-Promotions

. double Die erste Funktion hat keine Argumente, ist also kompatibel. Die zweite Funktion hat eine Single

-Argument, das mit den Standardargument-Promotions übereinstimmt, also auch kompatibel ist.

void f1(long);
void f2(int);

Um einige weitere Beispiele zu nennen, die folgenden Funktionen wären ebenfalls kompatibel:

void f3(float);
void f4(char);
void f5(short);

Aber diese würden nicht:
Benutzer-Avatar

StoryTeller – Unslander Monica Wie eine andere Antwort feststellt, ist der von Ihnen gezeigte Code gültig Cheute

. Dies kann sich jedoch aufgrund der Verwendung eines Funktionstyps ohne Parameterliste jederzeit in der Zukunft ändern.

6.11 Zukünftige Sprachrichtungen

6.11.6 Funktionsdeklaratoren 1

Die Verwendung von Funktionsdeklaratoren mit leeren Klammern (keine Parametertypdeklaratoren im Prototypformat) ist ein veraltetes Feature.

  • Eine obsolete Funktion wird in zukünftigen Standardversionen entfernt. Wenn Sie also möchten, dass Ihr Code zukunftssicher ist, sollten Sie dies am besten vermeiden.

    Vielen Dank. Leider scheint es mir ein bisschen hart zu sagen, dass diese Funktion veraltet ist.

    – dmuir

  • 21. Mai 2020 um 12:54 Uhr Dieser Abschnitt bezieht sich speziell auf die FunktionDeklaratoren void f(); . Es sagt, dass das Deklarieren einer Funktion als void() ist veraltet; es heißt nicht, dass die Existenz des Funktionstyps void (*)() oder der Zeigertyp

    ist obsolet. (Ich weiß nicht, was die zugrunde liegende Absicht war, aber der wörtliche Text spricht nur von Deklaratoren.)

    – Benutzer2357112

  • 22. Mai 2020 um 0:32 Uhrint main()@ user2357112supportsMonica Auf diese Weise ist der obige Code für arme Programmierer-Generika immer noch gültig, aber Funktionsdeklarationen im K & R-Stil ( void ) nicht mehr. Viele unterlassen die Hier würde das Denken “keine Argumente” bedeuten, obwohl es “unbestimmte Zahl” bedeutet, wenn es als eigenständige Deklaration verwendet wird, und “keine Argumente”.aber kein Prototyp

    ” wenn es als Teil einer Funktionsdefinition verwendet wird. In beiden Fällen warnt der Compiler nicht, wenn Sie versehentlich Argumente übergeben, und im zweiten Fall handelt es sich definitiv um ein undefiniertes Verhalten.

    – ljrk

  • 22. Mai 2020 um 8:44 Uhr void (*)() @ user2357 – Ein Deklarator ist ein syntaktisches Konstrukt, das zum Erstellen von Deklarationen verwendet wird. Und einige Typen werden von Deklaratoren abgeleitet.

    enthält einen Funktionsdeklarator über einem Zeigerdeklarator. Wie genau wird der Typ ohne die Syntax zu seiner Erstellung weiter existieren!??

    – StoryTeller – Unslander Monica


  • 22. Mai 2020 um 9:41 Uhr void (*)() @StoryTeller-UnslanderMonica: void (*)() enthält keinen Funktionsdeklarator. Deklaratoren deklarieren Dinge (siehe 6.7.6). void (*)() deklariert nichts.

    ist ein Typname.

    – Benutzer2357112

22. Mai 2020 um 9:55 Uhr
Benutzer-Avatar

Ian Abbott

#include    <stdio.h>
#include    <stdlib.h>

typedef void    funp(void);

static  void    funcall( funp* F, int args, double x)
{
    switch( args)
    {
        case    0:
            F();
            break;
        case    1:  
            {
                typedef void fn(double);
                ((fn *)F)(x);
            }
            break;
    }
}

static  void    fun0( void)
{
    printf( "zero\n");
}

static  void    fun1( double x)
{
    printf( "one\t%f\n", x);
}

int main( void )
{
    funcall( (funp*)fun0, 0, 17.0);
    funcall( (funp*)fun1, 1, 17.0);
    return EXIT_SUCCESS;
}

Wie in der Antwort von @StoryTeller erwähnt, ist die Verwendung von Funktionsdeklaratoren mit leeren Klammern eine veraltete Funktion, die jedoch vermieden werden kann: main EDIT: Geänderte Parameterliste von void zu


für die Einhaltung.

Als Antwort auf die Anfrage:

“Außerdem müssen die Parametertyplisten, falls beide vorhanden sind, in der Anzahl der Parameter übereinstimmen” scheint zu bedeuten, dass die Typen von funp und von fun1 nicht kompatibel sind. Ist es ok zu casten? Die Antwort ist ja, es ist in Ordnung zu casten. Aus C11-Entwurf6.3.2.3 Abs. 8

:

Ein Zeiger auf eine Funktion eines Typs kann in einen Zeiger auf eine Funktion eines anderen Typs umgewandelt werden und wieder zurück; das Ergebnis soll mit dem ursprünglichen Zeiger verglichen werden. Wenn ein konvertierter Zeiger verwendet wird, um eine Funktion aufzurufen, deren Typ nicht mit dem referenzierten Typ kompatibel ist, ist das Verhalten undefiniert. fun1 Im Code ist der Zeiger auf funcallwurde im Aufruf von in einen anderen Funktionszeigertyp konvertiert funcall und darin wieder in den ursprünglichen Typ konvertiert fun1kann also zum Anrufen verwendet werden

  • .

    Aus dem Auszug von dbush aus der Spezifikation “Darüber hinaus müssen die Parametertyplisten, wenn beide vorhanden sind, in der Anzahl der Parameter übereinstimmen” zu bedeuten scheint, dass die Typen von funp und fun1 nicht kompatibel sind. Ist es ok zu casten?

    – dmuir

  • 21. Mai 2020 um 14:47 Uhr @dmuir die erforderliche Kompatibilität liegt zwischen die aufgerufene Funktion undder Ausdruck des Funktionsaufrufs

    . Es spielt keine Rolle, ob ein anderer Typ verwendet wurde, um den Funktionszeiger zu transportieren, bevor wir zum Funktionsaufrufausdruck kamen.

    – MM

  • 21. Mai 2020 um 23:03 Uhr main @MM Huh, es gibt zwei Bedingungen, die erste betrifft die Typen (gegen die dies verstößt) und die zweite die Ausdrücke (gut). Allerdings sollte OPs Code meiner Meinung nach in Zukunft sogar in Ordnung sein, da die Typedef keine Funktionsdeklaration und damit legitim ist. Allerdings die Funktionsdeklaration

    in dieser Antwort ist nicht :p

    – ljrk

  • 22. Mai 2020 um 8:49 Uhr

    @larkey mir ist nicht klar, was du mit “dem ersten” meinst, könntest du ein Standardzitat liefern und / oder näher erläutern. Das Programm in dieser Antwort ist genau definiert

    – MM


  • 22. Mai 2020 um 8:55 Uhr main Ich habe das behoben

    Parameterliste (habe es vorher nie bemerkt!) und Sachen über die Umwandlung von Funktionszeigern hinzugefügt.

    – Ian Abbott

1382580cookie-checkLeere verwenden

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

Privacy policy