Zoeken…


Opmerkingen

Identificatieverklaring die verwijst naar object of functie wordt vaak kortweg aangeduid als eenvoudig een verklaring van object of functie.

Een functie uit een ander C-bestand oproepen

foo.h

#ifndef FOO_DOT_H    /* This is an "include guard" */
#define FOO_DOT_H    /* prevents the file from being included twice. */
                     /* Including a header file twice causes all kinds */
                     /* of interesting problems.*/

/**
 * This is a function declaration.
 * It tells the compiler that the function exists somewhere.
 */
void foo(int id, char *name);

#endif /* FOO_DOT_H */

foo.c

#include "foo.h"    /* Always include the header file that declares something
                     * in the C file that defines it. This makes sure that the
                     * declaration and definition are always in-sync.  Put this
                     * header first in foo.c to ensure the header is self-contained.
                     */
#include <stdio.h>
                       
/**
 * This is the function definition.
 * It is the actual body of the function which was declared elsewhere.
 */
void foo(int id, char *name)
{
    fprintf(stderr, "foo(%d, \"%s\");\n", id, name);
    /* This will print how foo was called to stderr - standard error.
     * e.g., foo(42, "Hi!") will print `foo(42, "Hi!")`
     */
}

main.c

#include "foo.h"

int main(void)
{
    foo(42, "bar");
    return 0;
}

Compileren en koppelen

Eerst compileren we zowel foo.c als main.c om bestanden te foo.c . Hier gebruiken we de gcc compiler, uw compiler heeft mogelijk een andere naam en heeft andere opties nodig.

$ gcc -Wall -c foo.c
$ gcc -Wall -c main.c

Nu koppelen we ze samen om ons definitieve uitvoerbare bestand te produceren:

$ gcc -o testprogram foo.o main.o

Een globale variabele gebruiken

Het gebruik van globale variabelen wordt over het algemeen afgeraden. Het maakt uw programma moeilijker te begrijpen en moeilijker te debuggen. Maar soms is het gebruik van een globale variabele acceptabel.

global.h

#ifndef GLOBAL_DOT_H    /* This is an "include guard" */
#define GLOBAL_DOT_H

/**
 * This tells the compiler that g_myglobal exists somewhere.
 * Without "extern", this would create a new variable named
 * g_myglobal in _every file_ that included it. Don't miss this!
 */
extern int g_myglobal; /* _Declare_ g_myglobal, that is promise it will be _defined_ by
                        * some module. */

#endif /* GLOBAL_DOT_H */

global.c

#include "global.h" /* Always include the header file that declares something
                     * in the C file that defines it. This makes sure that the
                     * declaration and definition are always in-sync.
                     */
                       
int g_myglobal;     /* _Define_ my_global. As living in global scope it gets initialised to 0 
                     * on program start-up. */

main.c

#include "global.h"

int main(void)
{
    g_myglobal = 42;
    return 0;
}

Zie ook Hoe gebruik ik extern om variabelen tussen bronbestanden te delen?

Wereldwijde constanten gebruiken

Headers kunnen worden gebruikt om algemeen gebruikte alleen-lezen bronnen aan te geven, zoals bijvoorbeeld string-tabellen.

Verklaar die in een aparte koptekst die wordt opgenomen in elk bestand (" Translation Unit ") dat er gebruik van wil maken. Het is handig om dezelfde koptekst te gebruiken om een gerelateerde opsomming aan te geven om alle tekenreeksbronnen te identificeren:

resources.h:

#ifndef RESOURCES_H
#define RESOURCES_H

typedef enum { /* Define a type describing the possible valid resource IDs. */
  RESOURCE_UNDEFINED = -1, /* To be used to initialise any EnumResourceID typed variable to be 
                              marked as "not in use", "not in list", "undefined", wtf.
                              Will say un-initialised on application level, not on language level. Initialised uninitialised, so to say ;-)
                              Its like NULL for pointers ;-)*/
  RESOURCE_UNKNOWN = 0,    /* To be used if the application uses some resource ID, 
                              for which we do not have a table entry defined, a fall back in 
                              case we _need_ to display something, but do not find anything 
                              appropriate. */

  /* The following identify the resources we have defined: */
  RESOURCE_OK,
  RESOURCE_CANCEL,
  RESOURCE_ABORT,
  /* Insert more here. */

  RESOURCE_MAX /* The maximum number of resources defined. */
} EnumResourceID;


extern const char * const resources[RESOURCE_MAX]; /* Declare, promise to anybody who includes 
                                      this, that at linkage-time this symbol will be around. 
                                      The 1st const guarantees the strings will not change, 
                                      the 2nd const guarantees the string-table entries 
                                      will never suddenly point somewhere else as set during 
                                      initialisation. */
#endif

Om de bronnen daadwerkelijk te definiëren, heeft u een gerelateerd .c-bestand gemaakt, dat is een andere vertaaleenheid die de werkelijke instanties bevat van wat in het gerelateerde header (.h) -bestand was aangegeven:

resources.c:

#include "resources.h" /* To make sure clashes between declaration and definition are
                          recognised by the compiler include the declaring header into
                          the implementing, defining translation unit (.c file).

/* Define the resources. Keep the promise made in resources.h. */
const char * const resources[RESOURCE_MAX] = {
  "<unknown>",
  "OK",
  "Cancel",
  "Abort"
};

Een programma dat dit gebruikt, kan er zo uitzien:

main.c:

#include <stdlib.h> /* for EXIT_SUCCESS */
#include <stdio.h>

#include "resources.h"


int main(void)
{
  EnumResourceID resource_id = RESOURCE_UNDEFINED;

  while ((++resource_id) < RESOURCE_MAX)
  {
    printf("resource ID: %d, resource: '%s'\n", resource_id, resources[resource_id]);
  }

  return EXIT_SUCCESS;
}

Compileer de drie bovenstaande bestanden met behulp van GCC en koppel ze om het main van het programmabestand te worden, bijvoorbeeld door dit te gebruiken:

gcc -Wall -Wextra -pedantic -Wconversion -g  main.c resources.c -o main

(gebruik deze -Wall -Wextra -pedantic -Wconversion om de compiler erg kieskeurig te maken, zodat je niets mist voordat je de code op SO zet, zal de wereld zeggen, of zelfs de moeite waard om het in productie te gebruiken)

Voer het gemaakte programma uit:

$ ./main

En krijg:

resource ID: 0, resource: '<unknown>'
resource ID: 1, resource: 'OK'
resource ID: 2, resource: 'Cancel'
resource ID: 3, resource: 'Abort'

Invoering

Voorbeelden van verklaringen zijn:

int a; /* declaring single identifier of type int */

In de bovenstaande verklaring wordt één ID genoemd met de naam a die verwijst naar een object met het type int .

int a1, b1; /* declaring 2 identifiers of type int */

De tweede aangifte declareert 2 identifiers genaamd a1 en b1 die naar enkele andere objecten verwijzen, hoewel met hetzelfde int type.

Kortom, de manier waarop dit werkt is als volgt: eerst plaats je een type , dan schrijf je een of meerdere expressies gescheiden via komma ( , ) ( die op dit punt niet worden geëvalueerd) en die anders als declarators moeten worden aangeduid in deze context ). Bij het schrijven van dergelijke uitdrukkingen is het u toegestaan om alleen de operatoren indirection ( * ), function call ( ( ) ) of subscript (of array-indexering - [ ] ) op een bepaalde identificatie toe te passen (u kunt ook helemaal geen operatoren gebruiken). De gebruikte identifier hoeft niet zichtbaar te zijn in het huidige bereik. Een paar voorbeelden:

/* 1 */ int /* 2 */ (*z) /* 3 */ , /* 4 */ *x , /* 5 */ **c /* 6 */ ;
# Beschrijving
1 De naam van het type geheel getal.
2 Niet-geëvalueerde expressie die indirection toepast op een of andere identifier z .
3 We hebben een komma die aangeeft dat er nog een uitdrukking zal volgen in dezelfde verklaring.
4 Niet-geëvalueerde expressie die indirection toepast op een andere identificatie x .
5 Niet-geëvalueerde expressie die indirection toepast op de waarde van de expressie (*c) .
6 Einde aangifte.

Merk op dat geen van de bovenstaande identificatiegegevens zichtbaar was vóór deze verklaring en dat de gebruikte uitdrukkingen daarvoor dus niet geldig waren.

Na elke dergelijke uitdrukking wordt de daarin gebruikte identificator geïntroduceerd in het huidige bereik. (Als aan de identifier een koppeling is toegewezen, kan deze ook opnieuw worden aangegeven met hetzelfde type koppeling zodat beide identifiers naar hetzelfde object of dezelfde functie verwijzen)

Bovendien kan het gelijke operator-teken ( = ) worden gebruikt voor initialisatie. Als een niet-beoordeelde uitdrukking (declarator) wordt gevolgd door = in de aangifte - zeggen we dat de identificator die wordt ingevoerd ook wordt geïnitialiseerd. Na het = -teken kunnen we weer wat expressie plaatsen, maar deze keer wordt het geëvalueerd en wordt de waarde gebruikt als initiaal voor het aangegeven object.

Voorbeelden:

int l = 90; /* the same as: */

int l; l = 90; /* if it the declaration of l was in block scope */

int c = 2, b[c]; /* ok, equivalent to: */

int c = 2; int b[c];

Later in uw code mag u exact dezelfde uitdrukking schrijven uit het declaratiegedeelte van de nieuw geïntroduceerde id, waardoor u een object krijgt van het type dat aan het begin van de declaratie is opgegeven, ervan uitgaande dat u geldige waarden hebt toegewezen aan alle toegangen objecten in de weg staan. Voorbeelden:

void f()
{
    int b2; /* you should be able to write later in your code b2 
            which will directly refer to the integer object
            that b2 identifies */
    
    b2 = 2; /* assign a value to b2 */
    
    printf("%d", b2); /*ok - should print 2*/

    int *b3; /* you should be able to write later in your code *b3 */

    b3 = &b2; /* assign valid pointer value to b3 */

    printf("%d", *b3); /* ok - should print 2 */

    int **b4; /* you should be able to write later in your code **b4 */

    b4 = &b3;

    printf("%d", **b4); /* ok - should print 2 */

    void (*p)(); /* you should be able to write later in your code (*p)() */

    p = &f; /* assign a valid pointer value */

    (*p)(); /* ok - calls function f by retrieving the
            pointer value inside p -    p
            and dereferencing it -      *p
            resulting in a function
            which is then called -      (*p)() -

            it is not *p() because else first the () operator is 
            applied to p and then the resulting void object is
            dereferenced which is not what we want here */
}

De declaratie van b3 geeft aan dat u mogelijk de waarde b3 kunt gebruiken als middel om toegang te krijgen tot een geheel getal.

Om indirection ( * ) op b3 , moet u natuurlijk ook een juiste waarde hebben opgeslagen (zie verwijzingen voor meer info). U moet ook eerst wat waarde in een object opslaan voordat u het probeert op te halen (u kunt hier meer over dit probleem zien ). We hebben dit allemaal gedaan in de bovenstaande voorbeelden.

int a3(); /* you should be able to call a3 */

Deze vertelt de compiler dat je a3 probeert te bellen. In dit geval verwijst a3 naar functie in plaats van een object. Een verschil tussen object en functie is dat functies altijd een soort koppeling hebben. Voorbeelden:

void f1()
{
    {
        int f2(); /* 1 refers to some function f2 */
    }
    
    {
        int f2(); /* refers to the exact same function f2 as (1) */
    }
}

In het bovenstaande voorbeeld verwijzen de 2 verklaringen naar dezelfde functie f2 , terwijl als ze objecten zouden verklaren dan in deze context (met 2 verschillende blokbereiken), dit 2 verschillende afzonderlijke objecten zouden zijn.

int (*a3)(); /* you should be able to apply indirection to `a3` and then call it */

Nu lijkt het misschien ingewikkeld te worden, maar als u weet dat operators prioriteit hebben, heeft u 0 problemen met het lezen van de bovenstaande verklaring. De haakjes zijn nodig omdat de operator * minder prioriteit heeft dan de ( ) .

In het geval van het gebruik van de subscript-operator, zou de resulterende expressie niet geldig zijn na de aangifte, omdat de index die erin wordt gebruikt (de waarde tussen [ en ] ) altijd 1 boven de maximaal toegestane waarde voor dit object / functie zal zijn.

int a4[5]; /* here a4 shouldn't be accessed using the index 5 later on */

Maar het moet toegankelijk zijn voor alle andere indexen lager dan 5. Voorbeelden:

a4[0], a4[1]; a4[4];

a4[5] zal resulteren in UB. Meer informatie over arrays vindt u hier .

int (*a5)[5](); /* here a4 could be applied indirection
                indexed up to (but not including) 5
                and called */

Helaas voor ons, hoewel syntactisch mogelijk, is de verklaring van a5 verboden door de huidige standaard.

typedef

Typedefs zijn verklaringen met het trefwoord typedef vóór en vóór het type. bv:

typedef int (*(*t0)())[5];

( je kunt technisch gezien ook het typedef achter het type plaatsen - zoals dit int typedef (*(*t0)())[5]; maar dit wordt afgeraden )

De bovenstaande verklaringen verklaren een identificatie voor een typedefnaam. Je kunt het achteraf als volgt gebruiken:

t0 pf;

Welke hetzelfde effect heeft als schrijven:

int (*(*pf)())[5];

Zoals u ziet, "bewaart" de typedef-naam de aangifte als een type dat later voor andere aangiften wordt gebruikt. Op deze manier kunt u enkele toetsaanslagen opslaan. typedef aangifte met typedef nog steeds een aangifte is, wordt u niet alleen beperkt door het bovenstaande voorbeeld:

t0 (*pf1);

Is hetzelfde als:

int (*(**pf1)())[5];

De regel "rechts-links" is een volledig reguliere regel voor het ontcijferen van C-aangiften. Het kan ook nuttig zijn om ze te maken.

Lees de symbolen zoals u ze tegenkomt in de verklaring ...

*   as "pointer to"          - always on the left side
[]  as "array of"            - always on the right side
()  as "function returning"  - always on the right side

Hoe de regel toe te passen

STAP 1

Zoek de identificatie. Dit is je startpunt. Zeg dan tegen jezelf: "identifier is." U bent begonnen met uw aangifte.

STAP 2

Kijk naar de symbolen rechts van de identificatie. Als u bijvoorbeeld () daar vindt, weet u dat dit de verklaring voor een functie is. Dus je zou dan hebben "identifier is function return" . Of als u daar een [] zou vinden, zou u zeggen "identifier is array of" . Ga door naar rechts totdat je geen symbolen meer hebt OF een rechter haakje raakt ) . (Als u een haakje links indrukt ( , dat is het begin van een () symbool, zelfs als er dingen tussen de haakjes staan. Meer daarover hieronder.)

STAP 3

Kijk naar de symbolen links van de identificatie. Als het niet een van onze bovenstaande symbolen is (zeg iets als "int"), zeg het dan gewoon. Anders vertaalt u het in het Engels met behulp van die tabel hierboven. Blijf links gaan totdat je geen symbolen meer hebt OF sla een haakje links ( .

Herhaal nu stap 2 en 3 totdat u uw aangifte hebt opgesteld.


Hier zijn enkele voorbeelden:

int *p[];

Zoek eerst de identificatie:

int *p[];
     ^

"p is"

Ga nu naar rechts totdat de symbolen uit zijn of de rechter haakjes raken.

int *p[];
      ^^

"p is array van"

Kan niet meer naar rechts bewegen (uit symbolen), dus ga naar links en zoek:

int *p[];
    ^

"p is een reeks van pointer naar"

Blijf links gaan en vind:

int *p[];
^^^

"p is array van pointer naar int".

(of "p is een array waarbij elk element van het type pointer naar int is" )

Een ander voorbeeld:

int *(*func())();

Zoek de identificatie.

int *(*func())();
       ^^^^

"func is"

Ga naar rechts.

int *(*func())();
           ^^

"func is functie retourneren"

Kan niet meer naar rechts bewegen vanwege het rechter haakje, dus ga naar links.

int *(*func())();
      ^

"func is functie die de aanwijzer terugzet naar"

Kan niet meer naar links bewegen vanwege het linker haakje, dus blijf rechts gaan.

int *(*func())();
              ^^

"func is functie terugkerende pointer naar functie terugkeert"

Kan niet meer naar rechts gaan omdat we geen symbolen meer hebben, dus ga naar links.

int *(*func())();
    ^

"func is functie terugkerende wijzer naar functie terugkerende wijzer naar"

En tot slot, blijf links gaan, want er is niets aan de rechterkant.

int *(*func())();
^^^

"func is functie terugkerende pointer naar functie terugkerende pointer naar int".

Zoals u ziet, kan deze regel behoorlijk nuttig zijn. U kunt het ook gebruiken om gezond verstand te controleren terwijl u verklaringen maakt en om u een hint te geven over waar u het volgende symbool moet plaatsen en of haakjes vereist zijn.

Sommige verklaringen zien er veel gecompliceerder uit dan ze te wijten zijn aan matrixgroottes en argumentlijsten in prototypevorm. Als u [3] , wordt dat gelezen als "array (grootte 3) van ..." . Als u (char *,int) dat gelezen als * "functie die (char , int) verwacht en terugkeert ..." .

Hier is een leuke:

int (*(*fun_one)(char *,double))[9][20];

Ik zal niet alle stappen doorlopen om deze te ontcijferen.

* "fun_one is een pointer die functioneert (verwacht, char , dubbel) en de pointer teruggeeft aan array (grootte 9) van array (grootte 20) van int."

Zoals je kunt zien, is het niet zo ingewikkeld als je de matrixgroottes en argumentenlijsten verliest:

int (*(*fun_one)())[][];

Je kunt het op die manier ontcijferen en later de matrixgroottes en argumentenlijsten invoeren.

Enkele laatste woorden:


Het is heel goed mogelijk om illegale verklaringen af te leggen met behulp van deze regel, dus enige kennis van wat legaal is in C is noodzakelijk. Als het bovenstaande bijvoorbeeld was geweest:

int *((*fun_one)())[][];

het zou hebben gelezen "fun_one is pointer om te functioneren, array van array van pointer terug naar int" . Aangezien een functie geen array kan retourneren, maar alleen een pointer naar een array, is die verklaring illegaal.

Illegale combinaties zijn onder meer:

[]() - cannot have an array of functions
()() - cannot have a function that returns a function
()[] - cannot have a function that returns an array

In alle bovengenoemde gevallen heeft u een set haakjes nodig om een * -symbool links tussen deze () en [] rechterkantsymbolen te binden om de verklaring legaal te maken.

Hier zijn nog enkele voorbeelden:


wettelijk

int i;               an int
int *p;              an int pointer (ptr to an int)
int a[];             an array of ints
int f();             a function returning an int
int **pp;            a pointer to an int pointer (ptr to a ptr to an int)
int (*pa)[];         a pointer to an array of ints
int (*pf)();         a pointer to a function returning an int
int *ap[];           an array of int pointers (array of ptrs to ints)
int aa[][];          an array of arrays of ints
int *fp();           a function returning an int pointer
int ***ppp;          a pointer to a pointer to an int pointer
int (**ppa)[];       a pointer to a pointer to an array of ints
int (**ppf)();       a pointer to a pointer to a function returning an int
int *(*pap)[];       a pointer to an array of int pointers
int (*paa)[][];      a pointer to an array of arrays of ints
int *(*pfp)();       a pointer to a function returning an int pointer
int **app[];         an array of pointers to int pointers
int (*apa[])[];      an array of pointers to arrays of ints
int (*apf[])();      an array of pointers to functions returning an int
int *aap[][];        an array of arrays of int pointers
int aaa[][][];       an array of arrays of arrays of int
int **fpp();         a function returning a pointer to an int pointer
int (*fpa())[];      a function returning a pointer to an array of ints
int (*fpf())();      a function returning a pointer to a function returning an int

Onwettig

int af[]();          an array of functions returning an int
int fa()[];          a function returning an array of ints
int ff()();          a function returning a function returning an int
int (*pfa)()[];      a pointer to a function returning an array of ints
int aaf[][]();       an array of arrays of functions returning an int
int (*paf)[]();      a pointer to a an array of functions returning an int
int (*pff)()();      a pointer to a function returning a function returning an int
int *afp[]();        an array of functions returning int pointers
int afa[]()[];       an array of functions returning an array of ints
int aff[]()();       an array of functions returning functions returning an int
int *fap()[];        a function returning an array of int pointers
int faa()[][];       a function returning an array of arrays of ints
int faf()[]();       a function returning an array of functions returning an int
int *ffp()();        a function returning a function returning an int pointer

Bron: http://ieng9.ucsd.edu/~cs30x/rt_lt.rule.html



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow