Лекции по увод в програмирането



страница15/16
Дата25.07.2016
Размер0.96 Mb.
#5224
ТипЛекции
1   ...   8   9   10   11   12   13   14   15   16

12 декември

Пример:


функция compstr сравнява два низа s1 и s2 символ по символ и връща първия символ от низа s1, който е различен от съответния в низа s2; ако низа s2 има повече символи се връща първият символ в s2 за който няма съответен в s1; ако съвпадат се връща нулева стойност;

compstr (char *s1, char *s2)

{ while (*s1)

if (*s1++!=*s2++) return *--s1;

return *s2;

}
тъй като указателите са променливи те могат да бъдат обединявани в масиви;


int *px[4];

int x1, x2, x3, x4;

p[0] = &x1; p[1] = &x2; p[2] = &x3; p[3] = &x4;
int *px[] = { &x1, &x2, &x3, &x4 };
28. Многомерни масиви и указатели.
char *error[] = { “Грешка1”, “Предупреждение1”, “Фатална грешка”, “Няма грешка” };
при тази дефиниция имаме масив от указатели към едномерни символни масиви;
char error[4][23] = { “Грешка1”, “Предупреждение1”, “Фатална грешка”, “Няма грешка” };

при тази дефиниция имаме двумерен масив от символен тип;


разлика в двата подхода:



    • за двумерния масив се разпределят 4х23 последователни байта и символните константи се записват във вече заделената памет, при указателите символните константи се намират на произволни места в паметта;

    • за всяко съобщение указателите използват точно толкова байтове, колкото се налага, а при двумерния масив за всяко съобщение се разпределят по 23 байта;

    • достъпа до масива от указатели се осъществява чрез бързата адресна аритметика; освен това се опростява управлението на паметта – например за да разменим местата на две съобщения при указателите трябва да разменим само адресите на указателите, а при двумерния масив трябва да разменяме съобщенията символ по символ;

сходство в двата подхода:

    • когато се разглежда двумерен масив, той реално се разглежда като масив от указатели към едномерни масиви, които имат една и съща дължина; в такъв случай и при двата подхода се спазва концепцията за дефиниране на указател към указател;

int x, *px, **ppx;

x = 20; px = &x; ppx = &px;

пряк достъп до стойността на x – x;

косвен достъп до стойността на x - *px, **ppx;


float matrix[2][2];

matrix[0]  &matrix[0][0];

matrix[1]  &matrix[1][0];

това е поради интерпретацията на двумерния масив като масив от указатели към едномерни масиви;

в сила е:

matrix[i][j]  *(matrix[i] + j)  * ( *(matrix+i) + j );


float a[DIM1][DIM2];

&a[i][j]  (float *)a + i*DIM2 + j;

това е така, защото името на един двумерен масив се разглежда като указател към указател към първия елемент на масива;

виждаме, че не се използва първа размерност за да се достигне до a[i][j];


ако имаме един указател, можем да го използваме с индекси
int data = 5;

int *ptr = &data;

ptr[0]  *ptr; ptr[i]  *(ptr + i);
29. Операции с отделни битове.
Езикът C предлага пълен набор от операции за обработване на отделни битове на целочислени стойности; тези операции се наричат поразрядни логически операции; те позволяват променяне, установяване или преместване на отделни битове от стойността на променлива от тип int или от тип char; операциите не могат да се използват с операнди от тип float, double, long double; поразрядните логически операции често се използват за създаване на драйверни програми за управление на външни устройства;

операциите са:

поразрядно логическо “и”;

поразрядно логическо “или”;

поразрядно логическо изключващо “или”;

поразрядно логическо отрицание;

поразрядно логическо изместване надясно;

поразрядно логическо изместване наляво;


прилагат се за всеки отделен бит на операнда;
операнд1 & операнд2

операндите са изрази от цял или символен тип; поразрядно се извършва операцията “и”;

например ако искаме да анулираме битове в едно число от тип int използваме маска, в която единицата запазва бита, а нулата го анулира;

11011011 & 10111101 – използваме маската 10111101 за да анулираме втори и седми бит в двоичното числото 11011011;


операнд1 | операнд2

операндите са изрази от цял или символен тип; поразрядно се извършва операцията “или”;

например ако искаме да направим определни битове единици в едно число от тип int използваме маска, в която единицата променя бита на единица, а нулата го запазва;

10110010 & 01000010 – използваме маската 01000010 за да променим втори и седми бит на единици в двоичното числото 10110010;


операнд1 ^ операнд2

операндите са изрази от цял или символен тип; поразрядно се извършва операцията изключващо “или” – събиране по mod 2, т.е. ако битовете са различни резултатът е 1, ако са еднакви – 0;


~операнд

операндът е от цял или символен тип; поразрядно се извършва операцията отрицание; операцията е едноместна – всеки бит 0 в операнда става 1, а всеки бит 1 – 0;


int x, y;

y = ~x + 1  y = -x, ако x > 0;

това е така, тъй като тип int се представя в допълнителен код в паметта;
операнд1 << брой битове

операндът е от цял или символен тип, брой битове е цяло число; поразрядно се извършва логическо изместване вляво;

освободените битове отдясно се запълват с нули, а първите ‘брой битове’ бита се губят;
операнд1 >> брой битове

операндът е от цял или символен тип, брой битове е цяло число; поразрядно се извършва логическо изместване вдясно;

освободените битове се запълват със знаковия бит, а последните ‘брой битове’ бита се губят;
и в двете операции, ако ‘брой битове’ има отрицателна стойност или е число по-голямо или равно на дължината на операнда, резултатът е неопределен;

17 декември

Операциите изместване наляво и изместване надясно могат да се използват за бързо умножение или деление на степени на двойката;

~ има приоритет 2 и е дясно-асоциативна;

<<, >> имат приоритет 5 и са ляво-асоциативни;

& има приоритет 8 и е ляво-асоциативна;

^ има приоритет 9 и е ляво-асоциативна;

| има приоритет 10 и е ляво-асоциативна;


двуместните поразрядни операции могат да участват в съкратената форма на присвояването;

например:

операнд1 = операнд1 << операнд2  операнд1 <<= операнд2;

асоциативното правило за това присвояване е от дясно на ляво;


Пример:

Функция, която преобразува десетично число в допълнителен код;


void detob (int d, int a[])

{ int i = 0, p = 1;

while (p!=0)

{ a[i++] = ( (d&p) && 1 );

p <<= 1;

}

}


30. Предаване на резултатите чрез формални параметри.
Вграденият механизъм в C за заместване на формалните параметри с фактически е по стойност, т.е. стойността на фактическия параметър се замества във формалния параметър; в този случай не можем да изнасяме стойности от функцията чрез фактическите параметри;

от функцията могат да се изнасят стойности чрез формалните параметри, ако заместването е реализирано по име;
void fun (int x, int *p)

{ …


*p = 10;

x += 10;


}

void main ()



{ int a = 15, b = 5; int a = 15, b = 5;

fun (a, &b);  int *q = &b;

… fun (a, q);

}
при извикване на функцията fun:

стойностите на фактическите параметри отдясно наляво се поместват в стека; след това заместване става от ляво надясно, първо за x се разпределя паметта в стека, където е записана стойността на променливата a, за указателя p се разпределя паметта в стека, където е записан адреса на променливата b;

във функцията fun *p  b от функцията main – по този начин предаваме резултат от извиканата функция в извикващата функция;

*p = 10 – изменяме променливата b от функцията main;

x += 10 променя стойността на x, която е записана в стека, но не и стойността на a;


това е причината scanf да изисква фактически параметър адрес на променливите;
в C++ може да се декларира име или псевдоним
int n;

int &nn = n;

по този начин nn го декларираме като псевдоним на n; nn и n означават една и съща променлива (една и съща памет);
double a[10];

double &last = a[9];

псевдоним на елемент от масив;
const char &new_line =’\n’;

това е псевдоним на вградена константа и за нея не се разпределя памет;


int i = 5;

int *p = &i;

int *&s = p;

s – псевдоним на указателя p;


важно приложение на псевдонимите – чрез тях на логическо ниво може да се реализира заместване по име; то се реализира като формалния параметър се прави псевдоним на фактическия параметър;
void swap (float *a, float *b)

{ float temp;

temp = *a;

*a = *b;


*b = temp;

}

void main ()



{ float x, y;

if (x > y) swap (&x, &y);



}

реализация с псевдоними:


void swap (float &a, float &b)

{ float temp;

temp = a;

a = b;


b = temp;

}

void main ()



{ float x, y;

if (x > y) swap (x, y);



}
в този случай формалният параметър a става псевдоним на x, а формалният параметър b – псевдоним на y;


на ниво машинен език в реализацията няма разлика между заместване с указатели и заместване с псевдоними;
31. Функции от тип указател.
Резултатът от една функция може да е указател – тогава самата функция е от тип указател;
int *f1 (списък_от_формални_параметри)

{



return (израз);

}


ако типът на израз не е указател от тип int ще се извършви автоматично преобразуване;




Сподели с приятели:
1   ...   8   9   10   11   12   13   14   15   16




©obuch.info 2024
отнасят до администрацията

    Начална страница