Search
Na cvičení budeme vycházet z následujícího programu v C, který budeme dále modifikovat.
Program pro zobrazování reprezentace čísel v paměti:
/* Simple program to examine how are different data types encoded in memory */ #include <stdio.h> /* * The macro determines size of given variable and then * prints individual bytes of the value representation */ #define PRINT_MEM(a) print_mem((unsigned char*)&(a), sizeof(a)) void print_mem(unsigned char *ptr, int size) { int i; printf("address = 0x%016lx\n", (long unsigned int)ptr); for (i = 0; i < size; i++) { printf("0x%02x ", *(ptr+i)); // == printf("0x%02x ", ptr[i]); } printf("\n"); } int main() { /* try for more types: long, float, double, pointer */ unsigned int unsig = 5; int sig = -5; /* Read GNU C Library manual for conversion syntax for other types */ /* https://www.gnu.org/software/libc/manual/html_node/Formatted-Output.html */ printf("value = %d\n", unsig); PRINT_MEM(unsig); printf("\nvalue = %d\n", sig); PRINT_MEM(sig); return 0; }
/opt/apo/binrep/print_binrep
gcc -Wall -pedantic -o print_binrep ./print_binrep.c
Makefile
Alternativa v jazyce Python
#!/usr/bin/python3 import struct a = 1234.56789 b = -11.1111111111111111111111 c = a + b buf = struct.pack('<f', c) print ('C float LE:' + ' '.join(["{0:02x}".format(b) for b in buf])) print ('C float LE:' + str(struct.unpack('<f', buf)[0])) buf = struct.pack('>f', c) print ('C float BE:' + ' '.join(["{0:02x}".format(b) for b in buf])) print ('C float BE:' + str(struct.unpack('>f', buf)[0])) buf = struct.pack('<d', c) print ('C double LE:' + ' '.join(["{0:02x}".format(b) for b in buf])) print ('C double LE:' + str(struct.unpack('<d', buf)[0])) buf = struct.pack('>d', c) print ('C double BE:' + ' '.join(["{0:02x}".format(b) for b in buf])) print ('C double BE:' + str(struct.unpack('>d', buf)[0]))
Více o ukládání proměnných v specifikovaném formátu datové reprezentace v moduly jazyka Python struct — Interpret bytes as packed binary data.
V situaci, kdy máme jeden zdrojový soubor není problém spouštět překladač pokaždé ručně. U větších projektů ale máme souborů víc a často různých typů – máme třeba zdrojové kódy v C a v assembleru, dokumentaci v XML a třeba i něco dalšího. Překládat ručně každý soubor zvlášť a ještě k tomu jiným překladačem a spojovat výsledek dohromady je zbytečně velká práce. Proto byl vymyšlen program make. Jeho základní myšlenka je ta, že se popíše jak se překládá který typ souboru a stanoví se které soubory jsou potřeba pro “vyrobení” jiného souboru.
Program make funguje tak, že po spuštění si přečte soubor Makefile, kde jsou zapsána pravidla pro kompilaci a vyvolává postupně překladače tak, jak je potřeba.
V souboru Makefile existují čtyři typy řádek:
Nejjednodušší Makefile pro kompilaci programu hello vypadá následovně.
hello: hello.c
Obsahuje pouze jednu řádku s popisem závislostí. Takto zapsaná řádka říká, že pro vytvoření souboru hello je potřeba soubor hello.c. Tomu, co je vlevo od dvojtečky se říká target (cíl) a vše co je napravo jsou takzvané prerekvizity.
Program make má v sobě vestavěna implicitní pravidla, díky nimž ví, jak má provést překlad. Kdyby tato pravidla neexistovala, musel by náš Makefile vypadat takto:
hello: hello.c gcc -Wall -o hello hello.c
Na druhé řádce, která začíná tabulátorem, je příkaz, který bude vykonán pokud make usoudí, že je potřeba překompilovat soubor hello.
Implicitní pravidla říkají, jak z jednoho typu souboru vyrobit jiný typ souboru. Tato pravidla jsou většinou definována pomocí proměnných. Chceme-li implicitní pravidlo jen trochu pozměnit, nemusíme ho definovat znova, ale stačí změnit jen hodnoty proměnných, které se v pravidlech používají.
CFLAGS = -g -Wall CC = m68k-elf-gcc hello: hello.c
Pomocí proměnné CFLAGS se například určuje, s jakými přepínači má být spouštěn překladač jazyka C. Jak se jmenuje překladač se zase specifikuje v proměnné CC[3].
Pokud chceme překládat větší projekt, bývá zvykem rozšířit Makefile o další targety:
all: hello clean: rm -f hello hello: hello.c gcc -Wall -g -o hello hello.c
O programu make by toho šlo napsat mnohem víc. Jdou pomocí něj překládat i velmi komplikované projekty jako například samotné jádro Linuxu. Dost často ale není potřeba vytvářet složité soubory Makefile ručně. Nástroje jako autoconf či nějaké IDE většinou Makefile generují automaticky.