Похожие презентации:
C Занятие 2 (1)
1. Основы С++
Часть I.Язык программирования С.
2. Адреса и указатели
При объявлении переменных компилятор по имени переменной формирует еёадрес. Если компьютер использует 16-и разрядный процессор, тогда в таком
компьютере возможно использование не более 216 = 65536 = 64К различных
адресов (К = 210 = 1024). Под адрес (адресную переменную) компилятор выделял
одно машинное слово – 16 двоичных разрядов. Для такого объема адресного
пространства была создана значительная часть программного обеспечения. Со
временем такого объема адресного пространства становилось недостаточно и
под адресную переменную стали выделять 2 машинных слова – 32 двоичных
32
разряда. При этом возможно использование не более 2 = 4294967296 = 4194304 К
различных адресов.
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Для того, чтобы не переделывать созданное
программное обеспечение, весь объем
памяти делится на сегменты по 64К, внутри
которых используются старые адреса
(относительные адреса). При этом
реальный адрес теперь состоит из двух частей – номера сегмента (первое
машинное слово) и относительного адреса в сегменте (второе машинное слово).
3. Адреса и указатели
Поэтому адрес записывается в виде двух частей– номера сегмента иотносительного адреса в сегменте в шестнадцатеричном коде, например:
A6E2:B4FD. В свою очередь, при выполнении программы используются три
сегмента: сегмент данных, сегмент кода и сегмента стека.
Сегмент 0
Сегмент 1
Data Segment DS
Сегмент 2
Code Segment CS
Сегмент 3
Stack Segment SS
int i, j=0, k;
float a,b;
…….
…….. for(i=0;i<10;i++)
{ …….};
a=func(b); …….
float func(float x)
{………..
}
Для описания адресных переменных используются указатели. Указатель
является переменной, которая содержит адрес другой переменной или функции.
Описание указателя определяет тип данных, на которые указатель ссылается.
Описание указателя имеет следующий вид:
тип указываемых данных * имя указателя ;
4. Адреса и указатели
Примеры возможных описаний указателей:int *ip; // указатель ip на целое число
double *dp; // указатель dp на число двойной точности
char *sn; // указатель sn на символ
С указателями связаны три специальные операции &, * и ->, которые имеют
наивысший приоритет. Эти операции являются унарными, т.е. имеют один
операнд, перед которыми они ставятся. Операция & означает «взять адрес».
Результатом операции * является значение переменной, на которую ссылается
указатель. Например:
int *ip, i = 1, j;
ip = &i; // указатель ip получает значение адреса целой переменной i
j = *ip; целая переменная j получает значение i .
Нельзя создать переменную типа void, но можно создать указатель на тип void.
Так как указателям типа void не соответствует никакой тип данных, к ним
нельзя применять арифметические операции.
5. Адреса и указатели
Указателю на void можно присвоить указатель любого другого типа. Однакопри обратном присваивании (раз’именовании указателя) необходимо использовать
явное преобразование указателя на void :
void *pv;
int a, *b, *c;
b = &a;
pv = b;
c = (int)pv;
В объявлении переменной, являющейся указателем, очень важен базовый тип.
По этому типу компилятор определяет сколько байт памяти занимает
переменная, на которую ссылается (указывает) данный указатель. Над указателями
можно производить сложение и вычитание целых чисел (операции ++ и - - являются
частными случаями операций сложения и вычитания). Так например, если
переменная Р является указателем на какой-либо тип, то при каждой операции Р++
значение указателя будет увеличиваться на количество байт, занимаемых переменной
данного типа. Пусть к – какое-либо целое число. Тогда для вычисления значения
указателя после выполнения операции Р = Р + к будет справедливо выражение:
Р = Р + к *(количество байт памяти типа указателя).
6. Адреса и указатели
Использование указателя в выражениях не соответствующих его типу приведетлибо к ошибке при компиляции, либо к получению неверного результата в
вычислениях. В языке существует предопределенная адресная константа NULL
(никуда), значение которой может быть присвоено указателю любого типа.
Указатели можно сравнивать. Применимы следующие операции:
<, >, <=, >=, =, ==, != .
Сравнение двух указателей Р < В означает, что адрес, находящийся в Р, меньше
адреса, находящегося в В.
В том случае, когда необходимо иметь адрес для хранения другого адреса,
можно объявить указатель на указатель. Например:
float dat;
float* pd = &dat;
float** Pp = &pd;
Однако язык С не позволяет использовать операцию двойной адресации.
Следующая запись будет восприниматься компилятором как ошибка:
float** Pp = &&dat;
7. Адреса и указатели
Другие операции над указателями запрещены, например нельзя сложить двауказателя, умножить указатель на число и т.д. Указатель на символьную
переменную часто используется при работе с символьными строками.
Символьная строка представляет собой массив байтов, заполненный кодами
символов, и завершающийся нулевым байтом (символом ‘\0’):
char Str[ ] = "Press any key";
Компилятор, выделяя память под этот массив, сам определит его длину, которая
в данном случае будет равна 13 байтам (12 байт под собственно данные и 1 байт
под завершающий нулевой байт). В данном примере фиксированный набор
заранее известных символов представляет собой строку-константу. Такие
строковые константы (фиксированные массивы символов) могут быть
объявлены как указатели на символ, т.к. имя массива является указателем на
массив:
char* Str = "Press any key";
В обоих случаях в конец строки автоматически добавляется нулевой байт.
8. Адреса и указатели
Если содержимое символьной строки в момент ее объявления неизвестно, можнообъявить пустую строку как массив символов:
char Message[20];
В дальнейшем в этот массив можно поместить содержательную строку.
Набор стандартных строк объявляется следующим образом:
char* Error[ ] = {"Can’t open file", "System error"};
При этом обращение к каждой строке производится как обращение к каждому элементу
массива:
for( i = 0; i < 2; i++)
printf("\n %s", Error[i]);
Для того чтобы корректно объявлять указатели и правильно понимать объявления
указателей необходимо соблюдать следующее правило: читать информацию при
объявлении указателя следует не слева – направо, а наоборот: справа – налево.
Например, следующую запись const double * dp надо интерпретировать так:
dp – это указатель на тип double постоянный (адрес – константа) .
Следующее объявление указателя double const * dp - это указатель на константу типа
double. Объявление float *Ramp[10] следует понимать как массив Ramp[10],
состоящий из десяти указателей на тип float.
9. Массивы и указатели
В языке С существует важная связь между массивами и указателями. Имямассива соответствует адресу его нулевого элемента. Поэтому можно
присваивать указателю адрес нулевого элемента, используя имя массива:
int Mas[10];
int* ip = Mas;
// То же, что и ip = &Mas[0];
Компилятор преобразует индексацию в арифметику указателей. Например:
Mas[5] = 20; представляется как *(Mas + 5) = 20; Если массив этот используется
как параметр для передачи в функцию, то для описания типа параметра удобно
использовать указатель int*.
Для работы с двумерными массивами может быть использован указатель на указатель:
int** b;
int* a[3], i, j, k;
int aa[ ][3] = {{1,2,3}, {4,5,6}, {7,8,9}}; // aa[3][3]
for(i = 0; i < 3; i++)
a[i]=&aa[i][0];
// a[i]=*(aa+i); допустимо
b = a;
// b=aa; недопустимо
k = *(*(b+2)+1);
j = aa[2][1];
10. Массивы и указатели
Две последние строки этого кода выполняют одну и ту же операцию – обращениек элементу третьей строки второго столбца (число 8). Здесь запись int* a[3]
означает объявление массива указателей на целое.
Строчная константа в языке С ассоциируется с адресом начала строки в памяти,
тип строки получается char*. Поэтому активно используется следующее
присваивание:
char* Str;
Str = ”Hello, friends!”
Часто массив указателей используется, если надо иметь стандартный набор строк:
char* Season[ ] = {“Winter”, Spring”, ”Summer”, ”Fall”};
11. Пример использования указателей
Изменим программу Калькулятор – станем вводить и обрабатывать в бесконечномцикле строку символов, содержащую первое число, знак операции, второе
число, знак « = ». После знака « = » будет появляться результат. Например:
2 1 . 7 8 + 3 . 1 4 = Идея обработки строки состоит
в следующем: функция atof()
при преобразовании строки символов в число обрабатывает только цифры и
десятичную точку. Любой другой символ воспринимается как конец строки.
Поэтому первое число мы получим, если исходную строку (определим её как
char st[20] ) обработаем функцией atof(st). Второе число мы получим, если
определим номер позиции j знака операции и функцией atof() обработаем строку
начиная с позиции, следующей за знаком операции, т.е. atof(st+j+1).
12. Пример использования указателей
/*========== КАЛЬКУЛЯТОР ( + , - , * , / ) =================
*/
//..подключение библиотек
#include <conio.h> //функции clrscr(), gotoxy(), getche()
#include <stdlib.h> //функции exit(), atof()
#include <stdio.h> //функции puts(), printf()
#include <math.h> //функция pow()
//..начало функции main()
void main(void)
{
char st[50];
//описание символьных переменных
char *s = st;
int i, j, k;
double a, b;
clrscr( );
//очистка экрана
13. Пример использования указателей
puts("Калькулятор выполняет операции: +, -, *, /, ^ ,$.($ - выход)");puts("\n Введите A, знак операции, B, знак""="" \n");
for( ; ; )
// while(1) // начало бесконечного цикла
{
i = 0; j = 0;
while ((*s = getche()) != '=')
{
if(*s == '$')
{
puts(" - Good bye!");
exit(0);
};
switch (*s)
//выбор варианта операции
{
case '+': ;
//переход на выполнение при sign='+'
case '-': ;
case '*': ;
case '/': ;
case '^': j= i; break;
};
14. Пример использования указателей
}s++; i++;
};
// конец ввода строки
s = st;
// переход на начало строки
a = atof(s);
b = atof(s + j+ 1);
switch (*(s+j))
//выбор варианта операции
{
case '+': printf(“%f \ n“, a + b); break;
case '-': printf(“%f \ n“, a - b); break;
case '*': printf(“%f \ n“, a * b); break;
case '/': if(b! = 0) printf(“%f \ n“, a / b);
else printf("Деление на нуль!\n"); break;
case '^': printf(“%f \ n“, pow(a,b)); break;
default: printf("\n Знак операции не определен\n");
}
//конец switch
};
//конец бесконечного цикла for(;;), (while(1))
//конец функции main()
Программирование