Struct book lib; //объявляется переменная типа book

После объявления переменной lib имеется возможность работать со структурой как с единым объектом данных, который имеет три поля:

title, authorиyear.

Обращение к тому или иному полю структуры осуществляется через точку:

lib.title, lib.authorиlib.year.

Таким образом, для записи в структуру информации можно использовать следующий фрагмент программы:

printf("Введите наименование книги: ");

scanf("%s",lib.title);

printf("Введите автора книги: ");

scanf("%s",lib.author);

printf("Введите год издания книги: ");

scanf("%d",&lib.year);

После этого в соответствующие поля будет записана введенная с клавиатуры информация и хранится в единой переменной lib.

Однако по условиям задачи необходимо осуществлять запись не по одной, а по 100 книгам. В этом случае целесообразно использовать массив структур типа book, который можно задать следующим образом:

struct book lib[100];

В этом случае программу ввода и хранения информации по книгам можно записать в виде:

#include <stdio.h>

struct book {

char title[100]; //наименование книги

char author[100]; //автор

int year; //год издания

};

Int main()

{ int cnt_book = 0, ch;

struct book lib[100];

do {

printf("Введите наименование книги: ");

scanf("%s",lib[cnt_book].title);

printf("Введите автора книги: ");

scanf("%s",lib[cnt_book].author);

printf("Введите год издания книги: ");

scanf("%d",&lib.year);

printf("Нажмите q для завершения ввода: "); cnt_book++;

}

while(scanf("%d",ch) == 1 && cnt_book < 100); return 0;

}

Данный пример показывает удобство хранения информации по книгам. Тот же алгоритм в общем случае можно реализовать и без структуры, но тогда пришлось бы использовать два двумерных массива символов и один одномерный массив для хранения года издания.

Несмотря на то, что формально такая запись была бы корректной с точки зрения языка С, но менее удобна в обращении. Графически массив структур можно представить в виде таблицы 4, в которой роль столбцов играют поля, а роль строк элементы массива структур:

Таблица 4. Массив структур.

  название автор год издания
lib[0] lib[0].title lib[0].author lib[0].year
lib[1] lib[1].title lib[1].author lib[1].year
lib[2] lib[2].title lib[2].author lib[2].year
lib[99 lib[99].title lib[99].author lib[99].year

Структуры можно автоматически инициализировать при их объявлении подобно массивам, используя следующий синтаксис:

struct bool lib = {

"Евгений Онегин",

"Пушкин А.С.",

};

При выполнении данного фрагмента программы в переменные структуры title, author и year будет записана соответственно информация:

“Евгений Онегин”, “Пушкин А.С.”, 1995.

Здесь следует обратить внимание, что последовательность данных при инициализации должна соответствовать последовательности полей в структуре. Это накладывает определенные ограничения, т.к. при инициализации необходимо помнить последовательность полей в структуре.

Стандарт C99 допускает более гибкий механизм инициализации полей структуры:

struct book lib = {.year = 1995,

.author = "Пушкин А.С.",

.title = "Евгений Онегин" };

или

struct book lib = { .year = 1995,

.title = "Евгений Онегин" };

или

struct book lib = {.author = "Пушкин А.С.",

.title = "Евгений Онегин",

1995 };

В первом и во втором примерах при инициализации указываются наименования полей через точку.

При этом их порядок и число не имеет значения. В третьем примере первые два поля указаны через имена, а последнее инициализируется по порядковому номеру - третьему, который соответствует полю year.

В некоторых случаях имеет смысл создавать структуры, которые содержат в себе другие (вложенные) структуры.

Например, при создании простого банка данных о сотрудниках предприятия целесообразно ввести, по крайней мере, две структуры.

Одна из них будет содержать информацию о фамилии, имени и отчестве сотрудника, а вторая будет включать в себя первую с добавлением полей о профессии и возрасте:

struct tag_fio

{ char last[100];

char first[100];

char otch[100];

};

struct tag_people

{ struct tag_fio fio; //вложенная структура

char job[100];

int old;

};

Рассмотрим способ инициализации и доступ к полям структуры people на следующем примере.

Int main ()

{ struct people man = {

{"Иванов", "Иван", "Иванович"},

"Электрик",50 };

printf(“Ф.И.О.:%s %s %s\n",man.fio.last,man.fio.first,man.fio.otch);

printf("Профессия : %s \n",man.job);

printf("Возраст : %d\n",man.old);

return 0;}

В данном примере показано, что для инициализации структуры внутри другой структуры следует использовать дополнительные фигурные скобки, в которых содержится информация для инициализации полей фамилии, имени и отчества сотрудника.

Для того чтобы получить доступ к полям вложенной структуры выполняется сначала обращение к ней по имени man.fio, а затем к ее полям: man.fio.last, man.fio.first и man.fio.otch. Используя данное правило, можно создавать многоуровневые вложения для эффективного хранения и извлечения данных.

Структуры, как и обычные типы данных, можно передавать функции в качестве аргумента.

Пример: Передача структур через аргументы функции.

#include <stdio.h>

struct tag_people

{

char name[100];

char job[100];

int old;

};

void show_struct(struct tag_people man);

Int main()

{ struct tag_people person = {"Иванов","Электрик",30};

show_struct(person);

return 0;

}

void show_struct(struct tag_people man)

{printf("Имя: %s\n",man.name);

printf("Профессия: %s\n",man.job);

printf("Возраст: %d\n",man.old);

}

В приведенном примере используется функция с именем show_struct, которая имеет тип аргумента struct tag_people и переменную-структуру man. При передаче структуры функции создается ее копия, которая доступная в теле функции show_struct под именем man.

Следовательно, любые изменения полей структуры с именем man никак не повлияют на содержание структуры с именем person.

Вместе с тем иногда необходимо выполнять изменение полей структуры функции и возвращать измененные данные вызывающей программе. Для этого можно задать функцию, которая будет возвращать структуру:

#include <stdio.h>

struct tag_people { char name[100]; char job[100]; int old;};

void show_struct(struct tag_people man);

struct tag_people get_struct();

Int main()

{struct tag_people person;

person = get_struct();

show_struct(person);

return 0;

}

void show_struct(struct tag_people man)

{printf("Имя: %s\n",man.name);

printf("Профессия: %s\n",man.job);

printf("Возраст: %d\n",man.old);

}

struct tag_people get_struct()

{struct tag_people man;

scanf("%s",man.name);

scanf("%s",man.job);

scanf("%d",man.old);

return man;

}

В данном примере используется функция get_struct(), которая инициализирует структуру с именем man, запрашивает у пользователя ввод значений ее полей и возвращает введенную информацию главной программе.

В результате выполнения оператора присваивания структуры man структуре person, происходит копирование информации соответствующих полей и автоматическое удаление структуры man.

Функциям в качестве аргумента можно также передавать массивы структур. Для этого используется следующее определение:

void show_struct(struct people mans[], int size);

Здесь size - число элементов массива, которое необходимо для корректного считывания информации массива mans.

Пример: Передача массив структур функции.

#include <stdio.h>

#define N 2

struct tag_people { char name[100]; char job[100]; int old;};

void show_struct(struct people mans[], int size);

Int main()

{struct people persons[N] = {

{ "Иванов", «Электрик», 35 },

{ "Петров", «Преподаватель», 50 },

} ;

show_struct(persons, N);

}

void show_struct(struct people mans[], int size)

{for(int i = 0;i < size;i++) {

printf("Имя: %s\n",mans[i].name);

printf("Профессия:%s\n",mans[i].job);

printf("Возраст: %d\n",mans[i].old);

}

}

При передаче аргумента persons выполняется копирование информации в массив mans и указывается дополнительный параметр size, для определения числа элементов массива mans. Затем в функции show_struct() реализуется цикл, в котором выполняется отображение информации массива структуры на экран монитора.

10 отличий C от C++.

C++ - это тот же C, но с некоторыми удобными упрощениями.

Существуют два стиля написания программ: стиль Си стиль C++. Они противопоставляются друг другу.

C++ ассоциируется с ООП (объектно-ориентированным программированием),

C - с ПОП (процедурно-ориентированным программированием).

Все, что есть нового в C++ - уже было в C.

Только в C++ это записывается чуть по-другому.

Отметим основные различия языков С и С++:

1. Объекты:

В C++ появились классы и объекты.

Класс C++ - это тип структуры в C, а объект - переменная такого типа. Разница в том, что в C++ есть модификаторы доступа и полями могут быть не только данные, но и функции (функции-методы).

Функция-метод - это обычная функция C, у которой первый параметр - это указатель на структуру, данные которой она обрабатывает: this.

Если сравнить, как выглядят функции-методы в C++ и функции с параметром-указателем на структуру в C, то видим, что изменилась лишь форма записи.

В C++ получается короче, так как this и имя типа во многих случаях писать не обязательно (подразумевается по умолчанию).

Модификаторы доступа - это слова public, private и protected.

В C вместо них была пунктуальность программиста:

public - значит с этими полями делаю, что хочу;

private - значит к этим полям обращаюсь только с помощью методов этой структуры;

protected - то же, что public, но еще можно обращаться из методов унаследованных структур .

2. Наследование:

То, что в C++ - наследование, в C - это просто структура в структуре.

При программировании в стиле C++ говорят , что "класс B порожден от класса A" или "класс B наследуется от класса A и является производным от него". На практике это значит, что структура B - это первое поле структуры A.

При этом имеем два улучшения:

поля B считаются так же и полями A, в результате доступ к ним записывается короче, чем в C.

в обоих структурах можно иметь функции-методы, у которых имена совпадают с точностью до имени структуры. Например, B::b и A::a .

Отсюда не надо изобретать имена вроде B_b и A_b, как это было в C, а префиксы B:: и A:: в большинстве случаев можно опускать.

3. new и delete:

В C++ это сокращения для распространенных вызовов функций malloc и free:

Пример в стиле C:

Point *p = (Point*) malloc(sizeof(Point));

free(p);

В стиле C++:

Point *p = new Point;

delete p;

При вызове new автоматически вызывается конструктор, а при вызове delete - деструктор .

Можно описать формулой:

new = malloc + конструктор, delete = free + деструктор.

Конструкторы и деструкторы.

Когда программируешь в стиле C, после того, как завел новую переменную типа структуры, часто надо ее инициализировать и об этом легко забыть. А перед тем как такая структура закончит свое существование, надо ее почистить, если там внутри есть ссылки на какие-то ресурсы.

В C++ появились функции, которые вызываются автоматически после создания переменной структуры (конструкторы) и перед ее уничтожением (деструкторы). Во всех остальных отношениях это - обычные функции, на которые наложен ряд ограничений. Некоторые из этих ограничений ничем не оправданы и мешают:

например, конструктор нельзя вызвать напрямую (деструктор можно). Нельзя вернуть из конструктора или деструктора значение. А деструктору нельзя задать параметры.

5. Виртуальные функции:

При программировании на C часто бывает так, что имеется несколько вариантов одной и той же структуры, для которых есть аналогичные функции. Например, есть структура, описывающая точку (B) и структура, описывающая окружность (A).

Для них обоих часто приходится выполнять операцию рисования . Так что, если у нас есть блок данных, где перемешаны точки, окружности и прочие графические примитивы, то перед нами стоит задача быстро вызвать для каждого из них свою функцию рисования.

Обычное решение - построить таблицу соответствия "вариант структуры - функция". Затем берется очередной примитив, определяется его тип, и по таблице вызывается нужная функция.

В Cэтот метод применять довольно нудно из-за того, что

во-первых, надо строить эту таблицу,

во-вторых, внутри структур заводить поле, сигнализирующее о том, какого она типа, и следить за тем, чтобы это поле содержало правильное значение.

В C++ всем этим занимается компилятор: достаточно обозначить функцию-метод как virtual, и для всех одноименных функций будет создана таблица и поле типа, за которыми следить будет опять-таки компилятор. Вам останется только пользоваться ими: при попытке вызвать функцию с таким именем, будет вызвана одна из серии одноименных функций в зависимости от типа структуры.

6. Исключения:

Исключение- это просто последовательность команд goto и return.

Основан на обычной C-технологии setjmp / longjmp.

Команды в С++ try и catch - это setjmp с проверкой в С.

Команда throw - это longjmp.

Когда вызывается throw, то проверяется: если он окажется внутри блока try, то выполняется goto на парный блок catch. Если нет, то делается return и ищется catch на уровень выше и так далее.

Наличие в throw/catch параметра ничего принципиально не меняет: и в обычном C можно было заполнить какие-то переменные перед вызовом longjmp и потом их проанализировать.

7. Перегруженные операторы:

Подобны сложным функциям.

a + b, где a и b - типа B это функция от двух аргументов a и b, возвращающая B:

B operator+(B a, B b)

Написать a+b равносильно вызову такой функции: operator+(a,b). Иногда эта технология удобна, а иногда вносит путаницу.

8. Ссылка:

В языке Pascal есть возможность возвращать из функции больше одного параметра. Для этого применялось магическое слово "var". В C для того, чтобы сделать то же самое, приходилось расставлять в тексте символы "*".

Разработчики C++ ввели слово var. А чтобы все это выглядел оригинально, "var" они переименовали в "&" и назвали "ссылкой".

Это вызвало большую путаницу, так как в C уже были понятия "указатель" (звездочка) и "адрес" ( символ &), а понятие "ссылка" звучит тоже как будто добавили слово var.

Использование ссылок намного сокращает текст программы. Но есть и неприятности.

Во-первых, вызов функции, в которой параметр является ссылкой, выглядит так же, как вызов с обычным параметром. В результате "на глаз" незаметно, что параметр может измениться. А в C это заметно по значку &.

Во-вторых, многочисленные звездочки в C напоминают программисту о том, что каждый раз выполняется дополнительная операция * разыменования указателя. Что побуждает сделать разумную оптимизацию.

В C++ эти операции остаются незамеченными.

9. Inline, template и default-аргумент:

Аргумент по-умолчанию - это то, о чем мечтали программисты C: чтобы иногда не надо было при вызове задавать некоторые параметры, которые в этом случае должны иметь некоторое "обычное" значение.

Желание программистов C контролировать типы параметров в define породило в C++ inline-функции.

Такая функция - это обычный define с параметрами, только не надо мучиться с символами "\" и проверяются типы.

Желание узаконить в параметрах define имя типа породило template. Главный плюс template - то, что #define с одинаковыми параметрами породит два одинаковых куска кода. А template в компиляторе скорее всего будет соптимизирован: одинаковые куски кода будут соединены в один. Имеется небольшой контроль типов по сравнению с #define, но не очень удобный.

В то же время template имеют ряд серьезных недостатков.

Первый - ужасный, неудобный синтаксис. Чтобы это ощутить, достаточно попробовать.

Второй недостаток - template остался так же неудобен при работе с отладчиком, как и #define.

Ну и последнее нововведение, продиктованное, видимо, все тем же стремлением избавиться от #define. Это - тип "имя поля" (pointer to member). В C удобно было применять имена полей структур в define. В C++ тоже самое можно сделать с помощью операторов ::*, .* и ->*.

Например:

&Circle::radius - это имя поля radius структуры A, а

A::*radius - соответствующий тип. Такую величину можно передать, как параметр. Фактически речь идет о смещении этого поля относительно начала структуры.

Язык более высокого уровня?

Когда появились все эти нововведения, код упростился, а значит те его свойства, которые раньше были замаскированы лишними символами, стали заметны. В этом и только в этом заключается более высокий уровень C++. Чуть больше абстракции, чуть меньше деталей - можно сосредоточится на более крупных блоках.

Существует мнение, что писать в стиле C на C++ - дурной стиль. Это мнение - всего лишь дань моде.

Если в стиле C++ получается короче, лучше, надежнее, то глупо писать в стиле C. Это так, но верно и обратное!

Простой пример: у вас есть большой массив из 100 тысяч структур Point, который инициализируется один раз (все поля на 0) и много раз копируется в такие же массивы. Писать для элемента такого массива конструктор оказывается накладно. При его создании будет вызван конструктор для каждого элемента. Потом вы создадите массив, куда его надо копировать - и снова вызовы конструкторов. Затем вы выполняете копирование и затираете результаты второй инициализации. Мало того, что 100 тысяч вызовов конструктора просто не сопоставимы с одним вызовом memset, но эта серия вызовов будет повторяться не один раз, а много.

Такие примеры можно привести для каждого нововведения C++. Каждый плюс неизбежно тянет за собой минусы.

Что касается объектно-ориентированного программирования, то на самом деле оно не имеет никакого отношения к разнице между C и C++. Благодаря ряду усовершенствований, код на C++ компактнее и надежнее, чем на C. Часть этих усовершенствований связана с ООП, а часть - нет. Например, аргументы функций по-умолчанию и inline-функции к ООП не имеют никакого отношения. Они имеют отношение к ужесточению контроля типов.

ООП - это просто идея: "в зависимости от данных, выполнить процедуру". А ПОП (процедурно ориентированное программирование) - "в зависимости от процедуры изменить данные".

Совместимость C/C++ .

Нередко программы на C++ содержат фрагменты, написанные на других языках. Аналогично, довольно часто фрагменты кода C++ используются как часть про­грамм, написанных в основном на другом языке. Могут встретиться определенные препятствия на пути подобного «сотрудничества» между фрагментами программ, написанных на различных языках, и даже между кодом, написанным на одном язы­ке, но скомпилированном разными компиляторами.

Чтобы помочь компоновщику применяют в объявлениях ключевое слово extern.

Пример:

Объявляется функция strcpy () стандартной библиотеки С и C++ и указыва­ется, что компоновка должна производиться в соответствии с соглашениями, при­нятыми в С:

extern "С" char* strcpy (char*, const char*);

Добавление extern "С" к большому количеству объявлений может причинять неудоб­ства.

Пример для группы объявлений:

extern "С" {char* strcpy (char*, const char*);

int strcmp (const char*, const char*);

int strlen (const char*);

//...}

Можно воспользоваться для включения целого заголовочного файла С таким образом, чтобы обеспечить использование указанного файла в C++.

Например:

extern "С" { #include <string.h>}

Можно воспользоваться директивами услов­ной компиляции для создания заголовочного файла, общего для С и C++:

# Ifdef _cplusplus

extern "С" {

#endif

char* strcpy (char*, const char*);

int strcmp (const char*, const char*);

int strlen (const char*);

#Ifdef cplusplus }

#endif

Предопределенное имя _cplusplus используется для того, чтобы исключить конст­рукции C++, когда файл используется в качестве заголовочного в С.

Большинство проблем с совместимостью возникает:

1. При попытках обновить программу на С, чтобы сделать ее программой на C++.

2. При попытках перенести написан­ную на C++ программу с одной нестандартной версии C++ на другую.

3. При попытках скомпилировать программу с новейшими особенностями языка на старом компиляторе.

За минимальными исключениями C++ является надмножеством С.

Наиболее значи­тельные различия возникают из-за большей строгости C++ при проверке типов. Хо­рошо написанные программы на С как правило оказываются и программами на C++. Все различия между C++ и С может выявить компилятор.

Программы, являющиеся программами на С и на C++, имеют в обоих языках одинаковый смысл. Исключения сравнительно незначительны:

1. в С размер символьной константы и перечисления равны sizeof(int).

в C++ sizeof('a') равно sizeof(char), и реализациям C++ разрешено выбирать размер, лучше всего подхо­дящий для перечисления .

2. C++ использует для комментариев знак // ;

С не позволяет этого официально (хотя многие реа­лизации С тоже допускают такой комментарий в качестве расширения языка).

Надо так:

/* написанный комментарий */

Несовместимости C/C++, вызывающие большинство реальных проблем, не очень изощренны. Многие из них легко вылавливаются компиляторами.

Различия С и C++:

1. На С большинство функций можно вызывать без предварительного объявления:

main () /* плохой стиль в С. */

{double sq2 = sqrt (2); /* вызов необъявленной функции */

printf {"корень из 2 равен %g \ n", sq2); /* вызов необъявленной функции */}

Полное и последовательное использование объявлений (прототипов) функций, вооб­ще говоря, рекомендуется и для С.

2. В языке С функция, объявленная без описания типов аргумента, может принимать любое число аргументов любого типа. Подобное использование признано в стандарте С устаревшим, но оно встречается:

void f (); /* не упомянуты типы аргументов */

Void g ()

{ f (2); /* плохой стиль в С */ }

3. В С функции можно определять с использованием синтаксиса, который (не обяза­тельно) описывает типы аргументов после перечня аргументов:

void f {а, р, с) char* р, char с, { /*...*/ } /* С, не C++ */

Такие определения надо переписать:

void f (int a, char* р, chare) { /*... */}

4. В С и старых версиях C++, предшествовавших стандарту, спецификатором типа по умолчанию считается int. Например:

const а = 7; /* в С подразумевается тип int. */

5. В C++ гораздо больше ключевых слов, чем в С. Если какое-нибудь из них появится в программе на С как идентификатор, эту программу нужно будет переделать для совместимости с C++:

Ключевые слова C++, которые не являются ключевыми словами С:

and and_eq asm bitand bitor boot
catch class compl const_cast delete dynamic_cast
explicit export false friend inline mutable
namespace new not not_eq operator or
or_eq private protected public reinterpret_cast static_cast
template this throw true try typeid
typename using virtual wchar_t xor xor_eq

6. В С некоторые из ключевых слов C++ являются макросами, определенными в стан­дартных заголовочных файлах:

Ключевые слова C++, являющиеся макросами на С

and and_eq bitand bitor bool compl false  
not not_eq or or_eq true wchar_t xor xor_eq
                 

Это подразумевает, что в С они могут проверяться при помощи #ifdef, переопределяться .

7. В языке С глобальные объекты данных можно объявлять в одной единице трансля­ции несколько раз без спецификатора extern. Если инициализатор встречается только в одном из таких объявлений, объект считается определенным лишь один раз.

Например:

int i; int i; /* определяет или объявляет одну целую переменную i /* не C++ */

В C++ переменная должна быть определена ровно один раз.

8. В C++ класс не может иметь то же имя, что и имя, определенное при помощи typedef, для обозначения другого типа в той же области видимости.

9. В С void* можно использовать в качестве правого операнда в присваивании или при инициализации переменной любого указательного типа; в C++ этого делать нельзя .

Например:

void f {int п)

{int* р = malloc (n*sizeof(int)); /* не C++. память в C++ при помощи new* /}

10. Язык С разрешает переходы goto в обход инициализации; C++ не разрешает.

11. в С имена вложенных структур размещаются в той же области видимости, что и структура, в которую они вложены.

Например:

struct S {

struct Т{ /* ...*/;

//... };

struct Т х; /* в С правильно и означает: S::T х. Не C++ */

В С массив может инициализироваться инициализатором, в котором элементов боль­ше, чем требуется для этого массива. Например:

char v[5] = "Оскар"; /* Допустимо в С, конечный 0 не используется. Не C++ */

12. Неявное преобразование строкового литерала в (неконстантный) char* нежела­тельно. Используйте именованые массивы char или избегайте присваивания строко­вых литералов переменным типа char* .

Приведения типа в стиле С тоже стали нежелательны после введения приведений в новом стиле.

Возможности C++, не обеспечиваемые С:

Особенности, предназначенные прежде всего для удобства записи:

1. комментарии с помощью // ; в настоящее время добавляется к С;

2. поддержка ограниченного набора символов;

3. поддержка расширенного набора символов ; в настоящее время добавляется к С;

4. неконстантные инициализаторы для объектов со статическим хранением;

5. const в константных выражениях;

6. объявления как операторы ;

7. объявления в инициализаторах операторов for

8. объявления в условиях;

9. имена структур не обязательно предварять словом struct .

Особенности, предназначенные прежде всего для усиления системы типов:

1, проверка типа аргумента в функции ; была добавлена в С позже;

2, компоновка, безопасная с точки зрения типов;

3, распределение свободной памяти при помощи new и delete ;

4. const ; было добавлено в С позже;

5, логический тип bool ; частично добавлен позже.

6. новый синтаксис приведения .

Средства, предназначенные для пользовательских типов:

1. классы ;

2. функции-члены и вложенные классы ;

3. конструкторы и деструкторы;

4. производные классы ;

5. виртуальные функции и абстрактные классы ;

6. контроль доступа, открытый/защищенный/закрытый (public/protected/ private) ;

7. дружественные функции ;

8. указатели на члены классов;

9. статические члены ;

10. ключевое слово mutable ;

11. перегрузка операторов ;

12. ссылки .

Наши рекомендации