C (programovací jazyk)

Aktuální verze stránky ještě nebyla zkontrolována zkušenými přispěvateli a může se výrazně lišit od verze recenzované 4. srpna 2022; kontroly vyžadují 3 úpravy .
C
Jazyková třída procesní
Typ provedení sestaven
Objevil se v 1972
Autor Dennis Ritchie
Vývojář Bell Labs , Dennis Ritchie [1] , US National Standards Institute , ISO a Ken Thompson
Přípona souboru .c— pro kódové soubory, .h— pro hlavičkové soubory
Uvolnění ISO/IEC 9899:2018 ( 5. července 2018 )
Typový systém staticky slabé
Hlavní implementace GCC , Clang , TCC , Turbo C , Watcom , Oracle Solaris Studio C, Pelles C
Dialekty "K&R" C ( 1978 )
ANSI C ( 1989 )
C99 ( 1999 )
C11 ( 2011 )
Byl ovlivněn BCPL , B
ovlivnil C++ , Objective-C , C# , Java , Nim
OS Microsoft Windows a operační systém podobný Unixu
 Mediální soubory na Wikimedia Commons
ISO/IEC 9899
Informační technologie — Programovací jazyky — C
Vydavatel Mezinárodní organizace pro standardizaci (ISO)
webová stránka www.iso.org
výbor (vývojář) ISO/IEC JTC 1/SC 22
webové stránky výboru Programovací jazyky, jejich prostředí a systémová softwarová rozhraní
ISS (ICS) 35,060
Aktuální vydání ISO/IEC 9899:2018
Předchozí vydání ISO/IEC 9899:1990/COR2:1996
ISO/IEC 9899:1999/COR3:2007
ISO/IEC 9899:2011/COR1:2012

C (z latinského písmene C , anglický jazyk ) je univerzální kompilovaný staticky typovaný programovací jazyk vyvinutý v letech 1969-1973 zaměstnancem Bell Labs Dennisem Ritchiem jako vývoj jazyka Bee . Původně byl vyvinut pro implementaci operačního systému UNIX , ale od té doby byl portován na mnoho dalších platforem. Svým designem jazyk úzce mapuje typické strojové instrukce a našel použití v projektech, které byly původní v jazyce symbolických instrukcí , včetně operačních systémů a různého aplikačního softwaru pro různá zařízení od superpočítačů po vestavěné systémy . Programovací jazyk C měl významný dopad na vývoj softwarového průmyslu a jeho syntaxe se stala základem pro takové programovací jazyky jako C++ , C# , Java a Objective-C .

Historie

Programovací jazyk C byl vyvinut v letech 19691973 v Bellových laboratořích a do roku 1973 byla většina jádra UNIX , původně napsaného v assembleru PDP-11 /20, přepsána do tohoto jazyka. Název jazyka se stal logickým pokračováním starého jazyka „ Bi[a] , jehož mnohé rysy byly vzaty jako základ.

Jak se jazyk vyvíjel, byl nejprve standardizován jako ANSI C a poté byl tento standard přijat mezinárodním normalizačním výborem ISO jako ISO C, také známý jako C90. Standard C99 přidal do jazyka nové funkce, jako jsou pole proměnné délky a inline funkce. A ve standardu C11 byla do jazyka přidána implementace proudů a podpora atomových typů. Od té doby se však jazyk vyvíjel pomalu a pouze opravy chyb ze standardu C11 se dostaly do standardu C18.

Obecné informace

Jazyk C byl navržen jako systémový programovací jazyk, pro který lze vytvořit jednoprůchodový kompilátor . Standardní knihovna je také malá. V důsledku těchto faktorů se kompilátory vyvíjejí relativně snadno [2] . Proto je tento jazyk dostupný na různých platformách. Kromě toho je jazyk navzdory své nízkoúrovňové povaze zaměřen na přenositelnost. Programy vyhovující jazykovému standardu mohou být kompilovány pro různé počítačové architektury.

Cílem jazyka bylo usnadnit psaní velkých programů s minimálními chybami ve srovnání s assemblerem, dodržovat zásady procedurálního programování , ale vyhnout se čemukoli, co by zavádělo další režii specifické pro jazyky na vysoké úrovni.

Hlavní vlastnosti C:

Zároveň C postrádá:

Některé z chybějících funkcí lze simulovat vestavěnými nástroji (například koroutiny lze simulovat pomocí funkcí setjmpalongjmp ), některé jsou přidány pomocí knihoven třetích stran (například pro podporu multitaskingu a síťových funkcí můžete použít knihovny pthreads , sockety a podobně, existují knihovny pro podporu automatického garbage collection [3] ), část je implementována v některých kompilátorech jako jazyková rozšíření (například vnořené funkce v GCC ). Existuje poněkud těžkopádná, ale docela funkční technika, která umožňuje implementovat mechanismy OOP v C [4] , založené na skutečném polymorfismu ukazatelů v C a podpoře ukazatelů na funkce v tomto jazyce. OOP mechanismy založené na tomto modelu jsou implementovány v knihovně GLib a jsou aktivně využívány v rámci GTK+ . GLib poskytuje základní třídu GObject, schopnost dědit z jediné třídy [5] a implementovat více rozhraní [6] .

Po svém zavedení byl jazyk dobře přijat, protože umožňoval rychlé vytváření kompilátorů pro nové platformy a také umožňoval programátorům být poměrně přesní v tom, jak byly jejich programy prováděny. Díky své blízkosti k nízkoúrovňovým jazykům běžely programy C efektivněji než programy napsané v mnoha jiných vysokoúrovňových jazycích a pouze ručně optimalizovaný kód assembleru mohl běžet ještě rychleji, protože poskytoval plnou kontrolu nad strojem. Dosavadní vývoj kompilátorů a komplikace procesorů vedly k tomu, že ručně psaný montážní kód (snad s výjimkou velmi krátkých programů) prakticky nemá žádnou výhodu oproti kódu generovanému kompilátorem, zatímco C je i nadále jedním z nejvíce efektivní jazyky na vysoké úrovni.

Syntaxe a sémantika

Tokeny

Jazyková abeceda

Jazyk používá všechny znaky latinské abecedy , čísla a některé speciální znaky [7] .

Složení abecedy [7]
znaky latinské abecedy

A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z
a, b, c, d, e, f, g, h, i, j, k, l, m, n, , , o_ _ _ _ _ _ _ _ _pqrstuvwxyz

Čísla 0, 1, 2, 3, 4, 5, 6, 7, 8_9
Speciální symboly , (čárka) , ;, . (tečka) , +, -, *, ^, & (ampersand) , =, ~ (vlnovka) , !, /, <, >, (, ), {, }, [, ], |, ( apostrof)% , (uvozovky) , (dvojtečka) , ( podtržítko ) ) , ,?' " : _ \#

Tokeny jsou tvořeny z platných znaků -  předdefinovaných konstant , identifikátorů a operačních znaků . Lexémy jsou zase součástí výrazů ; a příkazy a operátory se skládají z výrazů .

Když je program přeložen do C, jsou z kódu programu extrahovány lexémy o maximální délce obsahující platné znaky. Pokud program obsahuje neplatný znak, pak lexikální analyzátor (nebo kompilátor) vygeneruje chybu a překlad programu nebude možný.

Symbol #nemůže být součástí žádného tokenu a používá se v preprocesoru .

Identifikátory

Platný identifikátor  je slovo, které může obsahovat latinské znaky, čísla a podtržítka [8] . Identifikátory jsou dány operátorům, konstantám, proměnným, typům a funkcím.

Identifikátory klíčových slov a vestavěné identifikátory nelze použít jako identifikátory objektů programu. Existují také vyhrazené identifikátory, u kterých kompilátor nebude dávat chyby, ale které se v budoucnu mohou stát klíčovými slovy, což povede k nekompatibilitě.

Existuje pouze jeden vestavěný identifikátor - __func__, který je definován jako konstantní řetězec implicitně deklarovaný v každé funkci a obsahující její název [8] .

Doslovné konstanty

Speciálně formátované literály v C se nazývají konstanty. Doslovné konstanty mohou být celočíselné, reálné, znakové [9] a řetězcové [10] .

Celá čísla jsou standardně nastavena v desítkové soustavě . Pokud je zadán prefix 0x, pak je v šestnáctkové soustavě . Předpona 0 znamená, že číslo je v osmičkové soustavě . Přípona určuje minimální velikost konstantního typu a také určuje, zda je číslo podepsané nebo nepodepsané. Výsledný typ je brán jako nejmenší možný typ, ve kterém lze danou konstantu reprezentovat [11] .

Pořadí přiřazení datových typů celočíselným konstantám podle jejich hodnoty [11]
Přípona Pro desítkové Pro osmičkovou a šestnáctkovou soustavu
Ne int

long

long long

int

unsigned int

long

unsigned long

long long

unsigned long long

uneboU unsigned int

unsigned long

unsigned long long

unsigned int

unsigned long

unsigned long long

lneboL long

long long

long

unsigned long

long long

unsigned long long

unebo Uspolečně s lneboL unsigned long

unsigned long long

unsigned long

unsigned long long

llneboLL long long long long

unsigned long long

unebo Uspolečně s llneboLL unsigned long long unsigned long long
Příklady zápisu reálného čísla 1.5
Desetinný

formát

S exponentem Hexadecimální

formát

1.5 1.5e+0 0x1.8p+0
15e-1 0x3.0p-1
0.15e+1 0x0.cp+1

Konstanty reálného čísla jsou standardně typu double. Při zadávání přípony fje typ přiřazen konstantě floata při zadávání lnebo L - long double. Konstanta bude považována za skutečnou, pokud obsahuje tečku nebo písmeno, pnebo Pv případě hexadecimálního zápisu s předponou 0x. Desetinný zápis může obsahovat exponent za písmeny enebo E. V případě hexadecimálního zápisu se exponent uvádí za písmeny pnebo Pje povinný, což odlišuje reálné hexadecimální konstanty od celých čísel. V šestnáctkové soustavě je exponent mocninou 2 [12] .

Znakové konstanty jsou uzavřeny v jednoduchých uvozovkách ( ') a předpona určuje jak datový typ znakové konstanty, tak kódování, ve kterém bude znak reprezentován. V C je znaková konstanta bez prefixu typu int[13] , na rozdíl od C++ , kde je znaková konstanta char.

Předpony znakových konstant [13]
Předpona Datový typ Kódování
Ne int ASCII
u char16_t 16bitové vícebajtové kódování řetězce
U char32_t 32bitové vícebajtové kódování řetězce
L wchar_t Široké kódování řetězce

Řetězcové literály jsou uzavřeny do dvojitých uvozovek a mohou mít předponu typu dat a kódování řetězce. Řetězcové literály jsou obyčejná pole. Ve vícebajtových kódováních, jako je UTF-8 , však může jeden znak zabírat více než jeden prvek pole. Ve skutečnosti jsou řetězcové literály const [14] , ale na rozdíl od C++ jejich datové typy neobsahují modifikátor const.

Konstantní předpony řetězce [15]
Předpona Datový typ Kódování
Ne char * ASCII nebo vícebajtové kódování
u8 char * UTF-8
u char16_t * 16bitové vícebajtové kódování
U char32_t * 32bitové vícebajtové kódování
L wchar_t * Široké kódování řetězce

Několik po sobě jdoucích řetězcových konstant oddělených mezerami nebo novými řádky je při kompilaci zkombinováno do jednoho řetězce, který se často používá ke stylizaci kódu řetězce oddělením částí řetězcové konstanty na různých řádcích, aby se zlepšila čitelnost [16] .

Pojmenované konstanty Porovnání metod nastavení konstant [17]
Makro #define BUFFER_SIZE 1024
Anonymní
výčet
enum { BUFFER_SIZE = 1024 };
Proměnná
jako
konstanta
const int velikost_bufferu = 1024 ; externí konst int velikost_bufferu ;

V jazyce C je pro definování konstant obvyklé používat definice maker deklarované pomocí direktivy preprocesoru [17] : #define

#define název konstanty [ hodnota ]

Takto zavedená konstanta bude ve svém rozsahu účinná od okamžiku nastavení konstanty až do konce programového kódu, nebo dokud nebude účinek dané konstanty zrušen direktivou #undef:

#undef konstantní jméno

Stejně jako u každého makra je u pojmenované konstanty hodnota konstanty automaticky nahrazena v kódu programu, kdykoli je použit název konstanty. Proto při deklarování celých nebo reálných čísel uvnitř makra může být nutné explicitně specifikovat datový typ pomocí příslušné doslovné přípony, jinak bude číslo implicitně nastaveno na typ intv případě celého čísla nebo typ double v případě nemovitý.

Pro celá čísla existuje další způsob, jak vytvořit pojmenované konstanty - pomocí operátorových výčtů enum[17] . Tato metoda je však vhodná pouze pro typy menší nebo rovné type a nepoužívá se ve standardní knihovně [18] . int

Je také možné vytvořit konstanty jako proměnné s kvalifikátorem const, ale na rozdíl od ostatních dvou metod takové konstanty spotřebovávají paměť, lze na ně ukázat a nelze je použít v době kompilace [17] :

  • určit velikost bitových polí,
  • nastavit velikost pole (kromě polí s proměnnou délkou),
  • nastavit hodnotu prvku výčtu,
  • jako hodnotu operátora case.
Klíčová slova

Klíčová slova  jsou identifikátory určené k provedení konkrétního úkolu ve fázi kompilace nebo jako rady a pokyny pro kompilátor.

Klíčová slova jazyka C [19]
Klíčová slova Účel Standard
sizeof Získání velikosti objektu v době kompilace C89
typedef Zadání alternativního názvu pro typ
auto,register Kompilátor poradí, kde jsou proměnné uloženy
extern Řekněte kompilátoru, aby hledal objekt mimo aktuální soubor
static Deklarace statického objektu
void Žádná značka hodnoty; v ukazatelích znamená libovolná data
char... short_ int_long Celočíselné typy a jejich modifikátory velikosti
signed,unsigned Modifikátory typu Integer, které je definují jako podepsané nebo bez znaménka
float,double Reálné datové typy
const Modifikátor datového typu, který kompilátoru sděluje, že proměnné tohoto typu jsou pouze pro čtení
volatile Pokyn kompilátoru, aby změnil hodnotu proměnné zvenčí
struct Datový typ určený jako struktura se sadou polí
enum Datový typ, který ukládá jednu ze sady celočíselných hodnot
union Datový typ, který může ukládat data v reprezentacích různých datových typů
do.. for_while Příkazy smyčky
if,else Podmíněný operátor
switch.. case_default Operátor výběru podle celočíselného parametru
break,continue Příkazy přerušení smyčky
goto Operátor nepodmíněného skoku
return Návrat z funkce
inline Inline deklarace funkce C99 [20]
restrict Deklarování ukazatele, který odkazuje na blok paměti, na který neodkazuje žádný jiný ukazatel
_Bool[b] booleovský datový typ
_Complex[c] ,_Imaginary [d] Typy používané pro výpočty komplexních čísel
_Atomic Modifikátor typu, díky kterému je atomický C11
_Alignas[E] Explicitní určení zarovnání bajtů pro datový typ
_Alignof[F] Získání zarovnání pro daný datový typ v době kompilace
_Generic Výběr jedné ze sady hodnot v době kompilace na základě řízeného datového typu
_Noreturn[G] Oznámení kompilátoru, že funkce nemůže normálně skončit (tj. pomocí return)
_Static_assert[h] Zadání výrazů ke kontrole při kompilaci
_Thread_local[i] Deklarování lokální proměnné vlákna
Vyhrazené identifikátory

Jazyková norma kromě klíčových slov definuje vyhrazené identifikátory, jejichž použití může vést k nekompatibilitě s budoucími verzemi normy. Všechna slova kromě klíčových slov, která začínají podtržítkem ( _) následovaným buď velkým písmenem ( A- Z) nebo jiným podtržítkem [21] , jsou vyhrazena . Ve standardech C99 a C11 byly některé z těchto identifikátorů použity pro klíčová slova nového jazyka.

V rozsahu souboru je vyhrazeno použití jakýchkoli názvů začínajících podtržítkem ( _) [21] , to znamená, že je povoleno pojmenovávat typy, konstanty a proměnné deklarované v bloku instrukcí, například uvnitř funkcí, s podtržítkem.

Vyhrazenými identifikátory jsou také všechna makra standardní knihovny a jména z ní spojená ve fázi propojení [21] .

Použití vyhrazených identifikátorů v programech je standardem definováno jako nedefinované chování . Pokus o zrušení jakéhokoli standardního makra prostřednictvím #undeftaké povede k nedefinovanému chování [21] .

Komentáře

Text programu v jazyce C může obsahovat fragmenty, které nejsou součástí komentářů ke kódu programu . Komentáře jsou v textu programu označeny zvláštním způsobem a při kompilaci se přeskakují.

Zpočátku byly ve standardu C89 k dispozici vložené komentáře, které bylo možné umístit mezi sekvence znaků /*a */. V tomto případě není možné vnořit jeden komentář do druhého, protože první zaznamenaná sekvence */komentář ukončí a text bezprostředně následující za zápisem */bude kompilátorem vnímán jako zdrojový kód programu.

Další standard, C99 , zavedl ještě další způsob označování komentářů: za komentář je považován text začínající sekvencí znaků //a končící na konci řádku [20] .

Komentáře se často používají k vlastní dokumentaci zdrojového kódu, vysvětlují složité části, popisují účel určitých souborů a popisují pravidla pro používání a práci s určitými funkcemi, maker, datovými typy a proměnnými. Existují postprocesory, které dokážou převést speciálně formátované komentáře na dokumentaci. Mezi takovými postprocesory s jazykem C může fungovat dokumentační systém Doxygen .

Operátoři

Operátory používané ve výrazech jsou nějaká operace, která se provádí s operandy a která vrací vypočítanou hodnotu – výsledek operace. Operand může být konstanta, proměnná, výraz nebo volání funkce. Operátor může být speciální znak, sada speciálních znaků nebo speciální slovo. Operátory se rozlišují podle počtu zúčastněných operandů, konkrétně rozlišují mezi unárními operátory, binárními operátory a ternárními operátory.

Unární operátory

Unární operátory provádějí operaci s jedním argumentem a mají následující formát operace:

[ operátor ] [ operand ]

Operace inkrementace a dekrementace postfixu mají opačný formát:

[ operand ] [ operátor ] Unární C operátory [22]
+ unární plus ~ Převzetí návratového kódu & Získání adresy ++ Přírůstek předpony nebo přípony sizeof Získání počtu bajtů obsazených objektem v paměti; lze použít jako provoz i jako operátor
- unární mínus ! logická negace * Dereferencování ukazatele -- Snížení předpony nebo přípony _Alignof Získání zarovnání pro daný typ dat

Operátory inkrementace a dekrementace na rozdíl od ostatních unárních operátorů mění hodnotu svého operandu. Operátor prefixu nejprve hodnotu upraví a poté ji vrátí. Postfix nejprve vrátí hodnotu a teprve poté ji změní.

Binární operátory

Binární operátory jsou umístěny mezi dvěma argumenty a provádějí s nimi operaci:

[ operand ] [ operátor ] [ operand ] Základní binární operátory [23]
+ Přidání % Vezmeme zbytek divize << Bitový levý Shift > Více == Rovná se
- Odčítání & Bitové AND >> Bitový posun doprava < Méně != Ne rovné
* Násobení | Bitově NEBO && logické AND >= Větší nebo rovno
/ Divize ^ Bitový XOR || Logické NEBO <= Menší nebo rovno

Binární operátory v C také zahrnují levé operátory přiřazení, které provádějí operaci s levým a pravým argumentem a vkládají výsledek do levého argumentu.

Binární operátory s levým přiřazením [24]
= Přiřazení hodnoty pravého argumentu doleva %= Zbytek po dělení levého operandu pravým ^= Bitové XOR pravého operandu na levý operand
+= Přidání k levému operandu k pravému /= Dělení levého operandu pravým <<= Bitový posun levého operandu doleva o počet bitů daný pravým operandem
-= Odečítání od levého operandu od pravého &= Bitový AND pravý operand doleva >>= Bitový posun levého operandu doprava o počet bitů určený pravým operandem
*= Násobení levého operandu pravým |= Bitové OR pravého operandu doleva
Ternární operátory

V C je pouze jeden ternární operátor, zkrácený podmíněný operátor, který má následující tvar:

[ podmínka ] ?[ výraz1 ] :[ výraz2 ]

Zkrácený podmíněný operátor má tři operandy:

  • [ podmínka ] - logická podmínka, u které se kontroluje pravdivost,
  • [ výraz1 ] - výraz, jehož hodnota je vrácena jako výsledek operace, pokud je podmínka pravdivá;
  • [ výraz2 ] je výraz, jehož hodnota je vrácena jako výsledek operace, pokud je podmínka nepravdivá.

Operátor je v tomto případě kombinací znaků ?a :.

Výrazy

Výraz je uspořádaná množina operací s konstantami, proměnnými a funkcemi. Výrazy obsahují operace skládající se z operandů a operátorů . Pořadí, ve kterém jsou operace prováděny, závisí na formě záznamu a na prioritě operací. Každý výraz má hodnotu  – výsledek provedení všech operací obsažených ve výrazu. Během vyhodnocování výrazu se v závislosti na operacích mohou měnit hodnoty proměnných a lze také provádět funkce, pokud jsou ve výrazu přítomna jejich volání.

Mezi výrazy se rozlišuje třída levých přípustných výrazů  - výrazů, které mohou být přítomny vlevo od přiřazovacího znaku.

Priorita provádění operací

Priorita operací je definována standardem a určuje pořadí, ve kterém budou operace prováděny. Operace v C se provádějí podle tabulky priorit níže [25] [26] .

Priorita žetony Úkon Třída Asociativnost
jeden a[index] Odkazování podle indexu postfix zleva doprava →
f(argumenty) Volání funkce
. Přístup do terénu
-> Přístup do terénu pomocí ukazatele
++ -- Pozitivní a negativní přírůstek
(zadejte název ) {inicializátoru} Složený literál (C99)
(zadejte název ) {inicializátoru,}
2 ++ -- Přírůstky kladné a záporné předpony unární ← zprava doleva
sizeof Získání velikosti
_Alignof[F] Získat zarovnání ( C11 )
~ Bitově NE
! Logické NE
- + Indikace znaménka (mínus nebo plus)
& Získání adresy
* Odkaz na ukazatel (dereference)
(název typu) Typové odlévání
3 * / % Násobení, dělení a zbytek binární zleva doprava →
čtyři + - Sčítání a odčítání
5 << >> Shift doleva a doprava
6 < > <= >= Srovnávací operace
7 == != Kontrola rovnosti nebo nerovnosti
osm & Bitové AND
9 ^ Bitový XOR
deset | Bitově NEBO
jedenáct && logické AND
12 || Logické NEBO
13 ? : Stav trojice ← zprava doleva
čtrnáct = Přiřazení hodnoty binární
+= -= *= /= %= <<= >>= &= ^= |= Operace pro změnu hodnoty vlevo
patnáct , Sekvenční výpočty zleva doprava →

Priority operátorů v C se ne vždy ospravedlňují a někdy vedou k intuitivně obtížně předvídatelným výsledkům. Například protože unární operátory mají asociativitu zprava doleva, *p++výsledkem vyhodnocení výrazu bude přírůstek ukazatele následovaný dereference ( *(p++)), spíše než přírůstek ukazatele ( (*p)++). Proto se v případě obtížně srozumitelných situací doporučuje výrazy explicitně seskupovat pomocí závorek [26] .

Další důležitou vlastností jazyka C je, že vyhodnocení hodnot argumentů předávaných volání funkce není sekvenční [27] , to znamená, že čárka oddělující argumenty neodpovídá sekvenčnímu vyhodnocení z tabulky priorit. V následujícím příkladu mohou být volání funkcí zadaná jako argumenty jiné funkci v libovolném pořadí:

int x ; x = vypočítat ( get_arg1 (), get_arg2 ()); // nejprve zavolejte get_arg2().

Také se nemůžete spoléhat na prioritu operací v případě vedlejších efektů , které se objeví během vyhodnocování výrazu, protože to povede k nedefinovanému chování [27] .

Sekvenční body a vedlejší efekty

Dodatek C jazykového standardu definuje sadu sekvenčních bodů , u kterých je zaručeno, že nebudou mít trvalé vedlejší účinky z výpočtů. To znamená, že sekvenční bod je fází výpočtů, která odděluje hodnocení výrazů mezi sebou tak, že výpočty, které nastaly před sekvenčním bodem, včetně vedlejších efektů, již skončily a za sekvenčním bodem ještě nezačaly [28 ] . Vedlejším efektem může být změna hodnoty proměnné během vyhodnocování výrazu. Změna hodnoty zahrnuté ve výpočtu spolu s vedlejším efektem změny stejné hodnoty na další bod sekvence povede k nedefinovanému chování. Totéž se stane, pokud se do výpočtu zapojí dvě nebo více bočních změn stejné hodnoty [27] .

Sekvenční body definované standardem [27]
Bod na trase Událost před Událost po
Volání funkce Výpočet ukazatele na funkci a jejích argumentů Volání funkce
Logické operátory AND ( &&), OR ( ||) a sekvenční výpočet ( ,) Výpočet prvního operandu Výpočet druhého operandu
Operátor těsnopisu ( ?:) Výpočet operandu sloužícího jako podmínka Výpočet 2. nebo 3. operandu
Mezi dvěma úplnými výrazy (ne vnořenými) Jeden úplný výraz Následující úplný výraz
Dokončený kompletní deskriptor
Těsně před návratem z funkce knihovny
Po každé konverzi spojené s formátovaným specifikátorem I/O
Bezprostředně před a bezprostředně po každém volání porovnávací funkce a mezi voláním porovnávací funkce a všemi pohyby provedenými na argumentech předávaných porovnávací funkci

Úplné výrazy jsou [27] :

  • inicializátor, který není součástí složeného literálu;
  • izolovaný výraz;
  • výraz určený jako podmínka podmíněného příkazu ( if) nebo příkazu výběru ( switch);
  • výraz zadaný jako podmínka smyčky whiles předběžnou podmínkou nebo následnou podmínkou;
  • každý z parametrů smyčky for, pokud existuje;
  • operátor výraz return, pokud je zadán.

V následujícím příkladu se proměnná mezi body sekvence třikrát změní, což má za následek nedefinovaný výsledek:

int i = 1 ; // Deskriptor je první bod sekvence, úplný výraz je druhý i += ++ i + 1 ; // Úplný výraz - bod třetí sekvence printf ( "%d \n " , i ); // Může mít výstup buď 4 nebo 5

Další jednoduché příklady nedefinovaného chování, kterému je třeba se vyhnout:

i = i ++ + 1 ; // nedefinované chování i = ++ i + 1 ; // také nedefinované chování printf ( "%d, %d \n " , -- i , ++ i ); // nedefinované chování printf ( "%d, %d \n " , ++ i , ++ i ); // také nedefinované chování printf ( "%d, %d \n " , i = 0 , i = 1 ); // nedefinované chování printf ( "%d, %d \n " , i = 0 , i = 0 ); // také nedefinované chování a [ i ] = i ++ ; // nedefinované chování a [ i ++ ] = i ; // také nedefinované chování

Kontrolní příkazy

Řídicí příkazy jsou navrženy k provádění akcí a řízení toku provádění programu. Několik po sobě jdoucích příkazů tvoří posloupnost příkazů .

Prázdný výpis

Nejjednodušší jazykovou konstrukcí je prázdný výraz nazývaný prázdný příkaz [29] :

;

Prázdný příkaz nedělá nic a může být umístěn kdekoli v programu. Běžně se používá ve smyčkách s chybějícím tělem [30] .

Návod

Instrukce je druh základní akce:

( výraz );

Akce tohoto operátoru je provedení výrazu uvedeného v těle operátoru.

Několik po sobě jdoucích instrukcí tvoří sekvenci instrukcí .

Instrukční blok

Pokyny lze seskupit do speciálních bloků následující formy:

{

( sekvence instrukcí )

},

Blok příkazů, někdy nazývaný také složený příkaz, je ohraničen levou složenou závorkou ( {) na začátku a pravou složenou závorkou ( }) na konci.

Ve funkcích blok příkazu označuje tělo funkce a je součástí definice funkce. Složený příkaz lze také použít v příkazech smyčky, podmínky a volby.

Podmíněné příkazy

V jazyce jsou dva podmíněné operátory, které implementují větvení programu:

  • příkaz ifobsahující test jedné podmínky,
  • a prohlášení switchobsahující více podmínek ke kontrole.

Nejjednodušší forma operátoraif

if(( podmínka ) )( provozovatel ) ( další výpis )

Operátor iffunguje takto:

  • pokud je podmínka v závorkách pravdivá, provede se první příkaz a poté se provede příkaz za příkazem if.
  • není-li splněna podmínka uvedená v závorkách, okamžitě se provede příkaz zadaný za příkazem if.

Zejména následující kód, pokud je splněna zadaná podmínka, neprovede žádnou akci, protože ve skutečnosti se provede prázdný příkaz:

if(( podmínka )) ;

Složitější forma operátoru ifobsahuje klíčové slovo else:

if(( podmínka ) )( provozovatel ) else( alternativní operátor ) ( další výpis )

Pokud zde není splněna podmínka uvedená v závorkách, provede se příkaz zadaný za klíčovým slovem else.

I když standard umožňuje specifikovat příkazy na jednom řádku ifnebo jako elsejeden řádek, je to považováno za špatný styl a snižuje čitelnost kódu. Doporučuje se vždy zadat blok příkazů pomocí složených závorek jako těla [31] .

Příkazy provedení smyčky

Smyčka je část kódu, která obsahuje

  • podmínka provedení smyčky - podmínka, která je neustále kontrolována;
  • a tělo smyčky je jednoduchý nebo složený příkaz, jehož provedení závisí na stavu smyčky.

V souladu s tím existují dva typy cyklů:

  • smyčka s předběžnou podmínkou , kde se nejprve zkontroluje podmínka provedení smyčky, a pokud je podmínka splněna, provede se tělo smyčky;
  • smyčka s postcondition , kde se podmínka pokračování smyčky kontroluje po provedení těla smyčky.

Postpodmíněná smyčka zaručuje, že tělo smyčky bude provedeno alespoň jednou.

Jazyk C poskytuje dvě varianty cyklů s podmínkou: whilea for.

while(podmínka) [ tělo smyčky ] for( příkaz ;podmínky inicializačního bloku [ tělo smyčky ],;)

Smyčka forse také nazývá parametrická, je ekvivalentní následujícímu bloku příkazů:

[ inicializační blok ] while(stav) { [ tělo smyčky ] [ operátor ] }

V normální situaci inicializační blok obsahuje nastavení počáteční hodnoty proměnné, která se nazývá proměnná smyčky, a příkaz, který se provede ihned poté, co tělo smyčky změní hodnoty použité proměnné, podmínka obsahuje porovnání hodnoty použité smyčkové proměnné s nějakou předdefinovanou hodnotou, a jakmile se porovnávání přestane provádět, smyčka se přeruší a začne se provádět programový kód bezprostředně následující za příkazem smyčky.

Pro cyklus do-whileje podmínka uvedena za tělem cyklu:

do[ tělo smyčky ] while( podmínka)

Podmínka cyklu je booleovský výraz. Implicitní přetypování však umožňuje použít aritmetický výraz jako podmínku smyčky. To vám umožní uspořádat takzvanou "nekonečnou smyčku":

while(1);

Totéž lze provést s operátorem for:

for(;;);

V praxi se takové nekonečné smyčky obvykle používají ve spojení s break, gotonebo return, které přerušují smyčku různými způsoby.

Stejně jako u podmíněného příkazu je použití jednořádkového těla bez jeho uzavření do bloku příkazu se složenými závorkami považováno za špatný styl, který snižuje čitelnost kódu [31] .

Operátory nepodmíněného skoku

Nepodmíněné operátory větví umožňují přerušit provádění libovolného bloku výpočtů a přejít na jiné místo v programu v rámci aktuální funkce. Operátory nepodmíněného skoku se obvykle používají ve spojení s podmíněnými operátory.

goto[ štítek ],

Štítek je nějaký identifikátor, který přenáší řízení na operátora, který je v programu označen zadaným štítkem:

[ štítek ] :[ operátor ]

Pokud zadaný štítek není v programu přítomen nebo pokud existuje více příkazů se stejným štítkem, kompilátor ohlásí chybu.

Přenos ovládání je možný pouze v rámci funkce, kde je použit přechodový operátor, proto pomocí operátoru gotonelze přenést ovládání na jinou funkci.

Další příkazy skoku se týkají cyklů a umožňují přerušit provádění těla smyčky:

  • příkaz breakokamžitě přeruší provádění těla smyčky a řízení se přenese na příkaz bezprostředně následující za smyčkou;
  • operátor continuepřeruší provádění aktuální iterace smyčky a zahájí pokus o přechod na další.

Příkaz breakmůže také přerušit činnost příkazu switch, takže uvnitř příkazu switchběžícího ve smyčce příkaz breaknebude moci smyčku přerušit. Zadáno v těle smyčky, přeruší práci nejbližší vnořené smyčky.

Operátor continuelze použít pouze uvnitř operátorů do, whilea for. Pro smyčky whilea do-whileoperátor continuezpůsobí test podmínky smyčky a v případě smyčky for provedení operátoru uvedeného ve 3. parametru smyčky před kontrolou podmínky pro pokračování smyčky.

Funkce return příkaz

Operátor returnpřeruší provádění funkce, ve které je použit. Pokud by funkce neměla vracet hodnotu, použije se volání bez návratové hodnoty:

return;

Pokud funkce musí vrátit hodnotu, je návratová hodnota uvedena za operátorem:

return[ hodnota ];

Pokud jsou po příkazu return v těle funkce nějaké další příkazy, pak tyto příkazy nebudou nikdy provedeny, v takovém případě může kompilátor vydat varování. Za operátorem však returnmohou být uvedeny pokyny pro alternativní ukončení funkce, například omylem, a přechod na tyto operátory lze provést pomocí operátoru gotoza jakýchkoli podmínek .

Proměnné

Při deklaraci proměnné se zadává její typ a název a lze také zadat počáteční hodnotu:

[deskriptor] [jméno];

nebo

[deskriptor] [jméno] =[inicializátor] ;,

kde

  • [descriptor] - typ proměnné a volitelné modifikátory předcházející typu;
  • [name] — název proměnné;
  • [inicializátor] - počáteční hodnota proměnné přiřazená při jejím vytvoření.

Pokud proměnné není přiřazena počáteční hodnota, pak v případě globální proměnné je její hodnota vyplněna nulami a u lokální proměnné bude počáteční hodnota nedefinovaná.

V deskriptoru proměnné můžete určit proměnnou jako globální, ale omezenou na rozsah souboru nebo funkce, pomocí klíčového slova static. Pokud je proměnná deklarována jako globální bez klíčového slova static, lze k ní přistupovat i z jiných souborů, kde je nutné tuto proměnnou deklarovat bez inicializátoru, ale s klíčovým slovem extern. Adresy těchto proměnných se určují v čase spojení .

Funkce

Funkce je nezávislá část programového kódu, kterou lze v programu znovu použít. Funkce mohou přijímat argumenty a mohou vracet hodnoty. Funkce mohou mít při svém provádění i vedlejší efekty : změna globálních proměnných, práce se soubory, interakce s operačním systémem nebo hardwarem [28] .

Chcete-li definovat funkci v C, musíte ji deklarovat:

  • nahlásit název (identifikátor) funkce,
  • vypsat vstupní parametry (argumenty)
  • a zadejte typ návratu.

Je také nutné poskytnout definici funkce, která obsahuje blok příkazů, které implementují chování funkce.

Nedeklarování konkrétní funkce je chybou, pokud je funkce použita mimo rozsah definice, což v závislosti na implementaci vede ke zprávám nebo varováním.

Pro volání funkce stačí zadat její název s parametry uvedenými v závorkách. V tomto případě je adresa volacího bodu umístěna na zásobník, jsou vytvořeny a inicializovány proměnné zodpovědné za parametry funkce a řízení je přeneseno do kódu, který implementuje volanou funkci. Po provedení funkce je uvolněna paměť přidělená během volání funkce, návrat k bodu volání a pokud je volání funkce součástí nějakého výrazu, hodnota vypočítaná uvnitř funkce je předána návratovému bodu.

Pokud za funkcí nejsou uvedeny závorky, kompilátor to interpretuje jako získání adresy funkce. Adresu funkce lze zadat do ukazatele a následně funkci zavolat pomocí ukazatele na něj, což se aktivně využívá např. v pluginových systémech [32] .

Pomocí klíčového slova inlinemůžete označit funkce, jejichž volání chcete provést co nejrychleji. Překladač může nahradit kód takových funkcí přímo v místě jejich volání [33] . Na jednu stranu to zvyšuje množství spustitelného kódu, ale na druhou stranu šetří čas jeho provádění, protože se nepoužívá časově náročná operace volání funkce. Vzhledem k architektuře počítačů však mohou funkce inlining buď zrychlit nebo zpomalit aplikaci jako celek. Nicméně v mnoha případech jsou inline funkce preferovanou náhradou maker [34] .

Deklarace funkce

Deklarace funkce má následující formát:

[deskriptor] [jméno] ([seznam] );,

kde

  • [descriptor] — typový deskriptor hodnoty vrácené funkcí;
  • [jméno] - název funkce (jedinečný identifikátor funkce);
  • [seznam] - seznam (formálních) parametrů funkce nebo voidv případě jejich nepřítomnosti [35] .

Znakem deklarace funkce je ;symbol „ “, takže deklarace funkce je instrukce.

V nejjednodušším případě [deklarátor] obsahuje označení konkrétního typu návratové hodnoty. Funkce, která by neměla vracet žádnou hodnotu, je deklarována jako funkce typu void.

V případě potřeby může deskriptor obsahovat modifikátory specifikované pomocí klíčových slov:

  • externoznačuje, že definice funkce je v jiném modulu ;
  • staticdefinuje statickou funkci, kterou lze použít pouze v aktuálním modulu.

Seznam parametrů funkce definuje signaturu funkce.

C neumožňuje deklarovat více funkcí stejným názvem, přetížení funkcí není podporováno [36] .

Definice funkce

Definice funkce má následující formát:

[deskriptor] [jméno] ([seznam] )[tělo]

Kde [deklarátor], [jméno] a [seznam] jsou stejné jako v deklaraci a [tělo] je složený příkaz, který představuje konkrétní implementaci funkce. Kompilátor rozlišuje mezi stejnojmennými definicemi funkcí jejich podpisem, a tak (podpisem) vzniká spojení mezi definicí a odpovídající deklarací.

Tělo funkce vypadá takto:

{ [sekvence příkazů] return([vrácená hodnota]); }

Návrat z funkce se provádí pomocí operátoru , který buď určuje návratovou hodnotu, nebo ji nespecifikuje, v závislosti na datovém typu vráceném funkcí. Ve vzácných případech může být funkce označena jako nevracející se pomocí makra ze souboru záhlaví , v takovém případě není vyžadován žádný příkaz . Takto lze označit například funkce, které v sobě bezpodmínečně volají [33] . returnnoreturnstdnoreturn.hreturnabort()

Volání funkce

Volání funkce má provést následující akce:

  • uložení hlásiče do zásobníku;
  • automatické přidělování paměti pro proměnné odpovídající formálním parametrům funkce;
  • inicializace proměnných s hodnotami proměnných (skutečné parametry funkce) předávané funkci při jejím volání, stejně jako inicializace těch proměnných, pro které jsou v deklaraci funkce uvedeny výchozí hodnoty, ale pro které jim odpovídající skutečné parametry nebyly během hovoru specifikovány;
  • předání řízení tělu funkce.

V závislosti na implementaci kompilátor buď striktně zajistí, aby se typ skutečného parametru shodoval s typem formálního parametru, nebo, pokud je to možné, provede implicitní konverzi typu, což samozřejmě vede k vedlejším účinkům.

Pokud je funkci předána proměnná, pak se při volání funkce vytvoří její kopie ( paměť je alokována na zásobníku a hodnota je zkopírována). Například předání struktury funkci způsobí zkopírování celé struktury. Pokud je předán ukazatel na strukturu, zkopíruje se pouze hodnota ukazatele. Předání pole funkci také způsobí pouze zkopírování ukazatele na jeho první prvek. V tomto případě, abyste explicitně označili, že adresa začátku pole je brána jako vstup funkce, a nikoli ukazatel na jednu proměnnou, místo deklarování ukazatele za názvem proměnné můžete vložit hranaté závorky, např. příklad:

void example_func ( pole int []); // pole je ukazatel na první prvek pole typu int

C umožňuje vnořené hovory. Hloubka vnoření volání má zjevné omezení související s velikostí zásobníku přiděleného programu. Proto implementace C nastavují limit na hloubku vnoření.

Speciálním případem vnořeného volání je volání funkce uvnitř těla volané funkce. Takové volání se nazývá rekurzivní a používá se k organizaci jednotných výpočtů. Vzhledem k přirozenému omezení vnořených volání je rekurzivní implementace nahrazena implementací využívající smyčky.

Datové typy

Primitivní typy

Celá čísla

Celočíselné datové typy mají velikost od alespoň 8 do alespoň 32 bitů. Standard C99 zvyšuje maximální velikost celého čísla na alespoň 64 bitů. Datové typy Integer se používají k ukládání celých čísel (typ charse používá také k ukládání znaků ASCII). Všechny velikosti rozsahů datových typů níže jsou minimální a mohou být na dané platformě větší [37] .

V důsledku minimálních velikostí typů norma vyžaduje, aby velikosti integrálních typů splňovaly podmínku:

1= ≤ ≤ ≤ ≤ . sizeof(char)sizeof(short)sizeof(int)sizeof(long)sizeof(long long)

Velikosti některých typů z hlediska počtu bajtů se tedy mohou shodovat, pokud je splněna podmínka pro minimální počet bitů. Dokonce chara longmůže mít stejnou velikost, pokud jeden bajt bude trvat 32 bitů nebo více, ale takové platformy budou velmi vzácné nebo nebudou existovat. Standard zaručuje, že typ je char vždy 1 bajt. Velikost bajtu v bitech je určena konstantou CHAR_BITv hlavičkovém souboru limits.h, což je 8 bitů na systémech kompatibilních s POSIX [38] .

Minimální rozsah hodnot celočíselných typů podle standardu je definován od do pro typy se znaménkem a od do  pro typy bez znaménka, kde N je bitová hloubka typu. Implementace kompilátorů mohou tento rozsah rozšířit podle svého uvážení. V praxi se pro podepsané typy častěji používá rozsah od do . Minimální a maximální hodnoty každého typu jsou specifikovány v souboru jako definice maker. -(2N-1-1)2N-1-102N-2N-12N-1-1limits.h

Zvláštní pozornost by měla být věnována typu char. Formálně se jedná o samostatný typ, ale ve skutečnosti je charekvivalentní buď signed char, nebo unsigned char, v závislosti na kompilátoru [39] .

Aby se předešlo záměně velikostí typů, zavedl standard C99 nové datové typy popsané v stdint.h. Mezi nimi jsou takové typy jako: , , , kde = 8, 16, 32 nebo 64. Předpona označuje minimální typ, který pojme bity, předpona označuje typ alespoň 16 bitů, který je na této platformě nejrychlejší. Typy bez předpon označují typy s pevnou velikostí bitů. intN_tint_leastN_tint_fastN_tNleast-Nfast-N

Typy s předponami least-a fast-lze považovat za náhradu typů int, short, long, jen s tím rozdílem, že ty první dávají programátorovi na výběr mezi rychlostí a velikostí.

Základní datové typy pro ukládání celých čísel
Datový typ Velikost Minimální rozsah hodnot Standard
signed char minimálně 8 bitů od −127 [40] (= -(2 7 −1)) do 127 C90 [j]
int_least8_t C99
int_fast8_t
unsigned char minimálně 8 bitů 0 až 255 (=2 8 −1) C90 [j]
uint_least8_t C99
uint_fast8_t
char minimálně 8 bitů −127 až 127 nebo 0 až 255 v závislosti na kompilátoru C90 [j]
short int minimálně 16 bitů od -32,767 (= -(2 15 -1)) do 32,767 C90 [j]
int
int_least16_t C99
int_fast16_t
unsigned short int minimálně 16 bitů 0 až 65,535 (= 2 16 −1) C90 [j]
unsigned int
uint_least16_t C99
uint_fast16_t
long int minimálně 32 bitů −2 147 483 647 až 2 147 483 647 C90 [j]
int_least32_t C99
int_fast32_t
unsigned long int minimálně 32 bitů 0 až 4 294 967 295 (= 2 32 −1) C90 [j]
uint_least32_t C99
uint_fast32_t
long long int minimálně 64 bitů -9,223,372,036,854,775,807 až 9,223,372,036,854,775,807 C99
int_least64_t
int_fast64_t
unsigned long long int minimálně 64 bitů 0 až 18 446 744 073 709 551 615 (= 264 −1 )
uint_least64_t
uint_fast64_t
int8_t 8 bitů -127 až 127
uint8_t 8 bitů 0 až 255 (=2 8 −1)
int16_t 16 bit -32,767 až 32,767
uint16_t 16 bit 0 až 65,535 (= 2 16 −1)
int32_t 32 bitů −2 147 483 647 až 2 147 483 647
uint32_t 32 bitů 0 až 4 294 967 295 (= 2 32 −1)
int64_t 64 bitů -9,223,372,036,854,775,807 až 9,223,372,036,854,775,807
uint64_t 64 bitů 0 až 18 446 744 073 709 551 615 (= 264 −1 )
Tabulka ukazuje minimální rozsah hodnot podle jazykové normy. Kompilátory jazyka C mohou rozšířit rozsah hodnot.
Pomocné celočíselné typy

Od standardu C99 byly také přidány typy intmax_ta uintmax_t, odpovídající největším podepsaným a nepodepsaným typům. Tyto typy jsou vhodné při použití v makrech k ukládání mezilehlých nebo dočasných hodnot během operací s celočíselnými argumenty, protože umožňují přizpůsobit hodnoty libovolného typu. Tyto typy se například používají v makrech pro porovnání celých čísel knihovny Check unit testing pro C [41] .

V C existuje několik dalších celočíselných typů pro bezpečné zacházení s datovým typem ukazatele : intptr_ta uintptr_t. ptrdiff_tTypy intptr_ta uintptr_tze standardu C99 jsou navrženy pro ukládání hodnot se znaménkem a bez znaménka, které se vejdou do velikosti ukazatele. Tyto typy se často používají k uložení libovolného celého čísla do ukazatele, například jako způsob, jak se zbavit zbytečné alokace paměti při registraci funkcí zpětné vazby [42] nebo při použití propojených seznamů třetích stran, asociativních polí a dalších struktur, ve kterých data jsou uložena ukazatelem. Typ ptrdiff_tze souboru záhlaví stddef.hje navržen tak, aby bezpečně uložil rozdíl dvou ukazatelů.

Pro uložení velikosti je k dispozici nepodepsaný typ size_tze souboru záhlaví stddef.h. Tento typ je schopen pojmout maximální možný počet bajtů dostupných na ukazateli a obvykle se používá k uložení velikosti v bajtech. Hodnotu tohoto typu vrací operátor sizeof[43] .

Casting typu Integer

Konverze celočíselného typu mohou probíhat buď explicitně, pomocí operátoru přetypování, nebo implicitně. Hodnoty typů menší než int, když se účastní jakýchkoli operací nebo když jsou předány volání funkce, jsou automaticky přetypovány na typ inta pokud převod není možný, na typ unsigned int. Často jsou takové implicitní přetypování nutné, aby byl výsledek výpočtu správný, ale někdy vedou k intuitivně nepochopitelným chybám ve výpočtech. Pokud například operace zahrnuje čísla typu inta unsigned inta hodnota se znaménkem je záporná, pak převod záporného čísla na typ bez znaménka povede k přetečení a velmi velké kladné hodnotě, což může vést k nesprávnému výsledku operací porovnání. [44] .

Porovnání správného a nesprávného automatického lití typu
Podepsané a nepodepsané typy jsou menší nežint Podepsané je méně než nepodepsané a nepodepsané není méněint
#include <stdio.h> znak se znaménkem x = -1 ; znak bez znaménka y = 0 ; if ( x > y ) { // podmínka je false printf ( "Zpráva se nezobrazí. \n " ); } if ( x == UCHAR_MAX ) { // podmínka je false printf ( "Zpráva se nezobrazí. \n " ); } #include <stdio.h> znak se znaménkem x = -1 ; bez znaménka int y = 0 ; if ( x > y ) { // podmínka je pravdivá printf ( "Přetečení v proměnné x. \n " ); } if (( x == UINT_MAX ) && ( x == ULONG_MAX )) { // podmínka bude vždy true printf ( "Přetečení v proměnné x. \n " ); }
V tomto příkladu budou oba typy, podepsané i nepodepsané, přetypovány na podepsané int, protože umožňuje, aby se vešly rozsahy obou typů. Proto bude srovnání v podmíněném operátoru správné. Podepsaný typ bude přetypován na nepodepsaný, protože nepodepsaný typ je větší nebo roven velikosti int, ale dojde k přetečení, protože v typu bez znaménka není možné reprezentovat zápornou hodnotu.

Automatické přetypování bude také fungovat, pokud jsou ve výrazu použity dva nebo více různých celočíselných typů. Norma definuje sadu pravidel, podle kterých se volí typová konverze, která může dát správný výsledek výpočtu. Různým typům jsou v rámci transformace přiřazeny různé hodnosti a samotné hodnosti jsou založeny na velikosti typu. Pokud jsou ve výrazu zahrnuty různé typy, obvykle se volí přetypování těchto hodnot na typ vyšší úrovně [44] .

Reálná čísla

Čísla s plovoucí desetinnou čárkou v C jsou reprezentována třemi základními typy: float, doublea long double.

Reálná čísla mají reprezentaci, která se velmi liší od celých čísel. Konstanty reálných čísel různých typů, zapsané v desítkové soustavě, se nemusí navzájem rovnat. Například podmínka 0.1 == 0.1fbude nepravdivá kvůli ztrátě přesnosti typu float, zatímco podmínka 0.5 == 0.5fbude pravdivá, protože tato čísla jsou v binární reprezentaci konečná. Podmínka přetypování však (float) 0.1 == 0.1fbude také pravdivá, protože přetypování na méně přesný typ ztratí bity, které tyto dvě konstanty odlišují.

Aritmetické operace s reálnými čísly jsou také nepřesné a často mají nějakou plovoucí chybu [45] . Největší chyba nastane při práci s hodnotami, které se blíží minimu možnému pro konkrétní typ. Chyba se také může ukázat jako velká při výpočtu přes velmi malá (≪ 1) a velmi velká čísla (≫ 1). V některých případech lze chybu snížit změnou algoritmů a metod výpočtu. Například při nahrazení vícenásobného sčítání násobením se chyba může snížit tolikrát, kolikrát bylo původně operací sčítání.

V hlavičkovém souboru math.hjsou také dva další typy float_ta double_t, které odpovídají minimálně typům floatresp double., ale mohou se od nich lišit. Typy float_ta double_tjsou přidány ve standardu C99 a jejich shoda se základními typy je určena hodnotou makra FLT_EVAL_METHOD.

Reálné datové typy
Datový typ Velikost Standard
float 32 bitů IEC 60559 ( IEEE 754 ), rozšíření F normy C [46] [k] , jediné přesné číslo
double 64 bitů IEC 60559 (IEEE 754), rozšíření F normy C [46] [k] , číslo s dvojitou přesností
long double minimálně 64 bitů implementace závislá
float_t(C99) minimálně 32 bitů záleží na typu základny
double_t(C99) minimálně 64 bitů záleží na typu základny
Soulad doplňkových typů se základními [47]
FLT_EVAL_METHOD float_t double_t
jeden float double
2 double double
3 long double long double

Řetězce

Řetězce ukončené nulou

Ačkoli v jazyce C jako takový neexistuje žádný speciální typ pro řetězce, v jazyce se často používají řetězce zakončené nulou. Řetězce ASCII jsou deklarovány jako pole typu char, jehož posledním prvkem musí být kód znaku 0( '\0'). Je obvyklé ukládat řetězce UTF-8 ve stejném formátu . Všechny funkce, které pracují s řetězci ASCII, však považují každý znak za bajt, což omezuje použití standardních funkcí při použití tohoto kódování.

Navzdory rozšířenému používání myšlenky řetězců ukončených nulou a pohodlí jejich použití v některých algoritmech mají několik vážných nevýhod.

  1. Nutnost přidat koncový znak na konec řetězce neumožňuje získat podřetězec bez nutnosti jeho kopírování a jazyk neposkytuje funkce pro práci s ukazatelem na podřetězec a jeho délkou.
  2. Pokud je potřeba předem alokovat paměť pro výsledek algoritmu na základě vstupních dat, je pokaždé nutné projet celý řetězec, aby se vypočítala jeho délka.
  3. Při práci s velkým množstvím textu může být výpočet délky úzkým hrdlem .
  4. Práce s řetězcem, který není omylem ukončen nulou, může vést k nedefinovanému chování programu, včetně chyb segmentace , chyb přetečení vyrovnávací paměti a zranitelností .

V moderních podmínkách, kdy je výkon kódu upřednostňován před spotřebou paměti, může být efektivnější a jednodušší používat struktury, které obsahují jak samotný řetězec, tak jeho velikost [48] , například:

struct string_t { char * str ; // ukazatel na řetězec size_t str_size ; // velikost řetězce }; typedef struct řetězec_t řetězec_t ; // alternativní název pro zjednodušení kódu

Alternativní přístup k ukládání velikosti řetězce s nízkou pamětí by byl předpona řetězce jeho velikostí ve formátu s proměnnou délkou .. Podobný přístup se používá v protokolových bufferech , avšak pouze ve fázi přenosu dat, nikoli však jejich ukládání.

Řetězcové literály

Řetězcové literály v C jsou ze své podstaty konstanty [10] . Při deklaraci jsou uzavřeny do dvojitých uvozovek a terminátor je 0automaticky přidán kompilátorem. Řetězcový literál lze přiřadit dvěma způsoby: ukazatelem a hodnotou. Při přiřazení ukazatelem char *se do proměnné typu zadá ukazatel na neměnný řetězec, to znamená, že se vytvoří konstantní řetězec. Pokud zadáte řetězcový literál do pole, pak se řetězec zkopíruje do oblasti zásobníku.

#include <stdio.h> #include <řetězec.h> int main ( void ) { const char * s1 = "Const string" ; char s2 [] = "Řetězec, který lze změnit" ; memcpy ( s2 , "c" , strlen ( "c" )); // změňte první písmeno na malé klade ( s2 ); // zobrazí se text řádku memcpy (( char * ) s1 , "to" , strlen ( "to" )); // chyba segmentace klade ( s1 ); // řádek nebude proveden }

Protože řetězce jsou běžná pole znaků, lze místo literálů použít inicializátory, pokud se každý znak vejde do 1 bajtu:

char s [] = { 'I' , 'n' , 'i' , 't' , 'i' , 'a' , 'l' , 'i' , 'z' , 'e' , 'r' , '\0' };

V praxi má však tento přístup smysl pouze ve velmi vzácných případech, kdy je vyžadováno nepřidávat do řetězce ASCII koncovou nulu.

Široké čáry Typ kódování wchar_tv závislosti na platformě
Plošina Kódování
GNU/Linux USC-4 [49]
Operační Systém Mac
Okna USC-2 [50]
AIX
FreeBSD Záleží na lokalitě

není zdokumentováno [50]

Solaris

Alternativou k běžným řetězcům jsou široké řetězce, ve kterých je každý znak uložen ve speciálním typu wchar_t. Standardem daný typ by měl být schopen sám o sobě obsahovat všechny znaky největšího z existujících lokalit . Funkce pro práci s širokými řetězci jsou popsány v záhlaví souboru wchar.ha funkce pro práci s širokými znaky jsou popsány v záhlaví souboru wctype.h.

Při deklaraci řetězcových literálů pro široké řetězce se používá modifikátor L:

const wchar_t * wide_str = L "Široký řetězec" ;

Formátovaný výstup používá specifikátor %ls, ale specifikátor velikosti, pokud je uveden, je zadán v bajtech, nikoli ve znacích [51] .

Typ wchar_tbyl koncipován tak, aby se do něj vešel jakýkoli znak a široké řetězce - pro ukládání řetězců libovolného národního prostředí, ale ve výsledku se ukázalo, že API je nepohodlné a implementace byly závislé na platformě. Takže na platformě Windows bylo jako velikost typu zvoleno 16 bitů wchar_ta později se objevil standard UTF-32, takže typ wchar_tna platformě Windows již není schopen pojmout všechny znaky z kódování UTF-32, v důsledku čehož se význam tohoto typu ztrácí [50] . Zároveň na platformách Linux [49] a macOS tento typ zabírá 32 bitů, takže typ není vhodný pro implementaci úloh napříč platformami .wchar_t

Vícebajtové řetězce

Existuje mnoho různých kódování, ve kterých může být jeden znak naprogramován s různým počtem bajtů. Takové kódování se nazývá vícebajtové. Platí pro ně také UTF-8 . C má sadu funkcí pro převod řetězců z vícebajtových v rámci aktuálního národního prostředí na široké a naopak. Funkce pro práci s vícebajtovými znaky mají předponu nebo příponu mba jsou popsány v záhlaví souboru stdlib.h. Chcete-li podporovat vícebajtové řetězce v programech C, musí být tyto řetězce podporovány na aktuální úrovni národního prostředí . Chcete-li explicitně nastavit kódování, můžete změnit aktuální národní prostředí pomocí funkce setlocale()z locale.h. Určení kódování pro národní prostředí však musí podporovat používaná standardní knihovna. Například standardní knihovna Glibc plně podporuje kódování UTF-8 a je schopna převádět text do mnoha dalších kódování [52] .

Počínaje standardem C11 jazyk také podporuje 16bitové a 32bitové široké vícebajtové řetězce s vhodnými typy znaků char16_ta char32_tze souboru záhlaví uchar.ha také deklarování řetězcových literálů UTF-8 pomocí u8. 16bitové a 32bitové řetězce lze použít k uložení kódování UTF-16 a UTF-32, pokud jsou v hlavičce specifikovány uchar.hdefinice makra __STDC_UTF_16__a __STDC_UTF_32__. K určení řetězcových literálů v těchto formátech se používají modifikátory: upro 16bitové řetězce a Upro 32bitové řetězce. Příklady deklarování řetězcových literálů pro vícebajtové řetězce:

const char * s8 = u8 "vícebajtový řetězec UTF-8" ; const char16_t * s16 = u "16bitový vícebajtový řetězec" ; const char32_t * s32 = U "32bitový vícebajtový řetězec" ;

Všimněte si, že funkce c16rtomb()pro převod z 16bitového řetězce na vícebajtový řetězec nefunguje tak, jak bylo zamýšleno, a ve standardu C11 bylo zjištěno, že není možné překládat z UTF-16 do UTF-8 [53] . Oprava této funkce může záviset na konkrétní implementaci kompilátoru.

Vlastní typy

Výčty

Výčty jsou množinou pojmenovaných celočíselných konstant a jsou označeny klíčovým slovem enum. Pokud konstanta není přidružena k číslu, pak se automaticky nastaví buď 0pro první konstantu v seznamu, nebo pro číslo o jednu větší, než je zadané v předchozí konstantě. V tomto případě samotný datový typ výčtu může ve skutečnosti odpovídat jakémukoli podepsanému nebo nepodepsanému primitivnímu typu, do jehož rozsahu se všechny hodnoty výčtu vejdou; Kompilátor rozhodne, který typ použít. Explicitní hodnoty pro konstanty však musí být výrazy jako int[18] .

Typ výčtu může být také anonymní, pokud není zadán název výčtu. Konstanty zadané ve dvou různých výčtech jsou dvou různých datových typů, bez ohledu na to, zda jsou výčty pojmenované nebo anonymní.

V praxi se výčty často používají k indikaci stavů konečných automatů , k nastavení možností provozních režimů nebo hodnot parametrů [54] , k vytváření celočíselných konstant a také k výčtu jakýchkoli jedinečných objektů nebo vlastností [55] .

Struktury

Struktury jsou kombinací proměnných různých datových typů ve stejné oblasti paměti; označený klíčovým slovem struct. Proměnné uvnitř struktury se nazývají pole struktury. Z hlediska adresního prostoru jdou pole vždy za sebou ve stejném pořadí, v jakém jsou specifikována, ale kompilátory mohou zarovnávat adresy polí pro optimalizaci pro konkrétní architekturu. Pole tedy ve skutečnosti může mít větší velikost, než je uvedeno v programu.

Každé pole má určitý posun vzhledem k adrese struktury a velikosti. Offset lze získat pomocí makra offsetof()ze souboru záhlaví stddef.h. V tomto případě bude offset záviset na zarovnání a velikosti předchozích polí. Velikost pole je obvykle určena zarovnáním struktury: pokud je velikost zarovnání datového typu pole menší než hodnota zarovnání struktury, pak je velikost pole určena zarovnáním struktury. Zarovnání typu dat lze získat pomocí makra alignof()[f] z hlavičkového souboru stdalign.h. Velikost samotné konstrukce je celková velikost všech jejích polí včetně zarovnání. Některé kompilátory zároveň poskytují speciální atributy, které umožňují sbalit struktury a odstranit z nich zarovnání [56] .

Pole struktury lze explicitně nastavit na velikost v bitech oddělených dvojtečkou za definicí pole a počtem bitů, což omezuje rozsah jejich možných hodnot bez ohledu na typ pole. Tento přístup lze použít jako alternativu k příznakům a bitovým maskám pro přístup k nim. Určení počtu bitů však nezruší možné zarovnání polí struktur v paměti. Práce s bitovými poli má řadu omezení: nelze na ně aplikovat operátor sizeofnebo makro alignof(), nelze na ně získat ukazatel.

Asociace

Sjednocení je potřeba, když chcete na stejnou proměnnou odkazovat jako na různé datové typy; označený klíčovým slovem union. Uvnitř unie lze deklarovat libovolný počet protínajících se polí, která ve skutečnosti poskytují přístup do stejné oblasti paměti jako různé datové typy. Velikost svazku volí překladač na základě velikosti největšího pole ve svazu. Je třeba mít na paměti, že změna jednoho pole unie vede ke změně ve všech ostatních polích, ale správná je zaručeně pouze hodnota pole, které se změnilo.

Sjednocení může sloužit jako pohodlnější alternativa k přetypování ukazatele na libovolný typ. Například pomocí sjednocení umístěného ve struktuře můžete vytvářet objekty s dynamicky se měnícím datovým typem:

Strukturní kód pro změnu datového typu za běhu #include <stddef.h> výčet value_type_t { VALUE_TYPE_LONG , // celé číslo VALUE_TYPE_DOUBLE , // skutečné číslo VALUE_TYPE_STRING , // řetězec VALUE_TYPE_BINARY , // libovolná data }; struct binary_t { neplatná * data ; // ukazatel na data size_t data_size ; // velikost dat }; struct string_t { char * str ; // ukazatel na řetězec size_t str_size ; // velikost řetězce }; union value_contents_t { dlouhý jako_dlouhý ; // hodnota jako celé číslo double as_double ; // hodnota jako reálné číslo struct řetězec_t jako_řetězec ; // hodnota jako řetězec struct binary_t as_binary ; // hodnota jako libovolná data }; struct value_t { enum hodnota_typ_t typ ; // typ hodnoty union value_contents_t obsah ; // hodnota obsahu }; Pole

Pole v C jsou primitivní a jsou jen syntaktickou abstrakcí nad aritmetikou ukazatele . Samotné pole je ukazatelem na oblast paměti, takže všechny informace o dimenzi pole a jeho hranicích jsou přístupné pouze v době kompilace podle deklarace typu. Pole mohou být jednorozměrná nebo vícerozměrná, ale přístup k prvku pole spočívá v jednoduchém výpočtu offsetu vzhledem k adrese začátku pole. Protože pole jsou založena na aritmetice adres, je možné s nimi pracovat bez použití indexů [57] . Takže například následující dva příklady čtení 10 čísel ze vstupního proudu jsou navzájem totožné:

Porovnání práce přes indexy s prací přes aritmetiku adres
Příklad kódu pro práci s indexy Příklad kódu pro práci s aritmetikou adres
#include <stdio.h> int a [ 10 ] = { 0 }; // Nulová inicializace unsigned int count = sizeof ( a ) / sizeof ( a [ 0 ]); for ( int i = 0 ; i < počet ; ++ i ) {     int * ptr = &a [ i ]; // Ukazatel na aktuální prvek pole int n = scanf ( "%8d" , ptr );         if ( n != 1 ) {         perror ( "Nepodařilo se přečíst hodnotu" );         // Zpracování chyby break ;            } } #include <stdio.h> int a [ 10 ] = { 0 }; // Nulová inicializace unsigned int count = sizeof ( a ) / sizeof ( a [ 0 ]); int * a_konec = a + počet ; // Ukazatel na prvek následující za posledním for ( int * ptr = a ; ptr != a_end ; ++ ptr ) { int n = scanf ( "%8d" , ptr ); if ( n != 1 ) { perror ( "Nepodařilo se přečíst hodnotu" ); // Zpracování chyby break ; } }

Délka polí se známou velikostí se vypočítá v době kompilace. Standard C99 zavedl možnost deklarovat pole proměnné délky, jejichž délku lze nastavit za běhu. Taková pole mají přidělenou paměť z oblasti zásobníku, takže je třeba je používat opatrně, pokud lze jejich velikost nastavit mimo program. Na rozdíl od dynamické alokace paměti může překročení povolené velikosti v oblasti zásobníku vést k nepředvídatelným následkům a záporná délka pole je nedefinované chování . Počínaje C11 jsou pole s proměnnou délkou pro kompilátory volitelná a nedostatek podpory je dán přítomností makra __STDC_NO_VLA__[58] .

Pole s pevnou velikostí deklarované jako lokální nebo globální proměnné lze inicializovat tak, že jim přiřadíte počáteční hodnotu pomocí složených závorek a vypíšete prvky pole oddělené čárkami. Inicializátory globálního pole mohou používat pouze výrazy, které jsou vyhodnoceny při kompilaci [59] . Proměnné použité v takových výrazech musí být deklarovány jako konstanty s modifikátorem const. U lokálních polí mohou inicializátory obsahovat výrazy s voláním funkcí a použitím dalších proměnných, včetně ukazatele na samotné deklarované pole.

Od standardu C99 je povoleno deklarovat pole libovolné délky jako poslední prvek struktur, což je v praxi široce používané a podporované různými kompilátory. Velikost takového pole závisí na množství paměti alokované pro strukturu. V tomto případě nemůžete deklarovat pole takových struktur a nemůžete je umístit do jiných struktur. Při operacích na takové struktuře je pole libovolné délky obvykle ignorováno, včetně výpočtu velikosti struktury, a překročení pole vede k nedefinovanému chování [60] .

Jazyk C neposkytuje žádnou kontrolu nad polem mimo hranice, takže práci s poli musí sledovat sám programátor. Chyby ve zpracování pole ne vždy přímo ovlivňují provádění programu, ale mohou vést k chybám segmentace a zranitelnostem .

Typ synonym

Jazyk C vám umožňuje vytvářet vlastní názvy typů pomocí přípony typedef. Alternativní názvy lze přiřadit jak typům systémů, tak i uživatelsky definovaným. Takové názvy jsou deklarovány v globálním jmenném prostoru a nejsou v konfliktu s názvy struktur, výčtů a sjednocovacích typů.

Alternativní názvy lze použít jak ke zjednodušení kódu, tak k vytvoření úrovní abstrakce. Některé typy systémů lze například zkrátit, aby byl kód čitelnější nebo aby byl v uživatelském kódu jednotnější:

#include <stdint.h> typedef int32_t i32_t ; typedef int_fast32_t i32fast_t ; typedef int_least32_t i32least_t ; typedef uint32_t u32_t ; typedef uint_fast32_t u32fast_t ; typedef uint_least32_t u32least_t ;

Příkladem abstrakce jsou názvy typů v hlavičkových souborech operačních systémů. Například standard POSIX definuje typ pid_tpro uložení číselného ID procesu. Ve skutečnosti je tento typ alternativním názvem pro nějaký primitivní typ, například:

typedef int __kernel_pid_t ; typedef __kernel_pid_t __pid_t typedef __pid_t pid_t ;

Protože typy s alternativními názvy jsou pouze synonymy pro původní typy, je zachována plná kompatibilita a zaměnitelnost mezi nimi.

Preprocesor

Preprocesor pracuje před kompilací a transformuje text programového souboru podle direktiv, které se v něm vyskytují nebo které jsou předány preprocesoru . Technicky lze preprocesor implementovat různými způsoby, ale logicky je vhodné si jej představit jako samostatný modul, který zpracovává každý soubor určený ke kompilaci a tvoří text, který pak vstupuje na vstup kompilátoru. Preprocesor hledá v textu řádky, které začínají znakem #, za nimiž následují příkazy preprocesoru. Vše, co nepatří do direktiv preprocesoru a není podle direktiv vyjmuto z kompilace, je předáno na vstup kompilátoru beze změny.

Mezi funkce preprocesoru patří:

  • substituce daného lexému textem pomocí direktivy #define, včetně možnosti vytvářet parametrizované textové šablony (nazývané obdobně jako funkce), jakož i rušit takové substituce, což umožňuje provádět substituci v omezených oblastech textu programu;
  • podmíněné vkládání a odstraňování částí z textu, včetně samotných direktiv, pomocí podmíněných příkazů #ifdef, #ifndef, #ifa #else;#endif
  • vložit text z jiného souboru do aktuálního souboru pomocí #include.

Je důležité pochopit, že preprocesor poskytuje pouze substituci textu, nebere v úvahu syntaxi a sémantiku jazyka. Takže například definice maker #definese mohou vyskytovat uvnitř funkcí nebo definic typů a direktivy podmíněné kompilace mohou vést k vyloučení jakékoli části kódu z kompilovaného textu programu bez ohledu na gramatiku jazyka. Volání parametrického makra se také liší od volání funkce, protože sémantika argumentů oddělených čárkami není analyzována. Takže například není možné předat inicializaci pole argumentům parametrického makra, protože jeho prvky jsou také odděleny čárkou:

#define pole_of(typ, pole) (((typ) []) (pole)) int * a ; a = pole_of ( int , { 1 , 2 , 3 }); // chyba kompilace: // makro "array_of" předalo 4 argumenty, ale trvá pouze 2

Definice maker se často používají k zajištění kompatibility s různými verzemi knihoven, které změnily rozhraní API , včetně určitých částí kódu v závislosti na verzi knihovny. Pro tyto účely knihovny často poskytují definice maker popisující jejich verzi [61] , někdy i makra s parametry pro porovnání aktuální verze s verzí zadanou v preprocesoru [62] . Definice maker se také používají pro podmíněnou kompilaci jednotlivých částí programu, například pro umožnění podpory některých doplňkových funkcí.

Makro definice s parametry jsou široce používány v programech C k vytvoření analogií generických funkcí . Dříve se používaly i k implementaci inline funkcí, ale od standardu C99 tato potřeba odpadla díky přidání inline-funkcí. Vzhledem k tomu, že definice maker s parametry nejsou funkcemi, ale jsou volány podobným způsobem, mohou v důsledku chyby programátora nastat neočekávané problémy, včetně zpracování pouze části kódu z definice makra [63] a nesprávných priorit pro provádění operací [64] . Příkladem chybného kódu je kvadratické makro:

#include <stdio.h> int main ( void ) { #define SQR(x) x * x printf ( "%d" , SQR ( 5 )); // vše je správně, 5*5=25 printf ( "%d" , SQR ( 5 + 0 )); // má být 25, ale vypíše 5 (5+0*5+0) printf ( "%d" , SQR ( 4/3 ) ) ; // vše je správně, 1 (protože 4/3=1, 1*4=4, 4/3=1) printf ( "%d" , SQR ( 5/2 ) ) ; // má být 4 (2*2), ale vypíše 5 (5/2*5/2) návrat 0 ; }

Ve výše uvedeném příkladu je chyba v tom, že obsah argumentu makra je do textu nahrazen tak, jak je, bez ohledu na prioritu operací. V takových případech musíte použít inline-functions nebo explicitně upřednostnit operátory ve výrazech, které používají parametry maker pomocí závorek:

#include <stdio.h> int main ( void ) { #define SQR(x) ((x) * (x)) printf ( "%d" , SQR ( 4 + 1 )); // pravda, 25 návrat 0 ; }

Programování v C

Struktura programu

Moduly

Program je sada souborů C, které lze zkompilovat do objektových souborů . Soubory objektů pak procházejí krokem propojení mezi sebou navzájem, stejně jako s externími knihovnami, což vede ke konečnému spustitelnému souboru nebo knihovně . Propojení souborů mezi sebou, stejně jako s knihovnami, vyžaduje popis prototypů použitých funkcí, externích proměnných a potřebných datových typů v každém souboru. Je obvyklé ukládat taková data do samostatných hlavičkových souborů , které jsou propojeny pomocí direktivy #include v těch souborech, kde je vyžadována ta či ona funkčnost, a umožňují organizovat systém podobný modulovému systému. V tomto případě může být modul:

  • sada jednotlivých souborů se zdrojovým kódem, pro které je rozhraní prezentováno ve formě hlavičkových souborů;
  • knihovnu objektů nebo její část s příslušnými záhlavími;
  • samostatná sada jednoho nebo více hlavičkových souborů (knihovna rozhraní);
  • statická knihovna nebo její část s příslušnými záhlavími;
  • dynamickou knihovnu nebo její část s příslušnými hlavičkovými soubory.

Vzhledem k tomu, že směrnice #includepouze nahrazuje text jiného souboru ve fázi preprocesoru , zahrnutí stejného souboru vícekrát může vést k chybám při kompilaci. Proto takové soubory využívají ochranu proti opětovnému povolení pomocí maker #define a #ifndef[65] .

Soubory zdrojového kódu

Tělo souboru zdrojového kódu C se skládá ze sady definic globálních dat, typů a funkcí. Globální proměnné a funkce deklarované pomocí specifikátorů a staticjsou inlinedostupné pouze v souboru, ve kterém jsou deklarovány, nebo když je jeden soubor zahrnut do jiného prostřednictvím souboru #include. V tomto případě se funkce a proměnné deklarované v hlavičkovém souboru se slovem staticvytvoří znovu pokaždé, když se hlavičkový soubor připojí k dalšímu souboru se zdrojovým kódem. Globální proměnné a funkční prototypy deklarované s externím specifikátorem jsou považovány za zahrnuté z jiných souborů. To znamená, že mohou být použity v souladu s popisem; předpokládá se, že po sestavení programu budou propojeny linkerem s původními objekty a funkcemi popsanými v jejich souborech.

Globální proměnné a funkce, kromě statica inline, jsou přístupné z jiných souborů za předpokladu, že jsou tam správně deklarovány se specifikátorem extern. K proměnným a funkcím deklarovaným s modifikátorem staticlze přistupovat také v jiných souborech, ale pouze tehdy, když je jejich adresa předána ukazatelem. Zadejte deklarace typedefa nelze je importovat do jiných souborů struct. unionPokud je nutné je použít v jiných souborech, měly by být duplikovány tam nebo umístěny do samostatného hlavičkového souboru. Totéž platí pro inline-funkce.

Vstupní bod programu

Pro spustitelný program je standardním vstupním bodem funkce s názvem main, která nemůže být statická a musí být jediná v programu. Provádění programu začíná od prvního příkazu funkce main()a pokračuje až do jejího ukončení, poté se program ukončí a vrátí operačnímu systému abstraktní celočíselný kód výsledku své práce.

Platné funkční prototypy main()[66]
žádné argumenty S argumenty příkazového řádku
int main ( void ); int main ( int argc , char ** argv );

Při volání je proměnné argcpředán počet argumentů předávaných programu, včetně cesty k samotnému programu, takže proměnná argc obvykle obsahuje hodnotu ne menší než 1. Samotný argvřádek spuštění programu je předán proměnné jako pole textových řetězců, jejichž posledním prvkem je NULL. Překladač garantuje, že main()všechny globální proměnné v programu budou při spuštění funkce inicializovány [67] .

Výsledkem je, že funkce main()může vrátit libovolné celé číslo v rozsahu hodnot typu int, které bude předáno operačnímu systému nebo jinému prostředí jako návratový kód programu [66] . Jazyková norma nedefinuje význam návratových kódů [68] . Obvykle má operační systém, kde programy běží, nějaké prostředky, jak získat hodnotu návratového kódu a analyzovat ji. Někdy existují určité konvence o významech těchto kódů. Obecnou konvencí je, že návratový kód nula označuje úspěšné dokončení programu, zatímco nenulová hodnota představuje kód chyby. Hlavičkový soubor stdlib.hdefinuje dvě obecné definice maker EXIT_SUCCESSa EXIT_FAILURE, které odpovídají úspěšnému a neúspěšnému dokončení programu [68] . Návratové kódy lze také použít v aplikacích, které zahrnují více procesů pro zajištění komunikace mezi těmito procesy, v takovém případě samotná aplikace určuje sémantický význam každého návratového kódu.

Práce s pamětí

Model paměti

C poskytuje 4 způsoby alokace paměti, které určují dobu života proměnné a okamžik její inicializace [67] .

Metody alokace paměti [67]
Způsob výběru Cíle Čas výběru čas vydání Režijní náklady
Alokace statické paměti Globální proměnné a proměnné označené klíčovým slovem static(ale bez _Thread_local) Při spuštění programu Na konci programu Chybějící
Alokace paměti na úrovni vlákna Proměnné označené klíčovým slovem_Thread_local Když vlákno začne Na konci proudu Při vytváření vlákna
Automatické přidělování paměti Argumenty a návratové hodnoty funkcí, lokální proměnné funkcí včetně registrů a polí proměnné délky Při volání funkcí na úrovni zásobníku . Automaticky po dokončení funkcí Nevýznamné, protože se mění pouze ukazatel na horní část zásobníku
Dynamická alokace paměti Paměť přidělená pomocí funkcí malloc()acalloc()realloc() Ručně z haldy v okamžiku volání použité funkce. Ručně pomocí funkcefree() Velký pro alokaci i uvolnění

Všechny tyto způsoby ukládání dat jsou vhodné v různých situacích a mají své výhody a nevýhody. Globální proměnné vám neumožňují psát reentrantní algoritmy a automatické přidělování paměti vám neumožňuje vrátit libovolnou oblast paměti z volání funkce. Automatické přidělování také není vhodné pro přidělování velkého množství paměti, protože může vést k poškození zásobníku nebo haldy [69] . Dynamická paměť tyto nedostatky nemá, ale při používání má velkou režii a je obtížnější ji používat.

Tam, kde je to možné, je preferována automatická nebo statická alokace paměti: tento způsob ukládání objektů je řízen kompilátorem , což programátora zbavuje starostí s ručním přidělováním a uvolňováním paměti, která je obvykle zdrojem těžko dohledatelných úniků paměti, chyby segmentace a opětovné uvolnění chyb v programu . Bohužel mnoho datových struktur má proměnlivou velikost za běhu, takže protože automaticky a staticky alokované oblasti musí mít známou pevnou velikost v době kompilace, je velmi běžné používat dynamickou alokaci.

U automaticky alokovaných proměnných registerlze použít modifikátor, který kompilátoru napoví, jak k nim rychle přistupovat. Takové proměnné lze umístit do registrů procesoru. Kvůli omezenému počtu registrů a případným optimalizacím kompilátoru mohou proměnné skončit v běžné paměti, přesto na ně nebude možné z programu získat ukazatel [70] . Modifikátor registerje jediný, který lze zadat v argumentech funkce [71] .

Adresování paměti

Jazyk C zdědil lineární adresování paměti při práci se strukturami, poli a alokovanými paměťovými oblastmi. Jazykový standard také umožňuje provádět porovnávací operace na nulových ukazatelích a na adresách v polích, strukturách a alokovaných paměťových oblastech. Je také povoleno pracovat s adresou prvku pole následujícího za posledním, což se provádí pro usnadnění zápisu algoritmů. Porovnávání adresových ukazatelů získaných pro různé proměnné (nebo paměťové oblasti) by však nemělo být prováděno, protože výsledek bude záviset na implementaci konkrétního kompilátoru [72] .

Reprezentace paměti

Paměťová reprezentace programu závisí na hardwarové architektuře, na operačním systému a na kompilátoru. Takže například na většině architektur zásobník roste dolů, ale jsou architektury, kde zásobník roste [73] . Hranici mezi zásobníkem a haldou lze částečně chránit před přetečením zásobníku speciální paměťovou oblastí [74] . A umístění dat a kódu knihoven může záviset na možnostech kompilace [75] . Standard C abstrahuje od implementace a umožňuje vám psát přenosný kód, ale pochopení struktury paměti procesu pomáhá při ladění a psaní bezpečných aplikací odolných proti chybám.

Typická reprezentace paměti procesů v operačních systémech podobných Unixu

Když je program spuštěn ze spustitelného souboru, instrukce procesoru (strojový kód) a inicializovaná data se importují do paměti RAM. Současně jsou do vyšších adres importovány argumenty příkazového řádku (dostupné ve funkcích main()s následujícím podpisem ve druhém argumentu int argc, char ** argv) a proměnné prostředí .

Oblast neinicializovaných dat obsahuje globální proměnné (včetně těch deklarovaných jako static), které nebyly inicializovány v kódu programu. Tyto proměnné jsou po spuštění programu standardně inicializovány na nulu. Oblast inicializovaných dat - datový segment - také obsahuje globální proměnné, ale tato oblast zahrnuje ty proměnné, kterým byla přidělena počáteční hodnota. Neměnná data, včetně proměnných deklarovaných s modifikátorem const, řetězcových literálů a dalších složených literálů, jsou umístěna do segmentu textu programu. Segment textu programu také obsahuje spustitelný kód a je pouze pro čtení, takže pokus o úpravu dat z tohoto segmentu bude mít za následek nedefinované chování ve formě chyby segmentace .

Oblast zásobníku má obsahovat data spojená s voláním funkcí a místními proměnnými. Před každým spuštěním funkce se zásobník rozšíří, aby se do něj vešly argumenty předané funkci. V průběhu své práce může funkce alokovat lokální proměnné na zásobníku a alokovat na něm paměť pro pole proměnné délky a některé kompilátory také poskytují prostředky pro alokaci paměti v zásobníku prostřednictvím volání alloca(), které není zahrnuto v jazykovém standardu. . Po skončení funkce se zásobník sníží na hodnotu, která byla před voláním, ale to se nemusí stát, pokud je zásobník zpracován nesprávně. Paměť alokovaná dynamicky je poskytována z haldy .

Důležitým detailem je přítomnost náhodného odsazení mezi zásobníkem a horní oblastí [77] , stejně jako mezi inicializovanou datovou oblastí a haldou . To se provádí z bezpečnostních důvodů, jako je zabránění ukládání dalších funkcí.

Knihovny dynamických odkazů a mapování souborů systému souborů jsou umístěny mezi zásobníkem a haldou [78] .

Zpracování chyb

C nemá žádné vestavěné mechanismy kontroly chyb, ale existuje několik obecně přijímaných způsobů, jak zacházet s chybami pomocí jazyka. Obecně platí, že praxe zpracování chyb C v kódu odolném proti chybám nutí psát těžkopádné, často se opakující konstrukce, ve kterých je algoritmus kombinován se zpracováním chyb .

Značky chyb a errno

Jazyk C aktivně využívá speciální proměnnou errnoz hlavičkového souboru errno.h, do které funkce zadávají kód chyby, přičemž vrací hodnotu, která je znakem chyby. Chcete-li zkontrolovat, zda výsledek neobsahuje chyby, je výsledek porovnán se značkou chyby, a pokud se shodují, můžete analyzovat kód chyby uložený v errnoprogramu a opravit program nebo zobrazit zprávu o ladění. Ve standardní knihovně standard často definuje pouze vrácené značky chyb a nastavení errnoje závislé na implementaci [79] .

Následující hodnoty obvykle fungují jako značky chyb:

  • -1pro typ intv případech, kdy není použit negativní rozsah výsledku [80] ;
  • -1pro typ ssize_t(POSIX) [81] ;
  • (size_t) -1pro typ size_t[80] ;
  • (time_t) -1při použití některých funkcí pro práci s časem [80] ;
  • NULLpro ukazatele [80] ;
  • EOFpři streamování souborů [80] ;
  • nenulový kód chyby [80] .

Praxe vracení chybové značky místo chybového kódu, i když ušetří počet argumentů předávaných funkci, v některých případech vede k chybám v důsledku lidského faktoru. Například je běžné, že programátoři ignorují kontrolu výsledku typu ssize_ta samotný výsledek se dále používá ve výpočtech, což vede k jemným chybám, pokud je vráceno -1[82] .

Vrácení správné hodnoty jako ukazatele chyby [82] dále přispívá k výskytu chyb , což také nutí programátora provádět více kontrol a podle toho psát více opakujícího se kódu stejného typu. Tento přístup se praktikuje ve funkcích proudu, které pracují s objekty typu FILE *: značka chyby je hodnota EOF, která je také značkou konce souboru. Někdy proto EOFmusíte zkontrolovat proud znaků jak na konec souboru pomocí funkce feof(), tak na přítomnost chyby pomocí ferror()[83] . Některé funkce, které se mohou vrátit EOFpodle normy, přitom není nutné nastavovat errno[79] .

Nedostatek jednotného postupu zpracování chyb ve standardní knihovně vede k tomu, že se objevují vlastní metody zpracování chyb a kombinace běžně používaných metod v projektech třetích stran. Například v projektu systemd byly zkombinovány myšlenky vracení chybového kódu a čísla -1jako značky – vrací se záporný chybový kód [84] . A knihovna GLib zavedla praxi vracení booleovské hodnoty jako značky chyby , zatímco podrobnosti o chybě jsou umístěny ve speciální struktuře, jejíž ukazatel je vrácen prostřednictvím posledního argumentu funkce [85] . Podobné řešení používá projekt Enlightenment , který také používá jako značku typ Boolean, ale vrací chybové informace podobné standardní knihovně – prostřednictvím samostatné funkce [86] , kterou je třeba zkontrolovat, zda byla značka vrácena.

Vrácení chybového kódu

Alternativou k chybovým značkám je vrátit kód chyby přímo a vrátit výsledek funkce prostřednictvím argumentů ukazatele. Touto cestou se vydali vývojáři standardu POSIX, v jehož funkcích je obvyklé vracet chybový kód jako číslo typu int. Vrácení hodnoty typu intvšak explicitně nedává najevo, že je to chybový kód, který se vrací, a nikoli token, což může vést k chybám, pokud je výsledek takových funkcí porovnán s hodnotou -1. Rozšíření K standardu C11 zavádí speciální typ errno_tpro uložení chybového kódu. Existují doporučení použít tento typ v uživatelském kódu k vracení chyb, a pokud není poskytován standardní knihovnou, deklarujte to sami [87] :

#ifndef __STDC_LIB_EXT1__ typedef int errno_t ; #endif

Tento přístup, kromě zlepšení kvality kódu, eliminuje potřebu používat errno, což vám umožňuje vytvářet knihovny s reentrantními funkcemi, aniž byste museli zahrnout další knihovny, jako jsou POSIX Threads , pro správné definování errno.

Chyby v matematických funkcích

Složitější je zpracování chyb v matematických funkcích z hlavičkového souboru math.h, ve kterém se mohou vyskytnout 3 typy chyb [88] :

  • překročení rozsahu vstupních hodnot;
  • získání nekonečného výsledku pro konečná vstupní data;
  • výsledek je mimo rozsah použitého datového typu.

Prevence dvou ze tří typů chyb spočívá v kontrole vstupních dat pro rozsah platných hodnot. Je však extrémně obtížné předpovědět výstup výsledku za limity typu. Jazykový standard proto poskytuje možnost analyzovat matematické funkce na chyby. Počínaje standardem C99 je tato analýza možná dvěma způsoby, v závislosti na hodnotě uložené v math_errhandling.

  1. Je-li bit nastaven MATH_ERRNO, musí být proměnná errnonejprve resetována na 0a po volání matematické funkce zkontrolovat chyby EDOMa ERANGE.
  2. Pokud je bit nastaven MATH_ERREXCEPT, pak se případné matematické chyby předem vynulují funkcí feclearexcept()z hlavičkového souboru fenv.ha po zavolání matematické funkce se otestují pomocí funkce fetestexcept().

V tomto případě je způsob zpracování chyb dán konkrétní implementací standardní knihovny a může zcela chybět. Proto v kódu nezávislém na platformě může být nutné zkontrolovat výsledek dvěma způsoby najednou, v závislosti na hodnotě math_errhandling[88] .

Uvolnění zdrojů

Výskyt chyby obvykle vyžaduje, aby se funkce ukončila a vrátila indikátor chyby. Pokud se ve funkci může vyskytnout chyba v různých jejích částech, je nutné uvolnit alokované zdroje během její činnosti, aby se předešlo únikům. Je dobrým zvykem uvolnit zdroje v opačném pořadí před návratem z funkce a v případě chyb v opačném pořadí po hlavní return. V samostatných částech takového uvolnění můžete skákat pomocí operátoru goto[89] . Tento přístup umožňuje přesouvat části kódu, které nesouvisejí s implementovaným algoritmem, mimo samotný algoritmus, čímž se zvyšuje čitelnost kódu a je podobný práci operátora deferz programovacího jazyka Go . Příklad uvolnění zdrojů je uveden níže v sekci příkladů .

K uvolnění prostředků v rámci programu je k dispozici mechanismus obsluhy ukončení programu. Obslužné rutiny jsou přiřazovány pomocí funkce atexit()a jsou prováděny jak na konci funkce main()prostřednictvím příkazu return, tak po provedení funkce exit(). V tomto případě nejsou handlery prováděny funkcemi abort()a _Exit()[90] .

Příkladem uvolnění zdrojů na konci programu je uvolnění paměti alokované pro globální proměnné. Navzdory skutečnosti, že paměť je uvolněna tak či onak po ukončení programu operačním systémem a je povoleno neuvolňovat paměť, která je vyžadována po celou dobu činnosti programu [91] , explicitní dealokace je výhodnější, protože umožňuje snazší najít úniky paměti pomocí nástrojů třetích stran a snižuje pravděpodobnost úniků paměti v důsledku chyby:

Ukázkový programový kód s vydáním zdroje #include <stdio.h> #include <stdlib.h> int pocet_cisel ; int * čísla ; void volná_čísla ( void ) { zdarma ( čísla ); } int main ( int argc , char ** argv ) { if ( arg < 2 ) { exit ( EXIT_FAILURE ); } cisla_pocet = atoi ( argv [ 1 ]); if ( počet_počtů <= 0 ) { exit ( EXIT_FAILURE ); } čísla = calloc ( počet_počtů , velikost ( * čísla )); if ( ! čísla ) { perror ( "Chyba při přidělování paměti pro pole" ); exit ( EXIT_FAILURE ); } atexit ( volná_čísla ); // ... práce s polem čísel // Zde bude automaticky volána obsluha free_numbers(). return EXIT_SUCCESS ; }

Nevýhodou tohoto přístupu je, že formát přiřaditelných handlerů neumožňuje předávání libovolných dat funkci, což umožňuje vytvářet handlery pouze pro globální proměnné.

Příklady programů C

Minimální program C

Minimální program v C, který nevyžaduje zpracování argumentů, je následující:

int main ( void ){}

Je povoleno nezapsat operátor returnpro funkci main(). V tomto případě, podle standardu, funkce main()vrátí 0 a provede všechny handlery přiřazené funkci exit(). To předpokládá, že program byl úspěšně dokončen [40] .

Ahoj světe!

Ahoj světe! je uveden v prvním vydání knihy " Programovací jazyk C " od Kernighan a Ritchie:

#include <stdio.h> int main ( void ) // Nebere žádné argumenty { printf ( "Ahoj světe! \n " ); // '\n' - nový řádek return 0 ; // Úspěšné ukončení programu }

Tento program vytiskne zprávu Hello, world! ' na standardním výstupu .

Zpracování chyb pomocí čtení souboru jako příkladu

Mnoho funkcí C může vrátit chybu, aniž by udělaly to, co měly. Chyby je třeba kontrolovat a správně na ně reagovat, včetně často potřeby přenést chybu z funkce na vyšší úroveň pro analýzu. Současně lze funkci, ve které došlo k chybě, provést reentrantní , v takovém případě by funkce omylem neměla změnit vstupní ani výstupní data, což umožňuje její bezpečný restart po opravě chybové situace.

Příklad implementuje funkci pro čtení souboru v C, ale vyžaduje, aby funkce fopen()a fread()standard POSIX vyhovovaly , jinak nemusí nastavit proměnnou errno, což značně komplikuje jak ladění, tak psaní univerzálního a bezpečného kódu. Na platformách jiných než POSIX bude chování tohoto programu v případě chyby nedefinované . Rozdělení zdrojů na chyby je za hlavním algoritmem pro zlepšení čitelnosti a přechod se provádí pomocí [89] . goto

Příklad kódu čtečky souborů se zpracováním chyb #include <errno.h> #include <stdio.h> #include <stdlib.h> // Definujte typ pro uložení chybového kódu, pokud není definován #ifndef __STDC_LIB_EXT1__ typedef int errno_t ; #endif enum { EOK = 0 , // hodnota errno_t při úspěchu }; // Funkce pro čtení obsahu souboru errno_t get_file_contents ( const char * název souboru , void ** content_ptr , size_t * content_size_ptr ) { SOUBOR * f ; f = fopen ( název souboru , "rb" ); if ( ! f ) { // V POSIXu fopen() omylem nastaví errno návrat errno ; } // Získání velikosti souboru fseek ( f , 0 , SEEK_END ); long content_size = ftell ( f ); if ( velikost_obsahu == 0 ) { * content_ptr = NULL ; * content_size_ptr = 0 ; goto čištění_fopen ; } přetočit zpět ( f ); // Proměnná pro uložení vráceného chybového kódu errno_t uloženo_errno ; neplatný * obsah ; obsah = malloc ( velikost_obsahu ); if ( ! obsah ) { save_errno = errno ; goto aborting_fopen ; } // Přečte celý obsah souboru na ukazateli obsahu velikost_t n ; n = fread ( obsah , velikost_obsahu , 1 , f ); if ( n == 0 ) { // Nekontrolovat feof(), protože se uloží do vyrovnávací paměti po fseek() // POSIX fread() omylem nastaví errno save_errno = errno ; goto aborting_contents ; } // Vrátí přidělenou paměť a její velikost * content_ptr = obsah ; * content_size_ptr = content_size ; // Sekce vydání zdrojů o úspěchu cleaning_fopen : fclose ( f ); vrátit EOK ; // Samostatná sekce pro uvolnění zdrojů omylem aborting_contents : zdarma ( obsah ); aborting_fopen : fclose ( f ); return save_errno ; } int main ( int argc , char ** argv ) { if ( arg < 2 ) { return EXIT_FAILURE ; } const char * název_souboru = argv [ 1 ]; errno_t errnum ; neplatný * obsah ; velikost_t velikost_obsahu ; errnum = get_file_contents ( název souboru , & obsah , & velikost_obsahu ); if ( errnum ) { charbuf [ 1024 ] ; const char * text_chyby = strerror_r ( errnum , buf , sizeof ( buf )); fprintf ( stderr , "%s \n " , text_chyby ); exit ( EXIT_FAILURE ); } printf ( "%.*s" , ( int ) obsah_velikost , obsah ); zdarma ( obsah ); return EXIT_SUCCESS ; }

Vývojové nástroje

Kompilátory

Některé kompilátory jsou dodávány s kompilátory pro jiné programovací jazyky (včetně C++ ) nebo jsou součástí vývojového prostředí softwaru .

  • GNU Compiler Collection (GCC) plně podporuje standardy C99 a C17 ( C11 s opravami) [92] . Podporuje také rozšíření GNU, ochranu kódu pomocí dezinfekčních prostředků a řadu dalších funkcí, včetně atributů.
  • Clang také plně podporuje standardy C99 [93] a C17 [94] . Vyvinuto tak, aby bylo do značné míry kompatibilní s kompilátorem GCC, včetně podpory rozšíření GNU a ochrany kódu dezinfekce.
Implementace standardní knihovny

Navzdory skutečnosti, že standardní knihovna je součástí jazykového standardu, její implementace jsou oddělené od kompilátorů. Proto se jazykové standardy podporované kompilátorem a knihovnou mohou lišit.

Integrovaná vývojová prostředí
  • CLion plně podporuje C99, ale podpora C11 je částečná [99] , sestavení je založeno na CMake.
  • Code::Blocks  je bezplatné multiplatformní integrované vývojové prostředí pro jazyky C, C++, D, Fortran. Podporuje více než dvě desítky kompilátorů. S kompilátorem GCC jsou k dispozici všechny verze C od C90 do C17.
  • Eclipse  je bezplatné IDE, které podporuje standardní jazyk C C99. Má modulární architekturu, která umožňuje propojit podporu pro různé programovací jazyky a další funkce. K dispozici je integrační modul Git , ale neexistuje žádná integrace CMake .
  • KDevelop  je bezplatné IDE, které podporuje některé funkce jazyka C ze standardu C11. Umožňuje spravovat projekty pomocí různých programovacích jazyků, včetně C++ a Pythonu , podporuje sestavení systému CMake. Má vestavěnou podporu pro Git na úrovni souborů a přizpůsobitelné formátování zdrojového kódu pro různé jazyky.
  • Microsoft Visual Studio podporuje standardy C99 a C11 jen částečně, protože se zaměřuje na vývoj v C++, ale má vestavěnou podporu pro CMake.
Nástroje pro testování jednotek

Vzhledem k tomu, že jazyk C neposkytuje prostředky pro bezpečné psaní kódu a mnoho prvků jazyka přispívá k chybám, může být psaní vysoce kvalitního kódu odolného proti chybám zaručeno pouze psaním automatizovaných testů. Pro usnadnění takového testování existují různé implementace knihoven testů jednotek třetích stran .

  • Knihovna Check poskytuje rámec pro testování kódu C v běžném stylu xUnit . Z možností můžeme zmínit spouštění testů v samostatných procesech přes fork(), což umožňuje rozpoznat chyby segmentace v testech [100] , a také umožňuje nastavit maximální dobu provádění jednotlivých testů.
  • Knihovna Google Test také poskytuje testování ve stylu xUnit, ale je navržena pro testování kódu C++ , což umožňuje použití i pro testování kódu C. Podporuje také izolované testování jednotlivých částí programu. Jednou z výhod knihovny je oddělení testovacích maker na aserce a chyby, což může usnadnit ladění kódu.

Existuje také mnoho dalších systémů pro testování C kódu, jako je AceUnit, GNU Autounit, cUnit a další, ale ty buď netestují v izolovaných prostředích, poskytují málo funkcí [100] nebo se již nevyvíjejí.

Nástroje pro ladění

Podle projevů chyb není vždy možné vyvodit jednoznačný závěr o problémové oblasti v kódu, často však k lokalizaci problému pomáhají různé ladicí nástroje.

  • Gdb  je interaktivní konzolový debugger pro různé jazyky, včetně C.
  • Valgrind je nástroj pro dynamickou analýzu kódu, který dokáže detekovat chyby v kódu přímo během provádění programu. Podporuje detekci: úniků, přístupů do neinicializované paměti, přístupů na neplatné adresy (včetně přetečení vyrovnávací paměti). Podporuje také provádění v režimu profilování pomocí profilovacího nástroje callgrind [101] .
  • KCacheGrind  je grafické rozhraní pro vizualizaci výsledků profilování získaných pomocí profilovacího nástroje callgrind [ 102] .
Kompilátory pro dynamické jazyky a platformy

Někdy, aby bylo možné přenést určité knihovny, funkce a nástroje napsané v C do jiného prostředí, je nutné zkompilovat kód C do jazyka vyšší úrovně nebo do kódu virtuálního stroje určeného pro takový jazyk. Pro tento účel jsou navrženy následující projekty:

Další nástroje

Také pro C existují další nástroje, které usnadňují a doplňují vývoj, včetně statických analyzátorů a nástrojů pro formátování kódu. Statická analýza pomáhá identifikovat potenciální chyby a zranitelnosti. A automatické formátování kódu zjednodušuje organizaci spolupráce v systémech správy verzí a minimalizuje konflikty způsobené změnami stylu.

  • Cppcheck je analyzátor statického kódu s otevřeným zdrojovým  kódem pro C a C++ , který někdy poskytuje falešná pozitiva, která lze potlačit speciálně formátovanými komentáři v kódu.
  • Clang-format  je nástroj příkazového řádku pro formátování zdrojového kódu podle daného stylu, který lze specifikovat ve speciálně vytvořeném konfiguračním souboru. Má mnoho možností a několik vestavěných stylů. Vyvinuto jako součást projektu Clang [107] .
  • Obslužné programy Indent a GNU Indent také poskytují formátování kódu, ale možnosti formátování jsou specifikovány jako volby příkazového řádku [108] .

Rozsah

Jazyk je široce používán při vývoji operačního systému, na úrovni API operačního systému, ve vestavěných systémech a pro psaní vysoce výkonného nebo chybově kritického kódu. Jedním z důvodů širokého přijetí nízkoúrovňového programování je schopnost psát multiplatformní kód, se kterým lze na různém hardwaru a operačních systémech zacházet různě.

Schopnost psát vysoce výkonný kód jde na úkor naprosté svobody jednání pro programátora a absence přísné kontroly ze strany kompilátoru. Například první implementace Java , Python , Perl a PHP byly napsány v C. Zároveň jsou v mnoha programech části, které jsou nejvíce náročné na zdroje, obvykle napsány v C. Jádro Mathematicy [109] je napsáno v C, zatímco MATLAB , původně napsaný ve Fortranu , byl přepsán do C v roce 1984 [110] .

C se také někdy používá jako střední jazyk při kompilaci jazyků vyšší úrovně. Podle tohoto principu fungovaly například první implementace jazyků C++ , Objective-C a Go – kód napsaný v těchto jazycích byl přeložen do střední reprezentace v jazyce C. Moderní jazyky, které fungují na stejném principu, jsou Vala a Nim .

Další oblastí použití jazyka C jsou aplikace v reálném čase , které jsou náročné na odezvu kódu a dobu jeho provádění. Takové aplikace musí zahájit provádění akcí v přísně omezeném časovém rámci a samotné akce se musí vejít do určitého časového období. Zejména standard POSIX.1 poskytuje sadu funkcí a schopností pro vytváření aplikací v reálném čase [111] [112] [113] , ale tvrdá podpora v reálném čase musí být implementována také operačním systémem [114] .

Potomkové jazyky

Jazyk C byl a zůstává jedním z nejpoužívanějších programovacích jazyků již více než čtyřicet let. Jeho vliv lze přirozeně do určité míry vysledovat v mnoha pozdějších jazycích. Nicméně mezi jazyky, které dosáhly určité distribuce, existuje jen málo přímých potomků C.

Některé potomkové jazyky staví na C s dalšími nástroji a mechanismy, které přidávají podporu pro nová programovací paradigmata ( OOP , funkcionální programování , generické programování atd.). Mezi tyto jazyky patří především C++ a Objective-C a nepřímo jejich potomci Swift a D. Známé jsou také pokusy zlepšit C nápravou jeho nejvýznamnějších nedostatků, ale zachováním jeho atraktivních vlastností. Mezi nimi můžeme zmínit výzkumný jazyk Cyclone (a jeho potomka Rust ). Někdy se oba směry vývoje kombinují v jednom jazyce, příkladem je Go .

Samostatně je třeba zmínit celou skupinu jazyků, které ve větší či menší míře zdědily základní syntaxi C (použití složených závorek jako oddělovačů bloků kódu, deklarace proměnných, charakteristické formy operátorů for, while, if, switchs parametry v závorkách, kombinované operace ++, --, +=, -=a další), proto mají programy v těchto jazycích charakteristický vzhled spojený specificky s C. Jedná se o jazyky jako Java , JavaScript , PHP , Perl , AWK , C# . Ve skutečnosti se struktura a sémantika těchto jazyků velmi liší od jazyka C a jsou obvykle určeny pro aplikace, kde původní jazyk C nebyl nikdy použit.

C++

Programovací jazyk C++ byl vytvořen z C a zdědil jeho syntaxi a doplnil jej o nové konstrukce v duchu Simula-67, Smalltalk, Modula-2, Ada, Mesa a Clu [116] . Hlavními doplňky byla podpora pro OOP (popis třídy, vícenásobná dědičnost, polymorfismus založený na virtuálních funkcích) a generické programování (šablonový engine). Ale kromě toho bylo do jazyka provedeno mnoho různých dodatků. V současné době je C++ jedním z nejrozšířenějších programovacích jazyků na světě a je umístěn jako univerzální jazyk s důrazem na systémové programování [117] .

Zpočátku si C++ zachoval kompatibilitu s C, což bylo uváděno jako jedna z výhod nového jazyka. První implementace C++ jednoduše překládaly nové konstrukce do čistého C, poté byl kód zpracován běžným kompilátorem C. Aby byla zachována kompatibilita, tvůrci C++ z něj odmítli vyloučit některé z často kritizovaných funkcí C, místo toho vytvořili nové, „paralelní“ mechanismy, které se doporučují při vývoji nového kódu C++ (šablony místo maker, explicitní typové přetypování místo automatického standardní kontejnery knihoven namísto ručního dynamického přidělování paměti atd.). Jazyky se však od té doby vyvíjely nezávisle a nyní jsou C a C++ nejnovějších vydaných standardů kompatibilní pouze částečně: neexistuje žádná záruka, že kompilátor C++ úspěšně zkompiluje program C, a pokud bude úspěšný, neexistuje žádná záruka, že zkompilovaný program poběží správně. Obzvláště nepříjemné jsou některé jemné sémantické rozdíly, které mohou vést k odlišnému chování stejného kódu, který je syntakticky správný pro oba jazyky. Například znakové konstanty (znaky uzavřené v jednoduchých uvozovkách) mají typ intv C a typ charv C++ , takže množství paměti zabrané takovými konstantami se liší jazyk od jazyka. [118] Pokud je program citlivý na velikost znakové konstanty, bude se při kompilaci pomocí kompilátorů C a C++ chovat jinak.

Rozdíly, jako jsou tyto, ztěžují psaní programů a knihoven, které mohou kompilovat a pracovat stejným způsobem v C i C++ , což samozřejmě mate ty, kteří programují v obou jazycích. Mezi vývojáři a uživateli C i C++ jsou zastánci minimalizace rozdílů mezi jazyky, což by objektivně přineslo hmatatelné výhody. Existuje však i opačný názor, podle něhož kompatibilita není nijak zvlášť důležitá, i když je užitečná, a snahy o snížení nekompatibility by neměly bránit zdokonalování každého jazyka jednotlivě.

Objective-C

Další možností pro rozšíření jazyka C o objektově založené nástroje je jazyk Objective-C vytvořený v roce 1983. Objektový subsystém byl vypůjčen od Smalltalk a všechny prvky spojené s tímto subsystémem jsou implementovány ve své vlastní syntaxi, která se značně liší od syntaxe C (až do skutečnosti, že v popisech tříd je syntaxe pro deklarování polí opačná než syntaxe pro deklaraci proměnných v C: nejprve se zapíše název pole, pak jeho typ). Na rozdíl od C++ je Objective-C nadmnožinou klasického C, to znamená, že si zachovává kompatibilitu se zdrojovým jazykem; správný program C je správný program Objective-C. Dalším významným rozdílem od ideologie C++ je, že Objective-C implementuje interakci objektů výměnou plnohodnotných zpráv, zatímco C++ implementuje koncept „odeslání zprávy jako volání metody“. Plné zpracování zpráv je mnohem flexibilnější a přirozeně se hodí k paralelnímu zpracování. Objective-C, stejně jako jeho přímý potomek Swift , patří mezi nejoblíbenější na platformách podporovaných společností Apple .

Problémy a kritika

Jazyk C je jedinečný v tom, že to byl první jazyk na vysoké úrovni , který vážně nahradil assembler ve vývoji systémového softwaru . Zůstává jazykem implementovaným na největším počtu hardwarových platforem a jedním z nejpopulárnějších programovacích jazyků , zejména ve světě svobodného softwaru [119] . Přesto má jazyk mnoho nedostatků, od svého vzniku je kritizován mnoha odborníky.

Obecná kritika

Jazyk je velmi složitý a plný nebezpečných prvků, které lze velmi snadno zneužít. Svou strukturou a pravidly nepodporuje programování zaměřené na tvorbu spolehlivého a udržovatelného programového kódu, naopak, zrozený v éře přímého programování pro různé procesory, jazyk přispívá k psaní nebezpečného a matoucího kódu [119] . Mnoho profesionálních programátorů si myslí, že jazyk C je mocný nástroj pro tvorbu elegantních programů, ale zároveň s ním lze vytvářet extrémně nekvalitní řešení [120] [121] .

Kvůli různým předpokladům v jazyce mohou programy kompilovat s více chybami, což často vede k nepředvídatelnému chování programu. Moderní kompilátory poskytují možnosti pro statickou analýzu kódu [122] [123] , ale ani ony nejsou schopny odhalit všechny možné chyby. Negramotné programování v C může mít za následek zranitelnosti softwaru , což může ovlivnit bezpečnost jeho použití.

Xi má vysoký práh vstupu [119] . Jeho specifikace zabírá více než 500 stran textu, který je nutné prostudovat celý, protože pro vytvoření bezchybného a kvalitního kódu je třeba vzít v úvahu mnoho nesrozumitelných vlastností jazyka. Například automatické přetypování operandů celočíselných výrazů na typ intmůže poskytnout obtížně předvídatelné výsledky při použití binárních operátorů [44] :

znak bez znaménka x = 0xFF ; unsigned char y = ( ~ x | 0x1 ) >> 1 ; // Intuitivně se zde očekává 0x00 printf ( "y = 0x%hhX \n " , y ); // Vytiskne 0x80, pokud sizeof(int) > sizeof(char)

Nepochopení takových nuancí může vést k četným chybám a zranitelnostem. Dalším faktorem, který zvyšuje složitost ovládání C, je nedostatek zpětné vazby od kompilátoru: jazyk dává programátorovi naprostou svobodu jednání a umožňuje kompilovat programy se zjevnými logickými chybami. To vše ztěžuje použití C ve výuce jako prvního programovacího jazyka [119]

Konečně za více než 40 let existence jazyk poněkud zastaral a je problematické v něm používat mnoho moderních programovacích technik a paradigmat .

Nevýhody určitých prvků jazyka

Podpora primitivní modularity

V syntaxi C nejsou žádné moduly a mechanismy pro jejich interakci. Soubory zdrojového kódu se kompilují samostatně a musí obsahovat prototypy proměnných, funkcí a datových typů importovaných z jiných souborů. To se provádí zahrnutím hlavičkových souborů pomocí substituce maker . V případě narušení korespondence mezi soubory kódu a soubory záhlaví může dojít k chybám v době propojení a všem druhům chyb za běhu: od poškození zásobníku a haldy až po chyby segmentace . Vzhledem k tomu, že směrnice pouze nahrazuje text jednoho souboru do jiného, ​​zahrnutí velkého počtu hlavičkových souborů vede k tomu, že skutečné množství kódu, který se zkompiluje, mnohonásobně vzroste, což je důvodem relativně pomalého výkonu C kompilátory. Potřeba koordinovat popisy v hlavním modulu a hlavičkových souborech ztěžuje údržbu programu. #include#include

Varování místo chyb

Jazyková norma dává programátorovi větší volnost jednání a tím i vysokou šanci na chyby. Mnoho z toho, co je nejčastěji zakázáno, je povoleno jazykem a kompilátor vydává v nejlepším případě varování. Přestože moderní kompilátory umožňují převést všechna varování na chyby, tato funkce se používá zřídka a častěji jsou varování ignorována, pokud program běží uspokojivě.

Takže například před standardem C99 mohlo volání funkce mallocbez zahrnutí hlavičkového souboru stdlib.hvést k poškození zásobníku, protože při absenci prototypu byla funkce volána jako vracející typ int, zatímco ve skutečnosti vracela typ void*( došlo k chybě, když se velikosti typů na cílové platformě lišily). I tak to bylo jen varování.

Nedostatek kontroly nad inicializací proměnných

Automaticky a dynamicky vytvářené objekty nejsou ve výchozím nastavení inicializovány a po vytvoření obsahují hodnoty zbylé v paměti z objektů, které tam byly dříve. Taková hodnota je zcela nepředvídatelná, liší se od jednoho stroje k druhému, běh od běhu, od volání funkce k volání. Pokud program takovou hodnotu použije kvůli náhodnému opomenutí inicializace, pak bude výsledek nepředvídatelný a nemusí se objevit okamžitě. Moderní kompilátory se snaží diagnostikovat tento problém statickou analýzou zdrojového kódu, i když obecně je extrémně obtížné tento problém vyřešit statickou analýzou. K identifikaci těchto problémů ve fázi testování během provádění programu lze použít další nástroje: Valgrind a MemorySanitizer [124] .

Nedostatek kontroly nad aritmetikou adres

Zdrojem nebezpečných situací je kompatibilita ukazatelů s numerickými typy a možnost použití adresní aritmetiky bez přísné kontroly ve fázích kompilace a provádění. To umožňuje získat ukazatel na jakýkoli objekt, včetně spustitelného kódu, a odkazovat se na tento ukazatel, pokud tomu nezabrání systémový mechanismus ochrany paměti .

Nesprávné použití ukazatelů může způsobit nedefinované chování programu a vést k vážným následkům. Ukazatel může být například neinicializován nebo v důsledku nesprávných aritmetických operací může ukazovat na libovolné místo v paměti. Na některých platformách může práce s takovým ukazatelem vynutit zastavení programu, na jiných může dojít k poškození libovolných dat v paměti; Poslední chyba je nebezpečná, protože její důsledky jsou nepředvídatelné a mohou se objevit kdykoli, včetně mnohem později, než je okamžik skutečného chybného jednání.

Přístup k polím v C je také implementován pomocí aritmetiky adres a neznamená prostředky pro kontrolu správnosti přístupu k prvkům pole indexem. Například výrazy a[i]a i[a]jsou identické a jsou jednoduše přeloženy do formuláře *(a + i)a kontrola pole mimo hranice se neprovádí. Přístup na index vyšší než horní mez pole vede k přístupu k datům umístěným v paměti za polem, což se nazývá přetečení vyrovnávací paměti . Když je takové volání chybné, může vést k nepředvídatelnému chování programu [57] . Tato funkce se často používá v exploitech používaných k nelegálnímu přístupu k paměti jiné aplikace nebo paměti jádra operačního systému.

Dynamická paměť náchylná k chybám

Systémové funkce pro práci s dynamicky alokovanou pamětí nezajišťují kontrolu nad správností a včasností jejího přidělení a uvolnění, dodržení správného pořadí práce s dynamickou pamětí je plně v kompetenci programátora. Jeho chyby mohou vést k přístupu na nesprávné adresy, k předčasnému uvolnění nebo k úniku paměti (to je možné například v případě, že vývojář zapomněl zavolat free()nebo zavolat volající free()funkci, když to bylo požadováno) [125] .

Jednou z častých chyb je nekontrolování výsledku funkcí alokace paměti ( malloc()a calloc()dalších) na NULL, přičemž paměť nemusí být přidělena, pokud jí není dostatek nebo pokud bylo požadováno příliš mnoho, například kvůli redukce čísla -1přijatého v důsledku jakýchkoli chybných matematických operací na typ bez znaménka size_t, s následnými operacemi na něm . Dalším problémem s funkcemi systémové paměti je nespecifikované chování při požadavku na alokaci bloku s nulovou velikostí: funkce mohou vracet buď hodnotu reálného ukazatele nebo hodnotu reálného ukazatele, v závislosti na konkrétní implementaci [126] . NULL

Některé specifické implementace a knihovny třetích stran poskytují funkce, jako je počítání referencí a slabé reference [127] , chytré ukazatele [128] a omezené formy garbage collection [129] , ale všechny tyto funkce nejsou standardní, což přirozeně omezuje jejich použití .

Neefektivní a nebezpečné řetězce

Pro jazyk jsou standardní řetězce zakončené nulou , takže s nimi pracují všechny standardní funkce. Toto řešení vede k výrazné ztrátě efektivity v důsledku nevýznamné úspory paměti (ve srovnání s explicitním uložením velikosti): výpočet délky řetězce (funkce ) vyžaduje procházení celého řetězce od začátku do konce, kopírování řetězců je také obtížné optimalizovat díky přítomnosti ukončovací nuly [48] . Kvůli potřebě přidat ukončovací null k datům řetězce je nemožné efektivně získat podřetězce jako řezy a pracovat s nimi jako s běžnými řetězci; alokace a manipulace s částmi řetězců obvykle vyžaduje ruční alokaci a dealokaci paměti, což dále zvyšuje pravděpodobnost chyby. strlen()

Řetězce zakončené nulou jsou častým zdrojem chyb [130] . Ani standardní funkce obvykle nekontrolují velikost cílové vyrovnávací paměti [130] a nemusí přidat znak null [131] na konec řetězce , nemluvě o tom, že nemusí být přidán nebo přepsán kvůli chybě programátoru. [132] .

Nebezpečná implementace variadických funkcí

Zatímco C podporuje funkce s proměnným počtem argumentů , neposkytuje ani prostředek pro určení počtu a typů skutečných parametrů předávaných takové funkci, ani mechanismus pro bezpečný přístup k nim [133] . Informování funkce o složení skutečných parametrů spočívá na programátorovi a pro přístup k jejich hodnotám je nutné spočítat správný počet bajtů z adresy posledního pevného parametru na zásobníku, a to buď ručně, nebo pomocí sady makra va_argz hlavičkového souboru stdarg.h. Zároveň je nutné vzít v úvahu fungování mechanismu automatického implicitního povýšení typu při volání funkcí [134] , podle kterého intjsou celočíselné typy argumentů menší než přetypovány na int(nebo unsigned int), ale floatpřetypovány na double. Chyba ve volání nebo v práci s parametry uvnitř funkce se objeví až při provádění programu, což vede k nepředvídatelným následkům, od načtení nesprávných dat až po poškození zásobníku.

Standardním prostředkem formátovaných I/O jsou přitom funkce s proměnným počtem parametrů ( printf(), scanf()a další), které nejsou schopny zkontrolovat, zda se seznam argumentů shoduje s formátovacím řetězcem . Mnoho moderních kompilátorů provádí tuto kontrolu pro každé volání a generuje varování, pokud najdou neshodu, ale obecně tato kontrola není možná, protože každá variadic funkce zpracovává tento seznam jinak. Není možné staticky řídit ani všechna volání funkcí printf(), protože formátovací řetězec lze dynamicky vytvořit v programu.

Nedostatek sjednocení zpracování chyb

Syntaxe C nezahrnuje speciální mechanismus zpracování chyb. Standardní knihovna podporuje pouze ty nejjednodušší prostředky: proměnnou (v případě POSIX  makro) errnoz hlavičkového souboru errno.hpro nastavení posledního chybového kódu a funkce pro získání chybových zpráv podle kódů. Tento přístup vede k potřebě psát velké množství opakujícího se kódu, mísícího hlavní algoritmus se zpracováním chyb, a kromě toho není bezpečný pro vlákna. Navíc ani v tomto mechanismu neexistuje jediný řád:

  • při chybě a samotný kód musí být získán , pokud jej funkce odhalí;-1errno
  • v POSIX je obvyklé vracet chybový kód přímo, ale ne všechny funkce POSIX tak činí;
  • v mnoha funkcích, například , fopen()a fread(), fwrite()není nastavení errnostandardizováno a může se lišit v různých implementacích [79] (v POSIX jsou požadavky přísnější a některé možnosti pro možné chyby jsou specifikovány );
  • existují funkce, kde je chybová značka jednou z povolených návratových hodnot a před jejich voláním je nutné nastavit na nulu errno, abyste měli jistotu, že chybový kód byl nastaven touto funkcí [79] .

Ve standardní knihovně jsou kódy errnourčeny prostřednictvím definic maker a mohou mít stejné hodnoty, což znemožňuje analýzu chybových kódů prostřednictvím operátora switch. Jazyk nemá speciální datový typ pro příznaky a chybové kódy, předávají se jako hodnoty typu int. Samostatný typ errno_tpro uložení chybového kódu se objevil pouze v rozšíření K standardu C11 a nemusí být podporován kompilátory [87] .

Způsoby, jak překonat nedostatky jazyka

Nedostatky jazyka C jsou již dlouho dobře známé a od počátku tohoto jazyka došlo k mnoha pokusům o zlepšení kvality a bezpečnosti kódu C, aniž by byly obětovány jeho schopnosti.

Prostředky analýzy správnosti kódu

Téměř všechny moderní kompilátory C umožňují omezenou analýzu statického kódu s varováním o potenciálních chybách. Podporovány jsou také možnosti pro vkládání kontrol pro pole mimo meze, zničení zásobníku, mimo limity haldy, čtení neinicializovaných proměnných, nedefinovaného chování atd. do kódu. Dodatečné kontroly však mohou ovlivnit výkon finální aplikace, takže jsou nejčastěji se používá pouze ve fázi ladění.

Existují speciální softwarové nástroje pro statickou analýzu kódu C pro detekci nesyntaktických chyb. Jejich použití nezaručuje bezchybnost programů, ale umožňuje identifikovat významnou část typických chyb a potenciálních zranitelností. Maximálního účinku těchto nástrojů je dosaženo nikoli při občasném použití, ale při použití jako součást zaběhnutého systému neustálé kontroly kvality kódu, například v systémech průběžné integrace a nasazení. Může být také nutné opatřit kód speciálními komentáři, aby se vyloučily falešné poplachy analyzátoru na správných úsecích kódu, které formálně spadají pod kritéria pro chybné.

Bezpečné programovací standardy

O správném programování v C bylo publikováno značné množství výzkumů, od malých článků po dlouhé knihy. Pro zachování kvality kódu C jsou přijaty podnikové a průmyslové standardy. Zejména:

  • MISRA C  je standard vyvinutý asociací Motor Industry Software Reliability Association pro použití C při vývoji vestavěných systémů vozidel. Nyní se MISRA C používá v mnoha průmyslových odvětvích, včetně vojenského, lékařského a leteckého průmyslu. Vydání z roku 2013 obsahuje 16 směrnic a 143 pravidel, včetně požadavků na kód a omezení používání určitých jazykových vlastností (například je zakázáno používání funkcí s proměnlivým počtem parametrů). Na trhu existuje asi tucet nástrojů pro kontrolu kódu MISRA C a několik kompilátorů s vestavěnou kontrolou omezení MISRA C.
  • CERT C Coding Standard  je standard vyvíjený Koordinačním centrem CERT [135] . Jeho cílem je také poskytovat spolehlivé a bezpečné programování v jazyce C. Zahrnuje pravidla a pokyny pro vývojáře, včetně příkladů nesprávného a správného kódu případ od případu. Standard používají při vývoji produktů společnosti jako Cisco a Oracle [136] .
POSIX standardy

Sada standardů POSIX přispívá k vyrovnání některých nedostatků jazyka . Instalace je standardizována errnomnoha funkcemi, které umožňují ošetřit chyby, ke kterým dochází například při operacích se soubory, a jsou zavedeny vláknově bezpečné analogy některých funkcí standardní knihovny, jejichž bezpečné verze jsou v jazykové normě přítomny pouze v rozšíření K [137] .

Viz také

Poznámky

Komentáře

  1. B je druhé písmeno anglické abecedy a C je třetí písmeno anglické abecedy .
  2. Makro boolz hlavičkového souboru stdbool.htvoří obal nad klíčovým slovem _Bool.
  3. Makro complexz hlavičkového souboru complex.htvoří obal nad klíčovým slovem _Complex.
  4. Makro imaginaryz hlavičkového souboru complex.htvoří obal nad klíčovým slovem _Imaginary.
  5. Makro alignasz hlavičkového souboru stdalign.htvoří obal nad klíčovým slovem _Alignas.
  6. 1 2 3 Makro alignofz hlavičkového souboru stdalign.htvoří obal nad klíčovým slovem _Alignof.
  7. Makro noreturnz hlavičkového souboru stdnoreturn.htvoří obal nad klíčovým slovem _Noreturn.
  8. Makro static_assertz hlavičkového souboru assert.htvoří obal nad klíčovým slovem _Static_assert.
  9. Makro thread_localz hlavičkového souboru threads.htvoří obal nad klíčovým slovem _Thread_local.
  10. 1 2 3 4 5 6 7 První výskyt podepsaných a nepodepsaných typů , a charbyl shortv K&R C.intlong
  11. 1 2 Shoda formátu typu s floatnormou doubleIEC 60559 je definována příponou C F, takže formát se může na jednotlivých platformách nebo kompilátorech lišit.

Zdroje

  1. 1 2 http://www.bell-labs.com/usr/dmr/www/chist.html
  2. Rui Ueyama. Jak jsem napsal self-hosting C kompilátor za 40 dní  . www.sigbus.info (prosinec 2015). Získáno 18. února 2019. Archivováno z originálu dne 23. března 2019.
  3. A garbage collector pro C a C++ Archivováno 13. října 2005 na Wayback Machine 
  4. Objektově orientované programování s ANSI-C Archivováno 6. března 2016 na Wayback Machine 
  5. Okamžitě klasifikované typy:  objekty . Referenční příručka GObject . developer.gnome.org. Staženo 27. 5. 2019. Archivováno z originálu 27. 5. 2019.
  6. Neinstanciovatelné klasifikované typy:  rozhraní . Referenční příručka GObject . developer.gnome.org. Staženo 27. 5. 2019. Archivováno z originálu 27. 5. 2019.
  7. 1 2 Návrh standardu C17 , 5.2.1 Znakové sady, str. 17.
  8. 12 Návrh normy C17 , 6.4.2 Identifikátory, str. 43-44.
  9. Návrh normy C17 , 6.4.4 Konstanty, str. 45-50.
  10. 1 2 Podbelský, Fomin, 2012 , str. 19.
  11. 12 Návrh normy C17 , 6.4.4.1 Celočíselné konstanty, str. 46.
  12. Návrh normy C17 , 6.4.4.2 Plovoucí konstanty, str. 47-48.
  13. 1 2 Návrh normy C17 , 6.4.4.4 Znakové konstanty, str. 49-50.
  14. STR30-C.  Nepokoušejte se upravovat řetězcové literály - SEI CERT C Coding Standard - Confluence . wiki.sei.cmu.edu. Staženo 27. 5. 2019. Archivováno z originálu 27. 5. 2019.
  15. Návrh standardu C17 , 6.4.5 Řetězcové literály, str. 50-52.
  16. Možnosti stylu Clang-Format –  dokumentace Clang 9 . clang.llvm.org. Získáno 19. května 2019. Archivováno z originálu dne 20. května 2019.
  17. ↑ 1 2 3 4 DCL06-C. Použijte smysluplné symbolické konstanty k reprezentaci doslovných hodnot - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Staženo 6. února 2019. Archivováno z originálu 7. února 2019.
  18. 1 2 Návrh normy C17 , str. 84.
  19. Návrh normy C17 , 6.4.1 Klíčová slova, str. 42.
  20. ↑ 1 2 Free Software Foundation (FSF). Stav funkcí C99 v GCC  . Projekt GNU . gcc.gnu.org. Staženo 31. 5. 2019. Archivováno z originálu 3. 6. 2019.
  21. 1 2 3 4 Návrh standardu C17 , 7.1.3 Vyhrazené identifikátory, str. 132.
  22. Návrh standardu C17 , 6.5.3 Unární operátory, str. 63-65.
  23. Návrh normy C17 , 6.5 Výrazy, str. 66-72.
  24. Návrh standardu C17 , 6.5.16 Operátoři přiřazení, str. 72-74.
  25. Návrh normy C17 , str. 55-75.
  26. ↑ 1 2 Referenční příručka GNU C . 3.19  Přednost operátora . www.gnu.org . Staženo 13. února 2019. Archivováno z originálu 7. února 2019.
  27. ↑ 1 2 3 4 5 EXP30-C. Nezáleží na pořadí hodnocení vedlejších účinků - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Staženo 14. února 2019. Archivováno z originálu 15. února 2019.
  28. ↑ 12BB . _ Definice - Standard kódování SEI CERT C -  Confluence . wiki.sei.cmu.edu. Staženo 16. února 2019. Archivováno z originálu 16. února 2019.
  29. Podbelský, Fomin, 2012 , 1.4. Operace, str. 42.
  30. Podbelský, Fomin, 2012 , 2.3. Příkazy smyčky, str. 78.
  31. ↑ 12 EXP19 -C. Použijte složené závorky pro tělo příkazu if, for nebo while - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Staženo 2. června 2019. Archivováno z originálu 2. června 2019.
  32. Dynamically Loaded (DL)  knihovny . tldp.org. Získáno 18. února 2019. Archivováno z originálu 12. listopadu 2020.
  33. 1 2 Návrh normy C17, 6.7.4 Specifikátory funkcí , str. 90-91.
  34. PRE00-C. Upřednostňujte inline nebo statické funkce před funkčně podobnými makry - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Získáno 4. června 2019. Archivováno z originálu dne 7. srpna 2021.
  35. Návrh normy C17 , 6.11 Budoucí jazykové směry, str. 130.
  36. Podporuje C přetížení funkcí? | GeeksforGeeks . Datum přístupu: 15. prosince 2013. Archivováno z originálu 15. prosince 2013.
  37. Referenční příručka GNU C . www.gnu.org. Získáno 21. května 2017. Archivováno z originálu dne 27. dubna 2021.
  38. Šířka typu (Knihovna GNU C  ) . www.gnu.org. Staženo 7. prosince 2018. Archivováno z originálu 9. prosince 2018.
  39. Návrh normy C17 , 6.2.5 Typy, str. 31.
  40. ↑ 1 2 Společná technická komise ISO/IEC JTC 1. ISO/IEC 9899:201x. Programovací jazyky - C. - ISO / IEC, 2011. - S. 14. - 678 s. Archivováno 30. května 2017 na Wayback Machine
  41. Zkontrolujte 0.10.0: 4. Pokročilé  funkce . Zkontrolujte . check.sourceforge.net. Staženo 11. 2. 2019. Archivováno z originálu 18. 5. 2018.
  42. Typ konverzních maker: GLib Reference  Manual . developer.gnome.org. Staženo 14. ledna 2019. Archivováno z originálu 14. ledna 2019.
  43. INT01-C. Použijte rsize_t nebo size_t pro všechny celočíselné hodnoty představující velikost objektu - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Získáno 22. února 2019. Archivováno z originálu dne 7. srpna 2021.
  44. ↑ 1 2 3 INT02-C. Pochopte pravidla převodu celých čísel - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Datum přístupu: 22. února 2019. Archivováno z originálu 22. února 2019.
  45. FLP02-C. Vyhněte se používání čísel s plovoucí desetinnou čárkou, když je potřeba přesný výpočet - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Získáno 21. května 2019. Archivováno z originálu dne 7. srpna 2021.
  46. 1 2 Návrh normy C17 , IEC 60559 aritmetika s plovoucí desetinnou čárkou, str. 370.
  47. Návrh normy C17 , 7.12 Matematika <math.h>, str. 169-170.
  48. ↑ 1 2 Tábor Poul-Henning. Nejdražší chyba jednoho bajtu - ACM  Queue . queue.acm.org (25. července 2011). Staženo 28. 5. 2019. Archivováno z originálu 30. 4. 2019.
  49. ↑ 1 2 unicode(7) – manuálová  stránka Linuxu . man7.org. Staženo 24. února 2019. Archivováno z originálu 25. února 2019.
  50. ↑ 1 2 3 wchar_t mess - GNU libunistring  . www.gnu.org. Staženo 2. ledna 2019. Archivováno z originálu 17. září 2019.
  51. ↑ Programování se širokými znaky  . linux.com | Zdroj informací o Linuxu (11. února 2006). Získáno 7. června 2019. Archivováno z originálu 7. června 2019.
  52. Markus Kuhn . UTF-8 a Unicode FAQ  . www.cl.cam.ac.uk. Získáno 25. února 2019. Archivováno z originálu dne 27. února 2019.
  53. Souhrn hlášení závad pro C11 . www.open-std.org. Staženo 2. ledna 2019. Archivováno z originálu 1. ledna 2019.
  54. ↑ Standardní výčty : Referenční příručka GTK+ 3  . developer.gnome.org. Staženo 15. ledna 2019. Archivováno z originálu 14. ledna 2019.
  55. ↑ Vlastnosti objektu : Referenční příručka GObject  . developer.gnome.org. Staženo 15. ledna 2019. Archivováno z originálu 16. ledna 2019.
  56. Použití GNU Compiler Collection (GCC): Společné  atributy typu . gcc.gnu.org. Datum přístupu: 19. ledna 2019. Archivováno z originálu 16. ledna 2019.
  57. ↑ 12 ARR00 -C.  Pochopte, jak pole fungují - SEI CERT C Coding Standard - Confluence . wiki.sei.cmu.edu. Staženo 30. 5. 2019. Archivováno z originálu 30. 5. 2019.
  58. ARR32-C. Ujistěte se, že argumenty velikosti pro pole s proměnnou délkou jsou v platném rozsahu - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Staženo 18. února 2019. Archivováno z originálu 19. února 2019.
  59. Návrh standardu C17 , 6.7.9 Inicializace, str. 101.
  60. DCL38-C. Při deklaraci člena flexibilního pole použijte správnou syntaxi - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Staženo 21. února 2019. Archivováno z originálu 22. února 2019.
  61. Verze OpenSSL  . _ www.openssl.org. Získáno 9. prosince 2018. Archivováno z originálu 9. prosince 2018.
  62. ↑ Informace o verzi : Referenční příručka GTK+ 3  . developer.gnome.org. Získáno 9. prosince 2018. Archivováno z originálu 16. listopadu 2018.
  63. PRE10-C. Zabalte vícepříkazová makra do smyčky do-while - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Získáno 9. prosince 2018. Archivováno z originálu 9. prosince 2018.
  64. PRE01-C.  Používejte závorky v makrech kolem názvů parametrů - SEI CERT C Coding Standard - Confluence . wiki.sei.cmu.edu. Získáno 9. prosince 2018. Archivováno z originálu 9. prosince 2018.
  65. PRE06-C. Soubory hlaviček uzavřete do začlenění - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Staženo 25. 5. 2019. Archivováno z originálu 25. 5. 2019.
  66. 1 2 Návrh C17, 5.1.2.2 Hostované prostředí , str. 10-11.
  67. 1 2 3 Návrh standardu C17 , 6.2.4 Doba uložení objektů, str. třicet.
  68. 1 2 Návrh C17, 7.22.4.4 Funkce odchodu , str. 256.
  69. MEM05-C. Vyhněte se alokaci velkého zásobníku - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Staženo 24. 5. 2019. Archivováno z originálu 24. 5. 2019.
  70. C17 Draft , 6.7.1 Specifikace třídy úložiště, str. 79.
  71. Návrh normy C17 , 6.7.6.3 Deklarace funkcí (včetně prototypů), str. 96.
  72. Ukazatele v C jsou abstraktnější, než si možná myslíte . www.viva64.com. Staženo 30. prosince 2018. Archivováno z originálu 30. prosince 2018.
  73. Tanenbaum Andrew S, Bos Herbert. moderní operační systémy. 4. vyd . - Petrohrad. : Nakladatelství Piter, 2019. - S. 828. - 1120 s. — (Klasika "Informatika"). — ISBN 9785446111558 . Archivováno 7. srpna 2021 na Wayback Machine
  74. Jonathan Corbet. Vlnky z Stack  Clash . lwn.net (28. června 2017). Staženo 25. 5. 2019. Archivováno z originálu 25. 5. 2019.
  75. Kalení binárních souborů ELF pomocí Relocation Read-Only (RELRO  ) . www.redhat.com. Staženo 25. 5. 2019. Archivováno z originálu 25. 5. 2019.
  76. Tradiční procesní adresní prostor – statický  program . www.openbsd.org. Získáno 4. března 2019. Archivováno z originálu 8. prosince 2019.
  77. Doktor Thabang Mokoteli. ICMLG 2017 5. mezinárodní konference Management Leadership and Governance . - Akademické konference a publikování v omezeném množství, 2017-03. - S. 42. - 567 s. — ISBN 9781911218289 . Archivováno 7. srpna 2021 na Wayback Machine
  78. Tradiční procesní adresní prostor – program se sdílenými  knihovnami . www.openbsd.org. Získáno 4. března 2019. Archivováno z originálu 8. prosince 2019.
  79. ↑ 1 2 3 4 ERR30-C. Před voláním funkce knihovny, o které je známo, že nastavuje errno, nastavte errno na nulu a errno zkontrolujte až poté, co funkce vrátí hodnotu indikující selhání - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Staženo 23. 5. 2019. Archivováno z originálu 19. 11. 2018.
  80. ↑ 1 2 3 4 5 6 ERR33-C. Detekce a zpracování chyb standardní knihovny - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Staženo 23. 5. 2019. Archivováno z originálu 23. 5. 2019.
  81. sys_types.h.0p – manuálová  stránka Linuxu . man7.org. Staženo 23. 5. 2019. Archivováno z originálu 23. 5. 2019.
  82. ↑ 12 ERR02 -C. Vyhněte se indikátorům chyb uvnitř pásma - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Získáno 4. ledna 2019. Archivováno z originálu 5. ledna 2019.
  83. FIO34-C. Rozlišujte mezi znaky načtenými ze souboru a EOF nebo WEOF-SEI CERT C Coding Standard-  Confluence . wiki.sei.cmu.edu. Datum přístupu: 4. ledna 2019. Archivováno z originálu 4. ledna 2019.
  84. Styl  kódování . Systémový správce systému a služeb . github.com. Staženo 1. února 2019. Archivováno z originálu 31. prosince 2020.
  85. ↑ Hlášení chyb : Referenční příručka GLib  . developer.gnome.org. Staženo 1. února 2019. Archivováno z originálu 2. února 2019.
  86. ↑ Eina : Chyba  . docs.enlightenment.org. Staženo 1. února 2019. Archivováno z originálu 2. února 2019.
  87. ↑ 1 2 DCL09-C. Funkce , které vracejí errno , deklarujte s návratovým typem errno_t - SEI CERT C Coding Standard - Confluence . wiki.sei.cmu.edu. Staženo 21. prosince 2018. Archivováno z originálu 21. prosince 2018.
  88. ↑ 1 2 FLP32-C. Prevence nebo detekce doménových a rozsahových chyb v matematických funkcích - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Staženo 5. ledna 2019. Archivováno z originálu 5. ledna 2019.
  89. ↑ 12 MEM12 -C. Zvažte použití goto chain při ponechání funkce na chybě při používání a uvolňování zdrojů - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Získáno 4. ledna 2019. Archivováno z originálu 5. ledna 2019.
  90. ERR04-C.  Zvolte vhodnou ukončovací strategii - SEI CERT C Coding Standard - Confluence . wiki.sei.cmu.edu. Získáno 4. ledna 2019. Archivováno z originálu 5. ledna 2019.
  91. MEM31-C. Uvolněte dynamicky alokovanou paměť, když již není potřeba - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Staženo 6. ledna 2019. Archivováno z originálu 6. ledna 2019.
  92. Použití GNU Compiler Collection (GCC):  Standardy . gcc.gnu.org. Získáno 23. února 2019. Archivováno z originálu 17. června 2012.
  93. Jazyková kompatibilita  . clang.llvm.org. Staženo 23. února 2019. Archivováno z originálu 19. února 2019.
  94. Clang 6.0.0 Release Notes - dokumentace Clang 6 . releases.llvm.org. Staženo 23. února 2019. Archivováno z originálu 23. února 2019.
  95. Siddhesh Poyarekar - Knihovna GNU C verze 2.29 je nyní  k dispozici . sourceware.org. Staženo 2. února 2019. Archivováno z originálu 2. února 2019.
  96. Alpine Linux přešel na musl libc |  Alpine Linux . alpinelinux.org. Staženo 2. února 2019. Archivováno z originálu 3. února 2019.
  97. musl - Void Linux Handbook . docs.voidlinux.org . Získáno 29. ledna 2022. Archivováno z originálu dne 9. prosince 2021.
  98. Vlastnosti knihovny CRT . docs.microsoft.com. Získáno 2. února 2019. Archivováno z originálu dne 7. srpna 2021.
  99. Podporované jazyky ​​– Funkce | CLion  (anglicky) . proudové mozky. Staženo 23. února 2019. Archivováno z originálu dne 25. března 2019.
  100. ↑ 1 2 Kontrola 0.10.0: 2. Testování jednotek v C  . check.sourceforge.net. Získáno 23. února 2019. Archivováno z originálu 5. června 2018.
  101. ↑ 6. Callgrind : call-graf generující cache a profiler predikce větví  . Dokumentace Valgrind . valgrind.org. Staženo 21. 5. 2019. Archivováno z originálu 23. 5. 2019.
  102. Kcachegrind . kcachegrind.sourceforge.net. Staženo 21. 5. 2019. Archivováno z originálu 6. 4. 2019.
  103. Kompilátor Emscripten LLVM-to-JavaScript . Získáno 25. září 2012. Archivováno z originálu 17. prosince 2012.
  104. Flash C++ kompilátor . Získáno 25. ledna 2013. Archivováno z originálu 25. května 2013.
  105. Project Clue na SourceForge.net
  106. Axiomatic Solutions Sdn Bhd . Datum přístupu: 7. března 2009. Archivováno z originálu 23. února 2009.
  107. Dokumentace ClangFormat - Clang 9  . clang.llvm.org. Získáno 5. března 2019. Archivováno z originálu dne 6. března 2019.
  108. indent(1) – Manuová  stránka Linuxu . linux.die.net. Získáno 5. března 2019. Archivováno z originálu 13. května 2019.
  109. Wolfram Research, Inc. ROZHRANÍ A ZAVÁDĚNÍ SYSTÉMŮ  . Wolfram Mathematica® Tutorial Collection 36-37. library.wolfram.com (2008). Staženo 29. 5. 2019. Archivováno z originálu 6. 9. 2015.
  110. Cleve Moler. Růst MATLABu a The MathWorks během dvou desetiletí . Novinky a poznámky TheMathWorks . www.mathworks.com (leden 2006). Staženo 29. 5. 2019. Archivováno z originálu 4. 3. 2016.
  111. sched_setscheduler  . _ pubs.opengroup.org. Datum přístupu: 4. února 2019. Archivováno z originálu 24. února 2019.
  112. clock_gettime  . _ pubs.opengroup.org. Datum přístupu: 4. února 2019. Archivováno z originálu 24. února 2019.
  113. clock_nanosleep  . _ pubs.opengroup.org. Datum přístupu: 4. února 2019. Archivováno z originálu 24. února 2019.
  114. M. Jones. Anatomie architektur Linuxu v reálném čase . www.ibm.com (30. října 2008). Staženo 4. února 2019. Archivováno z originálu 7. února 2019.
  115. Index  TIOBE . www.tiobe.com . Staženo 2. února 2019. Archivováno z originálu 25. února 2018.
  116. Stroustrup, Bjarne Vývoj jazyka v reálném světě a pro něj: C++ 1991-2006 . Získáno 9. července 2018. Archivováno z originálu dne 20. listopadu 2007.
  117. Stroustrup FAQ . www.stroustrup.com. Staženo 3. června 2019. Archivováno z originálu 6. února 2016.
  118. Příloha 0: Kompatibilita. 1.2. C++ a ISO C. Pracovní dokument pro návrh navrhovaného mezinárodního standardu pro informační systémy – programovací jazyk C++ (2. prosince 1996). — viz 1.2.1p3 (odstavec 3 v části 1.2.1). Získáno 6. června 2009. Archivováno z originálu 22. srpna 2011.
  119. 1 2 3 4 Stolyarov, 2010 , 1. Předmluva, s. 79.
  120. Kronika jazyků. Si _ Nakladatelství "Otevřené systémy". Získáno 8. prosince 2018. Archivováno z originálu 9. prosince 2018.
  121. Allen I. Holub. Dostatek lana, abyste se vystřelili do nohy: Pravidla pro programování v C a C++ . - McGraw-Hill, 1995. - 214 s. — ISBN 9780070296893 . Archivováno 9. prosince 2018 na Wayback Machine
  122. Použití GNU Compiler Collection (GCC): Možnosti varování . gcc.gnu.org. Získáno 8. prosince 2018. Archivováno z originálu 5. prosince 2018.
  123. Diagnostické příznaky v dokumentaci Clang - Clang 8 . clang.llvm.org. Získáno 8. prosince 2018. Archivováno z originálu 9. prosince 2018.
  124. Dokumentace MemorySanitizer - Clang 8  . clang.llvm.org. Staženo 8. prosince 2018. Archivováno z originálu 1. prosince 2018.
  125. MEM00-C. Alokovat a uvolnit paměť ve stejném modulu, na stejné úrovni abstrakce - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Staženo 4. června 2019. Archivováno z originálu 4. června 2019.
  126. MEM04-C. Pozor na alokace nulové délky - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Staženo 11. ledna 2019. Archivováno z originálu 12. ledna 2019.
  127. Správa paměti objektů: GObject Reference Manual . developer.gnome.org. Získáno 9. prosince 2018. Archivováno z originálu dne 7. září 2018.
  128. Například snai.pe c-smart-pointers Archivováno 14. srpna 2018 na Wayback Machine
  129. Garbage Collection v programech C . Staženo 16. 5. 2019. Archivováno z originálu 27. 3. 2019.
  130. ↑ 1 2 Informace o počítačové bezpečnosti CERN . security.web.cern.ch. Staženo 12. ledna 2019. Archivováno z originálu 5. ledna 2019.
  131. CWE - CWE-170: Nesprávné nulové ukončení (3.2  ) . cwe.mitre.org. Staženo 12. ledna 2019. Archivováno z originálu 13. ledna 2019.
  132. STR32-C. Nepředávejte sekvenci znaků neukončenou nulou do funkce knihovny, která očekává řetězec - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Staženo 12. ledna 2019. Archivováno z originálu 13. ledna 2019.
  133. DCL50-CPP. Nedefinujte variadickou funkci ve stylu C - SEI CERT C++ Coding Standard -  Confluence . wiki.sei.cmu.edu. Staženo 25. 5. 2019. Archivováno z originálu 25. 5. 2019.
  134. EXP47-C. Nevolejte va_arg s argumentem nesprávného typu - SEI CERT C Coding Standard -  Confluence . wiki.sei.cmu.edu. Získáno 8. prosince 2018. Archivováno z originálu 9. prosince 2018.
  135. SEI CERT C kódovací standard - SEI CERT C kódovací standard - Confluence . wiki.sei.cmu.edu. Staženo 9. prosince 2018. Archivováno z originálu 8. prosince 2018.
  136. Úvod - SEI CERT C Coding Standard - Confluence . wiki.sei.cmu.edu. Staženo 24. 5. 2019. Archivováno z originálu 24. 5. 2019.
  137. CON33-C.  Při používání funkcí knihovny se vyvarujte rasových podmínek - SEI CERT C Coding Standard - Confluence . wiki.sei.cmu.edu. Staženo 23. ledna 2019. Archivováno z originálu 23. ledna 2019.

Literatura

  • ISO/IEC. ISO/IEC9899:2017 . Programovací jazyky - C (downlink) . www.open-std.org (2017) . Staženo 3. prosince 2018. Archivováno z originálu dne 24. října 2018. 
  • Kernigan B. , Ritchie D. Programovací jazyk C = Programovací jazyk C. - 2. vyd. - M .: Williams , 2007. - S. 304. - ISBN 0-13-110362-8 .
  • Gukin D. Programovací jazyk C pro figuríny = C For Dummies. - M . : Dialektika , 2006. - S. 352. - ISBN 0-7645-7068-4 .
  • Podbelsky V. V., Fomin S. S. Kurz programování v jazyce C: učebnice . - M. : DMK Press, 2012. - 318 s. - ISBN 978-5-94074-449-8 .
  • Prata S. Programovací jazyk C: Přednášky a cvičení = C Primer Plus. - M. : Williams, 2006. - S. 960. - ISBN 5-8459-0986-4 .
  • Prata S. Programovací jazyk C (C11). Přednášky a cvičení, 6. vydání = C Primer Plus, 6. vydání. - M. : Williams, 2015. - 928 s. - ISBN 978-5-8459-1950-2 .
  • Stolyarov A. V. Jazyk C a počáteční programovací školení  // Sbírka článků mladých vědců z fakulty CMC MSU. - Vydavatelské oddělení fakulty CMC Moskevské státní univerzity, 2010. - č. 7 . - S. 78-90 .
  • Schildt G. C: The Complete Reference, Classic Edition = C: The Complete Reference, 4th Edition. - M .: Williams , 2010. - S. 704. - ISBN 978-5-8459-1709-6 .
  • Programovací jazyky Ada, C, Pascal = Porovnávání a hodnocení programovacích jazyků Ada, C a Pascal / A. Feuer, N. Jehani. - M .: Radio and Sayaz, 1989. - 368 s. — 50 000 výtisků.  — ISBN 5-256-00309-7 .

Odkazy