А. Богатырев - Руководство полного идиота по программированию (на языке Си), страница 8
Описание файла
PDF-файл из архива "А. Богатырев - Руководство полного идиота по программированию (на языке Си)", который расположен в категории "". Всё это находится в предмете "информатика" из 1 семестр, которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Просмотр PDF-файла онлайн
Текст 8 страницы из PDF
Руководство полного идиота<br> по программированию (на языке Си)z = sqrt(12) + sqrt(23);где значение, вычисленное каждой функцией, НЕ хранитсяв своей собственной переменной (такая переменная на самомделе существует в компьютере, но программисту она ненужна и недоступна).Илиz = sqrt( sqrt(81));(корень из корня из 81 --> даст 3)Далее, что означает конструкция (double) n ?Функция sqrt() требует аргумента типа double.Мы же предлагаем ей целый аргументint n;Целые и действительные числа представлены в памятимашины ПО-РАЗНОМУ,поэтому числа12и12.0 хранятся в памяти ПО-РАЗНОМУ.Машина умеет преобразовывать целые числа в действительныеи наоборот, надо только сказать ей об этом.Оператор (double) xназывается "приведение типа к double".Заметим, что часто преобразование типавыполняется автоматически.Так, например, при сложении int и doubleint автоматически приводится к double, и результатимеет тип double.intvar1;double var2, var3;var1 = 2;var2 = 2.0;var3 = var1 + var2;что означает на самом делеvar3 = (double) var1 + var2;var3 станет равно 4.0Более того, к примеру тип char - это тоже ЦЕЛЫЕ ЧИСЛА из интервала0...255.
Каждая буква имеет код от 0 до 255.*/* 18_POINTERS.txt *УКАЗАТЕЛИ=========void f(int x){x = 7;}file:///Volumes/WININSTALL/assets/materials_informatiks.html50/6803.06.2015Андрей Богатырев. Руководство полного идиота<br> по программированию (на языке Си)main(){int y = 17;f(y);printf("y=%d\n", y);/* печатает: y=17 */}В аргументе x переда?тся КОПИЯ значения y,поэтому x=7; не изменяет значения у.Как все же сделать, чтобы вызываемая функциямогла изменять значение переменной?Отбросим два способа:- объявление y как глобальной(много глобальных переменных - плохой стиль),- y=f(y);(а что если надо изменить МНОГО переменных?return, к несчастью, может вернуть лишь одно значение).Используется новая для нас конструкция: УКАЗАТЕЛЬ.-------------------------------------------------Пример (@)void f(int *ptr){*ptr = 7;}/* #2 *//* #3 */main (){int y=17;f(&y);/* #1 */printf("y=%d\n", y);/* печатает: y=7 */}Ну как, нашли три отличия от исходного текста?---------------------------------------------------------------------Мы вводим две новые конструкции:&y"указатель на переменную y" или"адрес переменной y"*ptrозначает "разыменование указателя ptr"(подробнее - позже)int *ptr;означает объявление переменной ptr,которая может содержать в себеуказатель на переменную,хранящую int-число.Для начала определим, что такое указатель.int var1, var2, z;int *pointer;/* целочисленные переменные *//* указатель на целочисленную переменную */var1= 12;var2= 43;pointer = &var1;Мы будем изображать указатель в виде СТРЕЛКИ;это хороший прием и при практическом программировании.________file:///Volumes/WININSTALL/assets/materials_informatiks.html51/6803.06.2015Андрей Богатырев.
Руководство полного идиота<br> по программированию (на языке Си)/pointer/_/_______/_| значение|| есть |||| &var1 ||||_______|_|||&var1|V________/ var1 /_/_______/_| значение|| есть ||12 ||_________|Переменная, хранящая указатель(адрес другой переменной)- сам указатель на var1 "стрелка, указывающая на переменную var1".Она обозначается &var1Таким образом, УКАЗАТЕЛЬ - это "стрелка, указывающая на некий ящик-переменную".Начало этой стрелки можно (в свою очередь) хранить в какой-нибудь переменной.При этом, если стрелка указывает на переменную типа int,то тип переменной, хранящей начало стрелки, есть int *Если типа char, то тип - char *АДРЕС (указатель на) можно взять только от переменной или элемента массива,но не от выражения.int x;int arr[5];Законно&x& arr[3]стрелка на ящик "x"стрелка на ящик "arr[3]"Незаконно&(2+2)тут нет именованного "ящика",на который указывает стрелка,да и вообще ящика нет.ИСПОЛЬЗОВАНИЕ УКАЗАТЕЛЕЙУказатели несколько различно ведут себя СЛЕВА и СПРАВАот оператора присваивания.Нас интересует новая операция, применяемая только к указателям:*pointer-------------------------------------------------------------------СПРАВА от присваиваний и в формулах===================================*pointerозначает"взять значение переменной (лежащее в ящике),на которую указывает указатель,хранящийся в переменной pointer".В нашем примере - это число 12.file:///Volumes/WININSTALL/assets/materials_informatiks.html52/6803.06.2015Андрей Богатырев.
Руководство полного идиота<br> по программированию (на языке Си)То есть *pointer означает "пройти по стрелке и взять указываемое ею ЗНАЧЕНИЕ".printf("%d\n", *pointer);Печатает 12;z = *pointer;z = *pointer + 66;/* равноценно z = 12;/* равноценно z = 12 + 66;*/*/Заставим теперь указатель указывать на другую переменную(иначе говоря, "присвоим указателю адрес другой переменной")pointer = &var2;________/pointer/_/_______/_||| &var2 ||||_______|_|||&var2|V________/ var2 /_/_______/_||| 43||||_________|После этогоозначаетz = *pointer;z = 43;-------------------------------------------------------------------Таким образом, конструкцияz = *pointer;означаетz = *(&var2);означаетz = var2;То есть * и & взаимно СТИРАЮТСЯ.СЛЕВА от присваивания...*pointer = 123;Означает"положить значение правой части (т.е.
123)в переменную (ящик), на который указывает указатель,хранящийся в переменной pointer".Пройти по стрелке и положить значение в указываемую переменную.file:///Volumes/WININSTALL/assets/materials_informatiks.html53/6803.06.2015Андрей Богатырев. Руководство полного идиота<br> по программированию (на языке Си)В данном случае *pointer обозначаетне ЗНАЧЕНИЕ указываемой переменной,а САМУуказываемую переменную.________/pointer/_/_______/_||| &var2 ||||_______|_|||Положить туда 123|V________/ var2 /_/_______/_||| 123 ||||_________|pointer = &var2;*pointer = 123;означает*(&var2) = 123;означаетvar2 = 123;То есть снова * и & взаимно СТИРАЮТ друг друга.-------------------------------------------------------------------Ещ? пример:*pointer = *pointer + 66;или*pointer += 66;-------------------------------------------------------------------Вернемся к примеру с функцией (@).
Как он работает?В строке /* #1 */Мы вызываем функцию f(), передавая в нееУКАЗАТЕЛЬ на переменную y ("адрес переменной y").В строке /* #2 */Отводится локальная переменная с именем ptr,которая в качестве начального значенияполучает значение первого аргумента функции в точке вызова то есть УКАЗАТЕЛЬ на y.В строке /* #3 */Мы видим*ptr = 7;что следует рассматривать какfile:///Volumes/WININSTALL/assets/materials_informatiks.html54/6803.06.2015Андрей Богатырев.
Руководство полного идиота<br> по программированию (на языке Си)*(&y) = 7;точнее *(&main::y)=7;то есть какy = 7;точнее main::y=7;Что и хотелось.При этом отметим, что само имя "y" этой переменнойвнутри функции f() НЕВИДИМО и НЕИЗВЕСТНО!-------------------------------------------------------------------ПРИМЕР: обмен значений двух переменных.void main(){int x, y;int temporary; /* вспомогательная переменная */x=1; y=2;temporary=x; x=y; y=temporary;printf("x=%d y=%d\n", x, y);/* Печатает x=2 y=1 */}----------------------------------------------------------------------Теперь то же с использованием адресов и указателей:void swap(int *a, int *b){int tmp;tmp = *a; *a = *b; *b = tmp;}void main(){int x, y;x = 1; y = 2;swap(&x, &y);printf("x=%d y=%d\n", x, y);}------------------------------------------------------------------------Ещ? пример:int x;int *ptr1, *ptr2;ptr1 = &x; ptr2 = &x;*ptr1 = 77;printf("%d\n", *ptr2);/* Печатает 77 */То есть на одну переменную МОГУТ указывать несколько указателей.------------------------------------------------------------------------Ещ? пример:int x;int *ptr1;/* Не инициализирована */x = *ptr1;В ptr1 нет указателя ни на что, там есть мусор.Указатель указывает "в никуда" (пальцем в небо).Скорее всего произойд?т сбой в работе программы.Мораль: ВСЕГДА инициализируй переменные, указатели в том числе.file:///Volumes/WININSTALL/assets/materials_informatiks.html55/6803.06.2015Андрей Богатырев.
Руководство полного идиота<br> по программированию (на языке Си)МАССИВЫЯзык Си работает с именами массивов специальным образом.Имя массива "a" дляint a[5];является на самом деле указателем на его нулевой элемент.То есть у нас есть переменные (ящики)с именамиa[0]a[1]...a[4].При этом само имя a при его использовании в программе означает &a[0]a|||Va[0]a[1]a[2]a[3]a[4]_________________________________________||||||||||||||||||----------------------------------------Поэтомуint a[5];/* Переда?тся не КОПИЯ самого массива, а копия УКАЗАТЕЛЯ на его начало */void f(int *a){/* или f(int a[]), что есть равноценная запись */printf("%d\n", a[1]);a[2] = 7;}main (){a[1] = 777;f(a);/* аргумент - массив */printf("%d\n", a[2]);}Вызов f(a); сделает именно ожидаемые вещи.В этом примере мы видим два правила:ПРАВИЛО_1:При передаче в функцию имени массивав аргумент функции копируется не весь массив (жирновато будет),а указатель на его 0-ой элемент.ПРАВИЛО_2:Указатель на начало массиваМОЖНО индексировать как сам массив.Это вторая операция, помимо *pointer,применимая к указателям: pointer[n].Второе правило влечет за собой ряд следствий.file:///Volumes/WININSTALL/assets/materials_informatiks.html56/6803.06.2015Андрей Богатырев.
Руководство полного идиота<br> по программированию (на языке Си)int a[5];int *ptr;/* массив *//* указательная переменная */ptr = a;/* законно, означает ptr = &a[0]; */ptr[0] = 3;ptr[1] = 5;/* означает a[0] = 3;/* означает a[1] = 5;Теперь*/*/Более того. Возьмем теперьptr = &a[2];a[0]a[1]a[2]a[3]a[4]_________________________________________||||||a: ||||||||||||---------------------------------------------|||| ...ptr:||||-----------------------------2-1ptr[0] ptr[1] ptr[2]Мы как бы "приложили" к массиву a[] массив ptr[].В которомptr[0] естьptr[1] естьptr[2] естьptr[3]a[2]a[3]a[4]находится за концом массива a[], МУСОРБолее того, допустимы отрицательные индексы!ptr[-1] естьptr[-2] естьptr[-3]a[1]a[0]находится перед началом массива a[], МУСОРИтак: индексировать можно И массивы И указатели.Кстати, для имени массива a[]*a означает то же самое, что и a[0].Это обратное следствие из схожести массивов и указателей.19.c/* Задача: написать функцию инвертирования порядка символовв массиве char.A B C D ---> D C B AВ решении можно использовать рекурсию.*//* Мы приведем рекурсивное и нерекурсивное решения (два варианта) */#include <stdio.h>/* Сначала - несколько служебных функций.