620 likes | 1.13k Views
Масиви. Масив е множество от данни ( елементи ) от един и същи тип (базов тип) с общо име ( идентификатор ). Достъпът до елемент се прави посредством неговата позиция (индекс). Дефиниране тип идентификатор [ израз ] ;. int a[5]; // 5 целочислени елемента или 10 байта
E N D
Масиви • Масив е множество от данни (елементи) от един и същи тип (базов тип) с общо име (идентификатор). • Достъпът до елемент се прави посредством неговата позиция (индекс). • Дефиниране тип идентификатор[израз];
int a[5]; //5 целочислени елемента или 10 байта char c[10]; //10 символа или 10 байта float f[20]; //20 реални елемента или 80 байта • Памет – масивът се запазва в паметта като непрекъсната последователност от байтове. общ брой байтове = sizeof(базов тип) * размерност на масива
Указател към масив – това е указател към първия елемент в масива или името на масива. int a[5], *p; /* а е указател константа */ /* р е указател променлива */ p=a; /* Еквивалентно на p=&a[0]; */ • Достъп до елемент на масив • Чрез операция индексиране (директен достъп) - относително отместване спрямо началото на масива идентификатор[индекс] индекс - целочислен израз, определящ мястото на елемента спрямо началото на масива.
Първият елемент на масива е с индекс = 0, а последният – с индекс = размерност – 1. • Компилаторът на C не проверява дали стойността на индекса не надхвърля размерността на масива. • Указателят може да се индексира като масив. ( p[3] ) • Например: • int x[10], *p; • p=x+4; • p[-2]=p[2]=100; /* x[2]=x[6]=100 */ • Чрез операция намиране на стойност по адрес (косвен достъп) - абсолютно адресиране, което е по-бързо от относителното адресиране.*(указател+индекс)
Връзка между указатели и масиви • В езика C съществува връзка между указатели и масиви. • Името на масива без индекс е указател към първия елемент от масива. • Указател може да бъде индексиран, все едно че е деклариран като масив. • int a[10], *p; • p=a; • p[5]=100; /* чрез индекс */ • *(p+5)=100; /* Чрез адресна аритметика */ • Или р съвпада с &a[0] • Както и *(a+5)съвпада с a[5]иp[5] съвпада с *(p+5)
Например: int a[5], *p, x, i; /* Едномерен масив */ … p=a; /* p=&a[0]; */ x=*p; /* x=a[0]; */ x=*(p+1); /* x=a[1]; */ x=*(p+i); /* x=a[i]; */ x=*(a+i); /* x=a[i]; */ i=*++p; i=(*p)++; i=*p++; i=*p+1;
Предаване на масив като параметър на функция • Масивът като параметър на функция се предава по адрес, т.е. предава се адресът на първия елемент от масива и се работи с оригиналните елементи на масива. Следователно промени на елементите на масива в тялото на функцията ще се отразят и в извикващата функция. • Едномерният масив като формален параметър на функция се декларирапо един от три възможни начина: 1) като указател, 2) като масив с определен размер, и 3) като масив без определен размер.
Например: void funct(int *x); /* 1) Указател*/ или void funct(int x[10]);/* 2) Масив с определен размер */ или void funct(intx[]); /* 3) Масив без определен размер */ int main () { int a[10]; funct(a); /* Предаване на масив чрез адрес */ … return 0; } Пример: Брой от положителните елементите на едномерен масив и минимален елемент в едномерен масив.
А) Обработка на масива чрез използване на индекси. #include <stdio.h> #define N 50 int in(int a[]); void out(int a[],int n); int count(int a[],int n); void min_v(int a[],int n,int *min); void main() { int v[N],min,n,c; n=in(v); out(v,n); printf("\ncount=%d\n",count(v,n)); min_v(v,n,&min); printf("min=%d\n",min); } int in(int a[]) {int i=0, temp; while(printf("el.%d=",i+1),scanf("%d",&temp)==1&&i<N) a[i ++]=temp; return i; }
void out(int a[],int n) { int i; for(i=0;i<n;i++) printf("%3d",a[i]); } int count(int a[],int n) { int i,c; for(i=0,c=0;i<n;i++) if(a[i] > 0) c++; return c; } void min_v(int a[],int n,int *min) { int i; *min=a[0]; for(i=0;i<n;i++) { if(a[i]< *min) *min=a[i]; } }
В. Обработка на масива чрез указатели. #include <stdio.h> #define N 50 int in(int *a); void out(int *a,int n); int count(int *a,int n); void min_v(int *a,int n,int *min); void main() { int v[N],min,n; n=in(v); out(v,n); printf("\ncount=%d\n",count(v,n)); min_v(v,n,&min); printf("min=%d\n",min); } int in(int *a) {int i=0, temp; while(printf("el.%d=",i+1),scanf("%d",&temp)==1&&i<N) *(a + i ++)=temp; return i; }
void out(int *a,int n) { int i; for(i=0;i<n;i++) printf("%3d", *(a+i));// или printf("%3d", *a++); } int count(int *a,int n) { int c,*p; for(c=0,p=a;p<a+n;p++) if(*p > 0) c++; return c; } void min_v(int *a,int n,int *min) { int i,*p; *min=*(a+0);//или *min=*a; for(p=a;p<a+n;p++) { if(*p< *min) *min=*p; } }
Многомерни масиви • Многомерен масив е масив, чиито елементи са масиви. • Той може да има произволен брой размерности. • Масивът се запазва в паметта като непрекъсната последователност от байтове, като първо нараства най-десният индекс. Следователно двумерният масив се запазва в паметта по редове. • общ брой байтове = размерност на 1-вия индекс * размерност на 2-рия индекс *sizeof(базов тип) int x[2][3];
Например: int x[2][3], *p, r, i, j; /* Двумерен масив */ … p=x; /* p=&x[0][0];*/ r=*p; /* r=x[0][0]; */ r=*(x[i]+j); /* r=x[i][j]; j-та колона на i-тия ред * / r=*(*(x+i)+j); /* r=x[i][j]; */ • Инициализиране на масив тип идентификатор[размер1]…[размерN] = {списък от стойности}; floatb[2][3] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}; или floatb[2][3] = {{1.0, 2.0, 3.0}, {4.0, 5.0, 6.0}}; • Ако списъкът от стойности е по-малък от общия брой елементи на масива, неинициализираните елементи ще имат нулеви стойности; ако е по-голям, компилаторът ще даде грешка.
Двумерният масив като формален параметър на функция се декларира като масив, на който първата размерност може да се пропусне, но втората размерност задължително се дефинира. • Например: void funct(int x[2][3]);/* Масив с определениразмери */ или void funct(intx[][3]);/* Най-десният размер задължително се дефинира */ void main () { int a[2][3]; funct(a); /* Предаване на масив чрез адрес*/ }
Връзка между указатели и многомерни масиви int a[10][10],*p; p=a; • Или рсъвпада &a[0][0] • a[0] е адрес на ред нулев, а a[i] – адрес на i-ти ред. • Достъпът до елемент на двумерния масив се осъществява по два начина: • чрез индексиране - a[i][j] или • чрез указател - *(a+(i*дължина на реда)+j). • Например достъпът към елемент 1,2 е a[1][2]или *(a+12). • Достъпът към елемент a[i][j] може да се запише и като: *(a[i] + j)
Връзка между указатели и многомерни масиви • Двумерният масив може да се разглежда като указател към масив от едномерни масиви. • Може да се използва само един указател, за да се осъществи достъп до елементите вътре в ред на двумерния масив.
Пример: Определяне на сумите по редове и колони на двумерен масив с брой на редовете r<=10 и брой на колоните c<=10. #include<stdio.h> #define R 10 #define C 10 void in_m(int a[][C],int *r,int *c); void out_m(int a[][C],int r,int c); int sum_r(int a[],int c); void sum_r_m(int a[][R],int r,int c,int s_r[]); void sum_c_m(int a[][R],int r,int c,int s_c[]); void main() { int x[R][C],s_r[R],s_c[C],r,c,i; in_m(x,&r,&c); out_m(x,r,c); sum_r_m(x,r,c,s_r); printf("Sumi po redove\n"); for(i=0;i<r;i++) printf("%3d",*(s_r+i));//*s_r++ sum_c_m(x,r,c,s_c); printf("\nSumi po koloni\n"); for(i=0;i<c;i++) printf("%3d",*(s_c+i)); }
/* Попълване на матрицата по редове */ void in_m(int a[][C],int *r,int *c) { int i,j,*p; do { printf(“брой редове:"); scanf("%d",r); printf(“брой колони:"); scanf("%d",c); }while(*r>R || *c>C ||*r<1||*c<1); for(i=0;i<*r;i++) { printf(“Ред %d\n",i); p=a[i]; for(j=0;j<*c;j++) {printf("el[%d][%d]=",i,j); scanf("%d",p + j);//(a[i]+j) } } }
/* Извеждане на матрицата */ void out_m(int a[][C],int r,int c) { int i,j,*p; for(i=0;i<r;i++) {p=a[i]; for(j=0;j<c;j++) printf("%3d",*(p+j ));//*(a[i]+j) printf("\n"); } } /* Сумиране на едномерен масив (ред) */ int sum_r(int a[],int c) { int i,s; for(i=0,s=0;i<c;i++,a++) s+=*a; return s; }
/* Определяне на сумите по редове на матрицата */ void sum_r_m(int a[][R],int r,int c,int s_r[]) { int i; for(i=0;i<r;i++,s_r++) *s_r=sum_r(a[i],c);/* сумиране на i–ти ред */ } /* Определяне на сумите по колони на матрицата */ void sum_c_m(int a[][R],int r,int c,int s_c[]) { int i,j,*p; for(j=0;j<c;j++,s_c++) { *s_c=0; for(i=0;i<r;i++) { *s_c+=*(a[i]+j); } } }
Масив от указатели • Масив от указатели е масив, чиито елементи са указатели. • Двумерният масив може да се представи като едномерен масив от указатели, чиито стойности са адреси на първите елементи от всеки ред на двумерния масив. • Пример: Представяне н двумерен масив като масив от указатели. #include <stdio.h> void main () { int x[3][2] = {{10, 20}, {30, 40}, {50, 60}}, *y[3], i; y[0]=&x[0][0]; /* Указателят сочи към първия елемент на ред 0 */ y[1]=&x[1][0]; y[2]=&x[2][0]; for(i=0; i<3; i++) printf("%d ", *y[i]); printf("\n"); }
Когато двумерният масив се представя като масив от указатели, то е необходимо да се отдели памет за всеки ред на двумерния масив. /* Програмата отделя памет за двумерен масив t[20][5]. */ #include <stdio.h> #include <stdlib.h> void main () { float *t[20]; int i; for(i=0; i<20; i++) { t[i]=malloc(5*sizeof(float)); if(t[i]==NULL) { printf("Няма място.\n"); exit(1); } } /* t[0][0], . . ., t[19][4] */ }
В случай на двумерен масив с редове с променлива дължина, например ред 0 – 10 елемента, ред 1 - 100, ред 2 - 50, то: float *a[3], a0[10], a1[100], a2[50]; a[0]=a0; a[1]=a1; a[2]=a2;
Указател към указател • Указател към указател е указател, който сочи към друг указател, който от своя страна сочи към желания обект. тип**идентификатор; • Пример: #include <stdio.h> void main () { int x, *p, **q; x=10; p=&x; q=&p; printf("%d\n", **q); /* Отпечатва 10 */ }
Указател към функция • Указател към функция съдържа адреса на входната точка на функцията и може да се използва за извикване на функцията. • При компилация се установява входна точка за всяка функция в програмата. При изпълнение на програмата извикването на функция се осъществява чрез тази входна точка. Следователно, ако указател съдържа адреса на входната точка на функция, той може да се използва за извикване на тази функция. • Адресът на функцията се получава, като се използва името на функцията без скоби и аргументи.
Пример: Предаване на функция като параметър на друга функция. /* Програмата използва функция, която изчислява сумата от реципрочни стойности или сумата от квадратите на стойности, като предава функции като параметри. */ #include <stdio.h> double sum(int n, double (*funct)(int k)); double reciprocal(int k); double square(int k); void main () { printf("1/1+1/2+1/3+1/4+1/5 = %.6f\n", sum(5, reciprocal)); printf("1**2+2**2+3**2 = %.6f\n", sum(3, square)); }
/* Функцията sum изчислява сумата от n аргумента, като видът на аргумента се определя от функцията funct, с която се извиква sum. funct се декларира като указател към функция. */ double sum(int n, double (*funct)(int k)) { double s=0.0; int i; for(i=1; i<=n; i++) s += (*funct)(i); return(s); } /* Функцията изчислява реципрочната стойност на k. */ double reciprocal(int k) { return(1.0/k); } /* Функцията изчислява квадрата на k. */ double square(int k) { return((double)k*k); }
Използването на указател към функция позволява дадена функция да бъде извиквана с различни функции. • Тези функции обаче трябва да имат аналогични формални параметри и тип на резултата. • Предимството от използването на указател към функция е създаването на масив от функции.
Символен низ • Символен низ е масив от тип char, който завършва с нулевия символ ’\0’. • chars[11]; /* s може да съхрани 10 символа, като се оставя място за нулевия символ.*/ • Символна константа е последователност от символи, заградена с кавички ("). Тя може да се зададе чрез директивата #define. #defineMSG”Тестване на програмата” • Символен низ се дефинира посредством масив от символи или указател към символен низза който се заделя динамично памет. char s[10]; char *s = (char*) malloc(10);
Функции за въвеждане и извеждане на символен низ /* Въвежда низа s от клавиатурата, докато срещне символа нов ред ’\n’, който в низа се преобразува в ’\0’. Връща s или NULL при край на файл или грешка. */ char *gets(char *s); /* Извежда низа s върху екрана, като допълва със символа за нов ред. Връща последния записан символ или EOF при грешка. */ int puts(char *s); • При въвеждане на низ с функцията scanf() и форматна спецификация %sсимволите празна позиция, табулатор и нов ред сигнализират за край на низа.
Примери: #include <stdio.h> void main () {char s[11]; printf("Въведи низ: "); gets(s); puts(s); } Резултати: Въведи низ: Иван Петров Иван Петров • Функциите за операции със символни низове се • намират в библиотеката string.h.
/* Връща броя на символите в низа s (без нулевия символ) */ int strlen(char *s); /* Сравнява низовете s1 и s2. Връща стойност <0, ако s1s2; стойност ==0, ако s1 е същият като s2; стойност >0, ако s1>s2 */ int strcmp(char *s1, char *s2); /* Сравнява най-много maxlenсимвола от s2 и s1. Връща стойнст <0, ==0, >0 */ int strncmp(char *s1, char *s2, int maxlen); /* Сравнява s2 и s1 без да се вземат пред вид малки или главни букви. */ int stricmp(char *s1, char *s2); /* Копира низа s2 в низа s1 и връща s1 */ char *strcpy(char *s1, char *s2);
/* Копира най-много maxlenсимвола от низа s2 в низа s1 и връща s1. */ char *strncpy(char *s1, char *s2, int maxlen); /* Добавя низа s2 към низа s1 и връща s1 */ char *strcat(char *s1, char *s2); /* Добавя най-много maxlenсимвола от низа s2 към низа s1 и връща s1 */ char *strncat(char *s1, char *s2 int maxlen); /* Връща указател към първото срещане на символа c в низа s; ако c не се съдържа в s, връща NULL */ char *strchr(char *s, int c); /* Връща указател към последното срещане на символа c в низа s; ако c не се съдържа в s, връща NULL */ char *strrchr(char *s, int c);
/* Връща указател към първото срещане на подниза s2 в низа s1; ако s2 не се съдържа в s1, връща NULL*/ char *strstr(char *s1, char *s2); /* Връща указател към първото срещане на един от символите на символната константата с в низа s */ char *strpbrk(char *s, c); • Инициализация на символни низове chars1[] = "Тест на програма"; chars2[8] = "приятел"; или chars2[8] = {'п', 'р‘,'и','я','т','е','л','\0'};
Пример: Определя колко пъти се срещат 2 букви в даден текст. #include<stdio.h> #include<string.h> #include<stdlib.h> int let_2(char *s,char *l); void main() { char t[80],car[3],*buf=(char*)malloc(30); printf("Текст:"); gets(t); do { printf("2 букви:"); gets(buf); }while(strlen(buf)>2); strcpy(car,buf); printf("%s се срещат %d пъти в текста " "%s\n",car,let_2(t,car),t); }
Пример: Определя колко пъти се срещат 2 букви в даден текст. int let_2(char *s,char *l) { int c=0; while((s=strstr(s,l))!=NULL) { c++; s+=strlen(l); } return c; }
Какъв е резултатът от изпълнение на програмата? #include <stdio.h> #include <stdlib.h> #include <string.h> void change(char *s); void main() { char *s =(char*) malloc(20); s = "abc, def, gh.jjk,"; change(s); puts(s); } void change(char *s) { while( (s=strpbrk(s,".,"))!=NULL) { *s=' '; } }
Масив от символни низове • Масивът от символни низове се представя като двумерен символен масив. • Първият размер определя броя на низовете, а вторият - максималната дължина на всеки низ. char text[100][81]; • Достъпът до даден символен низ се осъществява чрез левия индекс. Например достъпът до третия символен низ се осъществява чрез text[2]или &text[2][0]. • Двумерният масив може да се представи като едномерен масив от указатели, чиито стойности са адреси на символните низове.
Пример: Въвежда и извежда масив от символни низове. Извежда низовете започващи със зададен символ. #include<stdio.h> #include<stdlib.h> #include<conio.h> #include <string.h> #define L 20 #define C 30 char *li(char *s); //въвежда един символен низ int te(char *s[]); //въвежда масив от символни низове void out(char *s[],int l); //извежда масив от низове void find(char *list[],int l); //търси низове с дадена //начална буква void main() { char *t[L]; //масив от указатели към низове int l; l=te(t); out(t,l); find(t,l); free (t); }
//въвежда един символен низ (ред от текст) char *li(char *s) { char c,*p=s; int i=0; printf(“Въведи символен низ.\n"); while(c=getchar(),c!='\n'&&c!=EOF&&i<C-1) { *s++=c; i++; } if(i==0&&c==EOF) return NULL; else {*s='\0'; return p; } }
//въвежда масив от символни низове (текст) int te(char *s[]) { int i=0, l; char *buf=(char *)malloc( C ); while(1) { printf("li %d\n",i); if(li(buf)==NULL || i==L)break; l=strlen(buf); s[i]=(char *)malloc(l+1); if(s[i]==NULL) { printf(“Няма място\n"); exit(1); } strcpy(s[i],buf); i++; } return i; }
//извежда масив от символни низове (текст) void out(char *s[],int l) { int i; for(i=0;i<l;i++) puts(s[i]); } //търси низовете започващи с въведена буква и ги извежда void find(char *list[],int l) { int i; char car; printf("E. car="); car=getche(); printf("\n"); for(i=0;i<l;i++) { if(*(list[i])== car) puts(list[i]); } }
ПРИМЕРИ • Масив – попълване, извеждане, сортиране, прехвърляне от един масив в друг #include <stdio.h> 1/5 #include <conio.h> #define SIZE 10 int in(int t [SIZE],int *n); void out(int t[],int n); void copy(int *t, int *c, int i, int n); void sort(int *a, int n);
ПРИМЕРИ • Масив – попълване, извеждане, сортиране, прехвърляне от един масив в друг void main() 2/5 {int x[SIZE], y[SIZE]; int n,nbr,i; in(x,&n); out(x,n); sort(x,n); out(x,n); printf("i="); fflush(stdin); scanf("%d",&i); printf("nbr="); scanf("%d",&nbr); copy(x,y,i,nbr); out(y,nbr); }
ПРИМЕРИ • Масив – попълване, извеждане, сортиране, прехвърляне от един масив в друг int in(int t[], int *n) 3/5 { *n=0; int temp; while( 1 ) { printf(“елемент %d:",*n+1); if(scanf("%d",&temp)==1 && *n<SIZE) { t[*n]=temp;//*t++=temp; (*n)++; } else { printf(“не се въвежда последната стойност!\n"); printf(“въведени са %d стойности\n",*n); return 0; } } }
ПРИМЕРИ • Масив – попълване, извеждане, сортиране, прехвърляне от един масив в друг void out(int t[],int n) 4/5 { int i; printf("\n"); for(i=0;i<n;i++) printf("%4d",*t++); printf("\n"); } void copy(int *t, int *c, int i, int n) { t=t+i-1; while(n) {*c++ = *t++; n--; } }
ПРИМЕРИ • Масив – попълване, извеждане, сортиране, прехвърляне от един масив в друг void sort(int *a, int n) 5/5 {int *p,*t, pos, key_ch, temp; pos=1; do { key_ch=0; p=a+n-pos; for(t=a; t<p; t++) if(*t>*(t+1)) { temp=*t; *t=*(t+1); *(t+1)=temp; key_ch=1; } pos++; }while(key_ch); }