1 of 29

C v kostce #2

Co všechno potřebujete vědět k úspěšnému ukončení předmetu

2 of 29

Dotaz - email - srozumitelnost

  • Je potřeba definovat konkrétní téma, nebo termín, kterému není rozumnět.
  • Náš koncept:
    • Snaha dostat všechny na stejnou úrověň. Uvést problematiku programování od HW, přes překlad zdrojového kódu, práci s OS, umístění kódu do paměti a spuštění. Uvést souvislosti.��Tělo základního bloku kódu obsahuje:�Standardní knihovny, funkce, předávání hodnot a to�argumentem a odkazem, specifikaci návratové �hodnoty, …�Studenti si stěžují, že neumí s Linuxem, �neznají základní příkazy pro interakci s OS, ...�
    • Následně bude podrobnější rozebrání jednotlivých témat.

#include "stdio.h"

int main( int argc, char* argv[] )

{

return 5;

}

3 of 29

Doporučená literatura

  • https://cw.fel.cvut.cz/wiki/courses/b0b99prp

  • Učebnice jazyka C 1.díl
  • Herout, Pavel

Případně jiné ...

4 of 29

… k zapamatování (první přednáška)

  • Abstrakce paměti:
    • řada šuplíků na čísla (do šuplíku uložím číslo, šuplík má omezenou �velikost a každý šuplík má adresu), nebo
    • jednotlivé poznámkové lístky �(každý má omezenou kapacitu, �a každý má svojí adresu - např. pořadí lístku)
  • Základní struktura programu
    • program je přeložen - vznikne soubor čísel (instrukcí), který OS nahraje do paměti.
    • program je v paměti uložen jako několik bloků s různým účelem - anatomie programu v paměti
    • program je “spuštěn” - skočí se na první instrukci ve funkci main
  • Paměť, int a char a jejich unsigned varianty

5 of 29

Obsah

  • Interakce OS s uživatelem, návratová hodnota
  • Konverze číselných soustav
  • Řetězce a paměť a ukazatele
  • Standardní rozhraní
  • Základní bloky programu

6 of 29

Vstupní bod programu

  • Dvě/tři možnosti zápisu - podle toho co potřebujeme� - jak to funguje na embedded systémem?� - můžou být v programu dvě funkce main?

  • Je potřeba předat návratovou hodnotu systému (podmínky runtime knihovny)
  • argc - integer, celé číslo, říká kolik následuje textových parametrů
  • argv - pole ukazatelů na textové řetězce různé délky. �Adresace: argv[0], argv[1], ... , argv[argc-1]

#include "stdio.h"

int main( int argc, char* argv[] )

{

return 5;

}

#include <stdio.h>

int main(void)

{

return 0;

}

#include <stdio.h>

void main(void)

{

}

Parametry programu - argc a argv

Stack ↓

Heap ↑

Globální proměnné

Text (a.out)

7 of 29

Výpis parametrů příkazové řádky

  • Adresace pole od 0 do (max-1)

...ska$ clang priklad01_args.c

...ska$ ./a.out param1 param2 param3 4 parametr6

Argument 0, ./a.out

Argument 1, param1

Argument 2, param2

Argument 3, param3

Argument 4, 4

Argument 5, parametr6

...ska$ echo $?

5

...ska$

#include "stdio.h"

int main( int argc, char* argv[] )

{

int iCnt;

for( iCnt = 0; iCnt < argc; iCnt++ )

{

printf( "Argument %d, %s\n", iCnt, argv[iCnt] );

}

return 5;

}

8 of 29

Vstup a výstup do programu/procesu

OS nahrává binární kopii programu do paměti

  • Tři základní soubory

  • Možnost přesměrování
  • Co se děje s návratovou hodnotou?

ČAS

9 of 29

  • Je rozdíl mezi parametrem �programu a vstupem
  • Obrázky: �defaultní situace, přesměrování, roura
  • Příkaz přesměrování
    • >, >>, 1>, 2>
    • <
    • 2>&1
    • |
    • >/dev/null, </dev/zero

10 of 29

Význam návratové hodnoty programu

  • Program dává OS vědět výsledek své činnosti
  • Návratová hodnota je přístupná pomocí echo $?
  • Přesné přiřazení významu výsledku je potřeba hledat v dokumentaci
    • Přiřazení jednotlivých hodnot definuje programátor - ten píše dokumentaci
  • Návratovou hodnotu je možné využít ve skriptech psaných v interpretu (příkazový řádek) daného OS

11 of 29

Čtení vstupu

  • Zápis na standardní soubory:
  • Přípravený vstupní soubor s �názvem in.txt

#include "stdio.h"

int main( int argc, char* argv[] )

{

int iCnt;

int iInput = 0;

for( iCnt = 0; iCnt < argc; iCnt++ )

{

printf( "Argument %d, %s\n", iCnt, argv[iCnt] );

}

printf( "Zadej cislo :" );

scanf( "%d", &iInput );

fprintf( stdout, "Number %d", iInput );

fprintf( stdout, "Novy radek\n" );

if( iInput < 18 )

{

fprintf( stderr, "Err: Too low %d\n", iInput );

}

fprintf( stderr, "stdin=%d, stdout=%d, stderr=%d\n",

(unsigned int)stdin, (unsigned int)stdout, (unsigned int)stderr );

return iInput;

}

Obsah souboru: in.txt

10

Soubor: in.txt v HEXa� - příkaz OS: xxd in.txt

00000000: 3130 0a0a 10..

Překlad:

clang priklad02_presmerovani.c

12 of 29

Převody číselných soustav

Převody čísel: Mocnost * váha

Desítková soustava Dvojková soustava Šestnácková soustava

Obsah souboru - předchozí slide: 3130 0a0a

  • 0x31 = 49 = ‘1’ = 0011 0001b
  • 0x30 = 48 = ‘0’ = 0011 0000b
  • 0x0A = 10 = ‘\n’ = 0000 1010b = LineFeed

13 of 29

Ilustrace přesměrování

  • Program negeneruje žádný výstup (priklad02_presmerovani.c)

  • Obsah výstupů - výpis obsahu souboru - příkaz “cat”

...ska$ ./a.out param1 param2 param3 4 parametr6 1>out.txt 2>err.txt <in.txt

...ska$

...ska$ cat out.txt

Argument 0, ./a.out

Argument 1, param1

Argument 2, param2

Argument 3, param3

Argument 4, 4

Argument 5, parametr6

Zadej cislo :Number 10Novy radek

...ska$

...ska$ cat err.txt

Err: Too low 10

stdin=177400032, stdout=177403424, stderr=177403200

...ska$

...ska$ echo $?

10

...ska$

14 of 29

Důraz

  • Rozdíl mezi printf a fprintf (scanf a fscanf)
  • Pozor na nové řádky \n
  • Výzva pro scanf se musí předem �vytisknout ručně
  • scanf načítá data až do vložení konce �řádky, tj. stisk Enter
  • Je možné určit na jaký výstup chci výpis�směrovat (stdin, stdout, stderr, �případně jiný)
  • Výstup je určený číslem v paměti
  • Program může vygenerovat svůj výstup

#include "stdio.h"

int main( int argc, char* argv[] )

{

int iCnt;

int iInput = 0;

for( iCnt = 0; iCnt < argc; iCnt++ )

{

printf( "Argument %d, %s\n", iCnt, argv[iCnt] );

}

printf( "Zadej cislo :" );

scanf( "%d", &iInput );

fprintf( stdout, "Number %d", iInput );

fprintf( stdout, "Novy radek\n" );

if( iInput < 18 )

{

fprintf( stderr, "Err: Too low %d\n", iInput );

}

fprintf( stderr, "stdin=%d, stdout=%d, stderr=%d\n",

(unsigned int)stdin, (unsigned int)stdout, (unsigned int)stderr );

return iInput;

}

15 of 29

Řetězce, načítání vstupu

  • Konstantní řetězce známé při překladu programu�Kde jsou uložené?

  • Pro řetězce načítané ze vstupu, je potřeba alokovat prostor

Parametry programu - argc a argv

Stack ↓

Heap ↑

Globální proměnné

Text (a.out)

for( iCnt = 0; iCnt < argc; iCnt++ )

{

printf( "Argument %d, %s\n", iCnt, argv[iCnt] );

}

#include "stdio.h"

#include "string.h"

int main( int argc, char* argv[] )

{

char chArray[16]; // alokovany prostor, 16 je tzv. Magická konstanta

printf( "Zadej text a stiskni Enter:" );

scanf( "%s", chArray );

printf(, "Number %s", chArray );

return strlen(chArray);

}

16 znamená max. 15 znaků a pak musí být nula

16 of 29

Interakce s uživateleme pomocí std rozhraní

Standardní rozhraní

  • stdin, stdout, stderr
  • Pro výpis na obrazovku: printf - píše na stdout
  • Pro čtení klávesnice: scanf - čte stdin
  • Pro výpis do souboru: fprintf - má jako parametr pointer na soubor
  • Pro čtení ze souboru: fscanf - má jako parametr pointer na soubor

Existují standardní rozhraní, které je možné přepojovat.

Prototypy funkcí:

int printf( const char *format, … );

int fprintf( FILE *stream, const char *format, ...);

Použití se stejným výsledkem - vytiskne řetězec na obrazovku:

printf( "Ahoj\n" );

fprintf( stdout, "Ahoj\n" );

17 of 29

Pole = řetězec (pozor na paměť)�

  • Texty zakončené nulou�Ne znakem ‘0’, tj. 0x30 nebo 48, ale 0

#include "stdio.h"

#include "string.h"

#define cARR_MAXLEN 16

int main( int argc, char* argv[] )

{

char chArray[ cARR_MAXLEN ]; //

int iCnt;

printf( "Zadej text a stiskni Enter:" );

scanf( "%s", chArray );

printf( "\nText = %s\n", chArray );

for( iCnt = 0; iCnt < cARR_MAXLEN; iCnt++)

{

printf("The value is %i or as char '%c'\n", chArray[iCnt], chArray[iCnt]);

}

return strlen(chArray);

}

...ska$ ./a.out �Zadej text a stiskni Enter:test

Text = test

The value is 116 or as char 't'

The value is 101 or as char 'e'

The value is 115 or as char 's'

The value is 116 or as char 't'

The value is 0 or as char ''

The value is 0 or as char ''

The value is 0 or as char ''

The value is 0 or as char ''

The value is -32 or as char '�'

The value is 4 or as char ''

The value is 64 or as char '@'

The value is 0 or as char ''

The value is 0 or as char ''

The value is 0 or as char ''

The value is 0 or as char ''

The value is 0 or as char ''

...ska$

#define = direktiva preprocesoru - definice konstanty�Jednoduchá definice konstant - možné použít na několika místech v kódu

18 of 29

Pole v paměti

  • Definice pole�typ jmeno[pocet_prvku];�char chArray[6]

  • int iArray[6]

#define cARR_MAXLEN 6

char chArray[ cARR_MAXLEN ];

chArray[0] = 't';

chArray[1] = 'e';

chArray[2] = 's';

chArray[3] = 't';

chArray[4] = 0 ;

t =0x74

e

s

t

\0

?

Index: 0 1 2 3 4 5

t =0x00 00 00 74

V případě typu int bude obsazení paměti jiné.

#define cARR_MAXLEN 6

int iArray[ cARR_MAXLEN ];

iArray[0] = 't';

iArray[1] = 'e';

iArray[2] = 's';

iArray[3] = 't';

iArray[4] = 0 ;

19 of 29

Abstrakce dat v paměti

char chTmp;�chTmp = '0';��int iArray[3];�iArray[0] = 't'; �iArray[1] = 255; �iArray[2] = 255+1+1; // 0xFF+1+1 = 0x100+1 = 0x101

Prom:

chTmp

iArray[0]

iArray[1]

iArray[2]

Hodnota:

'0'

0

0

0

't'

0

0

0

0xFF

0

0

0x01

0x01

Hodnota:

0x30

0x00

0x00

0x00

0x74

0x00

0x00

0x00

0xFF

0x00

0x00

0x00

0x00

Adresa:

100

101

102

103

104

105

106

107

108

109

110

111

112

  • Definice typů = zjednodušují orientaci programátorovi
  • Linker může za přiřazení adres jednotlivým proměnným
  • Nejmenší adresovatelná jednotka je char = 1 byte

20 of 29

Ukazatel - Pointer

char chTmp;char * pchTmp; // pointer na chTmp = pchTmp�chTmp = '0';

pchTmp = NULL; // přímé přiřazení�pchTmp = &chTmp; // přiřazení adresy proměnné chTmp

printf( "You entered %02i (%c)\n", chTmp, chTmp );

printf( "You entered %02i (%c)\n", *pchTmp, *pchTmp );

Prom:

chTmp

iArray[0]

iArray[1]

iArray[2]

pchTmp

Hodnota:

'0'

0

0

0

't'

0

0

0

0xFF

0

0

0x01

0x01

0

Hodnota:

0x30

0x00

0x00

0x00

0x74

0x00

0x00

0x00

0xFF

0x00

0x00

0x00

0x00

Adresa:

100

101

102

103

104

105

106

107

108

109

110

111

112

113

21 of 29

Ukazatel - Pointer

char chTmp;char * pchTmp; // pointer na chTmp = pchTmp�chTmp = '0';

pchTmp = NULL; // přímé přiřazenípchTmp = &chTmp; // přiřazení adresy proměnné chTmp

printf( "Value %02i (%c)\n", chTmp, chTmp ); // Value 48 (0)

printf( "Value %02i (%c)\n", *pchTmp, *pchTmp ); // Value 48 (0)

printf( "Value %02i (%c)\n", pchTmp, pchTmp ); // špatné použití ukazatele, výsledek 100, ASCII 100 = d, malé d // Value 100 (d)

Prom:

chTmp

iArray[0]

iArray[1]

iArray[2]

pchTmp

Hodnota:

'0'

0

0

0

't'

0

0

0

0xFF

0

0

0x01

0x01

100

Hodnota:

0x30

0x00

0x00

0x00

0x74

0x00

0x00

0x00

0xFF

0x00

0x00

0x00

0x00

0x00 00 00 64

Adresa:

100

101

102

103

104

105

106

107

108

109

110

111

112

113,114,115,116

  • Definice ukazatele typ * jméno
  • Velikost ukazatele je závislá na OS � 32 bitů na OS32, 64 bitů na OS64
  • Znak & - získá z proměnné adresu
  • Znak * - vezme obsah dané adresy - dereference

22 of 29

Ukazatel - Pointer na integer

int iArray[3];�iArray[0] = 't'; �iArray[1] = 255; �iArray[2] = 255+1+1;int * piArray; // pointer na iArray = piArray��

Prom:

chTmp

iArray[0]

iArray[1]

iArray[2]

piArray

Hodnota:

'0'

0

0

0

't'

0

0

0

0xFF

0

0

0x01

0x01

101

Hodnota:

0x30

0x00

0x00

0x00

0x74

0x00

0x00

0x00

0xFF

0x00

0x00

0x00

0x00

0x00 00 00 65

Adresa:

100

101

102

103

104

105

106

107

108

109

110

111

112

113,114,115,116

piArray = NULL; // přímé přiřazení��piArray = 101; // přiřazení adresy proměnné iArray[0]printf( "Value %i (%c)\n", *piArray, *piArray ); // Value 74 (t)

piArray = iArray; // přiřazení adresy proměnné iArray[0]printf( "Value %i (%c)\n", *piArray, *piArray ); // Value 74 (t)

piArray = &iArray[0]; // přiřazení adresy proměnné iArray[0]printf( "Value %i (%c)\n", *piArray, *piArray ); // Value 74 (t)

23 of 29

Ukazatel - Pointer na integer

Zápis

int x;

x = iArray[0]; // index je možné modifikovat

// x bude = ‘t’

Prom:

chTmp

iArray[0]

iArray[1]

iArray[2]

piArray

Hodnota:

'0'

0

0

0

't'

0

0

0

0xFF

0

0

0x01

0x01

101

Hodnota:

0x30

0x00

0x00

0x00

0x74

0x00

0x00

0x00

0xFF

0x00

0x00

0x00

0x00

0x00 00 00 65

Adresa:

100

101

102

103

104

105

106

107

108

109

110

111

112

113,114,115,116

24 of 29

Ukazatel - Pointer na integer

int x;

int * piArray; // pointer na iArray = piArray

piArray = iArray;

piArray = &iArray[0];

Prom:

chTmp

iArray[0]

iArray[1]

iArray[2]

piArray

Hodnota:

'0'

0

0

0

't'

0

0

0

0xFF

0

0

0x01

0x01

101

Hodnota:

0x30

0x00

0x00

0x00

0x74

0x00

0x00

0x00

0xFF

0x00

0x00

0x00

0x00

0x00 00 00 65

Adresa:

100

101

102

103

104

105

106

107

108

109

110

111

112

113,114,115,116

25 of 29

Ukazatel - Pointer na integer

int x;

int * piArray; // pointer na iArray = piArray

piArray = iArray;�piArray = &iArray[0]; // dva ekvivalentní zápisy

x = *piArray;

// x bude = ‘t’

Prom:

chTmp

iArray[0]

iArray[1]

iArray[2]

piArray

Hodnota:

'0'

0

0

0

't'

0

0

0

0xFF

0

0

0x01

0x01

101

Hodnota:

0x30

0x00

0x00

0x00

0x74

0x00

0x00

0x00

0xFF

0x00

0x00

0x00

0x00

0x00 00 00 65

Adresa:

100

101

102

103

104

105

106

107

108

109

110

111

112

113,114,115,116

26 of 29

K čemu jsou ukazatele

  • Realizace seznamů - hlavně dynamických
  • Možnost předávání parametrů do funkcí

#include "stdio.h"

int main( int argc, char* argv[] )

{

return 5;

}

Jak číst složité definice?

  1. Najdu jméno proměnné, nebo funkce
  2. Pokračuji doprava až narazím na konec
  3. Vrátím se na jméno a čtu doleva

Např. argv je pole, ukazatelů na char.

27 of 29

Pointer na funkci

Program je sada instrukcí v paměti �- paměťově mapované v oblasti kódu. �Je možné získat adresu instrukce

Parametry programu - argc a argv

Stack ↓

Heap ↑

Globální proměnné

Text (a.out)

#include <stdio.h>

void hello(void)

{

printf("I like PRP!\n");

}

int main(void)

{

hello();

return 0;

}

#include <stdio.h>

void hello(void)

{

printf("I like PRP!\n");

}

int main(void)

{

void (*pFunc)(void) = hello;

pFunc();

return 0;

}

28 of 29

Čtení definicí

char chTmp; // chTmp je proměnná typu char, v paměti zabírá 1 byte = 8 bitůint iRetVal; // iRetVal je proměnná typu char, v paměti zabírá 4 byte = 4*8 bitůchar chLogin[15]; // chLogin je pole patnácti promenných typu char, zabírá 15*1 byte�int iArray[3]; // iArray je pole tří proměnných typu int, v paměti zabírá 3*4 byte = 3*4*8 bitů�char * pchChar; // pchChar je ukazatel na hodnotu typu char, v paměti zabírá 4 byte = 4*8 bitů�int * piInt; // piInt je ukazatel na hodnotu typu int, v paměti zabírá 4 byte = 4*8 bitůvoid (*pFunc)(void) = hello; // pFunc je ukazatel na funkci, která nemá parametr (void) a nevrací žádnou hodnotu void a je inicializovaná na začátek funkce hello - z toho plyne, že hlavička funkce hello musí být void hello(void)

Jak číst složité definice?

  • Najdu jméno proměnné, nebo funkce
  • Pokračuji doprava až narazím na konec
  • Vrátím se na jméno a čtu doleva

29 of 29

Shrnutí

  • Vysvětlit pojem ukazatel - uvědomit si, jaké číslo ukazatel obsahuje
  • Konverze číselných soustav
  • Definice konstanty pomocí direktivy preprocesoru
  • Alokace proměnných, stack a změny na stacku v případě definice bloku, anatomie programu v paměti
  • Algoritmus čtení komplikovaných definicí

Co se hodí ke zkoušce a státnicím: viz seznam - být schopen mluvit o jednotlivých bodech