Реферат: Массивы
int *p, *g;
p=&x;
g=p;
printf("%p", p); /* друк вмісту p */ printf("%p", p++); /* друк вмісту g */ } Результат: FFF4 FFF6
Після виконання цієї програми ми побачимо, що при операції ++1 значення покажчика р збільшилося не на 1, а на 2. І це правильне, оскільки нове значення покажчика повинно вказувати не на наступну адресу пам'яті, а на адресу наступного цілого. А ціле, як ми пам'ятаємо, займає 2 байти. Якби базовий тип покажчика був не int, a double, то були б надруковані адреси, відмінні на 8 (Результат:
FFEE FFF6), саме стільки байт пам'яті займає змінна типу double, тобто при кожній операції ++р значення покажчика буде збільшуватися на кількість байт, що займаються змінної базового типу покажчика .
Операції над покажчиками не обмежуються тільки операціями ++ і
--. До покажчиків можна додавати деяке ціле або відняти ціле. int *p=2000; float *p=2000;
Р=Р+3; р=р+10;
Результат: р=2006 Результат: р=2040
Загальна формула для обчислення значення покажчика після виконання операції р=р+п; буде мати вигляд
<р>=<р>+п*<кільк.байтів пам'яті базового типу покажчика>
Можна також відняти один покажчик з іншого. Так, якщо р і pi -покажчики на елементи одного і того ж масиву, то операція р-рі дає такий же результат, як і віднімання індексів відповідних елементів масиву.
Інші арифметичні операції над покажчиками заборонені, наприклад не можна скласти два покажчики, помножити покажчик на число і т.д.
#include <std io. h > void rnai n() { int *p, *g, x; p=&x; g=p; printf("\n\n\np=%p", p); P= P + 8; printf(" p+5=%p", p); printf(" g=%p", g); printf(" p-g=%p", p-g); } Результат: p=07DO p+5=07EO g=07DO p-g=0008 |
#incl ude <std io. h> void main() { int *p, *g, x; p=&x; g=p; p r і n t f (" \ n \ n \ n p = % p ", p); P= P + 8; printf(" p+5=%p", p); printf(" g=%p", g); printf(" p+g=%p", p+g); } Результат: Error UKAZAT2.CPP 14: Invalid pointer addition |
Покажчики можна порівнювати. Застосовні всі 6 операцій:
<, >, <=, >=, =, == і !=.
Порівняння р < g означає, що адреса, що знаходиться в р, менше адреси, що знаходиться в g.
Якщо рід вказують на елементи одного масиву, то індекс елемента, на який вказує р, менше індексу масиву, на який вказує g.
ЗВ'ЯЗОК ПОКАЖЧИКІВ І МАСИВІВ
Будь-який доступ до елемента масиву за допомогою операції індексування може бути виконаний за допомогою покажчика (що в загальному випадку працює швидше).
Декларація
int a[10]
визначає масив а розміру 10:
Запис а[і] посилає нас до і-му елемента масиву. int *р;
р=&а[0]; /* р вказує на нульовий елемент а або містить адресу елемента а[0] */
х = *р; => х = а[0], У= *(Р+1); => У = а[1];
Значення змінної типу масив (ім'я масиву) є адреса нульового елемента масиву.
р = &а[0]; => р = а;
*(а+і) ^ а[і] &а[і] => а+і
Результат буде один і той же. Перевага використання другого варіанту полягає в тому, що арифметичні операції над покажчиками виконуються швидше, якщо ми працюємо з підряд йдучими елементами масиву. Якщо ж вибір елементів масиву випадковий, то швидше і більш наочна робота з індексами.
Між ім'ям масиву і покажчиком,-виступаючим в ролі імені масиву, існує одна відмінність. Покажчик змінна, тому можна написати р = а або р++. Але ім'я масиву не є змінною, і записи типу а = р або а++ не допускаються.
Дуже часто доводиться працювати над обробкою текстів, т. е. з масивами рядків. Як ми пам'ятаємо, в мові С рядок - це масив символів, що закінчується нульовим байтом. Розглянемо дві програми, що реалізовують практично, одні і ті ж дії.
#incl ude <std io. h>
#include <ctype.h>
void main()
{ char *p, str[]="String From Letters in Different Registers";
/* Рядок, що Складається з Букв в Різних Регістрах; */ int і=0; printf( "Рядок Буде Надрукований Заголовними Буквами");
while (str[i]) printf("%c", toupper(str[i++]));
p=str; printf(" Рядок Буде Надрукований Малими Буквами");
while (*p) printf("%c", tolower(*p++)); }
Якщо в цих прикладах замінити рядок на англійській мові на рядок, набраний російськими буквами, то ніякого перетворення букв в рядкові або, навпаки, в прописні не станеться. Це пов'язано з тим, що стандартні функції toupper() і tolower () аналізують значення
10 аргументу і повертають те ж саме значення, якщо він не є відповідно малою або великою буквою латинського алфавіту. Якщо ж аргумент є малою буквою латинського алфавіту, то значенням функції toupper() буде відповідна велика буква (точніше, код цієї букви). Функція tolower () змінює код лише великих букв латинського алфавіту. Прототипи цих функцій знаходяться в заголовному файлі ctype.h.
МАСИВИ ПОКАЖЧИКІВ
Покажчики, як і змінні будь-якого іншого типу, можуть об'єднуватися в масиви. Оголошення масиву покажчиків на 10 цілих чисел має вигляд int *x[10] ;
Кожному з елементів масиву можна привласнити адресу; наприклад, третьому елементу привласнимо адресу цілої змінної у:
х[2]=&у;
щоб знайти значення змінною у, можна написати *х(2].
Наведемо приклад використання масиву покажчиків. Частіше за все це буває зручно при обробці масиву рядків.
/* you must run. exe-file to watch the rezult of this program. Перегляд файлів в поточному каталозі з одним з шести розширень */
#include <std io. h >
#include <string.h> ^include <stdlib. h>
#include <conio. h>
main()
{char ch, s[80], *ext[]={"exe", "corn", "cpp", "c", "pas", "*"};
clrscr();
for(;;) {do { printf( "Файли з розширенням:^");
printf("1. exe\n"); "printf( 2. com\n"); "printf( 3. cpp\n"); "pnntf( 4. з \ n ");
printf("5. pas\n"); printf("6. *\n"); //any extension printf("7. quit\n");
printf("BauJ вибір(1-7):)( \n");
ch=getche();
printf("\n");
} while (ch<'1' ;! ch>'7');
if (ch=='7') break;
strcpy(s, "dir *."); strcat(s, ext[ch-'0'-1 ]); strcat(s, "/p"); system(s);} }
Тут функція system() - бібліотечна функція, яка примушує операційну систему DOS виконати команду, що є аргументом цієї функції.
Взагалі рядкова константа в мові С асоціюється з адресою початку рядка в пам'яті, тип рядка виходить char* (покажчик на тип char). Тому можливо і активно використовується наступне привласнення:
char *pc;
"рс = Hello, World!";
У мові С можлива також ситуація, коли покажчик вказує на покажчик. У цьому випадку опис буде мати наступний вигляд:
int -*'*point;
point має тип покажчик на покажчик на int. Відповідно, щоб набути цілочисельного значення змінною, на яку указьіваеі point, треба у вираженні використати **point.;
Приклад використання:
11
^include <stdio. h>
void m а і n()
{ int i, pi, ppi;
і =7; pi=&i;
p p i = & p i;
printf( "i = %d pi = %p ppi = %p \n", i, pi, ppi);
*pi++;
printf( "i = %d pi = %p ppi = %p \n", i, pi, ppi);
**ppi = 12;
printf( "i = %d pi = %p ppi = %p \n", i, pi, ppi);
}
ІНІЦІАЛІЗАЦІЯ ПОКАЖЧИКІВ
Після того як покажчик був оголошений, але до того, як йому було привласнене якесь значення, покажчик містить невідоме значення. Спроба використати покажчик до привласнення йому якогось значення є неприємною помилкою, оскільки вона може порушити роботу не
тільки вашої програми, але і операційної системи. Навіть якщо цього не сталося, результат роботи програми буде неправильним і знайти цю помилку буде досить складно.
Вважають, що покажчик, який вказує в "нікуди", повинен мати значення null, однак і це не робить його "безпечним". Після того, як він попаде в праву або ліву частину оператора привласнення, він знову може стати "небезпечним".
З іншого боку нульовий покажчик можна використати, наприклад, для позначення кінця масиву покажчиків.
Якщо була спроба привласнити яке-небудь значення тому, на що вказує покажчик з нульовим значенням, система видає попередження, що з'являється під час роботи програми (або після закінчення роботи програми) "Null pointer assignment". Поява цього повідомлення є мотивом для пошуку використання неініціалізувати покажчика в програмі.