VS | ||
Datum první verze | 1972 | |
---|---|---|
Paradigma | Imperativní , procedurální , strukturovaný | |
Autor | Dennis Ritchie , Brian Kernighan | |
Vývojář | Dennis Ritchie a Kenneth Thompson, Bell Labs | |
Psaní na stroji | Statický , slabý | |
Standardy | ANSI X3.159-1989 (ANSI C, C89) ISO / IEC 9899: 1990 (C90) ISO / IEC 9899: 1990 / AMD1: 1995 (C95) ISO / IEC 9899: 1999 (C99) ISO / IEC 9899: 2011 ( C11) ISO / IEC 9899: 2018 (C18) |
|
Ovlivněno | BCPL , B , Algol 68 , Fortran | |
Ovlivněno | awk , csh , C ++ , C # , Objective-C , D , souběžný C , Java , JavaScript , PHP , Perl | |
Implementace | GCC , MSVC , Borland C , Clang , TCC | |
Přípony souborů | .c, .h | |
Jedná se o obecný, nízkoúrovňový imperativní programovací jazyk . C, který vznikl na začátku 70. let za účelem přepsání systému UNIX , se stal jedním z nejpoužívanějších jazyků i dnes. Mnoho modernějších jazyků, jako je C ++ , C # , Java a PHP nebo Javascript, si vzalo syntaxi podobnou C a částečně si vzalo zpět svoji logiku. C nabízí vývojáři významnou míru kontroly nad strojem (zejména nad správou paměti), a proto se používá k vytváření „základů“ (překladačů, tlumočníků atd.) Těchto modernějších jazyků.
Jazyk C byl vynalezen v roce 1972 v Bell Laboratories . Byl vyvinut ve stejnou dobu jako UNIX od Dennis Ritchie a Kenneth Thompson. Kenneth Thompson vyvinul předchůdce jazyka C, jazyka B , který je sám inspirován BCPL . Dennis Ritchie vyvinul jazyk B do nové verze, která byla dostatečně odlišná, včetně přidávání typů , takže se jí říkalo C.
Ačkoli C je oficiálně inspirován B a BCPL, zaznamenáváme silný vliv PL / I (nebo PL360); mohli bychom říci, že C byl pro Unix a PDP-11, co PL / I pro přepis Multics .
Brian Kernighan následně pomohl popularizovat jazyk C. Rovněž došlo k několika změnám na poslední chvíli.
V roce 1978 byl Kernighan hlavním autorem Programovacího jazyka C, který popisoval konečně stabilizovaný jazyk; Ritchie se postaral o dodatky a příklady s Unixem. Tato kniha se také nazývá „K&R“ a mluvíme o tradičních C nebo C K&R, když mluvíme o jazyce, jaký v té době existoval.
V roce 1983 se American National Standards Institute (ANSI) tvořil jazykové normy výbor (X3J11), která v roce 1989 vyvrcholila tzv ANSI C nebo C89 (formálně ANSI X3.159-1989) standard. V roce 1990 byla tato norma přijata také Mezinárodní organizací pro normalizaci ( C90, C ISO , formálně ISO / IEC 9899: 1990). ANSI C je vývoj K&R C, který zůstává extrémně kompatibilní. Zabírá některé nápady C ++ , zejména představu o prototypech a kvalifikátorech typů.
V letech 1994 až 1996 pracovní skupina ISO (ISO / IEC JTC1 / SC22 / WG14) zveřejnila dvě opravy a jednu změnu C90: ISO / IEC 9899 / COR1: 1994 Technická oprava 1 , ISO / IEC 9899 / AMD1: 1995 Integrita C a ISO / IEC 9899 / COR1: 1996 Technická oprava 2 . Tyto poměrně skromné změny se někdy označují jako C89 s pozměňovacím návrhem 1 nebo C94 / C95. Byly přidány tři hlavičkové soubory, z nichž dva se týkají širokých znaků a další definující řadu maker souvisejících se znakovým standardem ISO 646 .
V roce 1999 byl nový vývoj jazyka standardizován podle ISO : C99 (formálně ISO / IEC 9899: 1999). Mezi nové funkce patří pole s proměnnou velikostí, omezené ukazatele, komplexní čísla, složené literály, smíšené příkazy s příkazy, vložené funkce , pokročilá podpora s plovoucí desetinnou čárkou a syntaxe komentářů C ++. C 99 Standardní knihovny byl obohacen o šesti hlavičkové soubory od předchozího standardu.
V roce 2011 , ISO ratifikuje novou verzi standardu: C11 , formálně ISO / IEC 9899: 2011. Tato změna byla zavedena, včetně podpory programování s více vlákny , výrazů v takové generice a lepší podpory Unicode .
Je to nezbytný a univerzální programovací jazyk . Je kvalifikován jako nízkoúrovňový jazyk v tom smyslu, že každá instrukce jazyka je navržena tak, aby byla zkompilována do řady strojových instrukcí, které jsou poměrně předvídatelné, pokud jde o obsazení paměti a výpočetní zátěž. Kromě toho nabízí řadu celé číslo a plovoucí čárkou typy navržen tak, aby bylo možné přímo odpovídají datové typy podporovaných procesorem . Nakonec intenzivně využívá výpočty adres paměti s konceptem ukazatele .
Kromě základních typů podporuje C výčtové , složené a neprůhledné typy . Nenabízí však žádnou operaci, která přímo zpracovává objekty vyšší úrovně ( počítačový soubor , řetězec znaků , seznam , hashovací tabulka atd.). Tyto více vyvinuté typy je třeba řešit manipulací s ukazateli a složenými typy. Stejně tak jazyk standardně nenabízí správu objektově orientovaného programování ani systém správy výjimek . K dispozici jsou standardní funkce pro správu vstupně-výstupních a znakových řetězců , ale na rozdíl od jiných jazyků neexistuje žádný konkrétní operátor, který by zlepšil ergonomii. To usnadňuje nahrazení standardních funkcí funkcemi speciálně navrženými pro daný program.
Tyto vlastnosti z něj činí preferovaný jazyk při pokusu o ovládání použitých hardwarových prostředků, přičemž jazyk stroje a binární data generovaná kompilátory jsou relativně předvídatelné. Tento jazyk je proto široce používán v oblastech, jako je vestavěné programování na mikrokontrolérech , intenzivní výpočty, psaní operačních systémů a modulů, kde je důležitá rychlost zpracování. Je to dobrá alternativa k montážnímu jazyku v těchto oblastech s výhodami expresivnější syntaxe a přenositelnosti zdrojového kódu . Jazyk C byl vynalezen pro psaní operačního systému UNIX a stále se používá pro programování systému. Proto se jádro velkých operačních systémů, jako jsou Windows a Linux, vyvíjí převážně v C.
Na druhou stranu je vývoj programů v jazyce C, zejména pokud používají složité datové struktury, obtížnější než v jazycích vyšších úrovní. Z důvodu výkonu vyžaduje jazyk C uživatele, aby naprogramoval určité procesy (uvolnění paměti, kontrola platnosti indexů na tabulkách atd.), O které se v jazycích na vysoké úrovni automaticky postará.
C je zbavený výhod poskytovaných standardní knihovnou a C je jednoduchý jazyk, stejně jako jeho kompilátor . Cítíme to v době vývoje kompilátoru C pro novou architekturu procesoru : Kernighan a Ritchie odhadli, že by mohl být vyvinut za dva měsíce, protože „všimneme si, že 80% kódu nového kompilátoru je identických s kódem kódy jiných existujících překladačů. "
Je to jeden z nejpoužívanějších jazyků, protože:
Jeho hlavní nevýhody jsou:
Svět Hello Program je nabízen jako například v roce 1978 v programovacím jazyce C by Brian Kernighan a Dennis Ritchie . Vytvoření programu zobrazujícího „ hello world “ se od té doby stalo příkladem ukázky základů nového jazyka. Zde je původní příklad 1 st Edition 1978:
main() { printf("hello, world\n"); }Stejný program v souladu s normou ISO a následujícími současnými osvědčenými postupy:
#include <stdio.h> int main(void) { printf("hello, world\n"); return 0; }Syntaxe C byla navržena tak, aby byla stručná. Historicky se často porovnává s jazykem Pascala , imperativního jazyka, který byl také vytvořen v 70. letech . Zde je příklad s faktoriální funkcí :
/* En C (norme ISO) */ int factorielle(int n) { return n > 1 ? n * factorielle(n - 1) : 1; } { En Pascal } function factorielle(n: integer) : integer begin if n > 1 then factorielle := n * factorielle(n - 1) else factorielle := 1 end.Kde Pascal 7 pomocí klíčových slov ( function, integer, begin, if, then, elsea end), C používá pouze 2 ( inta return).
Jazyk výrazůStručnost C není jen o syntaxi. Velký počet dostupných operátorů , skutečnost, že většina příkazů obsahuje výraz, že výrazy téměř vždy vytvářejí hodnotu a že testovací příkazy porovnávají pouze hodnotu testovaného výrazu s nulou, to vše přispívá ke stručnosti zdrojového kódu.
Zde je příklad funkce kopírování řetězce - jejímž principem je kopírování znaků, dokud nezkopírujete nulový znak, který podle konvence označuje konec řetězce v jazyce C - uvedený v The C Programming Language, 2. vydání , str. 106 :
void strcpy(char *s, char *t) { while (*s++ = *t++) ; }Smyčka whilepoužívá klasický styl psaní C, který mu pomohl dát si reputaci špatně čitelného jazyka. Výraz *s++ = *t++obsahuje: dvě dereference ukazatele ; dva přírůstky ukazatele; úkol ; a přiřazená hodnota se porovná s nulou pomocí while. Tato smyčka nemá žádné tělo, protože všechny operace jsou prováděny v testovacím výrazu while. Považujeme za nutné zvládnout tento druh notace, abychom zvládli C.
Pro srovnání by verze nepoužívající operátory zkratky a implicitní nulové srovnání poskytla:
void strcpy(char *s, char *t) { while (*t != '\0') { *s = *t; s = s + 1; t = t + 1; } *s = *t; }Program napsaný v jazyce C je obvykle rozdělen do několika zdrojových souborů sestavených samostatně.
C zdrojové soubory jsou textové soubory , obvykle v kódování znaků hostitelského systému. Mohou být psány pomocí jednoduchého textového editoru . Existuje mnoho editorů, dokonce i integrovaných vývojových prostředí (IDE), které mají specifické funkce pro podporu psaní zdrojů C.
Účelem je uvést název souboru rozšíření .c a .hzdrojové soubory C. Soubory .hse nazývají hlavičkové soubory , anglické hlavičky . Jsou navrženy tak, aby byly zahrnuty na začátku zdrojových souborů, a obsahují pouze příkazy .
Když soubor .cnebo .hpoužívá identifikátor deklarovaný v jiném souboru .h, pak zahrnuje druhý. Zásada, která se obecně používá, spočívá v napsání souboru .hpro každý soubor .ca v souboru deklarovat .hvše, co soubor exportuje .c.
Generování spustitelného souboru ze zdrojových souborů se provádí v několika krocích, které se často automatizují pomocí nástrojů, jako jsou make , SCons nebo nástroje specifické pro integrované vývojové prostředí. Ze zdroje do spustitelného souboru vedou čtyři kroky: předkompilace , kompilace , sestavení , úpravy odkazů . Když je projekt kompilován, pouze soubory .cjsou součástí seznamu souborů ke kompilaci; soubory .hjsou zahrnuty směrnicemi preprocesoru obsaženými ve zdrojových souborech.
Preprocesor C provádí směrnice obsažené ve zdrojových souborech. Pozná je podle toho, že jsou na začátku řádku a všechny začínají znakem kříže # . Mezi nejběžnější pokyny patří:
Kromě provádění směrnic preprocesor nahrazuje komentáře mezerou a nahrazuje makra. Zbytek se zdrojový kód přenáší jako takový do kompilátoru pro další fázi. Každý #includeve zdrojovém kódu však musí být rekurzivně nahrazen zahrnutým zdrojovým kódem. Kompilátor tedy přijímá jeden zdroj z preprocesoru, který tvoří kompilační jednotku.
Zde je příklad zdrojového souboru, který copyarray.hklasicky využívá direktivy preprocesoru:
#ifndef COPYARRAY_H #define COPYARRAY_H #include <stddef.h> void copyArray(int *, size_t); #endifSměrnice #ifndef, #definea #endifzajišťují, že kód uvnitř je kompilován pouze jednou, i když je zahrnut vícekrát. Směrnice #include <stddef.h>obsahuje záhlaví, které deklaruje typ size_tpoužitý níže.
Fáze kompilace obecně sestává z generování kódu sestavení . Toto je nejintenzivnější fáze léčby. Provádí jej samotný překladač. Pro každou kompilační jednotku získáme soubor jazyka sestavení.
Tento krok lze rozdělit do dílčích kroků:
Zneužíváním jazyka lze volat kompilaci celou fázi generování spustitelného souboru počínaje od zdrojových souborů. Ale toto je pouze jeden z kroků vedoucích k vytvoření spustitelného souboru.
Některé překladače jazyka C na této úrovni fungují ve dvou fázích, první generuje soubor kompilovaný v mezilehlém jazyce určeném pro ideální virtuální stroj (viz Bytecode nebo P-Code ) přenosný z jedné platformy na druhou, druhá převádí mezilehlý jazyk v assembleru jazyk závislý na cílové platformě. Jiné překladače jazyka C neumožňují generování montážního jazyka, ale pouze soubor zkompilovaný v mezilehlém jazyce , který bude automaticky interpretován nebo zkompilován v nativním kódu za běhu na cílovém počítači ( virtuálním strojem, který bude propojen ve finální verzi program).
Tento krok spočívá v generování souboru objektu v strojovém jazyce pro každý soubor kódu sestavy. Soubory objektů jsou obecně rozšířeny .ona Unix a .objs vývojovými nástroji pro MS-DOS , Microsoft Windows , VMS , CP / M … Tato fáze je někdy seskupena s předchozí fází vytvořením interního toku dat bez předávání souborů v prostředním jazyce nebo montážní jazyk. V tomto případě kompilátor přímo vygeneruje soubor objektu.
Pro překladače, které generují přechodný kód, lze tuto fázi sestavení také zcela vyloučit: jedná se o virtuální stroj, který bude tento jazyk interpretovat nebo kompilovat do nativního strojového kódu. Virtuální stroj může být součástí operačního systému nebo sdílené knihovny .
Propojení je posledním krokem a jeho cílem je spojit všechny prvky programu. Různé soubory objektů se poté spojí, stejně jako statické knihovny, aby se vytvořil pouze jeden spustitelný soubor.
Účelem propojení je vybrat užitečné prvky kódu přítomné v sadě kompilovaných kódů a knihoven a vyřešit vzájemné odkazy mezi těmito různými prvky, aby jim umožnily přímo odkazovat na provádění programu. Propojení selže, pokud chybí odkazované prvky kódu.
ASCII znaků sada je dostačující zapisovat v C. Je dokonce možné, ale neobvyklé, omezit jen na invariantní znakové sady ISO 646 standardu , pomocí escape sekvence nazývané trigraph. Zdroje C se obvykle zapisují pomocí znakové sady hostitelského systému. Je však možné, že znaková sada provedení není znakem sady zdroje.
C rozlišuje velká a malá písmena . Bílé znaky ( mezera , tabulátor , konec řádku ) lze pro rozvržení volně použít, protože jsou ve většině případů ekvivalentní jednomu mezeru.
C89 má 32 klíčových slov, z nichž pět v K&R C neexistovalo a které jsou v abecedním pořadí:
auto, break, case, char, const(C89), continue, default, do, double, else, enum(C89), extern, float, for, goto, if, int, long, register, return, short, signed(C89), sizeof, static, struct, switch, typedef, union, unsigned, void(C89), volatile(C89) while.Toto jsou vyhrazené termíny a neměly by být použity jinak.
Revize C99 přidává pět:
_Bool, _Complex, _Imaginary, inline, restrict.Tato nová klíčová slova začínají velkým písmenem s předponou podtržítka, aby byla maximalizována kompatibilita se stávajícími kódy. Standardní záhlaví knihovny poskytují aliasy bool( <stdbool.h>) complexa imaginary( <complex.h>).
Nejnovější revize C11 zavádí dalších sedm nových klíčových slov se stejnými konvencemi:
_Alignas, _Alignof, _Atomic, _Generic, _Noreturn, _Static_assert, _Thread_local.Standardní záhlaví <stdalign.h>, <stdnoreturn.h>, <assert.h>a <threads.h>poskytovat aliasů, respektive alignasi alignof, noreturn, static_asserta thread_local.
Na jazyk C preprocesoru nabídky těchto směrnic:
#include, #define, #pragma(C89) #if, #ifdef, #ifndef, #elif(C89), #else, #endif, #undef, #line, #error.Jazyk C chápe mnoho druhů z čísel , která zabírá více nebo méně bitů . Velikost typů je standardizována pouze částečně: standard nastavuje pouze minimální velikost a minimální velikost. Minimální velikosti jsou kompatibilní s jinými binárními reprezentacemi než s dvojkovým doplňkem , i když se tato reprezentace v praxi používá téměř vždy. Tato flexibilita umožňuje efektivní přizpůsobení jazyka široké škále procesorů , ale komplikuje přenositelnost programů napsaných v jazyce C.
Každý typ celého čísla má „podepsaný“ formulář, který může představovat záporná a kladná čísla, a „nepodepsaný“ formulář, který může představovat pouze přirozená čísla . Podepsané a nepodepsané tvary musí mít stejnou velikost.
Nejběžnějším typem je int, že představuje slovo stroj.
Na rozdíl od mnoha jiných jazyků je type charceločíselný typ jako každý jiný, i když se obecně používá k reprezentaci znaků. Jeho velikost je podle definice jeden bajt .
Typ | Minimální kapacita pro zastoupení | Minimální velikost požadovaná normou |
---|---|---|
char | jako signed charnebo unsigned char, v závislosti na implementaci | 8 bitů |
signed char | -127 až 127 | |
unsigned char (C89) | 0 až 255 | |
short signed short |
-32 767 až 32 767 | 16 bitů |
unsigned short | 0 až 65 535 | |
int signed int |
-32 767 až 32 767 | 16 bitů |
unsigned int | 0 až 65 535 | |
long signed long |
−2 147 483 647 až 2147 483 647 | 32 bitů |
unsigned long | 0 až 4 294 967 295 | |
long long(C99) signed long long(C99) |
−9 223 372 036 854 775 807 až 9 223 372 036 854 775 807 | 64 bitů |
unsigned long long (C99) | 0 až 18 446 744 073 709 552 000 |
Uvedené typy jsou definovány klíčovým slovem enum.
Existují typy čísla s plovoucí desetinnou čárkou , přesnost, tedy délka v bitech, proměnná; ve vzestupném pořadí:
Typ | Přesnost | Velikost |
---|---|---|
float | ≥ 6 desetinných míst | přibližně 10-37 až 10 +37 |
double | ≥ 10 desetinných míst | přibližně 10-37 až 10 +37 |
long double | ≥ 10 desetinných míst | přibližně 10-37 až 10 +37 |
long double (C89) | ≥ 10 desetinných míst |
C99 přidán float complex, double complexa long double complex, představující související komplexní čísla .
Vypracovat typy:
Typ _Boolje standardizován C99. V dřívějších verzích jazyka bylo běžné definovat synonymum:
typedef enum boolean {false, true} bool;Typ voidpředstavuje prázdnotu, například prázdný seznam parametrů funkce nebo funkci, která nic nevrací.
Typ void*je obecný ukazatel: jakýkoli datový ukazatel lze implicitně převést za do void*. Je to například typ vrácený standardní funkcí malloc, který přiděluje paměť. Tento typ není vhodný pro operace vyžadující znalost velikosti špičatého typu (aritmetika ukazatelů, dereferencování).
C podporuje složené typy s pojmem struktura . Chcete-li definovat strukturu, musíte použít klíčové slovo structnásledované názvem struktury. Členy pak musí být deklarovány v závorkách. Jako každé prohlášení, středník končí celou věc.
/* Déclaration de la structure personne */ struct Personne { int age; char *nom; };Pro přístup ke členům struktury musíte použít operátor ..
int main() { struct Personne p; p.nom = "Albert"; p.age = 46; }Funkce mohou přijímat ukazatele na struktury. Pracují se stejnou syntaxí jako klasické ukazatele. Operátor však ->musí být použit na ukazateli pro přístup k polím struktury. Je také možné dereference ukazatele, aby se tento operátor nepoužíval, a operátor stále používat ..
void anniversaire(struct Personne * p) { p->age++; printf("Joyeux anniversaire %s !", (*p).nom); } int main() { struct Personne p; p.nom = "Albert"; p.age = 46; anniversaire(&p); }Ve verzích C před C99 musely komentáře začínat lomítkem a hvězdičkou („/ *“) a končit hvězdičkou a lomítkem. Téměř všechny moderní jazyky používaly tuto syntaxi pro psaní komentářů v kódu. Všechno mezi těmito symboly je komentář, včetně zalomení řádku:
/* Ceci est un commentaire sur deux lignes ou plus */Standard C99 převzal od komentářů na konci řádku C ++, zavedených dvěma lomítky a končící řádkem:
// Commentaire jusqu'à la fin de la ligneSyntaxe různých existujících řídicích struktur v jazyce C je široce používána v několika dalších jazycích, například v C ++, ale samozřejmě také v Javě , C # , PHP nebo dokonce v JavaScriptu .
Existují tři hlavní typy struktur:
Funkce v C jsou bloky instrukcí, přijímání jednoho nebo více argumentů a schopnost vrátit hodnotu. Pokud funkce nevrátí žádnou hodnotu, použije se klíčové slovo void. Funkce také nemůže přijímat žádné argumenty. V voidtomto případě se doporučuje klíčové slovo .
// Fonction ne retournant aucune valeur (appelée procédure) void afficher(int a) { printf("%d", a); } // Fonction retournant un entier int somme(int a, int b) { return a + b; } // Fonction sans aucun argument int saisir(void) { int a; scanf("%d", &a); return a; } PrototypPrototyp spočívá v deklaraci funkce a jejích parametrů bez pokynů, které ji tvoří. Prototyp končí středníkem.
// Prototype de saisir int saisir(void); // Fonction utilisant saisir int somme(void) { int a = saisir(), b = saisir(); return a + b; } // Définition de saisir int saisir(void) { int a; scanf("%d", &a); return a; }Obvykle jsou všechny prototypy zapisovány do souborů .h a funkce jsou definovány v souboru .c .
Standard jazyka C úmyslně ponechává určité operace neurčené. Tato vlastnost C umožňuje překladačům přímo používat instrukce specifické pro procesor , provádět optimalizace nebo ignorovat určité operace, kompilovat krátké a efektivní spustitelné programy. Na oplátku je někdy příčinou chyb v přenositelnosti ze zdrojového kódu napsané v jazyce C.
Existují tři kategorie takového chování:
V jazyce C je chování definované implementací takové, kde implementace musí zvolit chování a držet se ho. Tato volba může být bezplatná nebo ze seznamu možností daných normou. Volba musí být dokumentována implementací, aby ji programátor mohl znát a používat.
Jedním z nejdůležitějších příkladů takového chování je velikost celočíselných datových typů. Standard C určuje minimální velikost pro základní typy, ale ne jejich přesnou velikost. Například typ int, který odpovídá strojovému slovu , tedy musí mít minimální velikost 16 bitů . Může být 16bitový na 16bitovém procesoru a 64bitový na 64bitovém procesoru.
Dalším příkladem je reprezentace celých čísel se znaménkem. Může to být dvě ‚s doplněk , něčí doplněk, nebo systém s znaménkový bit a hodnota bitů (EN) . Drtivá většina moderních systémů používá dvojkový doplněk, který je například jediným, který stále podporuje GCC. Staré systémy používají jiné formáty, například IBM 7090, který používá formát znak / hodnota, PDP-1 nebo UNIVAC a jeho potomci, z nichž některé se používají dodnes, například řada UNIVAC 1100/2200 # řada UNISYS 2200 ( en) , které používají vlastní doplněk.
Dalším příkladem je pravý posun celého čísla se záporným znaménkem. Implementace se obvykle může rozhodnout posunout jako pro celé číslo bez znaménka nebo šířit nejvýznamnější bit představující znaménko.
Nespecifikované chováníNespecifikované chování je podobné chování definovanému implementací, ale chování přijaté implementací není nutné dokumentovat. Nemusí to být za všech okolností stejné. Program přesto zůstává správný, programátor se prostě nemůže spoléhat na konkrétní pravidlo.
Například není zadáno pořadí vyhodnocení parametrů během volání funkce. Kompilátor se může dokonce rozhodnout vyhodnotit parametry dvou volání stejné funkce v jiném pořadí, pokud to pomůže jeho optimalizaci.
Nedefinované chováníStandard C definuje určité případy, kdy syntakticky platné konstrukce mají nedefinované chování. Podle standardu se pak může stát cokoli: kompilace může selhat, nebo vytvořit spustitelný soubor, jehož spuštění bude přerušeno, nebo který přinese falešné výsledky, nebo dokonce bude vypadat, že funguje bez chyby. Pokud program obsahuje nedefinované chování, stane se nedefinovaným chování celého programu, nejen chování instrukce obsahující chybu. Chybná instrukce tedy může poškodit data, která budou zpracována mnohem později, a tím oddálit projev chyby. A dokonce i bez provedení může nesprávná instrukce způsobit, že kompilátor provede optimalizace založené na nesprávných předpokladech, čímž vytvoří spustitelný soubor, který vůbec nedělá to, co se očekává.
PříkladyNa klasické dělení nulou nebo na vícenásobné přiřazení proměnné ve stejném výrazu můžeme poukázat na příkladu:
int i = 4; i = i++; /* Comportement indéfini. */Jeden by si tedy mohl myslet, že v tomto příkladu imůže mít hodnotu 4 nebo 5 v závislosti na volbě kompilátoru, ale stejně dobře může mít hodnotu 42 nebo může přiřazení zastavit provádění, nebo může kompilátor kompilaci odmítnout. Jakmile existuje nedefinované chování, neexistuje žádná záruka.
Abychom citovali jen několik příkladů, dereferencování nulového ukazatele, přístup k poli mimo jeho limity, použití neinicializované proměnné nebo přetékání celých čísel se znaménkem mají nedefinované chování. Kompilátor může v některých případech použít fakt, že sestavení není definováno, aby předpokládal, že k tomuto případu nikdy nedojde, a optimalizovat kód agresivněji. I když se výše uvedený příklad může zdát zřejmý, některé složité příklady mohou být mnohem subtilnější a někdy vést k vážným chybám.
Například spousta kódu obsahuje kontroly, aby se zabránilo provádění v případech mimo hranice, které by mohly vypadat takto:
char buffer[BUFLEN]; char *buffer_end = buffer + BUFLEN; unsigned int len; /* ... */ if (buffer + len >= buffer_end || /* vérification de dépassement du buffer */ buffer + len < buffer) /* vérification de débordement si len très large */ return; /* Si pas de débordement, effectue les opérations prévues */ /* ... */Tento kód je zjevně opatrný a provádí nezbytné kontroly zabezpečení, aby nedošlo k přetečení přidělené vyrovnávací paměti. V praxi mohou nejnovější verze překladačů, jako je GCC , Clang nebo Microsoft Visual C ++, potlačit druhý test a umožnit přetečení. Norma ve skutečnosti stanoví, že aritmetika ukazatele na objekt nemůže poskytnout ukazatel mimo tento objekt. Kompilátor proto může rozhodnout, že test je stále falešný, a odstranit jej. Správná kontrola je následující:
char buffer[BUFLEN]; unsigned int len; /* ... */ if (len >= BUFLEN) /* vérification de dépassement du buffer */ return; /* Si pas de débordement, effectue les opérations prévues */ /* ... */V roce 2008, kdy vývojáři GCC upravili kompilátor tak, aby optimalizoval určité kontroly přetečení, které byly založeny na nedefinovaném chování, vydal CERT varování o používání nejnovějších verzí GCC . Tyto optimalizace jsou ve skutečnosti přítomny ve většině moderních překladačů, CERT v tomto smyslu revidoval své odmítnutí odpovědnosti.
Existují některé nástroje k detekci těchto problematických sestavení a nejlepší kompilátoři detekují některé z nich (někdy musíte povolit konkrétní možnosti) a mohou je označit, ale žádný neprohlašuje, že je vyčerpávající.
Standardizován standardní knihovny , k dispozici u všech implementací, nabízí jednoduchost spojené s jazykem nízké hladiny. Zde je seznam některých záhlaví deklarujících typy a funkce standardní knihovny:
Standardizovaná standardní knihovna nenabízí žádnou podporu pro grafické rozhraní , síť, I / O na sériových nebo paralelních portech, systémy v reálném čase , procesy nebo dokonce pokročilé zpracování chyb (jako u strukturovaných výjimek ). To by mohlo dále omezit praktickou přenositelnost programů, které potřebují používat některé z těchto funkcí, aniž by existovalo velmi mnoho přenosných knihoven a kompenzovalo se tento nedostatek; ve světě UNIX tato potřeba vedla také ke vzniku dalšího standardu, POSIX .1.
Jazyk C je jedním z nejpoužívanějších programovacích jazyků a bylo vytvořeno mnoho knihoven pro použití s jazykem C: glib atd. Při vytváření datového formátu často existuje referenční knihovna C nebo software, který s formátem manipuluje. To je případ zlib , libjpeg , libpng , Expat , referenčních dekodérů MPEG , libsocket atd.
Zde je několik příkladů představujících velmi stručně některé vlastnosti C. Více informací najdete na WikiBooku „C Programování“ .
Struktura int_listpředstavuje jeden prvek propojeného seznamu celých čísel. Následující dvě funkce ( insert_nexta remove_next) slouží k přidání a odebrání položky ze seznamu.
/* La gestion de la mémoire n'est pas intégrée au langage mais assurée par des fonctions de la bibliothèque standard. */ #include <stdlib.h> struct int_list { struct int_list *next; /* pointeur sur l'élément suivant */ int value; /* valeur de l'élément */ }; /* * Ajouter un élément à la suite d'un autre. * node : élément après lequel ajouter le nouveau * value : valeur de l'élément à ajouter * Retourne : adresse de l'élément ajouté, ou NULL en cas d'erreur. */ struct int_list *insert_next(struct int_list *node, int value) { /* Allocation de la mémoire pour un nouvel élément. */ struct int_list *const new_next = malloc(sizeof *new_next); /* Si l'allocation a réussi, alors insérer new_next entre node et node->next. */ if (new_next) { new_next->next = node->next; node->next = new_next; new_next->value = value; } return new_next; } /* * Supprimer l'élément suivant un autre. * node : élément dont le suivant est supprimé * Attention : comportement indéterminé s'il n'y pas d'élément suivant ! */ void remove_next(struct int_list *node) { struct int_list *const node_to_remove = node->next; /* Retire l'élément suivant de la liste. */ node->next = node->next->next; /* Libère la mémoire occupée par l'élément suivant. */ free(node_to_remove); }V tomto příkladu jsou dvě základní funkce malloca free. První se používá k přidělení paměti, parametr, který obdrží, je počet bajtů , které chceme přidělit, a vrátí adresu prvního bajtu, který byl přidělen, jinak vrátí NULL. freese používá k uvolnění paměti, která byla přidělena malloc.