Слайд 1Основы программирования
Ст. преп.
каф. ПОВТ
Масленников Алексей Александрович
Лекция № 6
Строки, структуры
Слайд 2Пример обработки строки в виде массива
В следующей программе строки представлены
массивами. Копирование строки-источника в строку-приемник выполняется посимвольно через указатели с
инкрементированием указателей. Условием останова является достижение конца строки-источника (копирование символа '\0').
Слайд 3Пример обработки строки в виде массива
#define MAXLEN 200
void strcpy_(char* s,
char* t) {
while(*s++ = *t++);
}
void swaps(char* a, char *b)
{
char c[MAXLEN];
strcpy_(c, a); strcpy_(a, b); strcpy_(b, c);
}
int main() {
char s1[MAXLEN] = "first";
char s2[MAXLEN] = "second";
printf("%s, %s\n", s1, s2);
swaps(s1, s2);
printf("%s, %s\n", s1, s2);
return 0;
}
Слайд 4Пример обработки строки в виде указателей
В следующей программе строки представлены
указателями:
#define MAXLEN 200
void strcpy_(char* s, char* t) {
while(*s++ =
*t++);
}
void swaps(char* a, char *b) {
char c[MAXLEN];
strcpy_(c, a); strcpy_(a, b); strcpy_(b, c);
}
int main() {
char* s1 = "first";
char* s2 = "second";
printf("%s, %s\n", s1, s2);
swaps(s1, s2);
printf("%s, %s\n", s1, s2);
return 0;
}
Слайд 5Пример обработки строки в виде указателей
В результате выполнения этой программы
возникнет ошибка.
Основная причина этой ошибки – попытка изменить символы в
строковой константе, которая имеет свойство «только для чтения». Однако в этом случае есть более лёгкое решение – строки оставить на месте, а поменять значения указателей:
Слайд 6Пример обработки строки в виде указателей
В следующей программе строки представлены
указателями:
void swaps(char** a, char** b) {
char *c = *a;
*a = *b; *b = c;
}
int main() {
char* s1 = "first";
char* s2 = "second";
printf("%s, %s\n", s1, s2);
swaps(&s1, &s2);
printf("%s, %s\n", s1, s2);
return 0;
}
Слайд 7Массивы указателей
Так как указатели являются обычными переменными, их можно хранить
в массиве. Создадим объект arrs типа «массив строк», проинициализируем его
и выполним перестановку некоторых элементов этого массива:
void swaps(char** a, char** b) {
char *c = *a;
*a = *b; *b = c;
}
int main() {
char* arrs[] = {"first", "second", "third",
"fourth", "fifth"};
int i, n = sizeof(arrs)/sizeof(*arrs);
for(i=0; i swaps(&arrs[0], &arrs[2]);
swaps(&arrs[1], &arrs[4]);
printf("--------\n");
for(i=0; i return 0;
}
Слайд 8Структуры, объединения и перечисления
Структура – это набор нескольких именованных переменных,
объединённых в единое целое под общим именем.
Входящие в структуру
переменные называются элементами, полями или членами структуры.
Члены структуры могут иметь произвольные типы, а их имена должны быть уникальны в пределах структуры.
Доступ к членам структуры производится по составному имени, включающему имя структуры и имя члена структуры.
Слайд 9Структуры, объединения и перечисления
Пример. Объявление структуры и доступ к членам
структуры.
Синтаксис языка С позволяет отделить описание «внутреннего устройства» структуры от
определения конкретных переменных, имеющих это внутреннее устройство:
Такой подход позволяет рассматривать первое объявление как новый, структурный тип данных, а второе – как определение переменных этого типа. Допускается совмещать объявление структурного типа и объявление переменных этого типа.
struct {int x; int y;} pt1, pt2;
pt1.x = 3; pt1.y = 6;
pt2 = pt1;
struct point {int x; int y;};
struct point pt1, pt2;
Слайд 10Структуры, объединения и перечисления
Элементы структуры могут иметь произвольный тип, в
том числе, они сами могут быть структурами. Например, можно объявить
структуру rect (прямоугольник), используя ранее объявленную структуру point (точка):
struct rect { struct point p1, p2; };
Слайд 11Структуры, объединения и перечисления
Указатели на структуры и массивы структур
Структурные типы
данных так же, как и обычные типы данных, могут использоваться
для конструирования указателей и массивов.
Пусть struct T – структурный тип данных, тогда:
struct T x – описывает переменную типа struct T, или просто структуру x,
struct T *p – описывает указатель на структуру типа struct T,
struct T m[5] – описывает массив из пяти структур типа struct T,
struct T *pm[] – описывает массив неопределенного размера из указателей на структуры типа struct T,
struct T (*pm)[] – описывает указатель на массив неопределенного размера из структур типа struct T и т.д.
Слайд 12Структуры, объединения и перечисления
Указатели на структуры особенно полезны при передаче
аргументов-структур в функции, т.к. приводят к экономии памяти при передаче
громоздких структур.
При использовании указателей на структуры язык С имеет специальный синтаксис для обращения к членам структуры через указатель на структуру. Пусть объявлен структурный тип point, конкретная структура pt этого типа и указатель pp:
Обычный синтаксис доступа к членам структуры pt через указатель pp выглядит так (скобки нужны потому, что приоритет операции . выше, чем у операции *):
(*pp).x = 3; (*pp).y = 5;
С использованием специального синтаксиса (операции «стрелка») эти же операторы можно записать более наглядно:
pp->x = 3; pp->y = 5;
struct point { int x, y;};
struct point pt, *pp = &pt;
Слайд 13Инициализация структур
При объявлении структуры ее поля могут быть инициализированы с
использованием списка инициализации. Порядок размещения значений в таком списке и
их типы должны соответствовать порядку расположения и типам полей в структуре.
Пример. Программа проверяет принадлежность точки прямоугольнику с использованием функции pt_in_rect.
Допускается инициализация членов структуры по их именам (а не по порядку):
struct point { int x, y; };
struct rect {
struct point pt1, pt2;
};
int pt_in_rect(struct point p, struct rect r) {
if(p.x < r.pt1.x || p.x > r.pt2.x) return 0;
if(p.y < r.pt1.y || p.y > r.pt2.y) return 0;
return 1;
}
int main() {
struct point z = {2, 2};
struct rect rc = {{1, 1}, {4, 5}};
printf("%s\n",
pt_in_rect(z, rc) ? "inside" : "outside");
return 0;
}
struct rect rc = {.pt2={4, 5}, .pt1={1, 1}};
Слайд 14Анонимные структуры
Последние версии языка С позволяют создавать анонимные (безымянные) структуры.
Эта возможность используется для случаев, когда членом структуры является структура
и создавать для нее специальный структурный тип нецелесообразно.
Пример. Программа демонстрирует использование анонимной структуры и инициализацию членов структуры по именам.
struct person {
int id;
struct {char first[20]; char last[20];};
};
int main() {
struct person man = {.last = "Никитин", .id = 3452,
.first = "Афанасий"};
printf("%s\n", man.first);
printf("%s\n", man.last);
return 0;
}
Слайд 15Структуры и функции
Структура может быть и возвращаемым из функции значением.
Программа
находит минимальный прямоугольник, покрывающий заданное множество прямоугольников.
Слайд 16Структуры и функции
struct point { int x, y; };
struct rect
{ struct point pt1, pt2; };
struct rect max_rect(struct rect *r,
int n) {
struct rect rm = *r++;
for(int i = 1; i < n; i++, r++) {
if(r->pt1.x < rm.pt1.x) rm.pt1.x = r->pt1.x;
if(r->pt1.y < rm.pt1.y) rm.pt1.y = r->pt1.y;
if(r->pt2.x > rm.pt2.x) rm.pt2.x = r->pt2.x;
if(r->pt2.y > rm.pt2.y) rm.pt2.y = r->pt2.y;
}
return rm;
}
int main() {
struct rect rc[] = {{{1, 1}, {4, 5}},
{{2, 0}, {6, 6}},
{{3, 2}, {7, 4}}};
int n = sizeof(rc) / sizeof (*rc);
struct rect rcm = max_rect(rc, n);
printf("x1=%d, y1=%d, x2=%d, y2=%d\n",
rcm.pt1.x, rcm.pt1.y, rcm.pt2.x, rcm.pt2.y);
return 0;
}
Слайд 17Структуры и функции
Текст приведенной выше программы можно немного сократить и
сделать более «прозрачным», если воспользоваться возможностью создания синонимов для «громоздких»
типов с помощью оператора typedef (type definition). Приведем несколько примеров применения typedef:
Слайд 18Структуры и функции
Пример. Программа с синонимом типа.
typedef struct {
struct
{int x, y;} pt1, pt2;
} Rect;
Rect max_rect(Rect* r, int n)
{
Rect rm = *r++;
for(int i = 1; i < n; i++, r++) {
if(r->pt1.x < rm.pt1.x) rm.pt1.x = r->pt1.x;
if(r->pt1.y < rm.pt1.y) rm.pt1.y = r->pt1.y;
if(r->pt2.x > rm.pt2.x) rm.pt2.x = r->pt2.x;
if(r->pt2.y > rm.pt2.y) rm.pt2.y = r->pt2.y;
} return rm;
}
int main() {
Rect rc[] = {{{1, 1}, {4, 5}},
{{2, 0}, {6, 6}},
{{3, 2}, {7, 4}}};
int n = sizeof(rc) / sizeof (*rc);
Rect rcm = max_rect(rc, n);
printf("x1=%d, y1=%d, x2=%d, y2=%d\n",
rcm.pt1.x, rcm.pt1.y, rcm.pt2.x, rcm.pt2.y);
return 0;
}
Слайд 19Структуры и функции
Пример. Программа вычисляет разницу между двумя временными периодами.
Определим
сначала структуру и заголовок функции
#include
struct TIME
{
int seconds;
int
minutes;
int hours;
};
void differenceBetweenTimePeriod(struct TIME t1, struct TIME t2, struct TIME *diff);
Слайд 20Структуры и функции
Пример. Программа вычисляет разницу между двумя временными периодами.
Опишем
функцию
void differenceBetweenTimePeriod(struct TIME start, struct TIME stop, struct TIME *diff)
{
if(stop.seconds > start.seconds){
--start.minutes;
start.seconds += 60;
}
diff->seconds = start.seconds - stop.seconds;
if(stop.minutes > start.minutes){
--start.hours;
start.minutes += 60;
}
diff->minutes = start.minutes - stop.minutes;
diff->hours = start.hours - stop.hours;
}
Слайд 21Структуры и функции
Пример. Программа вычисляет разницу между двумя временными периодами.
Итог:
int main()
{
struct TIME startTime, stopTime, diff;
printf("Enter
start time: \n");
printf("Enter hours, minutes and seconds respectively: ");
scanf("%d %d %d", &startTime.hours, &startTime.minutes, &startTime.seconds);
printf("Enter stop time: \n");
printf("Enter hours, minutes and seconds respectively: ");
scanf("%d %d %d", &stopTime.hours, &stopTime.minutes, &stopTime.seconds);
// Calculate the difference between the start and stop time period.
differenceBetweenTimePeriod(startTime, stopTime, &diff);
printf("\nTIME DIFFERENCE: %d:%d:%d - ", startTime.hours, startTime.minutes, startTime.seconds);
printf("%d:%d:%d ", stopTime.hours, stopTime.minutes, stopTime.seconds);
printf("= %d:%d:%d\n", diff.hours, diff.minutes, diff.seconds);
return 0;
}
Слайд 22Структуры и функции
В результате выполнения полeчим:
Enter start time:
Enter hours, minutes
and seconds respectively: 12
34
55
Enter stop time:
Enter hours, minutes and seconds
respectively:8
12
15
TIME DIFFERENCE: 12:34:55 - 8:12:15 = 4:22:40
Слайд 23Структуры и функции
Пример. Хранение данных об оценках по определенным предметам:
#include
#include
struct course
{
int marks;
char subject[30];
};
Слайд 24Структуры и функции
Продолжение
int main()
{
struct course *ptr;
int
i, noOfRecords;
printf("Enter number of records: ");
scanf("%d",
&noOfRecords);
// Allocates the memory for noOfRecords structures with pointer ptr pointing to the base address.
ptr = (struct course*) malloc (noOfRecords * sizeof(struct course));
for(i = 0; i < noOfRecords; ++i)
{
printf("Enter name of the subject and marks respectively:\n");
scanf("%s %d", &(ptr+i)->subject, &(ptr+i)->marks);
}
printf("Displaying Information:\n");
for(i = 0; i < noOfRecords ; ++i)
printf("%s\t%d\n", (ptr+i)->subject, (ptr+i)->marks);
return 0;
}
Слайд 25Объединения
Помимо структур (struct) в языке С имеется еще один вид
конструкций, позволяющих собрать под одним именем разнотипные элементы – это
объединения (union).
Синтаксически объединения практически ничем не отличаются от структур, кроме использования ключевого слова union вместо ключевого слова struct.
Принципиальное отличие объединений от структур зключается в том, что
все элементы объединения размещаются в памяти с одного адреса и перекрывают друг друга.
Вследствие этого размер объединения в байтах определяется размером самого большого поля (возможно, с учетом необходимого выравнивания) и в каждый момент времени объединение может хранить только одно поле.
Слайд 26Объединения
Пример. Размер объединения u равен размеру его самого большого поля
ldval.
int main() {
union {
int ival;
long
double ldval;
char *sval;
} u;
printf("Size of u = %d \n", sizeof(u));
return 0;
}
Слайд 27Объединения
Пример. Размер объединения p больше размера его самого большого поля
c[5] из-за необходимости выравнивания на границу, кратную размеру поля f
типа float:
int main() {
union {
char c[5]; // 5 байт
float f; // 4 байта
} p = {.f = 1.23};
printf("Размер объединения p = %zu\n", sizeof p);
return 0;
}
Слайд 28Объединения
Пример. Размер объединения p больше размера его самого большого поля
c[5] из-за необходимости выравнивания на границу, кратную размеру поля f
типа float:
int main() {
union {
char c[5]; // 5 байт
float f; // 4 байта
} p = {.f = 1.23};
printf("Размер объединения p = %zu\n", sizeof p);
return 0;
}