Syntaxe C - C syntax
Syntaxe C programovací jazyk je soubor pravidel, kterými se řídí psaní software v jazyce C . Je navržen tak, aby umožňoval programy, které jsou extrémně stručné, mají blízký vztah s výsledným kódem objektu a přesto poskytují relativně vysokou úroveň abstrakce dat . C byl prvním široce úspěšným jazykem na vysoké úrovni pro vývoj přenosných operačních systémů .
Syntaxe jazyka C využívá principu maxima munch .
Datové struktury
Primitivní datové typy
Jazyk C představuje čísla ve třech formách: integrální , reálná a komplexní . Toto rozlišení odráží podobné rozdíly v architektuře instrukční sady většiny centrálních procesorových jednotek . Integrované datové typy ukládají čísla v sadě celých čísel , zatímco reálná a komplexní čísla představují čísla (nebo dvojice čísel) v sadě reálných čísel ve formě s plovoucí desetinnou čárkou .
Všechny C celočíselné typy mají signed
a unsigned
varianty. Pokud signed
nebo unsigned
není výslovně uvedeno, ve většině případů signed
se předpokládá. Z historických důvodů char
je však prostý typ odlišný od obou signed char
a unsigned char
. V závislosti na kompilátoru a znakové sadě to může být podepsaný typ nebo nepodepsaný typ (C zaručuje, že členové základní znakové sady C mají kladné hodnoty). Také typy bitových polí určené jako prosté int
mohou být podepsané nebo nepodepsané, v závislosti na kompilátoru.
Celočíselné typy
C celočíselné typy C mají různé pevné velikosti, schopné reprezentovat různé rozsahy čísel. Typ char
zabírá přesně jeden bajt (nejmenší adresovatelná úložná jednotka), který je typicky široký 8 bitů. (Ačkoli char
může představovat jakýkoli ze „základních“ znaků jazyka C, u mezinárodních znakových sad může být vyžadován širší typ.) Většina celočíselných typů má odrůdy se znaménkem i bez znaménka , označené klíčovými slovy signed
a unsigned
. Podepsané celočíselné typy mohou používat dvojkový doplněk , jedničkový doplněk nebo reprezentaci znaku a velikosti . V mnoha případech existuje několik ekvivalentních způsobů, jak určit typ; například a jsou synonymní.
signed short int
short
Reprezentace některých typů může zahrnovat nepoužité „polstrovací“ bity, které zabírají úložiště, ale nejsou zahrnuty v šířce. Následující tabulka obsahuje úplný seznam standardních celočíselných typů a jejich minimální povolené šířky (včetně jakéhokoli znaménkového bitu).
Nejkratší forma specifikátoru | Minimální šířka (bity) |
---|---|
_Bool
|
1 |
char
|
8 |
signed char
|
8 |
unsigned char
|
8 |
short
|
16 |
unsigned short
|
16 |
int
|
16 |
unsigned int
|
16 |
long
|
32 |
unsigned long
|
32 |
long long
|
64 |
unsigned long long
|
64 |
char
Typu se liší od obou signed char
a unsigned char
, ale je zaručeno, že mají stejné zastoupení jako jeden z nich. _Bool
A long long
typy jsou standardizovány od roku 1999, a nemusí být podporována staršími C kompilátory. Typ _Bool
je obvykle přístupný prostřednictvím typedef
názvu bool
definovaného standardním záhlavím stdbool.h .
Obecně platí, že šířky a schéma reprezentace implementované pro jakoukoli danou platformu jsou vybírány na základě architektury stroje s určitým ohledem na snadnost importu zdrojového kódu vyvinutého pro jiné platformy. Šířka int
typu se mezi implementacemi C velmi liší; často odpovídá té „nejpřirozenější“ velikosti slova pro konkrétní platformu. Standardní limity záhlaví. H definuje makra pro minimální a maximální reprezentovatelné hodnoty standardních celočíselných typů implementovaných na jakékoli konkrétní platformě.
Kromě standardních celočíselných typů mohou existovat i další „rozšířené“ celočíselné typy, které lze použít pro typedef
s ve standardních hlavičkách. Pro přesnější specifikaci šířky mohou a měli by programátoři použít typedef
s ze standardního záhlaví stdint.h .
Celočíselné konstanty mohou být ve zdrojovém kódu zadány několika způsoby. Číselné hodnoty lze zadat jako desetinné (příklad :)1022
, osmičkové s nulou (0) jako předponu ( 01776
) nebo hexadecimální s 0x (nula x) jako předponu ( 0x3FE
). Znak v jednoduchých uvozovkách (příklad:) 'R'
, nazývaný „znaková konstanta“, představuje hodnotu tohoto znaku v znakové sadě spuštění s typem int
. S výjimkou znakových konstant je typ celočíselné konstanty určen šířkou potřebnou k reprezentaci zadané hodnoty, ale vždy je alespoň tak široký jako int
. To lze přepsat přidáním explicitního modifikátoru délky a/nebo podpisu; například 12lu
má typ unsigned long
. Neexistují žádné záporné celočíselné konstanty, ale stejného účinku lze často dosáhnout použitím unárního negačního operátoru „-“.
Výčtový typ
Výčtu typu v C, stanoveno pomocí enum
klíčového slova, a často jen nazývá „enum“ (obvykle výraznější ee'-num /ˌi.nʌm/ nebo ee'-Noom /ˌi.nuːm/), je typ navržen tak, aby hodnoty představují napříč řadou pojmenovaných konstant. Každá z vyjmenovaných konstant má typ int
. Každý enum
typ je kompatibilní s char
celočíselným typem se znaménkem nebo bez znaménka, ale každá implementace definuje vlastní pravidla pro výběr typu.
Některé překladače varují, pokud je objektu s výčtovým typem přiřazena hodnota, která není jednou z jeho konstant. Takovému objektu však lze přiřadit libovolné hodnoty v rozsahu jejich kompatibilního typu a enum
konstanty lze použít kdekoli, kde se očekává celé číslo. Z tohoto důvodu enum
se místo #define
direktiv preprocesoru často používají hodnoty k vytváření pojmenovaných konstant. Takové konstanty je obecně bezpečnější použít než makra, protože se nacházejí v konkrétním prostoru jmen identifikátorů.
Výčtový typ je deklarován se enum
specifikátorem a volitelným názvem (nebo tagem ) pro výčet, následuje seznam jedné nebo více konstant obsažených v složených závorkách a oddělených čárkami a volitelný seznam názvů proměnných. Následné odkazy na konkrétní výčtový typ používají enum
klíčové slovo a název výčtu. Ve výchozím nastavení je první konstantě ve výčtu přiřazena hodnota nula a každá následující hodnota je zvýšena o jednu oproti předchozí konstantě. Specifické hodnoty mohou být také přiřazeny konstantám v deklaraci a jakékoli další konstanty bez konkrétních hodnot budou od tohoto bodu dále zvyšovány. Zvažte například následující prohlášení:
enum colors { RED, GREEN, BLUE = 5, YELLOW } paint_color;
To deklaruje enum colors
typ; jsou int
konstanty RED
(jehož hodnota je 0), GREEN
(jehož hodnota je vyšší než RED
1), BLUE
(jehož hodnota je dána hodnota, 5), a YELLOW
(jehož hodnota je o jednu větší než BLUE
, 6); a enum colors
proměnná paint_color
. Konstanty mohou být použity mimo kontext výčtu (kde je povolena jakákoli celočíselná hodnota) a mohou být přiřazeny hodnoty jiné než konstanty paint_color
nebo jakákoli jiná proměnná typu enum colors
.
Typy s plovoucí desetinnou čárkou
Forma s plovoucí desetinnou čárkou se používá k reprezentaci čísel se zlomkovou složkou. Nepředstavují však většinu racionálních čísel přesně; jsou místo toho blízkou aproximací. Existují tři typy skutečných hodnot, označených jejich specifikátory: jednoduchá přesnost ( float
), dvojitá přesnost ( double
) a dvojitá rozšířená přesnost ( long double
). Každý z nich může představovat hodnoty v jiné formě, často jeden z formátů IEEE s plovoucí desetinnou čárkou .
Specifikátory typu | Přesnost (desetinné číslice) | Rozsah exponentů | ||
---|---|---|---|---|
Minimální | IEEE 754 | Minimální | IEEE 754 | |
float
|
6 | 7,2 (24 bitů) | ± 37 | ± 38 (8 bitů) |
double
|
10 | 15,9 (53 bitů) | ± 37 | ± 307 (11 bitů) |
long double
|
10 | 34,0 (113 bitů) | ± 37 | ± 4931 (15 bitů) |
Konstanty s plovoucí desetinnou čárkou mohou být zapsány v desítkové soustavě , např 1.23
. Desetinnou vědeckou notaci lze použít přidáním e
nebo E
následováním desetinného exponentu, známého také jako E notace , např. 1.23e2
(Který má hodnotu 1,23 × 10 2 = 123,0). Je vyžadována buď desetinná čárka, nebo exponent (v opačném případě je číslo analyzováno jako celočíselná konstanta). Hexadecimální konstanty s plovoucí desetinnou čárkou se řídí podobnými pravidly, kromě toho, že musí mít předponu 0x
a použít je p
nebo P
určit binární exponent, např. 0xAp-2
(Který má hodnotu 2,5, protože A h × 2 −2 = 10 × 2 −2 = 10 ÷ 4 ). Desetinné i hexadecimální konstanty s plovoucí desetinnou čárkou mohou mít příponu f
nebo F
k označení konstanty typu float
, l
(písmeno l
) nebo L
k označení typu long double
, nebo pro double
konstantu ponechány bez přípony .
Standardní hlavičkový soubor float.h
definuje hodnoty minimální a maximální typů provádění je s plovoucí desetinnou čárkou float
, double
a long double
. Rovněž definuje další limity, které jsou relevantní pro zpracování čísel s plovoucí desetinnou čárkou.
Specifikátory třídy úložiště
Každý objekt má třídu úložiště. Toto určuje v zásadě dobu skladování , která může být statická (výchozí pro globální), automatická (výchozí pro místní) nebo dynamická (přidělená), spolu s dalšími funkcemi (nápověda pro propojení a registr).
Specifikátory | Život | Rozsah | Výchozí inicializátor |
---|---|---|---|
auto
|
Blok (zásobník) | Blok | Neinicializováno |
register
|
Blok (zásobník nebo registr CPU) | Blok | Neinicializováno |
static
|
Program | Bloková nebo kompilační jednotka | Nula |
extern
|
Program | Globální (celý program) | Nula |
(žádný) 1 | Dynamický (halda) | Neinicializováno (inicializováno na 0 při použití calloc() )
|
-
1 Přidělené a uvolněné pomocí funkcí
malloc()
afree()
knihovny.
Proměnné deklarované v rámci bloku mají ve výchozím nastavení automatické úložiště, stejně jako ty, které jsou výslovně deklarovány pomocí specifikátorů třídy úložiště auto
nebo register
. Specifikátory auto
a register
mohou být použity pouze v rámci funkcí a deklarací argumentů funkcí; jako takový je auto
specifikátor vždy nadbytečný. Objekty deklarované mimo všechny bloky a explicitně deklarované pomocí static
specifikátoru třídy úložiště mají trvání statického úložiště. Statické proměnné jsou ve výchozím nastavení inicializovány kompilátorem na nulu .
Objekty s automatickým ukládáním jsou lokální pro blok, ve kterém byly deklarovány, a jsou vyřazeny při opuštění bloku. Navíc objektům deklarovaným pomocí register
třídy úložiště může kompilátor pro přístup k registrům dát vyšší prioritu ; ačkoli se kompilátor může rozhodnout, že ve skutečnosti neuloží žádný z nich do registru. Objekty s touto třídou úložiště nelze používat s &
unárním operátorem address-of ( ). Objekty se statickým úložištěm přetrvávají po celou dobu trvání programu. Tímto způsobem lze ke stejnému objektu přistupovat pomocí funkce přes více hovorů. Objekty s přidělenou trvání skladování jsou vytvářeny a explicitně zničen malloc
, free
a souvisejících funkcí.
Specifikátor extern
třídy úložiště označuje, že úložiště pro objekt bylo definováno jinde. Při použití v bloku označuje, že úložiště bylo definováno deklarací mimo tento blok. Při použití mimo všechny bloky to znamená, že úložiště bylo definováno mimo kompilační jednotku. Specifikátor extern
třídy úložiště je při použití v deklaraci funkce nadbytečný. Udává, že deklarovaná funkce byla definována mimo kompilační jednotku.
Všimněte si, že specifikátory úložiště platí pouze pro funkce a objekty; další věci, jako jsou deklarace typu a výčtu, jsou soukromé pro kompilační jednotku, ve které se objevují. Typy na druhé straně mají kvalifikátory (viz níže).
Kvalifikátory typu
Typy mohou být kvalifikovány k označení speciálních vlastností jejich dat. Kvalifikátor typu const
označuje, že hodnota se po inicializaci nezmění. Pokus o úpravu const
kvalifikované hodnoty vede k nedefinovanému chování, takže některé kompilátory C je ukládají do rodata nebo (pro vestavěné systémy) do paměti jen pro čtení (ROM). Kvalifikátor typu volatile
indikuje optimalizujícímu kompilátoru , že nemusí odstranit zjevně nadbytečná čtení nebo zápisy, protože hodnota se může změnit, i když nebyla změněna žádným výrazem nebo příkazem, nebo může být nutné více zápisů, například pro mapování paměti I /O .
Neúplné typy
Neúplný typ je struktura nebo typ sjednocení, jehož členové ještě nebyli zadáni, typ pole, jehož dimenze ještě nebyla zadána, nebo void
typ ( void
typ nelze dokončit). Takový typ nemusí být vytvořen (jeho velikost není známa), ani nesmí být přístup k jeho členům (ani oni nejsou známí); lze však použít odvozený typ ukazatele (ale ne odkázaný).
Často se používají s ukazateli, buď jako předávací nebo externí deklarace. Například kód by mohl deklarovat neúplný typ takto:
struct thing *pt;
To deklaruje pt
jako ukazatel na struct thing
a neúplný typ struct thing
. Ukazatele na data mají vždy stejnou šířku bajtu bez ohledu na to, na co ukazují, takže toto prohlášení je platné samo o sobě (pokud pt
není zrušeno). Neúplný typ může být dokončen později ve stejném rozsahu jeho opětovným prohlášením:
struct thing {
int num;
}; /* thing struct type is now completed */
K implementaci rekurzivních struktur se používají neúplné typy ; tělo deklarace typu může být odloženo na později v překladové jednotce:
typedef struct Bert Bert;
typedef struct Wilma Wilma;
struct Bert {
Wilma *wilma;
};
struct Wilma {
Bert *bert;
};
Neúplné typy se také používají ke skrývání dat ; neúplný typ je definován v hlavičkovém souboru a tělo pouze v příslušném zdrojovém souboru.
Ukazatele
V deklaracích modifikátor hvězdičky ( *
) určuje typ ukazatele. Například pokud by specifikátor int
odkazoval na celočíselný typ, specifikátor int*
odkazuje na typ "ukazatel na celé číslo". Hodnoty ukazatele spojují dvě informace: adresu paměti a datový typ. Následující řádek kódu deklaruje proměnnou ukazatele na celé číslo s názvem ptr :
int *ptr;
Odkazování
Když je deklarován nestatický ukazatel, je s ním spojena nespecifikovaná hodnota. Adresa spojená s takovým ukazatelem musí být změněna přiřazením před jeho použitím. V následujícím příkladu je ptr nastaven tak, že ukazuje na data spojená s proměnnou a :
int a = 0;
int *ptr = &a;
K tomu &
se používá operátor „adresa-adresy“ (unární ). Produkuje umístění paměti datového objektu, který následuje.
Odhodlání
K namířeným datům lze přistupovat prostřednictvím hodnoty ukazatele. V následujícím příkladu je celočíselná proměnná b nastavena na hodnotu celočíselné proměnné a , která je 10:
int a=10;
int *p;
p = &a;
int b = *p;
K provedení tohoto úkolu se používá unární operátor dereference , označený hvězdičkou (*). Vrací data, na která jeho operand - který musí být typu ukazatele - ukazuje. Výraz * p tedy označuje stejnou hodnotu jako a . Dereferencování nulového ukazatele je nezákonné.
Pole
Definice pole
Pole se v jazyce C používají k reprezentaci struktur po sobě jdoucích prvků stejného typu. Definice pole (pevné velikosti) má následující syntaxi:
int array[100];
který definuje pole pojmenované pole pro uložení 100 hodnot primitivního typu int
. Pokud je deklarován v rámci funkce, dimenze pole může být také nestálým výrazem, v takovém případě bude přidělena paměť pro zadaný počet prvků. Ve většině kontextů v pozdějším použití je zmínka o proměnném poli převedena na ukazatel na první položku v poli. sizeof
Operátor je výjimka: sizeof array
získá velikost celého pole (to znamená, že 100 krát velikost a strukturu int
, a sizeof(array) / sizeof(int)
vrátí 100). Další výjimkou je operátor & (address-of), který například poskytuje ukazatel na celé pole
int (*ptr_to_array)[100] = &array;
Přístup k prvkům
Primárním prostředkem pro přístup k hodnotám prvků pole je operátor dolního indexu pole. Pro přístup k i -indexovanému prvku pole by byla syntaxe array[i]
, která odkazuje na hodnotu uloženou v tomto prvku pole.
Číslování indexu pole začíná na 0 (viz indexování založené na nule ). Největší povolený dolní index pole se proto rovná počtu prvků v poli mínus 1. Pro ilustraci to považujeme za pole a deklarované jako mající 10 prvků; první prvek by byl a[0]
a poslední prvek by byl a[9]
.
C neposkytuje žádné zařízení pro automatickou kontrolu hranic pro využití pole. Ačkoli by logicky byl poslední dolní index v poli 10 prvků 9, mohly by být náhodně specifikovány dolní indexy 10, 11 atd., S nedefinovanými výsledky.
Vzhledem k tomu, že pole a ukazatele jsou zaměnitelné, mohou být adresy každého z prvků pole vyjádřeny v ekvivalentní aritmetice ukazatele . Následující tabulka ukazuje obě metody pro existující pole:
Živel | První | Druhý | Třetí | n th |
---|---|---|---|---|
Dolní index pole |
array[0]
|
array[1]
|
array[2]
|
array[n - 1]
|
Dereferencovaný ukazatel |
*array
|
*(array + 1)
|
*(array + 2)
|
*(array + n - 1)
|
Protože je výraz a[i]
sémanticky ekvivalentní výrazu *(a+i)
, který je zase ekvivalentní *(i+a)
výrazu, lze výraz zapsat také jako i[a]
, i když se tato forma používá jen zřídka.
Pole s proměnnou délkou
C99 standardizovaná pole s proměnnou délkou (VLA) v rozsahu bloku. Takové proměnné pole jsou přidělovány na základě hodnoty celočíselné hodnoty za běhu při vstupu do bloku a jsou uvolněny na konci bloku. Od C11 již tuto funkci kompilátor nemusí implementovat.
int n = ...;
int a[n];
a[3] = 10;
Tato syntaxe vytváří pole, jehož velikost je do konce bloku pevná.
Dynamická pole
Pole, které je možné měnit velikost dynamicky se mohou vyrábět s pomocí na standardní knihovny C . malloc
Funkce poskytuje jednoduchý způsob pro přidělování paměti. Trvá to jeden parametr: velikost paměti, kterou je třeba přidělit v bajtech. Po úspěšném přidělení malloc
vrátí void
hodnotu ukazatele generic ( ) směřující na začátek přiděleného prostoru. Vrácená hodnota ukazatele je implicitně převedena na příslušný typ přiřazením. Pokud přidělení nebylo možné dokončit, malloc
vrátí nulový ukazatel . Následující segment má tedy podobnou funkci jako výše uvedená deklarace:
#include <stdlib.h> /* declares malloc */
...
int *a = malloc(n * sizeof *a);
a[3] = 10;
Výsledkem je int
proměnná „ukazatel na “ ( a ), která ukazuje na první z n souvislých int
objektů; kvůli ekvivalenci pole – ukazatel lze použít místo skutečného názvu pole, jak je uvedeno na posledním řádku. Výhodou při používání této dynamické alokace je, že množství paměti, které je jí přiděleno, může být omezeno na to, co je skutečně potřeba za běhu, a to lze podle potřeby změnit (pomocí standardní funkce knihovny realloc
).
Když již dynamicky alokovaná paměť není potřeba, měla by být uvolněna zpět do run-time systému. To se provádí voláním free
funkce. Trvá jeden parametr: ukazatel na dříve přidělenou paměť. Toto je hodnota, která byla vrácena předchozím voláním na malloc
.
Jako bezpečnostní opatření pak někteří programátoři nastavili proměnnou ukazatele na NULL
:
free(a);
a = NULL;
Tím je zajištěno, že další pokusy o dereference ukazatele na většině systémů způsobí selhání programu. Pokud se tak nestane, proměnná se stane visícím ukazatelem, což může vést k chybě po použití. Pokud je však ukazatel místní proměnnou, jeho nastavení na NULL
nezabrání programu v používání dalších kopií ukazatele. Statické analyzátory obvykle snadno rozpoznají místní chyby po použití . Proto je tento přístup méně užitečný pro místní ukazatele a je častěji používán s ukazateli uloženými v dlouhotrvajících strukturách. Obecně NULL
je však dobré nastavit ukazatele na, protože to umožňuje programátorovi NULL
-zkontrolovat ukazatele před dereferencováním, což pomáhá předcházet haváriím.
Připomínaje příklad pole, lze také vytvořit pole pevné velikosti prostřednictvím dynamické alokace:
int (*a)[100] = malloc(sizeof *a);
... Což dává ukazatel na pole.
Přístup k ukazateli na pole lze provést dvěma způsoby:
(*a)[index];
index[*a];
Iteraci lze také provést dvěma způsoby:
for (int i = 0; i < 100; i++)
(*a)[i];
for (int *i = a[0]; i < a[1]; i++)
*i;
Výhodou použití druhého příkladu je, že není vyžadován numerický limit prvního příkladu, což znamená, že ukazatel na pole může mít libovolnou velikost a druhý příklad lze spustit bez jakýchkoli úprav.
Vícerozměrná pole
Kromě toho C podporuje pole více dimenzí, která jsou uložena v pořadí hlavních řádků . Technicky jsou vícedimenzionální pole C pouze jednorozměrná pole, jejichž prvky jsou pole. Syntaxe pro deklarování vícerozměrných polí je následující:
int array2d[ROWS][COLUMNS];
kde Řádky a SLOUPKY jsou konstanty. To definuje dvourozměrné pole. Čtení dolních indexů zleva doprava, array2d je pole ROWS délky , z nichž každý prvek je pole COLUMNS celých čísel.
K přístupu k celočíselnému prvku v tomto vícerozměrném poli by člověk použil
array2d[4][3]
Opět, čtení zleva doprava, tím se přistupuje k 5. řadě a 4. prvku v této řadě. Výraz array2d[4]
je pole, které poté přihlašujeme pomocí [3], abychom získali přístup ke čtvrtému celému číslu.
Živel | První | Druhý řádek, druhý sloupec | i th řádek, j th sloupec |
---|---|---|---|
Dolní index pole |
array[0][0]
|
array[1][1]
|
array[i - 1][j - 1]
|
Dereferencovaný ukazatel |
*(*(array + 0) + 0)
|
*(*(array + 1) + 1)
|
*(*(array + i - 1) + j - 1)
|
Vyšší dimenze mohou být deklarována podobným způsobem.
Multidimenzionální pole by nemělo být zaměňováno s polem odkazů na pole (také známá jako vektory Iliffe nebo někdy pole polí ). První z nich je vždy obdélníkový (všechny dílčí pole musí mít stejnou velikost) a zabírá souvislou oblast paměti. Ten je jednorozměrné pole ukazatelů, z nichž každý může ukazovat na první prvek podoblasti na jiném místě v paměti a dílčí pole nemusí mít stejnou velikost. Ten lze vytvořit vícenásobným použitím malloc
.
Řetězce
V jazyce C jsou řetězcové literály obklopeny dvojitými uvozovkami ( "
), např. "Hello world!"
A jsou zkompilovány do pole zadaných char
hodnot s dalším kódem zakončujícím znak null (s hodnotou 0) k označení konce řetězce.
Řetězcové literály nesmí obsahovat vložené nové řádky; tento proskript poněkud zjednodušuje analýzu jazyka. Chcete -li do řetězce zahrnout nový řádek, lze použít únik zpětného lomítka \n
, jak je uvedeno níže.
Existuje několik standardních knihovních funkcí pro práci s řetězcovými daty (ne nutně konstantními) organizovanými jako pole char
používající tento formát zakončený nulou; viz níže .
Syntaxe doslovného řetězce C byla velmi vlivná a dostala se do mnoha dalších jazyků, jako je C ++, Objective-C, Perl, Python, PHP, Java, Javascript, C#, Ruby. V současné době téměř všechny nové jazyky přijímají nebo staví na syntaxi řetězců ve stylu C. Jazyky, kterým tato syntaxe chybí, obvykle předcházejí C.
Zpětné lomítko uniká
Pokud chcete do řetězce zahrnout dvojitou uvozovku, lze to provést například tak, že z něj uniknete zpětným lomítkem ( \
) "This string contains \"double quotes\"."
. Chcete -li vložit doslovné zpětné lomítko, musíte jej zdvojnásobit, např "A backslash looks like this: \\"
.
Zpětná lomítka lze použít k zadávání řídicích znaků atd. Do řetězce:
Uniknout | Význam |
---|---|
\\ |
Doslova zpětné lomítko |
\" |
Dvojitá citace |
\' |
Jediný citát |
\n |
Nový řádek (line feed) |
\r |
Návrat vozíku |
\b |
Backspace |
\t |
Vodorovná záložka |
\f |
Formulář |
\a |
Alert (zvonek) |
\v |
Svislá záložka |
\? |
Otazník (používá se k úniku z trigrafů ) |
%% |
Procentní značka, pouze řetězce formátu printf (Poznámka \% není standardní a není vždy rozpoznána) |
\OOO |
Znak s osmičkovou hodnotou OOO (kde OOO je 1-3 osmičkové číslice, '0'-'7') |
\xHH |
Znak s hexadecimální hodnotou HH (kde HH je 1 nebo více hexadecimálních číslic, '0'-'9', 'A'-'F', 'a'-'f') |
Použití jiných zpětných lomítek není standardem C definováno, přestože dodavatelé kompilátorů často poskytují jako doplňkové jazyky další únikové kódy. Jednou z nich je úniková sekvence \e
pro únikový znak s hexadecimální hodnotou ASCII 1B, která nebyla přidána do standardu C kvůli chybějící reprezentaci v jiných znakových sadách (například EBCDIC ). Je k dispozici v GCC , clang a tcc .
Řetězcové doslovné zřetězení
C má řetězcové doslovné zřetězení , což znamená, že sousední řetězcové literály jsou zřetězeny v době kompilace; to umožňuje rozdělení dlouhých řetězců na více řádků a také umožňuje použití řetězcových literálů vyplývajících z definice preprocesoru C a připojení maker k řetězcům v době kompilace:
printf(__FILE__ ": %d: Hello "
"world\n", __LINE__);
se rozšíří na
printf("helloworld.c" ": %d: Hello "
"world\n", 10);
který je syntakticky ekvivalentní
printf("helloworld.c: %d: Hello world\n", 10);
Znakové konstanty
Jednotlivé konstanty znaků jsou uvozovky, např. 'A'
, A mají typ int
(v C ++, char
). Rozdíl je v tom, že "A"
představuje pole se dvěma znaky zakončenými nulami, 'A' a '\ 0', zatímco 'A'
přímo představuje hodnotu znaku (65, pokud je použit ASCII). Jsou podporována stejná zpětná lomítka jako u řetězců, kromě toho, že (samozřejmě) "
lze platně použít jako znak, aniž by vám unikl, zatímco '
nyní je třeba jej uniknout.
Znaková konstanta nemůže být prázdná (tj. ''
Je neplatnou syntaxí), ačkoli řetězec může být (stále má nulový ukončovací znak). Víceznakové konstanty (např. 'xy'
) Jsou platné, i když jen zřídka užitečné-umožňují jednomu uložit několik znaků do celého čísla (např. 4 znaky ASCII se vejdou do 32bitového celého čísla, 8 do 64bitového). Protože int
není specifikováno pořadí, ve kterém jsou znaky zabaleny do (ponecháno na definici implementace), je přenosné použití víceznakových konstant obtížné.
V situacích omezených na konkrétní platformu a implementaci kompilátoru však víceznačné konstanty nacházejí své použití při zadávání podpisů. Jedním z běžných případů použití je OSType , kde kombinace kompilátorů Classic Mac OS a jeho inherentní big-endianness znamená, že bajty v celém čísle se objevují v přesném pořadí znaků definovaných v doslovném znění. Definice populárními „implementace“ jsou v podstatě shodné: v GCC, Clang a Visual C ++ , '1234'
výtěžky pod ASCII.
0x31323334
Široké řetězce znaků
Protože typ char
je široký 1 bajt, jedna char
hodnota obvykle může představovat nejvýše 255 odlišných znakových kódů, což není zdaleka dost pro všechny znaky používané po celém světě. Aby byla zajištěna lepší podpora pro mezinárodní znaky, zavedl první standard C (C89) široké znaky (zakódované v typu wchar_t
) a široké znakové řetězce, které jsou psány jakoL"Hello world!"
Široké znaky jsou nejčastěji buď 2 bajty (pomocí 2bajtového kódování, jako je UTF-16 ), nebo 4 bajty (obvykle UTF-32 ), ale standard C neurčuje šířku pro wchar_t
, ponechává volbu na implementátoru. Microsoft Windows obecně používá UTF-16, takže výše uvedený řetězec bude mít 26 bajtů pro kompilátor Microsoft; Unix svět preferuje UTF-32, tak překladače jako je GCC by generovat 52 bajt řetězec. Šířka 2 bajtů wchar_t
trpí stejným omezením jako char
v tom, že určité znaky (ty mimo BMP ) nemohou být reprezentovány v jednom wchar_t
; ale musí být reprezentovány pomocí náhradních párů .
Původní standard C určoval pouze minimální funkce pro provoz se širokými řetězci znaků; v roce 1995 byl standard upraven tak, aby zahrnoval mnohem rozsáhlejší podporu, srovnatelnou s tou pro char
řetězce. Příslušné funkce jsou většinou pojmenovány podle jejich char
ekvivalentů, s přidáním „w“ nebo nahrazením „str“ „wcs“; jsou specifikovány v <wchar.h>
, <wctype.h>
obsahující rozsáhlé klasifikační a mapovací funkce.
Nyní obecně doporučovaný způsob podpory mezinárodních znaků je prostřednictvím UTF-8 , který je uložen v char
polích, a lze jej zapsat přímo do zdrojového kódu, pokud používáte editor UTF-8, protože UTF-8 je přímým rozšířením ASCII .
Řetězce s proměnnou šířkou
Běžnou alternativou wchar_t
je použití kódování s proměnnou šířkou , přičemž logický znak může přesahovat více poloh řetězce. Řetězce s proměnnou šířkou mohou být doslovně zakódovány do literálů, s rizikem záměny kompilátoru nebo pomocí číselných zpětných lomítek (např. "\xc3\xa9"
Pro „é“ v UTF-8). UTF-8 kódování byl speciálně navržen (pod Plan 9 ) pro kompatibilitu se standardními knihovna řetězcové funkce; podpůrné funkce kódování zahrnují nedostatek vložených null, žádné platné interpretace pro podsekce a triviální resynchronizace. Kódování postrádající tyto funkce se pravděpodobně ukáže jako nekompatibilní se standardními funkcemi knihovny; v takových případech se často používají funkce řetězce podporující kódování.
Funkce knihovny
S řetězci , konstantními i proměnnými, lze manipulovat bez použití standardní knihovny . Knihovna však obsahuje mnoho užitečných funkcí pro práci s řetězci zakončenými hodnotou null.
Struktury a odbory
Struktury
Struktury a svazky v C jsou definovány jako datové kontejnery sestávající ze sekvence pojmenovaných členů různých typů. Jsou podobné záznamům v jiných programovacích jazycích. Členy struktury jsou uloženy na po sobě jdoucích místech v paměti, přestože kompilátor může vkládat odsazení mezi členy nebo za ně (nikoli však před první člen) pro účinnost nebo jako odsazení požadované pro správné zarovnání cílovou architekturou. Velikost struktury se rovná součtu velikostí jejích členů plus velikosti výplně.
Odbory
Odbory v C souvisejí se strukturami a jsou definovány jako objekty, které mohou obsahovat (v různých časech) objekty různých typů a velikostí. Jsou analogické záznamům variant v jiných programovacích jazycích. Na rozdíl od struktur odkazují všechny součásti svazku na stejné místo v paměti. Tímto způsobem lze sjednocení použít v různých časech k uložení různých typů objektů, aniž by bylo nutné pro každý nový typ vytvářet samostatný objekt. Velikost svazku se rovná velikosti jeho největšího typu součásti.
Prohlášení
Struktury jsou deklarovány pomocí struct
klíčového slova a odbory jsou deklarovány pomocí union
klíčového slova. Za klíčovým slovem specifikátoru následuje volitelný název identifikátoru, který se používá k identifikaci formy struktury nebo sjednocení. Za identifikátorem následuje deklarace struktury nebo těla svazu: seznam deklarací členů obsažených v složených závorkách, přičemž každá deklarace je ukončena středníkem. Nakonec deklarace končí volitelným seznamem jmen identifikátorů, které jsou deklarovány jako instance struktury nebo sjednocení.
Následující příkaz například deklaruje strukturu pojmenovanou, s
která obsahuje tři členy; také deklaruje instanci struktury známé jako tee
:
struct s {
int x;
float y;
char *z;
} tee;
A následující příkaz deklaruje podobný svaz pojmenovaný u
a jeho instanci pojmenovaný n
:
union u {
int x;
float y;
char *z;
} n;
Členové struktur a svazů nemohou mít neúplný nebo funkční typ. Členové tedy nemohou být instancí deklarované struktury nebo unie (protože je v daném bodě neúplná), ale mohou být ukazateli na deklarovaný typ.
Jakmile je struktura nebo svazový orgán deklarován a pojmenován, lze jej považovat za nový datový typ pomocí specifikátoru struct
nebo union
, podle potřeby, a názvu. Následující příkaz například vzhledem k výše uvedené deklaraci struktury deklaruje novou instanci s
pojmenované struktury r
:
struct s r;
Je také běžné používat typedef
specifikátor k eliminaci potřeby klíčového slova struct
nebo union
v pozdějších referencích na strukturu. První identifikátor za tělem struktury je brán jako nový název pro typ struktury (instance struktury v tomto kontextu nemusí být deklarovány). Následující příkaz například deklaruje nový typ známý jako s_type, který bude obsahovat určitou strukturu:
typedef struct {...} s_type;
Budoucí příkazy pak mohou odkazovat na strukturu pomocí specifikátoru s_type (namísto rozšířeného struct
... specifikátoru).
Přístup ke členům
Ke členům se přistupuje pomocí názvu instance struktury nebo svazku, tečky ( .
) a názvu člena. Vzhledem k deklaraci odpaliště shora je například člen známý jako y (typu float
) přístupný pomocí následující syntaxe:
tee.y
Ke strukturám se běžně přistupuje pomocí ukazatelů. Zvažte následující příklad, který definuje ukazatel na tee , známý jako ptr_to_tee :
struct s *ptr_to_tee = &tee;
Člen y z odpaliště se pak může přistupovat dereferencing ptr_to_tee a používání výsledek jako levý operand:
(*ptr_to_tee).y
Což je totožné s tím jednodušším tee.y
výše, pokud ptr_to_tee ukazuje na tee . Z důvodu priority operátoru („.“ Vyšší než „*“) je kratší *ptr_to_tee.y
pro tento účel nesprávný, místo toho je analyzován jako *(ptr_to_tee.y)
a proto jsou závorky nezbytné. Protože je tato operace běžná, C poskytuje zkrácenou syntaxi pro přístup ke členu přímo z ukazatele. S touto syntaxí se název instance nahradí názvem ukazatele a tečka se nahradí posloupností znaků ->
. Následující způsob přístupu k y je tedy identický s předchozími dvěma:
ptr_to_tee->y
Ke členům odborů se přistupuje stejným způsobem.
To lze zřetězit; například v propojeném seznamu lze odkazovat na n->next->next
druhý následující uzel (za předpokladu, že n->next
není null).
Úkol
Přiřazování hodnot jednotlivým členům struktur a svazků je syntakticky totožné s přiřazováním hodnot jakémukoli jinému objektu. Jediným rozdílem je, že hodnotou l přiřazení je název člena, ke kterému přistupuje výše uvedená syntaxe.
Strukturu lze také přiřadit jako jednotku jiné struktuře stejného typu. Struktury (a odkazy na struktury) lze také použít jako parametry parametrů a návratové typy.
Následující příkaz například přiřazuje hodnotu 74 (bod kódu ASCII pro písmeno 't') členu s názvem x v tee struktury shora:
tee.x = 74;
A stejné přiřazení pomocí ptr_to_tee místo odpaliště by vypadalo takto:
ptr_to_tee->x = 74;
Přiřazení členům odborů je totožné.
Ostatní operace
Podle standardu C je jedinou legální operací, kterou lze na struktuře provádět, zkopírovat ji, přiřadit k ní jako jednotku (nebo ji inicializovat), převzít její adresu pomocí &
unárního operátoru address-of ( ) a přistupovat k jejím členům . Odbory mají stejná omezení. Jedním ze způsobů implicitně zakázaných je srovnání: konstrukce a odbory nemohou být srovnávány pomocí C je standardní srovnávací zařízení ( ==
, >
, <
, atd.).
Bitová pole
C také poskytuje speciální typ členu struktury známý jako bitové pole , což je celé číslo s explicitně zadaným počtem bitů. Trochu pole je deklarován jako člen struktury typu int
, signed int
, unsigned int
, nebo _Bool
, po název člena dvojtečkou ( :
) a počet bitů by měl zabírat. Celkový počet bitů v jednom bitovém poli nesmí překročit celkový počet bitů v deklarovaném typu.
Jako zvláštní výjimka z obvyklých C syntaxových pravidel je definováno implementací, zda je bitové pole deklarováno jako typ int
, bez zadání signed
nebo unsigned
, je podepsáno nebo nepodepsáno. Proto se doporučuje pro přenositelnost explicitně specifikovat signed
nebo unsigned
na všech členech struktury.
Nejmenovaná pole skládající se pouze z dvojtečky následované několika bity jsou také povolena; tyto označují polstrování . Určení šířky nula pro nejmenované pole se používá k vynucení zarovnání na nové slovo.
Členové bitových polí nemají adresy, a proto je nelze použít s &
unárním operátorem address-of ( ). sizeof
Provozovatel nesmí být aplikován na bitových polí.
Následující deklarace deklaruje nový typ struktury známý jako f
a jeho instanci známou jako g
. Komentáře obsahují popis každého z členů:
struct f {
unsigned int flag : 1; /* a bit flag: can either be on (1) or off (0) */
signed int num : 4; /* a signed 4-bit field; range -7...7 or -8...7 */
signed int : 3; /* 3 bits of padding to round out to 8 bits */
} g;
Inicializace
Výchozí inicializace závisí na specifikátoru třídy úložiště , popsaném výše.
Kvůli gramatice jazyka může být skalární inicializátor uzavřen v libovolném počtu dvojic složených závorek. Většina kompilátorů však vydá varování, pokud existuje více než jeden takový pár.
int x = 12;
int y = { 23 }; //Legal, no warning
int z = { { 34 } }; //Legal, expect a warning
Struktury, odbory a pole lze inicializovat v jejich deklaracích pomocí seznamu inicializátorů. Pokud nejsou použity označovače, součásti inicializátoru odpovídají prvkům v pořadí, v jakém jsou definovány a uloženy, takže před hodnotou konkrétního prvku musí být poskytnuty všechny předchozí hodnoty. Všechny neurčené prvky jsou nastaveny na nulu (kromě svazků). Pokud zmíníte příliš mnoho inicializačních hodnot, dojde k chybě.
Následující příkaz inicializuje novou instanci struktury s známou jako pi :
struct s {
int x;
float y;
char *z;
};
struct s pi = { 3, 3.1415, "Pi" };
Určené inicializátory
Určené inicializátory umožňují inicializaci členů podle názvu v libovolném pořadí a bez výslovného poskytnutí předchozích hodnot. Následující inicializace je ekvivalentní té předchozí:
struct s pi = { .z = "Pi", .x = 3, .y = 3.1415 };
Použití označení v inicializátoru posune inicializační „kurzor“. V níže uvedeném příkladu, pokud MAX
je větší než 10, budou uprostřed prvky s nulovou hodnotou a
; pokud je menší než 10, některé z hodnot poskytnutých prvních pěti inicializátorů budou přepsány druhými pěti (pokud MAX
je menší než 5, dojde k chybě při kompilaci):
int a[MAX] = { 1, 3, 5, 7, 9, [MAX-5] = 8, 6, 4, 2, 0 };
V C89 byla inicializována unie s jedinou hodnotou aplikovanou na jejího prvního člena. To znamená, že výše definovaný svaz u mohl inicializovat pouze svého člena int x :
union u value = { 3 };
Pomocí určeného inicializátoru nemusí být člen, který má být inicializován, prvním členem:
union u value = { .y = 3.1415 };
Pokud pole má neznámou velikost (tj. Pole bylo neúplného typu ), počet inicializátorů určuje velikost pole a jeho typ se stává úplným:
int x[] = { 0, 1, 2 } ;
Sloučené identifikátory lze použít k zajištění explicitní inicializace, pokud by mohly být nesprávně pochopeny neozdobené seznamy inicializátorů. V níže uvedeném příkladu w
je deklarován jako pole struktur, přičemž každá struktura se skládá z člena a
(pole 3 int
) a člena b
(an int
). Inicializátor nastaví velikost w
na 2 a nastaví hodnoty prvního prvku každého z nich a
:
struct { int a[3], b; } w[] = { [0].a = {1}, [1].a[0] = 2 };
To je ekvivalentní:
struct { int a[3], b; } w[] =
{
{ { 1, 0, 0 }, 0 },
{ { 2, 0, 0 }, 0 }
};
Neexistuje způsob, jak určit opakování inicializátoru ve standardu C.
Složené literály
Je možné vypůjčit si inicializační metodiku ke generování složené struktury a literálů pole:
// pointer created from array literal.
int *ptr = (int[]){ 10, 20, 30, 40 };
// pointer to array.
float (*foo)[3] = &(float[]){ 0.5f, 1.f, -0.5f };
struct s pi = (struct s){ 3, 3.1415, "Pi" };
Složené literály jsou často kombinovány s určenými inicializátory, aby byla deklarace čitelnější:
pi = (struct s){ .z = "Pi", .x = 3, .y = 3.1415 };
Operátoři
Řídicí struktury
C je jazyk volného tvaru .
Styl osvěžení se liší programátor od programátora a může být předmětem debaty. Další podrobnosti viz Styl odsazení .
Složené prohlášení
V položkách v této části lze jakékoli <prohlášení> nahradit složeným příkazem . Složené příkazy mají tvar:
{
<optional-declaration-list>
<optional-statement-list>
}
a používají se jako tělo funkce nebo kdekoli, kde se očekává jediné prohlášení. Seznam deklarací deklaruje proměnné, které mají být použity v tomto oboru , a seznam příkazů jsou akce, které se mají provést. Závorky definují svůj vlastní rozsah a proměnné definované v těchto závorkách budou automaticky uvolněny v uzavíracích závorkách. Deklarace a prohlášení lze libovolně mísit v rámci složeného příkazu (jako v C ++ ).
Prohlášení o výběru
C má dva typy výběrových příkazů : if
příkaz a switch
příkaz .
if
Prohlášení je ve tvaru:
if (<expression>)
<statement1>
else
<statement2>
V if
příkazu, pokud je <expression>
v závorkách nenulové (true), přejde ovládací prvek na <statement1>
. Pokud je else
klauzule přítomna a <expression>
je nula (false), ovládací prvek přejde na <statement2>
. else <statement2>
Část je volitelná, a pokud je přítomen, falešný <expression>
jednoduše za následek přeskakování přes <statement1>
. An else
vždy odpovídá nejbližšímu předchozímu bezkonkurenčnímu if
; V případě potřeby nebo z důvodu srozumitelnosti lze k přepsání použít rovnátka.
Příkaz switch
způsobí přenos ovládacího prvku do jednoho z několika příkazů v závislosti na hodnotě výrazu , který musí mít integrální typ . Substatement řízen přepínačem je obvykle složený. Jakýkoli příkaz v substatementu může být označen jedním nebo více case
popisky, které se skládají z klíčového slova case
následovaného konstantním výrazem a poté dvojtečkou (:). Syntaxe je následující:
switch (<expression>)
{
case <label1> :
<statements 1>
case <label2> :
<statements 2>
break;
default :
<statements 3>
}
Žádné dvě konstanty případu spojené se stejným přepínačem nemusí mít stejnou hodnotu. K default
přepínači může být přidružen maximálně jeden štítek. Pokud se žádný z popisků případu nerovná výrazu v závorkách switch
, přejde ovládací prvek na default
štítek nebo, pokud žádný default
štítek neexistuje , pokračuje se v provádění hned za celou konstrukcí.
Přepínače mohou být vnořené; a case
nebo default
štítek je spojen s nejvnitřnějším, switch
který jej obsahuje. Přepínací příkazy mohou „propadnout“, to znamená, že když jedna sekce případů dokončí své provádění, příkazy budou nadále prováděny směrem dolů, dokud nenastane break;
příkaz. Propad je za určitých okolností užitečný, ale obvykle není žádoucí. V předchozím příkladu, pokud <label2>
je dosaženo, <statements 2>
jsou provedeny příkazy a nic více v závorkách. Pokud <label1>
je však dosaženo, oba <statements 1>
a <statements 2>
jsou provedeny, protože neexistuje žádný break
případ, který by oddělil dva příkazy případu.
Je možné, i když neobvyklé, vkládat switch
popisky do dílčích bloků jiných řídicích struktur. Mezi příklady patří Duffovo zařízení a implementace korutin Simona Tathama v Putty .
Iterační prohlášení
C má tři formy iteračního prohlášení:
do
<statement>
while ( <expression> ) ;
while ( <expression> )
<statement>
for ( <expression> ; <expression> ; <expression> )
<statement>
Ve while
a do
prohlášení, dílčí příkaz proveden opakovaně tak dlouho, dokud hodnota expression
zůstává nenulová (ekvivalentní na hodnotu true). S while
test, včetně všech vedlejších účinků <expression>
, probíhá před každou iterací (provedením <statement>
); s do
, test proběhne po každé iteraci. Příkaz tedy do
vždy provede svůj dílčí příkaz alespoň jednou, zatímco while
dílčí příkaz nemusí být proveden vůbec.
Prohlášení:
for (e1; e2; e3)
s;
je ekvivalentní:
e1;
while (e2)
{
s;
cont:
e3;
}
kromě chování continue;
příkazu (na který ve for
smyčce skočí e3
místo e2
). Pokud e2
je prázdné, bude muset být nahrazeno a 1
.
Kterýkoli ze tří výrazů ve for
smyčce může být vynechán. Chybějící druhý výraz způsobí, že while
test bude vždy nenulový a vytvoří potenciálně nekonečnou smyčku.
Od C99 může mít první výraz formu deklarace, obvykle včetně inicializátoru, například:
for (int i = 0; i < limit; ++i) {
// ...
}
Rozsah deklarace je omezen na rozsah for
smyčky.
Skoková prohlášení
Skokové příkazy přenášejí bezpodmínečně kontrolu. Existují čtyři typy výkazů skok ve složce C: goto
, continue
, break
a return
.
goto
Prohlášení vypadá takto:
goto <identifier> ;
Identifikátor musí být štítek (následované dvojtečkou) umístěný v aktuální funkci. Řídí přenosy do označeného příkazu.
Příkaz se continue
může objevit pouze v rámci příkazu iterace a způsobí, že řízení přejde do části pokračování smyčky nejvnitřnějšího uzavírajícího příkazu iterace. Tedy v rámci každého z prohlášení
while (expression)
{
/* ... */
cont: ;
}
do
{
/* ... */
cont: ;
} while (expression);
for (expr1; expr2; expr3) {
/* ... */
cont: ;
}
a continue
není obsažen ve vnořeném příkazu iterace je stejný jako goto cont
.
Příkaz break
slouží k ukončení for
smyčky, while
smyčky, do
smyčky nebo switch
příkazu. Ovládací prvek přechází do příkazu následujícího po ukončeném příkazu.
Funkce se vrací svému volajícímu return
příkazem. Když return
následuje výraz, hodnota se vrátí volajícímu jako hodnota funkce. Setkání s koncem funkce je ekvivalentní a return
bez výrazu. V takovém případě, pokud je funkce deklarována jako návrat hodnoty a volající se pokusí použít vrácenou hodnotu, výsledek není definován.
Uložení adresy štítku
GCC rozšiřuje jazyk C o unární &&
operátor, který vrací adresu štítku. Tato adresa může být uložena v void*
proměnném typu a může být použita později v goto
instrukci. Například následující tiskne "hi "
v nekonečné smyčce:
void *ptr = &&J1;
J1: printf("hi ");
goto *ptr;
Tuto funkci lze použít k implementaci skokové tabulky .
Funkce
Syntax
Definice funkce AC se skládá z návratového typu ( void
pokud není vrácena žádná hodnota), jedinečného názvu, seznamu parametrů v závorkách a různých příkazů:
<return-type> functionName( <parameter-list> )
{
<statements>
return <expression of type return-type>;
}
Funkce s nevratným void
typem by měla obsahovat alespoň jeden return
příkaz. Parametry jsou dány <parameter-list>
, čárkami oddělený prohlášení parametrů, každá položka v seznamu, že je typ dat následuje identifikátor: <data-type> <variable-identifier>, <data-type> <variable-identifier>, ...
.
Pokud nejsou k dispozici žádné parametry, <parameter-list>
může být pole ponecháno prázdné nebo volitelně specifikováno jediným slovem void
.
Funkci je možné definovat jako přijímající proměnný počet parametrů poskytnutím ...
klíčového slova jako posledního parametru namísto datového typu a identifikátoru proměnné. Běžně používaná funkce, která to dělá, je standardní funkce knihovny printf
, která má deklaraci:
int printf (const char*, ...);
Manipulaci s těmito parametry lze provést pomocí rutin v záhlaví standardní knihovny <stdarg.h>
.
Ukazatele funkcí
Ukazatel na funkci lze deklarovat následujícím způsobem:
<return-type> (*<function-name>)(<parameter-list>);
Následující program ukazuje použití ukazatele funkce pro výběr mezi sčítáním a odčítáním:
#include <stdio.h>
int (*operation)(int x, int y);
int add(int x, int y)
{
return x + y;
}
int subtract(int x, int y)
{
return x - y;
}
int main(int argc, char* args[])
{
int foo = 1, bar = 1;
operation = add;
printf("%d + %d = %d\n", foo, bar, operation(foo, bar));
operation = subtract;
printf("%d - %d = %d\n", foo, bar, operation(foo, bar));
return 0;
}
Globální struktura
Po předzpracování se program C na nejvyšší úrovni skládá ze sekvence deklarací v rozsahu souboru. Ty mohou být rozděleny do několika samostatných zdrojových souborů, které mohou být kompilovány samostatně; výsledné objektové moduly jsou pak propojeny spolu s implementačními podporovanými běhovými podpůrnými moduly k vytvoření spustitelného obrazu.
Deklarace zavádějí funkce , proměnné a typy . Funkce C jsou podobné podprogramům Fortranu nebo postupům Pascala .
Definice je speciální druh prohlášení. Definice proměnné odkládá úložiště stranou a případně jej inicializuje, definice funkce poskytuje své tělo.
Implementace C poskytující všechny standardní funkce knihovny se nazývá hostovaná implementace . Programy napsané pro hostované implementace jsou vyžadovány k definování speciální funkce main
, která se nazývá , což je první funkce, která se volá při spuštění programu.
Hostované implementace spouštějí spuštění programu vyvoláním main
funkce, která musí být definována podle jednoho z těchto prototypů:
int main() {...}
int main(void) {...}
int main(int argc, char *argv[]) {...}
int main(int argc, char **argv) {...}
První dvě definice jsou ekvivalentní (a obě jsou kompatibilní s C ++). Je pravděpodobně na individuální preferenci, která se použije (současný standard C obsahuje dva příklady main()
a dva main(void)
, ale návrh standardu C ++ používá main()
). Návratová hodnota main
(která by měla být int
) slouží jako stav ukončení vrácený do hostitelského prostředí.
Standard C definuje návratové hodnoty 0
a EXIT_SUCCESS
označuje úspěch a EXIT_FAILURE
selhání. ( EXIT_SUCCESS
a EXIT_FAILURE
jsou definovány v <stdlib.h>
). Jiné návratové hodnoty mají významy definované implementací; například v Linuxu program zabitý signálem poskytuje návratový kód číselné hodnoty signálu plus 128.
Minimálně správný program C se skládá z prázdné main
rutiny, bez argumentů a nedělání ničeho:
int main(void){}
Protože není k dispozici žádný return
příkaz, main
vrátí 0 při ukončení. (Jedná se o speciální funkci zavedenou v C99, která platí pouze pro main
.)
main
Funkce bude obvykle volat další funkce, aby se mohla plnit svou úlohu.
Některé implementace nejsou hostovány, obvykle proto, že nejsou určeny k použití s operačním systémem . Takovým implementacím se ve standardu C říká volně stojící . Volně stojící implementace může volně specifikovat, jak zpracovává spuštění programu; zejména nemusí vyžadovat, aby program definoval main
funkci.
Funkce mohou být napsány programátorem nebo poskytovány stávajícími knihovnami. Rozhraní pro tyto jsou obvykle deklarována zahrnutím hlavičkových souborů - se #include
směrnicí pro předzpracování - a objekty knihovny jsou propojeny s konečným spustitelným obrazem. Některé funkce knihovny, jako například printf
, jsou definovány standardem C; tyto funkce se označují jako standardní funkce knihovny .
Funkce může volajícímu vrátit hodnotu (obvykle jinou funkci C nebo hostitelské prostředí pro funkci main
). Výše printf
uvedená funkce vrací, kolik znaků bylo vytištěno, ale tato hodnota je často ignorována.
Předávání argumentů
V jazyce C jsou argumenty předávány funkcím podle hodnoty, zatímco jiné jazyky mohou předávat proměnné podle odkazu . To znamená, že přijímací funkce získá kopie hodnot a nemá žádný přímý způsob, jak změnit původní proměnné. Aby funkce změnila proměnnou předanou z jiné funkce, musí volající předat její adresu ( ukazatel na ni), což lze následně v přijímací funkci zrušit. Další informace najdete v části Ukazatele .
void incInt(int *y)
{
(*y)++; // Increase the value of 'x', in 'main' below, by one
}
int main(void)
{
int x = 0;
incInt(&x); // pass a reference to the var 'x'
return 0;
}
Funkce scanf funguje stejným způsobem:
int x;
scanf("%d", &x);
Aby bylo možné předat upravitelný ukazatel funkci (například pro účely vrácení přiděleného pole volajícímu kódu), musíte předat ukazatel tomuto ukazateli: jeho adresu.
#include <stdio.h>
#include <stdlib.h>
void allocate_array(int ** const a_p, const int A) {
/*
allocate array of A ints
assigning to *a_p alters the 'a' in main()
*/
*a_p = malloc(sizeof(int) * A);
}
int main(void) {
int * a; /* create a pointer to one or more ints, this will be the array */
/* pass the address of 'a' */
allocate_array(&a, 42);
/* 'a' is now an array of length 42 and can be manipulated and freed here */
free(a);
return 0;
}
Parametr int **a_p
je ukazatel na ukazatel na an int
, což je adresa ukazatele p
definovaného v hlavní funkci v tomto případě.
Parametry pole
Funkční parametry typu pole se na první pohled mohou zdát jako výjimka z pravidla C pro hodnotu pass-by. Následující program vytiskne 2, ne 1:
#include <stdio.h>
void setArray(int array[], int index, int value)
{
array[index] = value;
}
int main(void)
{
int a[1] = {1};
setArray(a, 0, 2);
printf ("a[0]=%d\n", a[0]);
return 0;
}
Toto chování má však jiný důvod. Ve skutečnosti je parametr funkce deklarovaný s typem pole považován za jeden deklarovaný jako ukazatel. To znamená, že předchozí prohlášení o setArray
je ekvivalentní následujícímu:
void setArray(int *array, int index, int value)
Pravidla C pro použití polí ve výrazech současně způsobí, že hodnota a
volání setArray
bude převedena na ukazatel na první prvek pole a
. Ve skutečnosti je to tedy stále příklad předávané hodnoty, s výhradou, že se jedná o adresu prvního prvku pole předávaného podle hodnoty, nikoli o obsah pole.
Smíšený
Vyhrazená klíčová slova
Následující slova jsou vyhrazena a nesmí být použita jako identifikátory:
|
|
|
|
Implementace mohou rezervovat jiná klíčová slova, například asm
, ačkoli implementace obvykle poskytují nestandardní klíčová slova, která začínají jedním nebo dvěma podtržítky.
Citlivost na malá a velká písmena
Identifikátory C rozlišují velká a malá písmena (např foo
. FOO
, A Foo
jsou názvy různých objektů). Některé linkery mohou mapovat externí identifikátory na jeden případ, i když to je u většiny moderních linkerů neobvyklé.
Komentáře
Text začínající tokenem /*
je považován za komentář a je ignorován. Komentář končí v dalším */
; může se vyskytovat ve výrazech a může zahrnovat více řádků. Náhodné vynechání terminátoru komentářů je problematické v tom, že k ukončení původního komentáře bude použit správně vytvořený terminátor komentáře dalšího komentáře a veškerý kód mezi komentáři bude považován za komentář. Komentáře ve stylu C se vnořují; to znamená, že neúmyslné vložení komentáře do komentáře má nezamýšlené výsledky:
/*
This line will be ignored.
/*
A compiler warning may be produced here. These lines will also be ignored.
The comment opening token above did not start a new comment,
and the comment closing token below will close the comment begun on line 1.
*/
This line and the line below it will not be ignored. Both will likely produce compile errors.
*/
Komentáře řádků ve stylu C ++ začínají //
a končí na konci řádku. Tento styl komentáře vznikl v BCPL a stal se platnou syntaxí C v C99 ; není k dispozici v původním K&R C ani v ANSI C :
// this line will be ignored by the compiler
/* these lines
will be ignored
by the compiler */
x = *p/*q; /* this comment starts after the 'p' */
Argumenty příkazového řádku
Tyto parametry uvedené na příkazovém řádku jsou předány programu C se dvěma předdefinovaných proměnných - počet argumentů příkazového řádku v argc
a jednotlivé argumenty jako znakové řetězce v ukazatel pole argv
. Takže příkaz:
myFilt p1 p2 p3
výsledkem je něco jako:
m | y | F | já | l | t | \ 0 | p | 1 | \ 0 | p | 2 | \ 0 | p | 3 | \ 0 |
argv [0] | argv [1] | argv [2] | argv [3] |
Zatímco jednotlivé řetězce jsou pole souvislých znaků, neexistuje žádná záruka, že jsou řetězce uloženy jako souvislá skupina.
Název programu, argv[0]
může být užitečný při tisku diagnostických zpráv nebo při vytváření jedné binární služby pro více účelů. Jednotlivé hodnoty parametrů mohou být přístupné s argv[1]
, argv[2]
a argv[3]
, jak je uvedeno v následující program:
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("argc\t= %d\n", argc);
for (int i = 0; i < argc; i++)
printf("argv[%i]\t= %s\n", i, argv[i]);
}
Pořadí hodnocení
V každém přiměřeně složitého výrazu, vzniká možnost volby, pokud jde o pořadí, v jakém se vyhodnotit části výrazu: může být hodnocena v pořadí , , , , nebo v pořadí , , , . Formálně může vyhovující kompilátor C vyhodnotit výrazy v libovolném pořadí mezi body sekvence (to umožňuje kompilátoru provést určitou optimalizaci). Sekvenční body jsou definovány:
(1+1)+(3+3)
(1+1)+(3+3)
(2)+(3+3)
(2)+(6)
(8)
(1+1)+(3+3)
(1+1)+(6)
(2)+(6)
(8)
- Výkaz končí středníkem.
- Operátor sekvenování : čárka. Čárky, které oddělují argumenty funkce, však nejsou body sekvence.
- Tyto subjekty zkratové : logické a (
&&
, který může být čten a poté ) a logické nebo (||
, který může být čten jinak ). - Ternární operátor (
?:
): Tento operátor vyhodnocuje první sub-výraz první, a pak jeho druhý nebo třetí (nikdy oba) na základě hodnoty prvního. - Vstup do a ukončení volání funkce (ale ne mezi vyhodnocením argumentů).
Výrazy před bodem posloupnosti jsou vždy vyhodnoceny před výrazy za bodem posloupnosti. V případě vyhodnocení zkratu nemusí být druhý výraz vyhodnocen v závislosti na výsledku prvního výrazu. Například ve výrazu , pokud je první argument vyhodnocen jako nenulový (true), výsledek celého výrazu nemůže být nic jiného než true, takže není vyhodnocen. Podobně ve výrazu , pokud je první argument vyhodnocen na nulu (false), výsledek celého výrazu nemůže být nic jiného než false, takže není vyhodnocen.
(a() || b())
b()
(a() && b())
b()
Argumenty pro volání funkce lze vyhodnotit v libovolném pořadí, pokud jsou všechny vyhodnoceny v době, kdy je funkce zadána. Následující výraz má například nedefinované chování:
printf("%s %s\n", argv[i = 0], argv[++i]);
Nedefinované chování
Aspekt standardu C (není jedinečný pro C) spočívá v tom, že chování určitého kódu je údajně „nedefinováno“. V praxi to znamená, že program vytvořený z tohoto kódu může dělat cokoli, od práce, jak programátor zamýšlel, až po zhroucení při každém spuštění.
Následující kód například produkuje nedefinované chování, protože proměnná b je změněna více než jednou bez zasahujícího bodu sekvence:
#include <stdio.h>
int main(void)
{
int b = 1;
int a = b++ + b++;
printf("%d\n", a);
}
Protože mezi úpravami b v " b ++ + b ++" neexistuje žádný sekvenční bod , je možné provést vyhodnocovací kroky ve více než jednom pořadí, což má za následek nejednoznačné prohlášení. To lze opravit přepsáním kódu pro vložení bodu posloupnosti za účelem vynucení jednoznačného chování, například:
a = b++;
a += b++;
Viz také
- Bloky (rozšíření jazyka C)
- Programovací jazyk C.
- Typy proměnných C a deklarace
- Operátory v C a C ++
- Standardní knihovna C.
- Seznam programovacích jazyků rodiny C (jazyky ovlivněné C)
Reference
- Všeobecné
- Kernighan, Brian W .; Ritchie, Dennis M. (1988). Programovací jazyk C (2. vydání). Upper Saddle River, New Jersey: Prentice Hall PTR. ISBN 0-13-110370-9.
- Americká národní norma pro informační systémy - Programovací jazyk - C - ANSI X3.159-1989