Пример:
функция 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 ще се извършви автоматично преобразуване;
Сподели с приятели: |