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. ссылки .