Составные типы данных - Томский политехнический университет

advertisement
МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РОССИЙСКОЙ ФЕДЕРАЦИИ
Государственное автономное образовательное учреждение высшего образования
«НАЦИОНАЛЬНЫЙ ИССЛЕДОВАТЕЛЬСКИЙ
ТОМСКИЙ ПОЛИТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ»
Раздел 2. Основы программирования на языке Си
Тема 2.2. Составные типы данных
Преподаватель каф. ЭАФУ
Егорова Ольга Викторовна
Томск – 2016
 составные типы языка Си:
 массивы
 указатели
 функции пользователя
 структуры
 битовые поля
 объединения
 оператор описания типа
 преобразование типов
 директивы препроцессора
2
это
Массив –
составной
тип
данных,
состоящий
из
элементов одного и того же типа (элементы в памяти
располагаются подряд)
Оператор объявления массива:
тип_данных имя [n1][n2]…[nk];
Обращение к элементу массива:
имя_массива [i1][i2]…[ik]
где
где
ni – размерности массива
0 ≤ ij ≤ nj-1
k – мерность массива
nj – максимальное значение j-го
индекса массива
!!!!!! элементы массива нумеруются с 0 до ni-1
Примеры:
int mass[10]; // описание одномерного массива
float A[10][9]; // описание
двумерного массива
A[1][3]=5; mass[9]=2; // обращение к элементам массивов
3
Указатель –
это объект заданного типа, который содержит
адрес другого объекта, т.е. адрес ячейки памяти
Оператор объявления указателя:
тип данных *имя указателя;
Примеры:
int *p; // указатель на переменную типа int
struct {int x,y;} *p; //указатель на структуру
Операции над указателями:
Операция
&
Операция
*
- «взять адрес»
- «взять значение, по
указанному адресу»
complex *p; // указатель на пользовательский
тип complex
4
Оперативная
память
Пример 1
# include <stdio.h>
main ()
pf
{
632
float x = 10.1, y;
float * pf ;
y
pf = &x ;
10.1
y = *pf ;
printf(“x = %f y= %f”, x, y);
// Результат: x=10.1; y=10.1; pf=632
x
}
10.1
Размер ячейки 8 бит
5
Пример 2:
Над указателями можно
производить:
# include <stdio.h>
Оперативная
память
main()
{
 арифметические операции:
int *p;
 сложения и вычитания
int x;
 операции (++) и (--)
p = &x;
 операции отношения:
x
printf (“%p”, p);
 <, >, < =, > =, =, = =, ! =
++p;
printf (“%p”,p);
p
632
630
}
6
Динамический объект – объект создаваемый в процессе выполнения
программы
Функции динамического выделения памяти:
char * malloc(size);
char * calloc(nelem, elsize);
обе функции возвращают указатель на
созданный динамический объект типа char
где
unsigned int size
-
unsigned int nelem -
unsigned int elsize -
объем памяти, который
нужно выделить (в байтах)
число элементов, для которых
необходимо выделить память
объем памяти, который
необходимо выделить для
каждого элемента (в байтах)
sizeof (T)
возвращает число байт
необходимых для хранения
объекта T в памяти
7
Пример 1(Выделение памяти под переменную типа int)
free(Т)
– функция явного освобождения
памяти от динамического
int *P=(int *) malloc(sizeof (int));
Пример 2 (Выделение памяти под одномерный массив)
int *r = (int*) malloc (n*sizeof(int));
или
int *r = (int*) calloc (n, sizeof(int));
Пример 3 (Выделение памяти под двумерный массив)
объекта Т
Пример
free(P);
Указание на произвольную ячейку
памяти
Например, определен указатель
int *p;
тогда указатель на ячейку памяти 0777000 можно
int **r = (int**) malloc(n*sizeof(int*));
получить с помощью следующего оператора:
for (i=0; i<n; i++) r[i] = (int*) malloc(n*sizeof(int));
p=(int*)0777000;
8
имя массива
массив
это указатель-константа, который
можно рассматривать как
содержит адрес его первого элемента
индексированный указатель
Соответственно
действие записи:
имя_массива [индекс];
можно объяснить так
*(имя_массива + индекс);
Пример 1
int mas[3];
12
int *ptr;
ptr = mas;
// присваивает адрес указателю
// следующие операции дадут один и тот же результат:
12
2
mas[2] = 20;
*(ptr + 2) = 20;
// следующая операция прибавит 2 к первому элементу:
*ptr=*ptr + 2;
20
9
Пример 2
int mas[ ] = {1, 2, 3, 4};
Операторы
int mas[ ]; и int *mas;
оба объявляют mas указателем
int *ptr=(int*)malloc(sizeof(n*sizeof(int));
mas= ptr;
// недопустимый оператор
ptr=mas; // опасный оператор
!!!!!!!Нужно помнить!!!!!
 mas[ ] – указатель-константа
 *mas – указатель-переменная
Пример 3
ptr++ // допустимая конструкция
mas++ // запрещенная конструкция
ptr + i; mas + i; // разрешенные операции
для любого массива соблюдается:
имя_массива == &имя_массива==&имя_массивах[0]
10
Оперативная
память
int mas[2][2]; // объявление двумерного массива
int *ptr; // объявление указателя
ptr = mas;
// указателю присваивается адрес первого
элемента массива
(ptr = = mas = = &mas[0][0])
ptr +1; // увеличим значение указателя на 1
(ptr = = mas = = &mas[0][1])
ptr + 2; // увеличим значение указателя на 2
(ptr = = mas = = &mas[1][0])
ptr
mas
635
637
633
631
631
ptr + 3; // увеличим значение указателя на 3
mas[0][0]
(ptr = = mas = = &mas[1][1])
mas[0][1]
т.о. многомерный массив можно
представить как массив массивов
mas[1][0]
mas[1][1]
11
Объявление двумерного массива r[n][m]
int **r = (int**) malloc (n*sizeof(int*));
for (i=0; i<n; i++) r[i] = (int*) malloc (m*sizeof(int));
12
Возможные типы определений
символьных массивов:
1) «Прямолинейное» определение:
char string_1[10][20] = {“Иванов”, “Петров”, “Сидоров”};
2) char string_2[ ][20] = {“Иванов”, “Петров”, “Сидоров”};
3) При помощи массива указателей типа char*:
char* string_3[ ] = {“Иванов”, “Петров”, “Сидоров”};
13
это
Функция -
независимая
операторов,
совокупность
предназначенная
объявлений
для
и
выполнения
определенной задачи
Виды функций:
 функции, возвращающие значения в вызывающую программу
 функции, не возвращающие значения
С использованием функции связаны операторы:
 определения функции
 описания функции
 вызова функции
14
задает
Определение функции -
ее
заголовок,
объявления
локальных
объектов (констант, переменных и т.п.) и операторы,
которые определяют действие функции
Определение функции возвращающей значение:
Заголовок
[static] тип_данных имя_функции (описания формальных параметров)
{
операторы;
Тело функции
return возвращаемый результат;
}
Пример
int max (int a, int b)
{
int r;
r = (a>b) ? a : b;
return r;
}
15
Определение функции, не возвращающей значение:
Заголовок
[static] void имя_функции (описания формальных параметров)
{
Тело функции
операторы;
}
Пример:
void swap (int *a, int *b)
{
int temp;
temp = *a;
*a = *b;
*b = temp;
}
16
задает
Описание функции –
(объявление или прототип)
ее
имя,
параметров,
типы
тип
и
число
значения,
формальных
возвращаемого
функцией, и класс памяти
Объявление функции возвращающей значение:
[static/extern] тип_результата имя_функции (описания формальных параметров);
Пример 1
int max(int a, int b);
Объявление функции не возвращающей значение:
[static/ extern] void имя_функции (описания формальных параметров);
Пример 2
void swap(int *a, int *b);
Вызов функции:
имя_функции (список фактических параметров);
Пример 3
max(a, s);
17
Формальные параметры – это фактически шаблоны, под которые в
действительности не выделяется память, в
момент определения функции
Фактические параметры – это те, которые передаются из вызывающей
программы в функцию по значению (т.е. они
существуют, под них выделено место в памяти)
Формальные
Фактические и формальные параметры
должны совпадать по:
 количеству
 по порядку следования
 по типу данных
int max(int a, int b)
{
int r;
r = (a>b) ? a : b;
return r;
}
main()
{
int a, s, g;
a=3; s=2;
g = max(a, s);
}
Фактические
18
Правила описания функций:

описание функции нужно размещать до места вызова этой функции

определение и описание функции не может размещаться в теле другой
функции
Примеры:
или
#include<stdio.h>
int max(int a, int b)
{
int r;
r=(a>b) ? a : b;
return r;
}
main()
{
int a, s, l;
a=3; s=2;
l=max(a, s);
printf(“%d”,l);
}
#include<stdio.h>
int max(int a, int b);
main()
{
int a, s, l;
a=3; s=2;
l = max(a, s);
printf(“%d”, l);
}
int max(int a, int b)
{
int r;
r = (a>b) ? a : b;
return r;
}
19
#include<stdio.h>
int fun(int x, int y)
{
int p;
p = x + y;
return p;
}
void main()
Оперативная память
(возможное расположение (схематичное))
3
2
1
сегмент данных
main()
{
int x, y, z;
x = 1;
y = 2;
z = fun(x, y);
printf(“%d”, z);
2
1
3
сегмент данных
fun()
}
20
С использованием указателей:
Оперативная память
(возможное расположение (схематичное))
void swap(int *a, int *b)
{
int temp;
temp = *a;
*a = *b;
*b = temp;
12
21
сегмент данных
main()
}
void main()
{
int a, b;
011
012
1
сегмент данных
swap()
a = 1; b = 2;
swap(&a, &b);
}
21
С использованием ссылочных переменных:
Ccылка – это другое имя переменной (объекта)
Оператор объявления ссылочной переменной:
тип данных &имя ссылки = имя объекта;
Пример:
int x = 20;
float y;
int &n = x;
float &m = y;
22
void swap(int *a, int *b)
{
{
int temp;
int temp;
temp = *a;
temp = a;
*a = *b;
a = b;
*b = temp;
b = temp;
}
}
main()
{
}
void swap(int &a, int &b)
main()
{
int a, b;
int a, b;
a = 2; b = 3;
a = 2;
swap(&a, &b);
swap(a, b);
b = 3;
}
23
Пример:
void mass ( int n, float A[ ] )
Оперативная память
(возможное расположение (схематичное))
{
int i;
011
for( i = 0; i < n; i++)
10
сегмент данных
main()
scanf( “ %f ”, &A[ i ] );
}
2
main()
{
011
float B[3];
int a;
2
сегмент данных
mass ()
a = 2;
mass(a, B);
}
24
Определение указателя на функцию:
тип_функции (*имя_указателя ) (описания формальных параметров);
Оператор записи адреса функции в указатель:
имя указателя = имя функции;
Оператор вызова функции через указатель:
(*имя_указателя) (список фактических параметров);
Пример:
void f1(void)
{
print(“Выполняется f1”);
}
void f3(void (*p)())
{
(*p)();
}
void f2(void)
{
print(“Выполняется f2”);
}
void main()
{
void (*p)();
p = f1;
f3(p);
}
25
Рекурсивный вызов функции – это когда функция вызывает саму себя
Пример
рекурсивной функции, вычисляющей значение
факториала для n > 0 (n! = 1*2*3…*n):
fact (int n)
{
int a;
if (n = = 1) return 1;
a = fact (n – 1)*n;
return a;
}
26
Формат заголовка функции с переменным количеством
параметров:
тип_данных
имя_функции (описания формальных параметров, …)
Подходы
Пример:
количества
и
типов
параметров функции при ее вызове:
long summa (int k, …)
{
определения
по
1-ый подход:
int *p = &k;
одному
параметров
из
явно
определяется
задаваемых
реальное
количество фактических параметров
long t = 0;
for (; k; k--) t += *(++p);
return t;
}
по специальному параметру-индикатору
2-ой подход:
уникальное значение, которого будет
сигнализировать об окончании списка
фактических параметров
27
1-ый подход
#include <stdio.h>
long summa (int k, …)
{
int *p = &k;
long t = 0;
for (; k; k--) t + = *(++p);
return t;
}
void main()
{
long r, p;
r = summa(2, 6, 4);
P = summa(6, 1, 2, 3, 4, 5, 6);
printf (“r=%d\n”, r);
printf (“p=%d\n”, p);
}
Результат выполнения программы:
r = 10
p = 21
2-ой подход
#include <stdio.h>
double prod (double arg, …)
{
double aa = 1.0;
double *p = &arg;
if (*p == 0.0) return 0.0;
for (; *p; p++) aa* = *p;
return aa;
}
void main()
{
double r, p;
r = prod(2.0, 4.0, 3.0, 0.0);
p = prod(1.4, 3.0, 0.0, 16.0);
printf (“r=%d\n”,r);
printf (“p=%d\n”,p);
}
Результат выполнения программы:
r = 9.0
p = 4.4
28
#include <stdio.h>
long minimum (char z, int k, …)
{
if(z == ’i’)
{
int *pi = &k + 1;
int min = *pi;
for (; k; k--, pi++)
min = min >*pi ? *pi : min;
return (long) min;
}
if(z == ’l’)
{
long *pl = (long*)(&k + 1);
long min = *pl;
for (; k; k--, pl++)
min = min >*pl ? *pl : min;
return (long)min;
}
printf(“неверно задан 1-й параметр”);
return 2222L;
}
void main()
{
long p,p1;
p=minimum(‘l’,2,10L,20L);
p1=minimum(‘i’, 3,11,2,3);
}
29
Стандартной библиотекой языка Си (также известной как libc, crt)
называется
посвященная
часть
стандарта
библиотечным
ANSI
C,
функциям
(обычно поставляется вместе с компилятором)
Содержит функции, обеспечивающие:
 файловый и консольный ввод-вывод
 обработку строк
 вычисление математических функций
 конвертацию типов данных
Примеры заголовочных файлов:
<math.h>
<stdio.h>
для вычисления основных
математических функций
реализует основные
возможности ввода и вывода
в языке Си
Структура библиотеки:
имя и характеристики каждой функции
указываются в файле, именуемом
заголовочным
(имеет
обычно
расширение *.h). Всего 24 файла.
текущая реализация функций описана
отдельно в библиотечном файле (имеет
расширение обычно *.lib)
Каждый заголовочный файл содержит:
 описания (прототипы) одной или
более функций
 определения типов данных;
 макросы
30
Примеры функций, описанных в некоторых
заголовочных файлах:
1) Функции, предназначенные для математических вычислений
(описания содержаться в заголовочном файле math.h):
тригонометрические
функции
обратные
тригонометрические
функции
арктангенс от (y/x)
sin(double x), cos(double x), tan(double x)
asin(double x), acos(double x), atan(double x)
atan2(double x)
2) Функции, отвечающие за файловый и консольный ввод-вывод
(описания содержаться в файле stdio.h):
printf ()
putc ()
putchar ()
puts () и др. (о них говорили при расс.
ввода-вывода данных)
Полный перечень функций библиотеки ANSI C можно
посмотреть в книге:
Подбельский В.В. Язык Си++: Учебное пособие. – 5-изд. – М.:
Финансы и статистика, 2004 (стр. 32), а также в любой
литературе, посвященной языку Си
31
Для того, чтобы организовать доступ к библиотечным функциям, в начале
программного файла с помощью директивы препроцессора #include необходимо
подключить нужный заголовочный файл (*.h)
Оператор вызова функции:
имя (список аргументов);
При обращении к функциям надо
помнить:
типы, порядок и количество фактических
параметров (перечисляемых при конкретном
обращении) должны совпадать с описанием
формальных параметров (данных в описании
функции)
Пример 1
#include<stdio.h>
main()
{
FILE *in;
in = fopen("AUTO.DAT", "r");
if ( in == NULL)
{
printf("Невозможно открыть входной
файл.\n");
}
}
Пример 2
функция вычисления синуса описана в
math.h следующим образом:
double sin(double x);
значит при обращении ей необходимо
передать 1 аргумент типа double:
double d;
d = 30;
ss = sin (xgr*M_PI/180);
32
Перечисляемый тип (enum)
Определение типа enum:
1 вариант:
typedef enum {a0, a1, …, an} E;
где E - определяемый перечисляемый тип (новый тип)
позволяет в качестве значения
использовать
имена объектов (идентификаторы)
Пример
1) Определяем новый тип day:
typedef enum {mon, tru, wed, thu, fri,
sat, sun} day;
Описание переменной данным типом:
E имя переменной;
2 вариант (с использованием метки):
enum M {a0, a1, …, an};
где M – метка перечисляемого типа
Описание переменной данным типом:
enum M имя переменной;
2) Определяем переменную данным
типом:
day f;
3) Использование данной переменной:
switch(f)
{
case mon: оператор 1;
case tru: оператор 2;
}
33
Структура –
это составной объект, в котором под одним именем
объединяются элементы одного или разных типов
Определение структуры:
Пример 1
struct
{
список описаний элементов;
};
ИЛИ
struct
{
int a;
float b;
};
struct метка
{
список описаний элементов;
};
Пример 2
метка – это имя структуры
struct student
{
char name[25];
int id, age;
char sex;
};
34
1 вариант
struct
{
список описаний элементов;
} имя объекта;
2 вариант
(с использованием
меток)
struct метка
{
список описаний элементов;
} имя объекта 1, имя объекта 2;
Пример 1:
struct
{
float a;
int b;
} r, t[10];
Пример 2:
struct student
{
char name[25];
int age;
char sex;
}s, t[5];
Пример 3:
struct метка
struct student
{
{
список описаний элементов;
char name[25];
};
int age;
char sex;
struct метка имя объекта 1, имя объекта 2; };
struct student s, t[5];
35
Элемент структур по другому еще называют поле
Доступ к отдельному полю :
.
name title
имя
структурного
объекта
При определении указателя
на структурный объект:
имя поля
.
(*name) title
Вместо такого обращения можно
использовать:
name -> title
Пример 1:
struct student
{
char name[25];
int age;
char sex;
};
struct student s, t[5];
s.age = 5;
Пример 2:
struct student
{
char name[25];
int age;
char sex;
};
struct student *n_student;
(*n_student).age;
или
n_student - > age;
36
Битовое поле задается следующим образом:
где тип может быть:
 char
 unsigned char
 int
 unsigned int
тип имя поля : ширина;
Пример 1:
struct str
{
int d: 3;
unsigned j: 4;
int : 4;
int k: 2;
} a;
Распределение памяти:
15 14 12 11 10 9 8 7 6 5 4 3 2 1 0
x x x x x x x x x x x x x x x
< --- > < ---------- > < ----------- > < ----- >
k
не испол.
j
d
Пример 2:
#include<stdio.h>
void main (void)
{
struct str {
int d:1;
unsigned int j:4;
int p:4;
int k:2;
} a;
a.d = 1;
printf(“%d”,a.d);
}
Результат работы программы: -1
37
это составной объект подобный структуре, однако в
Объединение –
каждый момент времени может использоваться (или
являться
активным)
только
один
из
его
элементов
(компонентов)
Объединения применяются для:
 минимизации используемого объема памяти, если
в каждый момент времени только один объект из
многих является активным
 интерпретации основного представления объекта
одного типа, как если бы этому объекту был
присвоен другой тип
38
Определение объединения:
union
{
описание компонента 1;
описание компонента 2;
};
или
union метка
{
список описаний компонентов;
};
Описание объекта типа объединения:
union
{
список описаний компонентов;
} имя объекта;
union метка
{
список описаний компонентов;
};
union метка имя объекта 1, имя объекта 2;
union метка
{
список описаний компонентов;
} имя объекта 1, имя объекта 2;
Пример:
union
{
float radius;
float a[2];
Доступ к компонентам объединения:
осуществляется тем же способом, что и
к полям структуры см. слайд 34)
int b[3];
position p;
} geom_fig;
39
Оператор описания типа:
typedef спецификатор типа имя нового типа;
где спецификатор типа это:
 основной тип
 производный тип
 тип ранее определенный программистом
Примеры:
typedef float miles, speed;
typedef float a[5], *p;
typedef struct {float x, y;} point;
С помощью этих типов можно определять объекты:
point s1, s2, *p;
40
Неявные преобразования:
 при выполнении арифметических операций
 при выполнении операций присваивания
 при передаче аргументов в функцию
При этом:
char может быть преобразован в int, short int, long int
int в – char, short int, long int,также в float, double
short – аналогично типу int
long – аналогично типу int
float – double, также в int, short, long
double – float, а также int, short, long
41
В операциях присваивания тип значения, которое присваивается,
преобразуется
к
типу
переменной,
получающей это значение
В арифметических операциях:




операнды типов char и short преобразуется к типу int, операнды типа float
преобразуется к типу double
если хотя бы один из операндов имеет тип double, то и другой операнд
преобразуется к типу double, результат имеет тип double
если хотя бы один операнд имеет тип long, то и другой операнд преобразуется к
типу long, результат имеет тип long
если хотя бы один операнд имеет тип unsigned, то и другой операнд
преобразуется к типу unsigned, результат имеет тип unsigned
Преобразования при вызове функции: если задан прототип функции и он включает
объявление
типов
аргументов,
то
над
аргументами в вызове функции выполняются
только
обычные
преобразования
арифметические
42
Выражение E может быть явно преобразовано к
типу (имя_типа) при помощи оператора:
(имя_типа) E;
Пример:
int a = 2; long с = 2;
Замечание:
 все
типы
могут
быть
явно
преобразованы в тип void;
double d; float f;
d = (double)a * (double)с; f = (float)d;
 тип void НЕЛЬЗЯ преобразовать в
какой-либо другой тип;
 явное преобразование не реализованы
для struct и union
43
Препроцессор –
это программа, выполняющая обработку входных
данных для другой программы
Директивы препроцессора:
Определение
#define
#undef
#include
#if
#ifdef
#ifndef
#else
#elif
#endif
#line
#error
#pragma
#
Назначение
Определение макроса
Отмена определения макроса
Включение объекта-заголовка
Компиляция, если выражение истинно
Компиляция, если макрос определен
Компиляция, если макрос не определен
Компиляция, если выражение в if ложно
Составная директива else/if
Окончание группы компиляции по условию
Замена новым именем строки или имени исходного файла
Формирование ошибок трансляции
Действие определяется реализацией
Null- директива
44
Общая форма определения макроса:
#define
ИМЯ_МАКРОСА
строка замещения
Отмена определения макроса:
#undef ИМЯ_МАКРОСА
Примеры:
Пример 1
Пример 2
#define MAX 100
#define MIN(a, b) ((9a)<(b)) ? (a) : (b)
#define NAME “ Turbo C++”
printf(“Минимум из x и y “ % d, MIN(x ,y));
printf(“Минимум из a и b “ % d, MIN(n ,m));
45
Директивы условной компиляции:
Пример
# include <stdio.h>
# define MAX 100
main(void)
{
# if MAX>99
#if, #else, #elif, #endif
Директива #elif:
#if <выражение>
последовательность операторов
#elif <выражение 1>
последовательность операторов
#elif <выражение 2>
последовательность операторов
…………………………………..
printf(“ MAX равно %d \n”, MAX);
# endif
# endif
Директива #ifdef
}
#ifdef ИМЯ_МАКРОСА
последовательность операторов
Директива #error
# endif
#error сообщение_об_ошибке
Директива #ifndef
#ifndef ИМЯ_МАКРОСА
последовательность операторов
# endif
46
Download