C Language
Lagringskurser
Sök…
Introduktion
En lagringsklass används för att ställa in omfattningen för en variabel eller funktion. Genom att känna till en variabels lagringsklass kan vi bestämma den variabelns livslängd under programmets körtid.
Syntax
[auto | register | statisk | extern] <Datatyp> <Variabelnamn> [= <Värde>];
[statisk _Thread_local | extern _Thread_local | _Thread_local] <Datatyp> <Variabelnamn> [= <Value>]; / * sedan> = C11 * /
Exempel:
typedef int foo ;
extern int foo [2];
Anmärkningar
Lagringsklassspecifikationer är nyckelorden som kan visas bredvid en toppdeklarationstyp för en deklaration. Användningen av dessa nyckelord påverkar lagringsvaraktigheten och länken för det deklarerade objektet, beroende på om det deklareras till filomfång eller till blockomfång:
Nyckelord | Lagringstid | koppling | Anmärkningar |
---|---|---|---|
static | Statisk | Inre | Ställer in intern länk för objekt i filens omfattning; anger statisk lagringstid för objekt i blockomfång. |
extern | Statisk | Extern | Implicit och därför överflödig för objekt definierade i filomfång som också har en initialisering. När den används i en deklaration till filens omfattning utan en initialisering, antyder att definitionen finns i en annan översättningsenhet och kommer att lösas vid länktid. |
auto | Automatisk | Irrelevant | Underförstådd och därför överflödig för objekt som deklarerats som blockomfång. |
register | Automatisk | Irrelevant | Enbart relevant för objekt med automatisk lagringstid. Ger ett tips om att variabeln ska lagras i ett register. En påtvingad begränsning är att man inte kan använda operatörens unära & "adress" på ett sådant objekt, och därför kan inte objektet alias. |
typedef | Irrelevant | Irrelevant | Inte en lagringsklassspecifikation i praktiken, men fungerar som en från en syntaktisk synvinkel. Den enda skillnaden är att den deklarerade identifieraren är en typ snarare än ett objekt. |
_Thread_local | Tråd | Intern extern | Introducerad i C11, för att representera trådlagringsvaraktighet . Om den används i blockomfång ska den också innehålla extern eller static . |
Varje objekt har en tillhörande lagringsperiod (oavsett omfattning) och länkning (endast relevant för deklarationer i filomfång), även om dessa sökord utelämnas.
Beställningen av lagringsklassspecifikationer med avseende på toppnivådypspecifikationer ( int
, unsigned
, short
, etc.) och toppklasskvalifikatorer ( const
, volatile
) genomförs inte, så båda dessa deklarationer är giltiga:
int static const unsigned a = 5; /* bad practice */
static const unsigned int b = 5; /* good practice */
Det anses emellertid som en bra praxis att sätta lagringsklassspecifikationer först, sedan alla typkvalifikatörer, sedan typspecifikatorn ( void
, char
, int
, signed long
, unsigned long long
, long double
...).
Inte alla lagringsklassspecifikationer är lagliga inom ett visst omfång:
register int x; /* legal at block scope, illegal at file scope */
auto int y; /* same */
static int z; /* legal at both file and block scope */
extern int a; /* same */
extern int b = 5; /* legal and redundant at file scope, illegal at block scope */
/* legal because typedef is treated like a storage class specifier syntactically */
int typedef new_type_name;
Lagringstid
Lagringstiden kan vara antingen statisk eller automatisk. För ett deklarerat objekt bestäms det beroende på dess omfattning och lagringsklassens specifikationer.
Statisk lagringstid
Variabler med statisk lagringstid lever hela programmets genomförande och kan deklareras både till filomfång (med eller utan static
) och i blockomfång (genom att uttrycka static
uttryckligen). De tilldelas och initialiseras vanligtvis av operativsystemet vid programstart och återvinns när processen avslutas. I praktiken har körbara format dedikerade avsnitt för sådana variabler ( data
, bss
och rodata
) och hela dessa avsnitt från filen mappas till minne i vissa intervall.
Trådlagringstid
Denna lagringsvaraktighet infördes i C11. Detta var inte tillgängligt i tidigare C-standarder. Vissa kompilatorer ger en icke-standardförlängning med liknande semantik. Till exempel stöder gcc __thread
som kan användas i tidigare C-standarder som inte hade _Thread_local
.
Variabler med trådlagringsvaraktighet kan deklareras både i filomfång och i blockomfång. Om de deklareras som blockomfång ska den också använda static
eller extern
lagringsspecifikation. Dess livslängd är hela utförandet av tråden som den skapas i. Detta är den enda lagringsspecifikationen som kan visas tillsammans med en annan lagringsspecifikator.
Automatisk lagringstid
Variabler med automatisk lagringstid kan endast deklareras inom blockomfång (direkt inom en funktion eller inom ett block i den funktionen). De kan endast användas under perioden mellan att gå in och lämna funktionen eller blocket. När variabeln har gått ur räckvidden (antingen genom att återvända från funktionen eller genom att lämna blocket) omplaceras dess lagring automatiskt. Eventuella ytterligare referenser till samma variabel från pekare är ogiltiga och leder till odefinierat beteende.
I typiska implementeringar är automatiska variabler lokaliserade vid vissa förskjutningar i stapelramen för en funktion eller i register.
Extern och intern koppling
Länkning är endast relevant för objekt (funktioner och variabler) som deklareras till filens omfattning och påverkar deras synlighet i olika översättningsenheter. Objekt med extern länk är synliga i alla andra översättningar (förutsatt att lämplig deklaration ingår). Objekt med intern koppling exponeras inte för andra översättningsenheter och kan endast användas i översättningsenheten där de är definierade.
typedef
Definierar en ny typ baserad på en befintlig typ. Dess syntax speglar den för en variabel deklaration.
/* Byte can be used wherever `unsigned char` is needed */
typedef unsigned char Byte;
/* Integer is the type used to declare an array consisting of a single int */
typedef int Integer[1];
/* NodeRef is a type used for pointers to a structure type with the tag "node" */
typedef struct node *NodeRef;
/* SigHandler is the function pointer type that gets passed to the signal function. */
typedef void (*SigHandler)(int);
Även om det inte tekniskt är en lagringsklass, kommer en kompilator att behandla den som en eftersom ingen av de andra lagringsklasserna är tillåtna om typedef
nyckelordet används.
typedef
erna är viktiga och bör inte ersättas med #define
makro.
typedef int newType;
newType *ptr; // ptr is pointer to variable of type 'newType' aka int
Dock,
#define int newType
newType *ptr; // Even though macros are exact replacements to words, this doesn't result to a pointer to variable of type 'newType' aka int
bil
Denna lagringsklass anger att en identifierare har automatisk lagringsvaraktighet. Detta innebär att när det räckvidd i vilket identifieraren definierats upphör, är det objekt som anges av identifieraren inte längre giltigt.
Eftersom alla objekt, som inte lever i global omfattning eller som förklaras static
, har automatisk lagringsvaraktighet när de definieras, är detta nyckelord mestadels av historiskt intresse och bör inte användas:
int foo(void)
{
/* An integer with automatic storage duration. */
auto int i = 3;
/* Same */
int j = 5;
return 0;
} /* The values of i and j are no longer able to be used. */
statisk
Den static
lagringsklassen har olika syften, beroende på platsen för deklarationen i filen:
Att endast begränsa identifieraren till den översättningsenheten (scope = file).
/* No other translation unit can use this variable. */ static int i; /* Same; static is attached to the function type of f, not the return type int. */ static int f(int n);
För att spara data för användning med nästa samtal av en funktion (scope = block):
void foo() { static int a = 0; /* has static storage duration and its lifetime is the * entire execution of the program; initialized to 0 on * first function call */ int b = 0; /* b has block scope and has automatic storage duration and * only "exists" within function */ a += 10; b += 10; printf("static int a = %d, int b = %d\n", a, b); } int main(void) { int i; for (i = 0; i < 5; i++) { foo(); } return 0; }
Den här koden skriver ut:
static int a = 10, int b = 10 static int a = 20, int b = 10 static int a = 30, int b = 10 static int a = 40, int b = 10 static int a = 50, int b = 10
Statiska variabler behåller sitt värde även när de anropas från flera olika trådar.
Används i funktionsparametrar för att beteckna en matris förväntas ha ett konstant minimalt antal element och en icke-null-parameter:
/* a is expected to have at least 512 elements. */ void printInts(int a[static 512]) { size_t i; for (i = 0; i < 512; ++i) printf("%d\n", a[i]); }
Det nödvändiga antalet objekt (eller till och med en pekare som inte är noll) kontrolleras inte nödvändigtvis av kompilatorn, och kompilatorer krävs inte att meddela dig på något sätt om du inte har tillräckligt med element. Om en programmerare passerar färre än 512 element eller en nollpekare, är odefinierat beteende resultatet. Eftersom det är omöjligt att genomdriva detta måste extra försiktighet användas när man överför ett värde för den parametern till en sådan funktion.
extern
Används för att förklara ett objekt eller en funktion som definieras någon annanstans (och som har extern länk ). I allmänhet används det för att förklara ett objekt eller en funktion som ska användas i en modul som inte är den där motsvarande objekt eller funktion definieras:
/* file1.c */
int foo = 2; /* Has external linkage since it is declared at file scope. */
/* file2.c */
#include <stdio.h>
int main(void)
{
/* `extern` keyword refers to external definition of `foo`. */
extern int foo;
printf("%d\n", foo);
return 0;
}
Saker blir lite mer intressanta med introduktionen av inline
nyckelordet i C99:
/* Should usually be place in a header file such that all users see the definition */
/* Hints to the compiler that the function `bar` might be inlined */
/* and suppresses the generation of an external symbol, unless stated otherwise. */
inline void bar(int drink)
{
printf("You ordered drink no.%d\n", drink);
}
/* To be found in just one .c file.
Creates an external function definition of `bar` for use by other files.
The compiler is allowed to choose between the inline version and the external
definition when `bar` is called. Without this line, `bar` would only be an inline
function, and other files would not be able to call it. */
extern void bar(int);
Registrera
Tips till kompilatorn att åtkomst till ett objekt ska vara så snabbt som möjligt. Huruvida kompilatorn faktiskt använder tipset är implementeringsdefinierat; det kan helt enkelt behandla det som motsvarande auto
.
Den enda egenskapen som är definitivt annorlunda för alla objekt som deklareras med register
är att de inte kan få sin adress beräknad. register
kan därmed vara ett bra verktyg för att säkerställa vissa optimeringar:
register size_t size = 467;
är ett objekt som aldrig kan alias eftersom ingen kod kan skicka sin adress till en annan funktion där det kan ändras oväntat.
Den här egenskapen innebär också att en matris
register int array[5];
kan inte ruttna till en pekare till det första elementet (dvs. array
förvandlas till &array[0]
). Detta innebär att elementen i en sådan grupp inte kan nås och själva matrisen inte kan överföras till en funktion.
I själva verket är den enda lagliga användningen av en grupp som deklareras med en register
operatörens sizeof
; någon annan operatör skulle kräva adressen till det första elementet i matrisen. Av den anledningen bör matriser i allmänhet inte deklareras med register
eftersom det gör dem värdelösa för något annat än storleksberäkning av hela matrisen, vilket kan göras lika enkelt utan register
.
register
är mer lämplig för variabler som definieras inuti ett block och nås med hög frekvens. Till exempel,
/* prints the sum of the first 5 integers*/
/* code assumed to be part of a function body*/
{
register int k, sum;
for(k = 1, sum = 0; k < 6; sum += k, k++);
printf("\t%d\n",sum);
}
Operatören _Alignof
tillåts också användas med register
.
_Thread_local
Det här var en ny lagringsspecifikation som introducerades i C11 tillsammans med flera trådar. Detta är inte tillgängligt i tidigare C-standarder.
Anger trådlagringsvaraktighet . En variabel som deklareras med _Thread_local
lagringsspecifikator anger att objektet är lokalt för den tråden och dess livslängd är hela exekveringen av tråden som det skapas i. Det kan också visas tillsammans med static
eller extern
.
#include <threads.h>
#include <stdio.h>
#define SIZE 5
int thread_func(void *id)
{
/* thread local variable i. */
static _Thread_local int i;
/* Prints the ID passed from main() and the address of the i.
* Running this program will print different addresses for i, showing
* that they are all distinct objects. */
printf("From thread:[%d], Address of i (thread local): %p\n", *(int*)id, (void*)&i);
return 0;
}
int main(void)
{
thrd_t id[SIZE];
int arr[SIZE] = {1, 2, 3, 4, 5};
/* create 5 threads. */
for(int i = 0; i < SIZE; i++) {
thrd_create(&id[i], thread_func, &arr[i]);
}
/* wait for threads to complete. */
for(int i = 0; i < SIZE; i++) {
thrd_join(id[i], NULL);
}
}