/*funkcja przeszukująca tablicę*/
#include<stdio.h>
#include<stdlib.h>
#define MAXKOL 8
/*makro wypelnia tablice o podanym rozmiarze liczbami losowymi z przedzialu okreslonymi parametrami wywolania oraz drukuje zawartosc tej tablicy*/
#define LOSUJ_TAB(t,n,a1,a2) \
{ \
int i; \
srand(a1+a2); \
for(i=0; i<=n-1; i++) \
{ \
do \
t[i]=rand(); \
while(t[i]<a1 || t[i]>a2); \
} \
printf(”\n Tablica ”); \
printf(#t); \
printf(”\n”); \
for (i=0; i<=n-1; i++) \
{ \
if (i % MAXKOL ==0 && i>1) \
printf(”\n”); \
printf(”%7d”,t[i]); \
} \
}
#define RA 28
#define RB 23
main()
{
int a[RA], b[RB], max;
int podaj_max( int t[],int n);
LOSUJ_TAB(a,RA,100,200);
max=podaj_max(a,RA);
printf(”\nWartosc maksymalna = %d\n”,max);
LOSUJ_TAB(b,RB,1000,2000);
max=podaj_max(b,RB);
printf(”\nWartosc maksymalna = %d\n”,max);
}
/* funkcja max*/
int podaj_max(int t[],int n)
{
int i, max;
max = t[0];
for (i=0; i<=n-1; i++)
if (t[i] > max)
max = t[i];
return (max);
}
Wynik:
Tablica a
134 140 176 125 107 187 156 137
149 165 111 128 195 100 147 159
174 131 181 188 197 104 194 182
123 110 158 180
Wartosc maksymalna = 197
Tablica b
1548 1127 1487 1449 1032 1753 1350 1725
1359 1984 1133 1499 1234 1716 1145 1192
1421 1751 1852 1736 1743 1840 1587
Wartosc maksymalna = 1984
/* modyfikacja tablicy przez funkcje*/
#include <stdio.h>
#include <math.h>
/* makro wypelnia tablice o podanym rozmiarze liczbami*/
#define WYP_TAB(t,n,fun,typ,xp,dx) \
{ \
int i; \
typ x; \
x = xp; \
for(i=0; i<=n-1; i++) \
{ \
t[i] =fun(x); \
x += dx; \
} \
}
#define MAXKOL 8
/*makro drukuje tablice t o rozmiarze n*/
#define DRUKUJ_TAB(t,n,fm) \
{ \
int i; \
printf(”\n Tablica ”); \
printf(#t); \
printf(”\n”); \
for (i=0; i<=n-1; i++) \
{ \
if (i % MAXKOL ==0 && i>1) \
printf(”\n”); \
printf(#fm,t[i]); \
} \
}
#define RA 28
#define RB 23
/* funkcja modyfikujaca tablice - elementy dodatnie zostaja zamienione na 1 pozostale na 0*/
void mod_tab (int t[], int n)
{
int i;
for (i=0; i<=n-1; i++)
if(t[i] > 0)
t[i] = 1;
else
t[i] = 0;
}
/* modul glowny*/
main()
{
int a[RA], b[RB];
WYP_TAB(a,RA,20*sin,double,0.,0.5);
DRUKUJ_TAB(a,RA,%7d);
printf(”\n------modyfikacja------\n”);
mod_tab(a,RA);
DRUKUJ_TAB(a,RA,%7d);
printf(”\n********************\n”);
WYP_TAB(b,RB,30*cos,double,1.,0.5);
DRUKUJ_TAB(b,RB,%7d);
printf(”\n------modyfikacja------\n”);
mod_tab(b,RB);
DRUKUJ_TAB(b,RB,%7d);
}
Wynik:
Tablica a
0 9 16 19 18 11 2 -7
-15 -19 -19 -14 -5 4 13 18
19 15 8 -1 -10 -17 -19 -17
-10 -1 8 16
------modyfikacja------
Tablica a
0 1 1 1 1 1 1 0
0 0 0 0 0 1 1 1
1 1 1 0 0 0 0 0
0 0 1 1
Tablica b
16 2 -12 -24 -29 -28 -19 -6
8 21 28 29 22 10 -4 -18
-27 -29 -25 -14 0 14 25
------modyfikacja------
Tablica b
1 1 0 0 0 0 0 0
1 1 1 1 1 1 0 0
0 0 0 0 0 1 1
/* Wskazniki*/
#include<stdio.h>
main()
{
int u =13, v, *pu, *pv;
pu=&u;
pv=&v;
v=*pu;
printf(”\n&u = %p &v = %p”, &u, &v);
printf(”\n&pu = %p &pv = %p”, &pu, &pv);
printf(”\npu = %p pv = %p”, pu, pv);
printf(”\n u = %-12d v = %d”, u, v);
}
Wynik:
&u = 1D74:0FD4 &v = 1D74:0FD6
&pu = 1D74:0FD8 &pv = 1D74:0FDC
pu = 1D74:0FD4 pv = 1D74:0FD6
u = 13 v = 13
#include<stdio.h>
main()
{
int u[] = {1,2,3,4,5,6,7,8,9,10,11,12,13};
printf(”\n u = %p &u[0] = %p”, u, &u[0]);
printf(”\n&u[1] = %p &u[2] = %p”, &u[1], &u[2]);
printf(”\n u[0] = %-12d u[8] = %d”, u[0], u[8]);
printf(”\n *u = %-12d *(u+8) = %d”, *u, *(u+8));
}
Wynik:
u = 1D78:0FC6 &u[0] = 1D78:0FC6
&u[1] = 1D78:0FC8 &u[2] = 1D78:0FCA
u[0] = 1 u[8] = 9
*u = 1 *(u+8) = 9
Deklarowanie wskaźników
typ danych * nazwa zmiennej wskaźnikowej
float u, v;
float *pv = &v;
z użyciem instrukcji przypisania : pv=&v;
Szczególnym przypadkiem wartości wskaźnika jest zero, czyli pusta wartość (wskazanie na nic). Zwyczajowo definiuje się dla takiej sytuacji stałą o nazwie NULL:
#define NULL 0
float u, v;
float *pv = NULL;
Przykładowe deklaracje wskaźników
Deklaracja |
Komentarz |
int *p |
p jest wskaźnikiem do obiektu typu int |
char *tab[8] tablica dla 8 wskaźników do obiektów typu char
char **tab deklaracja wskaźnika do wskaźnika
int (*p)[10] p jest wskaźnikiem do tablicy 10-elementowej
int *p(void) p jest bezparametrową funkcją zwracającą
wskaźnik do obiektu typu int
int (*p)(char *s) p jest wskaźnikiem do funkcji zwracającej
wartość typu int, której argumentem jest
wskaźnik do łańcucha znaków
int (*p[10])(int i); p jest 10-elementową tablicą wskaźników
do funkcji zwracających wartości całkowite,
których argumentami są również wartości
całkowite
int *(*p[10])(int *i); p jest 10-elementową tablicą wskaźników
do funkcji zwracających wskaźniki do obiektów
typu int, których argumentami są wskaźniki
również do obiektów typu int.
Wskaźnik i funkcje
# include<stdio.h>
main()
{
int x =13, y = 33;
void zeruj1(int u, int v);
void zeruj2(int *pu, int *pv);
printf(”\n Przed wywolaniem zeruj1: x =%d y = %d”, x, y);
zeruj1(x, y);
printf(”\n Po wywolaniu zeruj1: x =%d y = %d”, x, y);
zeruj2(&x, &y);
printf(”\n Po wywolaniu zeruj2: x =%d y = %d”, x, y);
}
void zeruj1(int u, int v)
{
u = 0;
v = 0;
}
void zeruj2(int *pu, int *pv)
{
*pu = 0;
*pv = 0;
}
Wynik:
Przed wywolaniem zeruj1: x = 13 y = 33
Po wywolaniu zeruj1: x = 13 y = 33
Po wywolaniu zeruj2: x = 0 y = 0
#include<stdio.h>
#include<ctype.h>
main()
{
char linia[80];
int litery =0; /*liczba liter w linii */
int cyfry = 0; /*liczba cyfr w linii */
int odstepy = 0; /*liczba znakow odstepu */
int inne = 0; /*inne znaki */
int suma; /*calkowita liczba znakow */
int analiza(char linia[], int *l, int *c, int *o, int *i);
printf(”\nPodaj linie tekstu:\n”);
scanf(”%[^\n]”,linia);
suma = analiza(linia, &litery, &cyfry, &odstepy, &inne);
printf(”\n----------Raport----------”);
printf(”\n Liczba znaków = %2d, w tym:”, suma);
printf(”\n Liczba liter = %2d”, litery);
printf(”\n Liczba cyfr = %2d”, cyfry);
printf(”\n Liczba odstepow = %2d”, odstepy);
printf(”\n Liczba innych = %2d”, inne);
}
int analiza(char linia[], int *l, int *c, int *o, int *i)
{
char znak;
int j = 0;
while((znak=toupper(linia[j]))!='\0')
{
if(znak >= 'A' && znak <='Z')
++*l;
else if(znak >='0' && znak <= '9')
++ *c;
else if(znak == ' ' || znak == '\t')
++ *o;
else
++ *i;
++j;
}
return(j);
}
Wynik:
Podaj linie tekstu:
”Moj program do zastosowania wskaznikow.”
----------Raport----------
Liczba znakow = 41, w tym:
Liczba liter = 34
Liczba cyfr = 0
Liczba odstepow = 4
Liczba innych = 3
Wybór funkcji przez wskaźnik:
#include<stdio.h>
main() /*wybor funkcji przez indeks*/
{
void proces1(void);
void proces2(void);
void proces3(void);
void (*wybor[3])(void) = {proces1, proces2, proces3};
int i=0, j=1, k=2;
wybor[i]();
wybor[k]();
wybor[j]();
wybor[0]();
}
void proces1(void)
{
printf(“\n111111111111111111111111”);
}
void proces2(void)
{
printf(“\n2 2 2 2 2 2 2 2 2 2 2 2 ”);
}
void proces3(void)
{
printf(“\n3 3 3 3 3 3 3 3”);
}
Wynik:
111111111111111111111111
3 3 3 3 3 3 3 3
2 2 2 2 2 2 2 2 2 2 2 2
111111111111111111111111
W powyższym programie zadeklarowano tablicę wskaźników do bliźniaczych funkcji oraz dokonano wywołań tych funkcji w oparciu o numer indeksu.
Argumentem funkcji - wskaźnik do innej funkcji
#include<stdio.h>
#include<math.h>
main()
{
double spc(double x);
double funkcja2(double x, double (*fun)(double x));
double y, x=1.12345;
y=funkcja2(x,sin);
printf(”\nx = %f, sin(x) = %f, 2 * sin(x) = %f”,x, sin(x), y);
y=funkcja2(x,cos);
printf(”\nx = %f, cos(x) = %f, 2 * cos(x) = %f”,x, cos(x), y);
y=funkcja2(x,tan);
printf(”\nx = %f, tan(x) = %f, 2 * tan(x) = %f”,x, tan(x), y);
y=funkcja2(x,spc);
printf(”\nx = %f, spc(x) = %f, 2 * spc(x) = %f”,x, spc(x), y);
}
double spc(double x)
{
return (sin(x) + cos(x));
}
double funkcja2(double x, double (*fun)(double(x))
{
return (2 * fun(x));
}
Wynik:
x = 1.123450, sin(x) = 0.901598, 2 * sin(x) = 1.803196
x = 1.123450, cos(x) = 0.432575, 2 * cos(x) = 0.865149
x = 1.123450, tan(x) = 2.084261, 2 * tan(x) = 4.168522
x = 1.123450, spc(x) = 1.334173, 2 * spc(x) = 2.668345
Funkcja funkcja2 podwaja wartość obliczoną przez inną funkcję dostępną przez wskaźnik.
/* rownowaznosc zapisow dostepu do elementow tablicy 1- wymiarowej przez indeks i przez wskaznik*/
#include<stdio.h>
main()
{
int i, x[10] = {100, 101, 102, 103, 104, 105, 106, 107, 108, 109};
for (i=0; i<=9; i++)
printf(”\ni=%d x[i]=%d *(x+i)=%d &x[i]=%p x+i=%p”,
i, x[i], *(x+i), &x[i], x+i );
}
Wynik:
i=0 x[i]=100 *(x+i)=100 &x[i]=1D73:0FCC x+i= 1D73:0FCC
i=1 x[i]=101 *(x+i)=101 &x[i]=1D73:0FCE x+i= 1D73:0FCE
i=2 x[i]=102 *(x+i)=102 &x[i]=1D73:0FD0 x+i= 1D73:0FD0
i=3 x[i]=103 *(x+i)=103 &x[i]=1D73:0FD2 x+i= 1D73:0FD2
i=4 x[i]=104 *(x+i)=104 &x[i]=1D73:0FD4 x+i= 1D73:0FD4
i=5 x[i]=105 *(x+i)=105 &x[i]=1D73:0FD6 x+i= 1D73:0FD6
i=6 x[i]=106 *(x+i)=106 &x[i]=1D73:0FD8 x+i= 1D73:0FD8
i=7 x[i]=107 *(x+i)=107 &x[i]=1D73:0FDA x+i= 1D73:0FDA
i=8 x[i]=108 *(x+i)=108 &x[i]=1D73:0FDC x+i= 1D73:0FDC
i=9 x[i]=109 *(x+i)=109 &x[i]=1D73:0FDE x+i= 1D73:0FDE
/*dla tablicy 2-wymiarowej */
int i, x[10][20];
i = x[5][8];
i = *(x[5] + 8);
i = *(*(x +5) +8);
Równoważne deklaracje tablic:
Tablica 1-wymiarowa 10-elementowa typu int:
int *x; int x[10]; #define MAX 10 int x[MAX];
x = (int*)malloc(10 * sizeof(int));
lub
x=(int*)calloc(10, sizeof(int));
Funkcje z modułu alloc.h
void *malloc(size_t n) - zwraca wskaźnik do n bajtów niezainicjowanej
pamięci lub NULL
void *calloc(size_t n, size_t size) - zwraca wskaźnik do obszar mogące-
go pomieścić tablicę n elementów
podanego rozmiaru size
void *realloc(void *ptr, size_t size) - zmienia rozmiar bloku wskazane-
go przez ptr na size
void free(void *ptr); - zwalnia obszar pamięci wskazywany przez ptr,
ptr powinien być wartością zwróconą wcześniej
przez funkcje: calloc, malloc lub realloc.
Tablica 2-wymiarowa 10 razy 20 elementów typu int:
int (*x)[20]; int x[10][20];
Tablica n-wymiarowa, ogólny zapis deklaracji:
typ danych (*wskaźnik_tab) [wyrażenie2]...[wyrażenie N];
zamiast
typ danych nazwa_tab[wyrażenie1]...[wyrażenieN];
#include<stdio.h>
#include<alloc.h>
#define WIER 10
#define KOL 20
#define WYP_TAB(t,maxw,maxk,op) \
{ \
int w, k; \
for (w=0; w<=maxw-1; w++) \
for (k=0; k<=maxk-1; k++) \
*(*(*t+w)+k) = w op k; \
}
main()
{
int (*a)[WIER][KOL], (*b)[2*WIER][3*KOL];
/*przydzielenie pamieci dla tablic */
a = (int*)malloc(WIER * KOL * sizeof(int));
b = (int*)malloc(2 * WIER * 3 * KOL * sizeof(int));
/*wypelnienie tablic liczbami */
WYP_TAB(a, WIER, KOL, +);
WYP_TAB(b, 2*WIER, 3*KOL, *);
/*wyrywkowa kontrola */
printf(”\n a[0][0] = %4d /ma być 0/”,*(*(*a ) ));
printf(”\n a[5][5] = %4d /ma być 10/”,*(*(*a+5)+5));
printf(”\n a[9][8] = %4d /ma być 17/”,*(*(*a+9)+8));
printf(”\n b[0][0] = %4d /ma być 0/”,***b );
printf(”\n b[5][5] = %4d /ma być 25/”,*(*(*b+5)+5));
printf(”\n b[9][8] = %4d /ma być 72/”,*(*(*b+9)+8));
/*zwolnienie pamieci */
free(a);
free(b);
}
Wynik:
a[0][0] = 0 /ma być 0/
a[5][5] = 10 /ma być 10/
a[9][8] = 17 /ma być 17/
b[0][0] = 0 /ma być 0/
b[5][5] = 25 /ma być 25/
b[9][8] = 72 /ma być 72/
/*Wygenerowanie tablicy trójkątnej*/
#include<stdio.h>
#include<alloc.h>
#define WIER 8
main()
{
int *x[WIER]; /*1-wymiarowa tablica wskaźników*/
int w,k; /*indeksy: wiersz, kolumna*/
/*przydzielenie pamieci dla tablicy trojkatnej */
/*liczba elementow w wierszu = numer wiersza +1*/
for(w=0;w<=WIER -1;w++)
x[w]=(int*)malloc((w + 1)*sizeof(int));
/*wypelnienie tablicy liczbami*/
for(w=0;w<=WIER -1 ;w++)
for(k=0;k<=w;k++)
*(x[w] + k) = 100 + w + k;
/*wydruk tablicy*/
for(w=0;w<=WIER -1 ;w++)
{
printf(”\n”);
for(k=0;k<=w;k++)
printf(”%7d”,*(x[w] + k));
}
}
Wynik:
100
101 102
102 103 104
103 104 105 106
104 105 106 107 108
105 106 107 108 109 110
106 107 108 109 110 111 112
107 108 109 110 111 112 113 114
Program operujący na tablicy wskaźników do łańcuchów znaków (tj. linii tekstu o różnych długościach). Program umożliwia wprowadzenie j linii tekstu, a następnie wyprowadza je w porządku alfabetycznym.
#include<stdio.h>
#include<alloc.h>
#include<string.h>
#define WIER 4000
#define MAXL 80
main()
{
void sortuj_tab(char *a[], int max);
char *x[WIER]; /*tablica wskaźników do linii tekstu */
char s[MAXL]; /*linia wprowadzana z klawiatury */
char koniec = `#'; /*znak konca wprowadzania */
int i = 0; /*indeks: numer linii tekstu */
int j = 0; /*liczba wprowadzonych linii */
/*Wprowadzenie linii tekstu*/
printf(”\nPodaj linie tekstu do posortowania alfabetycznego”);
printf(”\nMaksymalna liczba linii wynosi : %4d”, WIER);
printf(”\nMaksymalna liczba znakow w linii : %4d”, MAXL);
printf(”\nZnak konca wprowadzenia : %c”, koniec);
printf(”\n---------------------------------------------\n”);
while(i<=WIER-1)
{
printf(”> ”); /*znak zachety*/
gets(s);
if (*s == koniec)
{
x[i] = NULL;
break;
}
else
{
x[i] = (char*)malloc(strlen(s) +1); /*rezerwacja pamieci*/
strcpy(x[i], s); /*skopiowanie linii s w miejsce x[i]*/
j++;
}
++i;
}
printf(”\n---------------------------------------------\n”);
/*Sortowanie w porzadku alfabetycznym */
sortuj_tab(x, j);
/*Wyprowadzenie posortowanych linii */
printf(”\n---------------------------------------------\n”);
i=0;
while(i<=WIER - 1)
{
if(x[i] == NULL)
break;
else
printf(”\n %s”, x[i]);
++i;
}
printf(”\n---------------------------------------------\n”);
}
void sortuj_tab(char *a[], int max)
{
int i, j;
char *s;
for(i=0; i<=max-2; i++)
for(j=i+1; j<=max-1; j++)
if(strcmp(a[i], a[j]) > 0)
{
s = a[i];
a[i] = a[j];
a[j] = s;
}
}
Operacje na wskaźnikach:
Jeżeli dana jest deklaracja np. int a[10]; to do i-tego elementu tablicy możemy się odwołać dwojako: y=a[i]; lub y=*(a+i);
Jeśli jest dana deklaracja np. int b[9][9]; to do elementu (i,j) w tablicy możemy odwołać się trojako: y=b[i][j]; lub y=*(b[i]+j); lub y=*(*(b+i)+j);
Jeżeli p jest wskaźnikiem zadeklarowanym jako int *p; to przypisanie p=&a[0]; ustawia p na początek tablicy a. Jeżeli zapiszemy x=*p; będzie to równoważne zapisowi x=a[0];
Jeżeli p wskazuje na pewien element tablicy a, to z definicji p+1 wskazuje na element następny, a p-1 na element poprzedni;
Zapis p+i oznacza adres, natomiast *(p+i) zawartość pod wskazanym adresem;
Jeżeli mamy zapis p+1(dodaj 1 do wskaźnika), to z definicji znaczy to, że (bez względu na typ obiektu) wskazanie dotyczyć będzie obiektu następnego. /Przyrost skalowany jest przez rozmiar pamięci zajmowanej przez wskazany obiekt/. p++ wskazuje na następny obiekt, a p-- na poprzedni obiekt;
Ponieważ nazwa tablicy reprezentuje położenie jej zerowego elementu, przypisanie p=&a[0]; może być zastąpione prostszym zapisem: p=a;
W definicji parametrów formalnych funkcji równoważne są zapisy: char s[]; oraz char *s;
Równoważna jest również deklaracja tablicy: char *t[10]; oraz char **t;
Zapis **p jest równoważny *(*p) i oznacza wskazanie pośrednie (wskaźnik do wskaźnika). Zapis ***p jest równoważny *(*(*p)) i jest wskaźnikiem do wskaźnika do wskaźnika ...
Równoważne są również następujące zapisy: *p+=1; oraz (*p)++; W postaci drugiej zapisu nawiasy są niezbędne, gdyż chodzi o zwiększenie o jeden zawartości wskazywanego obiektu, a nie wskaźnika;
Na wskaźnikach mogą być wykonywane następujące operacje:
nadanie wskaźnikowi wartości początkowej,
dodanie lub odejmowanie wskaźnika i wartości całkowitej,
porównywanie dwóch wskaźników, /w instrukcji if np.: (px<py), (px>=py), (px==py), (px!=py), (px==NULL).