You are on page 1of 45

PB071 Programovn v jazyce C

Funkce, pam a ukazatel, pole

vod do C, 3.3.2014

PB071

Organizan

vod do C, 3.3.2014

PB071

Organizan
Prce na Unixovch strojch
Poadujeme jen elementrn zklady (ssh, gcc) http://www.ee.surrey.ac.uk/Teaching/Unix/

Sloventina v zadn kol / vuce


Je oficiln vukov jazyk na MU Polote prosm dotaz do fra pi nejasnosti

C vs. Python
Pipravili jsme pro vs pehled zkladnch rozdl (dky M. Klurovi) http://cecko.eu/public/cvspython
vod do C, 3.3.2014

PB071

Vnitrosemestrln test
Termn 7.4. v 12:00 a 13:00 v D1 Nutno se pihlsit pes IS (bude oteveno pozdji) Formou paprovho odpovdnku Celkov zisk max. 20 bod

vod do C, 3.3.2014

PB071

Funkce, deklarace, definice

vod do C, 3.3.2014

PB071

Funkce - koncept
Dleit prvek jazyka umoujc zachytit rozdlen eenho problmu na podproblmy Logicky samostatn st programu (podprogram), kter zpracuje svj vstup a vrt vstup
vstup v nvratov hodnot nebo pomoc vedlejch efekt
zmna argument nebo globlnch promnnch
int main(void) { // ... float f2c(float fahr) { for (int fahr = low; fahr <= high; fahr += step) { return F2C_RATIO * (fahr - 32); celsius = 5.0 / 9.0 * (fahr 32); } } int main(void) { return 0; } // ...

implementace funkce

vod do C, 3.3.2014

for (int fahr = low; fahr <= high; fahr += step) { float celsius = f2c(fahr); PB071 voln funkce // ...

Funkce - deklarace
Znme ji funkci main
int main() { return 0; }

Funkce mus bt deklarovny ped prvnm pouitm


prvn pkaz v souboru obsahujc zavoln funkce

Dva typy deklarace

void foo1(void); int foo2(void); pedbn deklarace float foo3(int a); deklarace a definice zrove float foo3(int); void foo4(int a, float* b); Deklarace funkce

// voln foo1(); x = foo2(); x = foo3(15);

nvratov_typ jmno_funkce(arg1, arg2, arg3...); lze i bez uveden jmen promnnch (pro prototyp nejsou dleit)

vod do C, 3.3.2014

PB071

Funkce - definice
Implementace tla funkce (kd) Uzaveno v bloku pomoc {}
pozor, lokln promnn zanikaj

Funkce kon provedenm poslednho pkazu


lze ukonit ped koncem pkazem return funkce vracejc hodnotu mus volat return hodnota;

Lze deklarovat a definovat zrove


float f2c(float fahr); // declaration only ---float f2c(float); // declaration only ---float f2c(float fahr) { // declaration and definition float celsius = (5.0 / 9.0) * (fahr - 32); return celsius; }
vod do C, 3.3.2014

PB071

Funkce - argumenty
Funkce me mt vstupn argumenty
dn, fixn poet, promnn poet lokln promnn v tle funkce maj piazen datov typ (typov kontrola)

Argumenty funkce se v C vdy pedvaj hodnotou


pi zavoln funkce s parametrem se vytvo lokln promnn X hodnota vrazu E pi funknm voln se zkopruje do X zane se provdt tlo funkce
float f2c(float fahr) { return (5.0 / 9.0) * (fahr - 32); } int main(void) { int a = 10;

implementace funkce, fahr je lokln promnn voln funkce s argumentem 100, 100 je vraz
PB071

float celsius = f2c(100); float celsius = f2c(a);


return 0; vod do C, 3.3.2014

Zpsob pedvn funknch argument


Argument funkce je jej lokln promnn
lze ji st a vyuvat ve vrazech lze ji ve funkci mnit (pokud nen const) na konci funkce promnn zanik

Poad pedvn argument funkci nen definovno


tedy ani poad vyhodnocen vraz ped funknm volnm int i = 5; foo(i, ++i) foo(5, 6) nebo foo(6,6)?

Problm: jak penst hodnotu zpt mimo funkci?


vyuit nvratov hodnoty funkce vyuit argument pedvanch hodnotou ukazatele

vod do C, 3.3.2014

PB071

Funkce nvratov hodnota


Funkce nemus vracet hodnotu
deklarujeme pomoc void
void div(int a, int b) { printf("%d", a / b); }

Funkce me vracet jednu hodnotu


klov slovo return a hodnota

Jak vracet vce hodnot?

int div(int a, int b) { int div = a / b; return div; }

vyuit globlnch promnnch (vtinou nevhodn) strukturovan typ na vstup (struct, ukazatel) modifikac vstupnch parametr (pedn ukazatelem)
int div = 0; int rem = 0; void divRem(int a, int b) { div = a / b; rem = a % b; } int main() { divRem(7, 3); return 0; } PB071

vod do C, 3.3.2014

Realizace objekt v pamti, ukazatel

vod do C, 3.3.2014

PB071

Pamt
Pam obsahuje velk mnostv slot s fixn dlkou
short

0x00 0x00 0x2b 0x00

adresovateln typicky na rovni 8 bit == 1 bajt

V pamti mohou bt umstny entity


promnn, etzce

int

Entita me zabrat vce ne jeden slot

nap. promnn typu short zabr na x86 16 bit (2 bajty) adresa entity v pamti je dna prvnm slotem, kde je entita umstna

0xff

0xff
0xff 0x2b 0x00 0x00

Adresy a hodnoty jsou typicky uvdny v hexadecimln soustav s pedponou 0x


0x0a == 10 dekadicky, 0x0f == 15 dekadicky adresy na x86 zabraj pro netypovan ukazatele typicky 4 bajty, na x64 8 bajt na konkrtn dlky nespolhejte, ovte pro clovou platformu

vod do C, 3.3.2014

PB071

Organizace pamti
Instrukce (program)
nemn se

Celkov pam programu Instrukce

push %ebp 0x00401345 mov %esp,%ebp 0x00401347 sub $0x10,%esp 0x0040134d call 0x415780

Statick data (static)


vtina se nemn, jsou pmo v binrce globln promnn (mn se)

Statick a glob. data


Hello, World {0xde 0xad 0xbe 0xef}

Zsobnk (stack)
mn se pro kadou funkci (stackframe) lokln promnn

Zsobnk
lokalniProm1 lokalniProm2

Halda (heap)
mn se pi kadm malloc, free dynamicky alokovan prom.
Halda
dynAlokace1 dynAlokace2
vod do C, 3.3.2014

PB071

Promnn opakovn
Kad msto v pamti m svou adresu Pomoc nzvu promnn meme st nebo zapisovat do tto pamti instrukce assembleru pro
nap. promenna = -213;
zpis do pamti

Peklada nahrazuje jmno promnn jej adresou


typicky relativn k zsobnku movl $0xffffff2b,0xc(%esp)
-213 hexadecimln relativn adresa promnn k adrese zsobnku poten adresa zsobnku pro danou funkci

vod do C, 3.3.2014

PB071

Promnn na zsobnku
POZOR: Zsobnk je zde znzornn uebnicovm zpsobem (rst nahoru) C kd: int promennaX = -213; Asembler kd: movl
esp + 0xc 0x00000000 esp + 0x8 0x00000000 esp + 0x4 0x00000000 relativn dno zsobnku (esp) pro aktuln funkci
vod do C, 3.3.2014

$0xffffff2b,0xc(%esp)
0xffffff2b promennaX

4 bajty
PB071

Opertor &
Opertor & vrac adresu svho argumentu
msto v pamti, kde je argument uloen

Vsledek lze piadit do ukazatele


int promennaX = -213; int* pX = &promennaX;
obsah pamti adresa pamti

int promennaX = -213; (promennaX je adresa schrnky 1, obsahuje -213) int* pX = &promennaX; (pX je adresa schrnky 2, obsahuje adresu schrnky 1)

-213

vod do C, 3.3.2014

PB071

Promnn typu ukazatel


Promnn typu ukazatel je stle promnn
tj. oznauje msto v pamti s adresou X na tomto mst je uloena dal adresa Y

Pro kompletn specifikaci promnn nen dostaujc


chyb datov typ pro data na adrese Y Jak se maj bity pamti na adrese Y interpretovat? datov typ pro data na adrese X je ale znm

je to adresa Neinicializovan ukazatel


v pamti na adrese X je smet (typicky velk slo, ale nemus bt)

Nulov ukazatel (int* a = 0;)


v pamti na adrese X je 0
obsah pamti adresa pamti

Pozor na int* a, b;
a je int*, b jen int

-213

Y X
PB071

ukazatel
vod do C, 3.3.2014

Promnn typu ukazatel na typ


C kd: int* pX = &promennaX;
Asembler kd:
lea mov 0x8(%esp),%eax %eax,0xc(%esp)
0x0022ff14 0xffffff2b esp + 0x8 absolutn adresa 0x0022ff14 0x00000000 0x00000000 relativn dno zsobnku (esp) pro aktuln funkci absolutn adresa 0x0022ff0c pX promennaX opertor & vrt adresu argumentu (zde promennaX)

esp + 0xc absolutn adresa 0x0022ff18

vod do C, 3.3.2014

PB071

Opertor dereference *
Pracuje nad promnnmi typu ukazatel
podvej se na adresu v promnn X jako na hodnotu typu Y

Zpstupn hodnotu, na kterou ukazatel ukazuje


nikoli vlastn hodnotu ukazatele (co je adresa) ukazatel ale me ukazovat na hodnotu, kter se interpretuje zase jako adresa (nap. int**)

Pklady (pseudosyntaxe, oznauje zmnu typu vrazu):


&int => int* *(int*) => int *(int**) => int* **(int**) => int *(&int) => int **(&&int) => int

Pokud je dereference pouita jako l-hodnota, tak se mn odkazovan hodnota, nikoli adresa ukazatele
int* pX; pX = 10; (typicky patn nechceme mnit ukazatel) int* pX; *pX = 10; (typicky OK chceme mnit hodnotu, kter je na adrese na kterou pX ukazuje)

vod do C, 3.3.2014

PB071

Promnn a ukazatele - ukzka


obsah pamti adresa pamti

int promennaX = -213; (promennaX je adresa schrnky 1, obsahuje -213) int* pX = &promennaX; (pX je adresa schrnky 2, obsahuje adresu schrnky 1) int** ppX = &pX; (ppX je adresa schrnky 3, obsahuje adresu schrnky 2, kter obsahuje adresu schrnky 1)

-213

print(*pX); vype? (-213) (-213) print(*ppX); vype? 1 print(promennaX); vype? print(pX); vype? 1 print(&pX); vype? 2 print(ppX); vype? 2 print(*(&pX)); vype? 1
vod do C, 3.3.2014

PB071

Segmentation fault
Promnn typu ukazatel obsahuje adresu
int promennaX = -213; int* pX = &promennaX;

Adresa nemus bt pstupn naemu programu


pX = 12345678; // nejsp nen nae adresa

Pi pokusu o pstup mimo povolenou pam vjimka


*pX = 20; segmentation fault

vod do C, 3.3.2014

PB071

Vce ukazatel na stejn msto v pamti


Nen principiln problm
int promennaX = -213; int* pX = &promennaX; int* pX2 = &promennaX; int* pX3 = &promennaX;

Vechny ukazatele mohou msto v pamti mnit


*pX3 = 1; *pX = 10;

Je nutn hldat, zda si navzjem nepepisuj


logick chyba v programu problm pi pouit paralelnch vlken

Zpis resp. ten je atomick


ten, rozhodnut a zpis u atomick nemus bt kritick sekce, test&set instrukce
vod do C, 3.3.2014

PB071

ast problm
V em je problm s nsledujcm kdem?
int* a, b; a[0] = 1; b[0] = 1;

Specifikace ukazatele * se vztahuje jen k prvn promnn (a) ne ji ke druh (b) Radji deklarujte a i b na samostatnm dku a inicializujte

vod do C, 3.3.2014

PB071

Samostudium
Detailnj rozbor zsobnku a haldy
http://duartes.org/gustavo/blog/post/anatomy-of-aprogram-in-memory http://www.inf.udec.cl/~leo/teoX.pdf

vod do C, 3.3.2014

PB071

Problm s pedvnm hodnotou


void foo(int X) { X = 3; // X is now 3 } int main() { int variable = 0; foo(variable); // x is (magically) back to 0 return 0; } void foo(int* P) { *P = 3; } int main() { int x = 0; foo(&x);

// x is 3
return 0; }

Po zniku lokln promnn se zmna nepropaguje mimo tlo funkce eenm je pedvat adresu promnn
a modifikovat hodnotu na tto adrese, namsto lokln promnn

vod do C, 3.3.2014

PB071

Argumenty pedvan hodnotou ukazatele


S vyuitm ukazatel meme pedat vstup pes vstupn argumenty Pokud je vstupnm argumentem ukazatel:
1. 2. 3. 4. 5. vznikne lokln promnn P typu ukazatel pedv se hodnotou, do P zkopruje se hodnota (== adresa, nap. X) pomoc opertoru dereference * meme modifikovat pam na adrese X (*P == X) lokln promnn P na konci funkce zanik hodnota na adrese X ale zstv modifikovna

Zmna proveden ve funkci zstv po jejm ukonen


void foo(int* P) { *P = 3; } int main() { int x = 0; foo(&x); return 0; } PB071

vod do C, 3.3.2014

Pedvnm hodnotou ukazatele - ilustrace


obsah pamti adresa pamti

void foo(int* P) { *P = 3; } int main() { int x = 0; foo(&x); return 0; }

int x = 0; (x je adresa schrnky 1, obsahuje hodnotu 0) void foo(int* P); (P je adresa schrnky 2, kam se ulo argument funkce foo() foo(&x); (&x vrt adresu schrnky 1, ta se ulo do lokln promnn P) *P = 3; (*P vrt obsah schrnky 2, tedy adresu 1)
vod do C, 3.3.2014

----------------1 ----------------3 1 2

PB071

Pedvn hodnotou a hodnotou ukazatele


int main() { int myValue1 = 1; int myValue2 = 1; valuePassingDemo(myValue1, &myValue2); return 0; }

Zsobnk myValue1
1

myValue2
1 7

void valuePassingDemo(int value, int* pValue) { value = 5; *pValue = 7; }

value
1 5

pValue
&myValue2

Promnn value i pValue zanik, ale zpis do myValue2 zstv


vod do C, 3.3.2014

PB071

Jednorozmrn pole

vod do C, 3.3.2014

PB071

Jednorozmrn pole
Jednorozmrn pole lze implementovat pomoc ukazatele na souvislou oblast v pamti
jednotliv prvky pole jsou v pamti za sebou prvn prvek umstn na pamov pozici uchovvan ukazatelem

Prvky pole jsou v pamti kontinuln za sebou Deklarace: datov_typ jmno_promnn[velikost];


velikost udv poet prvk pole

0x00

0x00

0x2b

0x00

0xff

0xff

0xff

0x2b

0x00

0x00
PB071

vod do C, 3.3.2014

int myArray [10]; Jednorozmrn pole


for (int i = 0; i < 10; i++) myArray[i] = i+10;

Syntaxe pstupu: jmno_promnn[pozice];


na prvek pole se pistupuje pomoc opertoru [] indexuje se od 0, tedy n-t prvek je na pozici [n-1] promnn obsahuje ukazatel na prvek [0]
myArray[6] (hodnota)

myArray[9] (hodnota)

vod do C, 3.3.2014

10

11

12

13

14

15

16

17

18

19

myArray[10] (problem)

myArray[0] (hodnota)

myArray (adresa)

PB071

Zjitn velikosti pole


Vyuit opertoru sizeof() sizeof(pole)
velikost pamti obsazen polem v bajtech funguje jen pro statick pole, jinak velikost ukazatele

sizeof(ukazatel)
velikost ukazatele (typicky 4 nebo 8 bajt)

Pozor, pole v C nehld meze!


ten/zpis mimo alokovanou pam me zpsobit pd nebo nedouc zmnu jinch dat
int array[10]; array[100] = 1; // runtime exception

vod do C, 3.3.2014

PB071

Pole vs. hodnota


Jak je rozdl mezi ukazatelem na msto v pamti obsahujcm hodnotu typu integer a promnnou typu pole integer?
int myInt = -213; int* pMyInt = &myInt; int myIntArray[100];

Jmno pole bez [] vrac adresu na zatek pole Co vrac &myIntArray ?

vod do C, 3.3.2014

PB071

Jaktoe &array == array ?


int main() { char array[10]; char* array2 = 0; printf("array=%p\n", array); printf("&array=%p\n", &array); printf("array2=%p\n", array2); printf("&array2=%p\n", &array2); array2 = array; printf("array2=%p\n", array2); printf("&array2=%p\n", &array2);

array=0x0028FF18 &array=0x0028FF18 array2=0x00000000 &array2=0x0028FF14 array2=0x0028FF18 &array2=0x0028FF14

printf("tmp=%p, %d\n", array+1); // address of second element printf("tmp=%p, %d\n", &array+1);// == array + sizeof(char) * 10 return 0; }

Jmno pole array se obvykle vyhodnot na adresu prvnho prvku pole (zvis na pekladae)
&array == array Ale array+1 != &array+1 (pokud je pole del jak jeden element)

http://stackoverflow.com/questions/2528318/c-how-come-an-arrays-addressis-equal-to-its-value
vod do C, 3.3.2014

PB071

Prce s poli

vod do C, 3.3.2014

PB071

Jednorozmrn pole - opakovn


Jednorozmrn pole lze implementovat pomoc ukazatele na souvislou oblast v pamti
int array[10];

Jednotliv prvky pole jsou v pamti za sebou Promnn typu pole obsahuje adresu prvnho prvku pole
int *pArray = array; int *pArray = &array[0];

Indexuje se od 0
n-t prvek je na pozici [n-1]

Pole v C nehld pmo meze pi pstupu!


int array[10]; array[100] = 1;

pro kompiltor OK, me nastat vjimka pi bhu (ale nemus!)


vod do C, 3.3.2014

PB071

Jednorozmrn pole promnn dlka


Dynamick alokace bude probrno pozdji
promnn typu ukazatel int* pArray; msto na pole alokujeme (a odebrme) pomoc specilnch funkc na hald
malloc(), free()

Deklarace pole s variabiln dlkou


variable length array (VLA) int arraySize = 20; scanf("%d", &arraySize); a od C99 int arrayLocal[arraySize]; arrayLocal[10] = 9; alokuje se na zsobnku nen nutn se starat o uvolnn (lokln promnn) nedoporuuje se pro pli velk pole (pout haldu)
PB071

vod do C, 3.3.2014

Ukazatelov aritmetika
Aritmetick opertory provdn nad ukazateli Vyuv se faktu, e array[X] je definovno jako *(array + X) Opertor + pit k adrese na rovni prvk pole
nikoli na rovni bajt! obdobn pro -, *, /, ++ ...

Adresu potku pole lze piadit do ukazatele

vod do C, 3.3.2014

PB071

Ukazatelov aritmetika - ilustrace


int myArray[10]; for (int i = 0; i < 10; i++) myArray[i] = i+10; int* pArray = myArray + 5;

*(myArray+2) (hodnota)

myArray[0] (hodnota)

myArray+2 (adresa)

myArray (adresa)

10

11

12

13

14

pArray (adresa)

15

16

myArray[6] (hodnota) 17

18

19

vod do C, 3.3.2014

PB071

Demo ukazatelov aritmetika

void demoPointerArithmetic() { const int arrayLen = 10; int myArray[arrayLen]; int* pArray = myArray; // value from variable myArray is assigned to variable pArra int* pArray2 = &myArray; // wrong, address of variable array, //not value of variable myArray (warning) for (int i = 0; i < arrayLen; i++) myArray[i] = i; myArray[0] = 5; // OK, first item in myArray *(myArray + 0) = 6; // OK, first item in myArray //myArray = 10; // wrong, we are modifying address itself, not value on address pArray = myArray + 3; // pointer to 4th item in myArray //pArray = 5; // wrong, we are modifying address itself, not value on address *pArray = 5; // OK, 4th item pArray[0] = 5; // OK, 4th item *(myArray + 3) = 5; // OK, 4th item pArray[3] = 5; // OK, 7th item of myArray pArray++; // pointer to 5th item in myArray pArray++; // pointer to 6th item in myArray pArray--; // pointer to 5th item in myArray

int numItems = pArray - myArray; // should be 4 (myArray + 4 == pArray) } PB071 vod do C, 3.3.2014

Ukazatelov aritmetika - otzky


int myArray[10]; for (int i = 0; i < 10; i++) myArray[i] = i+10; int* pArray = myArray + 5;

Co vrt myArray[10]? Co vrt myArray[3]? Co vrt myArray + 3? Co vrt *(pArray 2) ? Co vrt pArray - myArray ?

vod do C, 3.3.2014

PB071

Typick problmy pi prci s poli


Zpis do pole bez specifikace msta
int array[10]; array = 1;

promnnou typu pole nelze naplnit (rozdl oproti ukazateli)

Zpis tsn za konec pole, ast N+1 problm


int array[N]; array[N] = 1; v C pole se indexuje od 0

Zpis za konec pole


nap. dsledek ukazatelov aritmetiky nebo chybnho cyklu int array[10]; array[someVariable + 5] = 1;

Zpis ped zatek pole


mn ast, ukazatelov aritmetika

ten/zpis mimo alokovanou pam me zpsobit pd nebo nedouc zmnu jinch dat (kter se zde nachz)
int array[10]; array[100] = 1; // runtime exception

vod do C, 3.3.2014

PB071

Tutoril v etin
Programovn v jazyku C
http://www.sallyx.org/sally/c/

vod do C, 3.3.2014

PB071

Shrnut
Platnost promnnch
promnn zanik na konci bloku kde byla deklarovna vjimkou jsou globln promnn

Funkce, pedvn hodnotou resp. ukazatelem


dleit koncept, realizace v pamti

Pole

vod do C, 3.3.2014

PB071

You might also like