Jazyk C je typový jazyk a definuje tak základní číselné typy různých vlastností. V tomto článku bude stručně vysvětlena definice proměnné a konstanty a dále načtení a výpis proměnné.
Typy
Jazyk C je typový jazyk stejně jako drtivá většina ostatních staticky kompilovaných jazyků. Proto jazyk C definuje několik základních číselných typů. Tyto typy se liší rozsahy i typem uchovávaných dat.
Staticky kompilovaný jazyk znamená tolik, že překlad zdrojového kódu do strojového kódu (kterému rozumí procesor) a sestavení do výsledného programu dělá programátor pomocí překladače (gcc) a linkeru (ld).
Nejprve se podíváme na aritmetické typy.
signed/unsigned char
Jde o nejmenší z typů, běžně se používá pro uchovávání znaků. Podle standardu má minimálně 8 bitů.
Na našem obrázku je v paměti uloženo binární číslo 1000001, což je dekadicky 65 a pokud je interpretováno jako znak, tak znamená ASCII znak 'A'.footnote:[Jak vypadá ASCII tabulka kódování znaků se mužete podívat třeba zde] Počet různých hodnot, které můžeme uložit do typu char je \(2^8=256\).
signed/unsigned short int
Celočíselný typ o velikosti minimálně 16 bitů. Slovo signed znamená, že naše celé číslo má znaménko, slovo unsigned znamená, že naše číslo znaménko nemá a ukládají se do paměti jenom přirozená čísla.
Na obrázku je uloženo v paměti binární číslo 110001101001011, což je dekadicky 25419. Nejvyšší bit se používá k uchovávání znaménka (světle modrý), pokud je tento bit 0, uchovávané číslo je kladné. Pokud je znaménkový bit 1, je uchovávané číslo záporné. Maximální počet různých hodnot, které můžeme uložit do našeho typu signed short int je \(2^{16}-1=65535\). Je to 32767 kladných čísel plus 0 plus 32768 záporných celých čísel.
Na obrázku je uloženo v paměti binární číslo 1110001101001011, což je dekadicky 58187. Maximální počet různých hodnot, které můžeme uložit do typu unsigned short int je \(2^{16}=65536\). Znaménkový bit se zde nepoužívá. Unsigned typy se velmi často používají při nízkoúrovňovém programování (ovladače hardware).
signed/unsigned int
Celočíselný typ o velikosti minimálně 16 bitů. Musí být alespoň tak velký jako typ short int.
signed/unsigned long int
Celočíselný typ o velikosti minimálně 32 bitů. Musí být alespoň tak velký jako typ int.
signed/unsigned long long int
Celočíselný typ o velikosti minimálně 64 bitů. Tento typ se nachází až ve standardu C99. Musí být alespoň tak velký jako typ long.
float
Číselný typ o velikosti 32 bitů. Uchovává čísla s plovoucí desetinnou tečkou s přesností 5-7 desetinných míst.
double
Číselný typ o velikosti 64 bitů. Uchovává čísla s plovoucí desetinnou tečkou s přesností 15-16 desetinných míst.
Jak je uložen typ double v paměti popisuje standard IEEE 754 double-precision binary floating-point format: binary64 a jak to funguje se můžete dočíst třeba na Wikipedii
Typy short int, long int a long long int mají své kratší ekvivalenty short, long a long long. Je vidět, že standard neurčuje pevně velikost typů a toto rozhodnutí je ponecháno na cílové platformě. V počítačích s procesory x86 je int jako základní typ běžně 32 bitový. Nikomu se ovšem nebrání vytvořit si svoji platformu, kde budou mít všechny typy např. 32 bitů.
Abychom věděli přesně, kolik bitů má který typ a jak je tedy dlouhý, máme v jazyce C operátor sizeof(). Určitě ho prozkoumáme v dalším výkladu. sizeof(typ) vrací velikost typu nebo proměnné v bajtech.
To je v podstatě z jednoduchých datových typů vše. O složených datových typech pole, struktura a union se pobavíme později. Stejně tak se pobavíme později o jednoduchém datovém typu ukazatel (pointer).
Původní definice jazyka C nedefinovala typ bool, kde by se ukládaly hodnoty pravda (true) nebo nepravda (false), ale to moc nevadí, protože můžeme pro zápis logické hodnoty true nebo false použít třeba typ short a pokud v něm bude 0 budeme to považovat za false (nepravda) a pokud v něm bude cokoliv jiného, třeba 1, tak to budeme považovat za true (pravda). Z hlediska čistoty návrhu jazyka to dobré není a například jazyk C++, který je nástupcem jazyka C, datový typ bool definuje. Standard jazyka C99 už typ _Bool má.
_Bool v C99
Typ _Bool pro ukládání pravdivostních hodnot byl doplněn do jazyka C ve standardu C99. Má dvě hodnoty 0 (false — česky nepravda) a 1 (true — česky pravda).
#include <stdio.h>
int main(void) {
_Bool x = 1;
_Bool y = 0;
if(x) /* Je stejné jako if (x == 1) */
{
puts("Toto se vypise!");
}
if (!y) /* Je stejné jako if (y == 0) */
{
puts("Toto se také vypise!");
}
}
Typ _Bool je celočíselný typ, který se vyznačuje speciálními pravidly pro konverzi z jiných typů.
Výsledek konverze je podobný jako u typů, které se používají v konstrukci if( výraz ).
V následující konverzi
_Bool z = X;
to dopadne takto:
-
Je-li
Xaritmetický typ (libovolné číslo),zbude mít hodnotu0jestližeX == 0. Jinak bude mítzhodnotu1. -
Je-li
Xukazatel,zbude mít hodnotu0, jestližeXje nulový ukazatel. Jinak bude mítzhodnotu1.
Pokud bychom chtěli psát v programu krásná slova: bool, false a true, musíme vložit hlavičkový soubor <stdbool.h>.
|
Shrnutí
Typ proměnné nebo konstanty nám říká, jaké místo zabírá proměnná nebo konstanta v paměti a jak ji máme interpretovat (co si po tím představujeme).
Proměnné a konstanty
Proměnná v jazyce C se definuje zápisem typu před jméno proměnné. Chceme-li tedy vytvořit proměnnou pojmenovanou x typu int (celé číslo), provedeme to zápisem
int x;
Definice proměnné není nic jiného, než že pro proměnnou připravíme nějaké místo v paměti a pojmenujeme ji.
Proměnnou můžeme součaně v jednom kroku definovat a také inicializovat (přiřadit do ní počáteční hodnotu). To provedeme zápisem
int x = 3;
což je stejné, jako napsat
int x; // definice
x = 3; // inicializace
Zde jsme proměnné x nastavili hodnotu 3.
Můžeme udělat i to, že definujeme několik proměnných stejného typu na jednom řádku
1
2
int x, y; (1)
short red=255, green=255, blue=255; (2)
| 1 | Neinicializované proměnné typu int x a y |
| 2 | Inicializované promněnné typu short red, green, blue |
| Proměnné v jazyce C se implicitně nenulují! V proměnné bude po jejím vytvoření hodnota, která je v danou chvíli na daném místě v paměti (jinými slovy v podstatě náhodná). Proto je vždy nutné před prvním použitím proměnné nastavit počáteční hodnotu, většinou nulu. |
V jazyce C existují dva typy proměnných, a to lokální a globální. Předvedeme na příkladu jejich rozdíl.
Konstanty zapisujeme úvodním klíčovým slovem const. Překladač hlídá, že konstantu můžeme inicializovat pouze jednou a potom do ní zapisovat již nelze.
1
2
3
4
5
6
7
8
const int x = 1; (1)
int main (int argc, char **argv)
{
int x = 3; (2)
x = 4;
// ...
}
| 1 | globální konstanta typu int se jménem x |
| 2 | lokální inicializovaná proměnná se stejným jménem x |
Na řádce jedna vytváříme tzv. konstantu. Konstanta se definuje tak, že před typ konstanty napíšeme klíčové slovo const. Konstantu musíme při vytvoření inicializovat, nelze to udělat později. V tuto chvíli máme konstantu typu int se jménem x a s hodnotou 1. Jelikož je definice této konstanty mimo jakékoliv tělo funkce (mimo jakékoliv složené závorky, na úrovni souboru), bude hodnota této konstanty přístupná všude v celém souboru (pokud nedojde k zastínění). Na řádce pět definujeme tzv. lokální proměnnou, neboť je umístěna v těle funkce main. Tato definice lokální proměnné se jménem x zastíní globální definici konstanty se stejným jménem x. Jinými slovy v těle funkce main se bude pracovat s lokální proměnnou x. To tedy mimo jiné znamená, že přiřazení na řádce šest bude v pořádku. Kdybychom řádek pět vymazali, program se nepřeloží, neboť se budeme pokoušet přiřadit hodnotu do konstanty.
Příklad na globální a lokální proměnné
#include <stdio.h>
int y = 20; // globální
int soucet(int a, int b)
{
int x = 8; // lokální
printf("soucet: Globalni promenna y ma hodnotu %d\n", y);
printf("soucet: Hodnota lokalni promenne x je %d\n", x);
x = a+b;
printf("soucet: promenna x je %d\n", x);
y = y + x + 1;
return x;
}
int main(void)
{
int x=5; // lokální
printf("main: Globalni promenna y ma hodnotu %d\n", y);
printf("main: Lokalni promenna x ma hodnotu %d\n", x);
x = soucet(18,56);
printf("main: Promenna x ma hodnotu %d\n", x);
printf("main: Promenna y ma hodnotu %d\n", y);
x = soucet(23,56);
printf("main: Promenna x ma hodnotu %d\n", x);
printf("main: Promenna y ma hodnotu %d\n", y);
return 0;
}
jirka@jirka-Precision-M4800:~/vyuka_sspvc/c_programming$ gcc -o jtest jtest.c
jirka@jirka-Precision-M4800:~/vyuka_sspvc/c_programming$ ./jtest
main: Globalni promenna y ma hodnotu 20
main: Lokalni promenna x ma hodnotu 5
soucet: Globalni promenna y ma hodnotu 20
soucet: Hodnota lokalni promenne x je 8
soucet: promenna x je 74
main: Promenna x ma hodnotu 74
main: Promenna y ma hodnotu 95
soucet: Globalni promenna y ma hodnotu 95
soucet: Hodnota lokalni promenne x je 8
soucet: promenna x je 79
main: Promenna x ma hodnotu 79
main: Promenna y ma hodnotu 175
jirka@jirka-Precision-M4800:~/vyuka_sspvc/c_programming$
Globální proměnné překladač umísťuje do sekce spustitelného souboru data nebo bss a lokální proměnné se definují na zásobníku (stack) Viz: C progam v paměti počítače.
|
Příklad programu, co nepůjde přeložit
Tento program nepůjde přeložit, protože nedodržujeme pravidla pro pojmenování proměnných.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
char prvni_pismeno_jmena; (1)
char 1pismeno_jmena; (2)
short int cervena_slozka=255, modra_slozka=255, zelena_slozka=255; (3)
unsigned char cervenaSlozka, modraSlozka, zelenaSlozka; (4)
int červenáSložka, modráSložka, zelenáSložka; (5)
long barva_cervena+modra+zelena=6 (6)
int _Bool=0; (7)
int main(void)
{
printf("1pismeno_jmena ma hodnotu %c\n", 1pismeno_jmena);
return 0;
}
| 1 | platné jméno pro proměnnou typu char |
| 2 | neplatné jméno pro proměnnou typu char: jméno začíná číslicí |
| 3 | platná jména pro proměnné typu short int |
| 4 | také platná jména, jsou to jiné proměnné než short int cervena_slozka=255, modra_slozka=255, zelena_slozka=255; |
| 5 | toto bude problém, můžeme mít překladač, který nám háčky a čárky schroustne, ale většina překladačů nám vynadá |
| 6 | neplatné jméno: obsahuje nepovolený znak + a navíc nám chybí za definicí proměnné středník; |
| 7 | neplatné jméno: _Bool je klíčové slovo v C99 |
jirka@jirka-Precision-T3610:~/vyuka_sspvc/c_programming$ gcc -o prom prom.c
prom.c:6:6: error: invalid suffix "pismeno_jmena" on integer constant
6 | char 1pismeno_jmena; (2)
| ^~~~~~~~~~~~~~
prom.c:6:6: error: expected identifier or ‘(’ before numeric constant
prom.c:9:5: error: stray ‘\304’ in program
9 | int ��ervenáSložka, modráSložka, zelenáSložka; (5)
| ^
prom.c:9:6: error: stray ‘\215’ in program
9 | int ��ervenáSložka, modráSložka, zelenáSložka; (5)
| ^
prom.c:9:12: error: stray ‘\303’ in program
9 | int červen��Složka, modráSložka, zelenáSložka; (5)
| ^
prom.c:9:13: error: stray ‘\241’ in program
9 | int červen��Složka, modráSložka, zelenáSložka; (5)
| ^
prom.c:9:14: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘Slo’
9 | int červenáSložka, modráSložka, zelenáSložka; (5)
| ^~~
prom.c:9:17: error: stray ‘\305’ in program
9 | int červenáSlo��ka, modráSložka, zelenáSložka; (5)
| ^
prom.c:9:18: error: stray ‘\276’ in program
9 | int červenáSlo��ka, modráSložka, zelenáSložka; (5)
| ^
prom.c:9:14: error: unknown type name ‘Slo’
9 | int červenáSložka, modráSložka, zelenáSložka; (5)
| ^~~
prom.c:9:27: error: stray ‘\303’ in program
9 | int červenáSložka, modr��Složka, zelenáSložka; (5)
| ^
prom.c:9:28: error: stray ‘\241’ in program
9 | int červenáSložka, modr��Složka, zelenáSložka; (5)
| ^
prom.c:9:32: error: stray ‘\305’ in program
9 | int červenáSložka, modráSlo��ka, zelenáSložka; (5)
| ^
prom.c:9:33: error: stray ‘\276’ in program
9 | int červenáSložka, modráSlo��ka, zelenáSložka; (5)
| ^
prom.c:9:43: error: stray ‘\303’ in program
9 | int červenáSložka, modráSložka, zelen��Složka; (5)
| ^
prom.c:9:44: error: stray ‘\241’ in program
9 | int červenáSložka, modráSložka, zelen��Složka; (5)
| ^
prom.c:9:48: error: stray ‘\305’ in program
9 | int červenáSložka, modráSložka, zelenáSlo��ka; (5)
| ^
prom.c:9:49: error: stray ‘\276’ in program
9 | int červenáSložka, modráSložka, zelenáSlo��ka; (5)
| ^
prom.c:10:19: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘+’ token
10 | long barva_cervena+modra+zelena=6 (6)
| ^
prom.c: In function ‘main’:
prom.c:15:43: error: invalid suffix "pismeno_jmena" on integer constant
15 | printf("1pismeno_jmena ma hodnotu %c\n", 1pismeno_jmena);
| ^~~~~~~~~~~~~~
Přetypování
Přetypování je změna typu. Je to věc v některých případech nutná, ale je zárověň i nebezpečná.
V jazyce C existují dva typy přetypování, a to explicitní a implicitní. Implicitní přetypování probíhá na pozadí a provádí ho překladač. Explicitní přetypování dělá programátor ve zdrojovém kódu.
double a = 3;
Zde jsme do proměnné typu double přiřadili celočíselnou hodnotu. Překladač správně pochopí, co chceme udělat a hodnotu 3 převede na 3.0. Toto je implicitní přetypování.
Někdy není možné hodnotu převést automaticky, aniž by to nezpůsobilo problémy. V tom případě překladač vypíše nejméně varování. Pokud jsme si jisti, co děláme, můžeme provést tzv. explicitní přetypování.
double a = 3.0;
double b = 2.0;
int c = (int) a % (int) b;
Zde byl použit operátor modulo % (zbytek po dělení), který je možné aplikovat pouze na celá čísla. Protože jsme z nějakého důvodu měli čísla a a b uložená jako double, musíme explicitně říct, že je chápeme jako proměnné typu int. To provedeme zápisem cílového typu do závorek před proměnnou, kterou chceme přetypovat. Pokud bychom chtěli přetypovat výsledek složitějšího výrazu, museli bychom uzavřít celý výraz do závorek a před ně umístit cílový typ v závorkách.
Výpis pomocí printf()
Proměnnou libovolného typu lze vypsat pomocí již dříve zmíněné funkce printf(). Výpis proměnné bude nejlepší vysvětlit na příkladu.
int x = 3;
printf("promenna x ma hodnotu %d\n", x);
// ^
// |
// +-- dekadická hodnota proměnné x se vloží sem
První parametr funkce printf() je řetězec s maskou. Speciální sekvence uvozená %d říká, že na toto místo se bude vkládat proměnná typu int (viz níže). Druhý parametr pak bude samotná proměnná, která se bude vypisovat. Samozřejmě je možné v jednom volání vypsat libovolné množství hodnot, jen je nutné mít ke každému místu v masce přiřazenou právě jednu proměnnou správného typu. Výše uvedený program vypíše na výstupu:
promenna x ma hodnotu 3
Pro každý typ existuje jiná sekvence. Několik nejpoužívanějších uvedeme.
%c |
zastupuje jeden znak (typ char nebo unsigned char) |
%s |
zastupuje řetězec (pole) znaků (co to je pole si řekneme později) |
%d |
zastupuje znaménkový celočíselný typ (signed int) |
%u |
zastupuje neznaménkový celočíselný typ (unsigned int) |
%f |
zastupuje typ s plovoucí desetinnou tečkou (float, double) |
%e |
zastupuje typ s plovoucí desetinnou tečkou ve vědeckém zápisu s exponentem (float, double) |
Jak je vidět, funkce printf() [1] toho umí opravdu hodně. To ale ještě není vše. Výpis proměnné lze dále upravovat pomocí parametrů řídící sekvence. Obecně je plný tvar sekvence
<[vlajka]><[šířka]><[.přesnost]><[délka]>typ
-
vlajka
Hodnota - určuje zarovnání vlevo ve sloupci dané šířky. Zarovnání vpravo je výchozí. Hodnota + vynutí výpis znaménka. Mezera znamená, že místo znaménka, které by se netisklo, bude vložena mezera. Hodnota # vynutí vypsání 0, 0x, 0X nebo desetinné tečky u oktalových, hexadecimálním nebo necelých čísel. Konečně 0 znamená, že se bude hodnota zleva doplňovat nulami namísto mezer. -
šířka
Udává minimální počet znaků, které se budou tisknout. Je-li číslo kratší, bude doplněno znakem nastaveným výše. V případě, že zadáme *, uvádíme velikost šířky v přidané proměnné namísto čísla v sekvenci masky. -
přesnost
U decimálních čísel uvádí minimální počet vytisknutých hodnot (jako šířka). Je-li přesnost nastavená na nulu, nebude pro nulu vytisknut žádný znak. U necelých čísel jde o počet číslic za desetinnou tečkou. U řetězců tímto omezíme maximální vypsanou délku. Stejně jako u šířky můžeme toto číslo zadat jako přídavný parametr zapsáním *. -
délka Hodnota h značí short a je použitelná jen u decimálních hodnot. Hodnota l značí long u decimálních hodnot nebo double u neceločíselných hodnot. Hodnota L značí long double a je použitelná jen u neceločíselných hodnot.
Načtení hodnoty pomocí scanf()
K načtení hodnoty ze vstupu slouží funkce scanf(). Tato funkce opět používá masku jako první parametr. Nejlepší bud uvést příklad.
int x;
scanf("%d", &x);
Maska má v této funkci úlohu importního filtru. Hodnota je načtena jen a pouze, pokud vstup vyhoví masce a na určeném místě je hodnota správného typu, v tomto případě typu int. Lze si představit, že bychom chtěli např. načítat hodnotu vektoru zapsaného jako [3,2]. Pak lze zavolat funkci scanf takto:
int a, b;
scanf("[%d,%d]", &a, &b);
Všechny bílé znaky před každým zástupným symbolem jsou ignorovány, tedy i vstup [ 3, 2 ] by vyhověl masce. Znak & je u volání této funkce nutný. Důvod bude vysvětlen v kapitole o ukazatelích.
|
Podrobnou dokumentaci k funkcím jazyka C obdržíme pomocí příkazu: man 3 funkce man 3 printf — zobrazí se manuálová stránka (v angličtině) funkce printf() a příbuzných funkcí man 3 scanf — zobrazí se manuálová stránka funkce scanf() a příbuzných funkcí |
Toť pro dnešek vše.
Cvičení
Úkol
Napište jednoduchý program, který pomocí funkce printf a operátoru sizeof() vytiskne velikosti všech jednoduchých datových typů v bytech.
Zde je kostra programu:
/* vtypy.c
Program pro zjisteni velikosti datovych typu.
(c) Jirka Chraska 2023, <jirka@lixis.cz>
*/
#include <stdio.h>
int main()
{
printf("Velikost datoveho typu char je %lu bytu.\n", sizeof(char)); (1)
printf("Velikost datoveho typu unsigned short je %lu bytu.\n", sizeof(unsigned short int));
// zde budou další typy ...
return 0;
}
| 1 | operátor sizeof() vrací typ unsigned long, proto je ve funkci printf použito %lu |
Moje řešení
Překlad a spuštění programu v terminálu na Linuxu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
jirka@jirka-Precision-T3610:~/vyuka_sspvc/c_programming$ uname -a (1)
Linux jirka-Precision-T3610 5.4.0-150-generic #167-Ubuntu SMP Mon May 15 17:35:05 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
jirka@jirka-Precision-T3610:~/vyuka_sspvc/c_programming$ gcc -o vtypy vtypy.c (2)
jirka@jirka-Precision-T3610:~/vyuka_sspvc/c_programming$ ./vtypy (3)
Velikost datoveho typu char je 1 bytu. (4)
Velikost datoveho typu unsigned char je 1 bytu.
Velikost datoveho typu short je 2 bytu.
Velikost datoveho typu unsigned short je 2 bytu.
Velikost datoveho typu int je 4 bytu.
Velikost datoveho typu unsigned int je 4 bytu.
Velikost datoveho typu long je 8 bytu.
Velikost datoveho typu unsigned long je 8 bytu.
Velikost datoveho typu long long je 8 bytu.
Velikost datoveho typu unsigned long long je 8 bytu.
Velikost datoveho typu float je 4 bytu.
Velikost datoveho typu double je 8 bytu.
jirka@jirka-Precision-T3610:~/vyuka_sspvc/c_programming$
| 1 | Výpis jádra systému a architektury počítače, architektura je x84_64, 64bitový počítač |
| 2 | Překlad a vytvoření spustitelného programu. |
| 3 | Spuštění programu. |
| 4 | Výpis výsledků programu. |
Nakopíruji pomocí scp zdrojový kód na počítač s jinou architekturou.
1
2
3
jirka@jirka-Precision-T3610:~/vyuka_sspvc/c_programming$ scp vtypy.c jirka@c5.jr.lixis.cz:~/c_programming/
vtypy.c 100% 1291 568.9KB/s 00:00
jirka@jirka-Precision-T3610:~/vyuka_sspvc/c_programming$
Překlad na jiné architektuře, i686 (32bit):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
jirka@c5 ~/c_programming $ uname -a
Linux c5 6.1.28-gentoo #4 SMP PREEMPT_DYNAMIC Thu Jul 6 02:04:17 -00 2023 i686 Intel(R) Atom(TM) CPU N270 @ 1.60GHz GenuineIntel GNU/Linux
jirka@c5 ~/c_programming $ gcc -o vtypy vtypy.c
vtypy.c: In function ‘main’:
vtypy.c:10:50: warning: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘unsigned int’ [-Wformat=] (1)
10 | printf("Velikost datoveho typu char je %lu bytu.\n", sizeof(char)); // vytiskne velikost typu char
| ~~^ ~~~~~~~~~~~~
| | |
| | unsigned int
| long unsigned int
| %u
vtypy.c:11:59: warning: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘unsigned int’ [-Wformat=]
11 | printf("Velikost datoveho typu unsigned char je %lu bytu.\n", sizeof(unsigned char)); // vytiskne velikost typu unsigned char
| ~~^ ~~~~~~~~~~~~~~~~~~~~~
| | |
| | unsigned int
| long unsigned int
| %u
vtypy.c:12:51: warning: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘unsigned int’ [-Wformat=]
12 | printf("Velikost datoveho typu short je %lu bytu.\n", sizeof(short int)); // vytiskne velikost typu short int
| ~~^ ~~~~~~~~~~~~~~~~~
| | |
| | unsigned int
| long unsigned int
| %u
vtypy.c:13:60: warning: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘unsigned int’ [-Wformat=]
13 | printf("Velikost datoveho typu unsigned short je %lu bytu.\n", sizeof(unsigned short int)); // vytiskne velikost typu unsigned short int
| ~~^ ~~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | unsigned int
| long unsigned int
| %u
vtypy.c:14:49: warning: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘unsigned int’ [-Wformat=]
14 | printf("Velikost datoveho typu int je %lu bytu.\n", sizeof(int)); // vytiskne velikost typu int
| ~~^ ~~~~~~~~~~~
| | |
| | unsigned int
| long unsigned int
| %u
vtypy.c:15:58: warning: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘unsigned int’ [-Wformat=]
15 | printf("Velikost datoveho typu unsigned int je %lu bytu.\n", sizeof(unsigned int));
| ~~^ ~~~~~~~~~~~~~~~~~~~~
| | |
| | unsigned int
| long unsigned int
| %u
vtypy.c:16:50: warning: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘unsigned int’ [-Wformat=]
16 | printf("Velikost datoveho typu long je %lu bytu.\n", sizeof(long));
| ~~^ ~~~~~~~~~~~~
| | |
| | unsigned int
| long unsigned int
| %u
vtypy.c:17:59: warning: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘unsigned int’ [-Wformat=]
17 | printf("Velikost datoveho typu unsigned long je %lu bytu.\n", sizeof(unsigned long));
| ~~^ ~~~~~~~~~~~~~~~~~~~~~
| | |
| | unsigned int
| long unsigned int
| %u
vtypy.c:18:55: warning: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘unsigned int’ [-Wformat=]
18 | printf("Velikost datoveho typu long long je %lu bytu.\n", sizeof(long long));
| ~~^ ~~~~~~~~~~~~~~~~~
| | |
| | unsigned int
| long unsigned int
| %u
vtypy.c:19:64: warning: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘unsigned int’ [-Wformat=]
19 | printf("Velikost datoveho typu unsigned long long je %lu bytu.\n", sizeof(unsigned long long));
| ~~^ ~~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | unsigned int
| long unsigned int
| %u
vtypy.c:20:51: warning: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘unsigned int’ [-Wformat=]
20 | printf("Velikost datoveho typu float je %lu bytu.\n", sizeof(float));
| ~~^ ~~~~~~~~~~~~~
| | |
| | unsigned int
| long unsigned int
| %u
vtypy.c:21:52: warning: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘unsigned int’ [-Wformat=]
21 | printf("Velikost datoveho typu double je %lu bytu.\n", sizeof(double));
| ~~^ ~~~~~~~~~~~~~~
| | |
| | unsigned int
| long unsigned int
| %u
jirka@c5 ~/c_programming $
| 1 | Vidím spoustu varování, kompilátor mě říká, že sizeof() vrací jenom unsigned int a já mám ve fukci printf použitu masku %lu pro unsigned long int. |
Program se vytvořil a jde spustit i přes varování kompilátoru. Není však dobrou programátorskou praktikou ignorovat varování, musím proto opravit program, změním ve funkci printf %lu na %u a přejmenuju si zdrojový kód na vtypy32.c.
/* vtypy32.c
Program pro zjisteni velikosti datovych typu. Pro 32bitovou architekturu i686
(c) Jirka Chraska 2023, <jirka@lixis.cz>
*/
#include <stdio.h>
int main()
{
printf("Velikost datoveho typu char je %u bytu.\n", sizeof(char)); (1)
printf("Velikost datoveho typu unsigned short je %u bytu.\n", sizeof(unsigned short int));
// zde budou další typy ...
return 0;
}
| 1 | operátor sizeof() vrací na architektuře i686 typ unsigned int, proto je ve funkci printf použito %u |
jirka@c5 ~/c_programming $ gcc -o vtypy32 vtypy32.c
jirka@c5 ~/c_programming $ ./vtypy32
Velikost datoveho typu char je 1 bytu.
Velikost datoveho typu unsigned char je 1 bytu.
Velikost datoveho typu short je 2 bytu.
Velikost datoveho typu unsigned short je 2 bytu.
Velikost datoveho typu int je 4 bytu.
Velikost datoveho typu unsigned int je 4 bytu.
Velikost datoveho typu long je 4 bytu.
Velikost datoveho typu unsigned long je 4 bytu.
Velikost datoveho typu long long je 8 bytu.
Velikost datoveho typu unsigned long long je 8 bytu.
Velikost datoveho typu float je 4 bytu.
Velikost datoveho typu double je 8 bytu.
Nyní je všechno v pořádku, program se překládá bez varování a i funguje dobře. Vidíme, že na 32bitové architektuře jsou délky jednotlivých typů stejné jako na 64bitové architektuře x86_64.
Ještě si nakopíruju výsledky práce na architektuře i686 na hlavní PC a porovnám velikosti výsledných souborů.
jirka@jirka-Precision-T3610:~/vyuka_sspvc/c_programming$ scp jirka@c5.jr.lixis.cz:~/c_programming/vtypy32* . (1)
vtypy32 100% 14KB 4.8MB/s 00:00
vtypy32.c 100% 1281 993.8KB/s 00:00
jirka@jirka-Precision-T3610:~/vyuka_sspvc/c_programming$ ls -l vtypy* (2)
-rwxrwxr-x 1 jirka jirka 16696 čec 22 18:03 vtypy (3)
-rw-rw-r-- 1 jirka jirka 1291 čec 22 18:03 vtypy.c (4)
-rwxr-xr-x 1 jirka jirka 14600 čec 22 18:36 vtypy32 (5)
-rw-r--r-- 1 jirka jirka 1281 čec 22 18:36 vtypy32.c (6)
jirka@jirka-Precision-T3610:~/vyuka_sspvc/c_programming$
| 1 | kopírování pomocí scp na hlavní PC |
| 2 | výpis adresáře |
| 3 | spustitelný soubor pro 64 bitovou architekturu |
| 4 | zdrojový soubor pro 64 bitovou architekturu |
| 5 | spustitelný soubor pro 32 bitovou architekturu |
| 6 | zdrojový soubor pro 32bitovou architekturu |
Tisk bitů v proměnné
Jonáš Erlebach naprogramoval při hodině pěkný program, který umí vytisknout proměnné po bitech. Použil union a další konstrukce, které budeme probírat později. Děkuji.
#include <stdio.h>
typedef union {
float f;
unsigned int i;
} FloatIntUnion;
void printFloatBits(float num) {
FloatIntUnion u;
u.f = num;
int bits = sizeof(u.i) * 8;
for (int i = bits - 1; i >= 0; i--) {
printf("%d", (u.i >> i) & 1);
}
printf("\n");
}
void printBits(unsigned int num) {
int bits = sizeof(num) * 8;
for (int i = bits - 1; i >= 0; i--) {
printf("%d", (num >> i) & 1);
}
printf("\n");
}
int main(){
unsigned int a = 110;
float b = -12.23;
a = * (unsigned int*) &b; // bity b se zapíšou do a
printBits(a);
printFloatBits(b);
return 0;
}