Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

37.ИНФОРМАТИКА Book си

.pdf
Скачиваний:
37
Добавлен:
23.03.2016
Размер:
1.14 Mб
Скачать

Часть 1. Язык Си

51

 

 

union u { int i; char ch;

long int l;

};

Это объявление задает шаблон объединения. Можно объявить переменные:

union u alfa, beta;

Можно было объявить переменные одновременно с заданием шаблона. В отличие от структуры, для переменной типа union места в памяти выделяется ровно столько, сколько надо элементу объединения, имеющему наибольший размер в байтах. В приведенном выше примере под каждую переменную будет выделено 4 байта памяти. В самом деле, элемент i требует 2 байта, элемент ch — 1 байт, а элемент l — 4 байта. Остальные переменные будут располагаться в том же месте памяти. Синтаксис использования элементов объединения такой же, как и для структуры:

u.ch ='5';

Для объединений также разрешена операция ->, если мы обращаемся к объединению с помощью указателя.

Программа, приведенная ниже, выдает на экран двоичный код ASCII символа, вводимого с клавиатуры:

#include <stdio.h> #include <conio.h> struct byte {

unsigned b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1, b8:1;

};

union bits { char ch;

struct byte bit;

} u; /* Определено объединение */ void decode (union bits b) {

printf ("%c:", b.ch);

if(b.bit.b8) printf("1"); else printf("0"); if(b.bit.b7) printf("1"); else printf("0");

51

52

Си и Си++

if(b.bit.b6) printf("1"); else printf("0"); if(b.bit.b5) printf("1"); else printf("0"); if(b.bit.b4) printf("1"); else printf("0"); if(b.bit.b3) printf("1"); else printf("0"); if(b.bit.b2) printf("1"); else printf("0"); if(b.bit.b1) printf("1"); else printf("0"); printf(" (%d)\n", b.ch);

}

void main () { do{

u.ch=getch(); decode (u);

} while (u.ch != 'q');

}

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

#include <stdio.h> #include <conio.h>

void decode

(char a) {

unsigned

char i;

printf ("%c:",a);

for (i=0x80; i; i>>=1) { /* 0x80= 1000 0000

 

сдвигается вправо, пока i>0 */;

if (a

& i) printf("1"); else printf("0");

}

 

printf("

(%d)\n",a);

}

void main () { char ch; do{

ch=getch(); decode (ch);

} while (ch != 'q');

}

Перечислимый тип

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

52

Часть 1. Язык Си

53

 

 

типа следующая:

enum <имя типа> {<список названий>} [<список переменных>];

Список переменных может быть пустым. Пример определения перечислимого типа и переменной данного типа:

enum seasons { win, spr, sum, aut}; enum seasons s;

Ключом к пониманию сущности перечислимого типа является то, что каждое из имен win, spr, sum и aut представляет собой целую величину. Если эти величины не определены по-другому, то по умолчанию они соответственно равны нулю, единице, двум и трем. Оператор

printf ("%d %d", win, aut);

выдаст на экран числа 0 и 3. Во время объявления типа можно одному или нескольким символам присвоить другие значения, например:

enum value (one=1, two, three, ten=10, thousand=1000, next);

Если теперь напечатать значения

printf ("%d %d %d %d %d\n",

one, two, ten, thousand, next);

то на экране появятся числа 1 2 10 1000 1001, т.е. каждый следующий символ увеличивается на единицу по сравнению с предыдущим, если нет другого присваивания.

С переменными перечислимого типа можно производить следующие операции:

присвоить переменную типа enum другой переменной того же типа;

провести сравнение с целью выяснения равенства или неравенства;

арифметические операции с константами типа enum (например, i=win-aut).

Нельзя использовать арифметические операции и операции ++ и -- для переменных типа enum.

53

54

Си и Си++

 

 

Основная причина использования перечислимого типа — это улучшение читаемости программ.

Переименование типов

Язык Си позволяет дать новое название уже существующим типам данных. Для этого используется ключевое слово typedef. При этом не создается новый тип данных. Например:

typedef char symbol; typedef unsigned u_int; typedef float real;

Достаточно часто используется оператор typedef с применением структур:

typedef struct st { char name[50]; int kurs;

char group[8]; int stip;

} STUDENT;

Теперь для определения переменной можно использовать

struct st Kurs[100];

а можно

STUDENT Kurs[100];

54

Часть 2. Язык Си++

2.1Простейшие расширения Си++

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

Комментарии

Язык Си++ позволяет применять более простой способ записи комментариев. Можно закомментировать конец строки, отделив его двумя символами '/'. Все символы в строке, следующие за этими двумя, транслятор игнорирует.

// Это однострочный комментарий /* Вложенный комментарий /* не допускается */ */

/* Вложенный комментарий // так можно делать */

//Вложенный /* так тоже можно */ делать

//Вложенный комментарий /* А вот так нельзя делать */

Константы

В языке Си именованные константы вводятся с помощью макроподстановок:

#define year 2001

Вязыке Си++ константы описываются следующим образом: const int year=2001;

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

const char *str="asdf";

// Указатель на постоянный объект.

56

Си и Си++

 

 

 

str="abcd";

//

Допустимый оператор.

str[3]="e";

//

Ошибочный оператор.

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

char * const str="asdf";

// Постоянный указатель на объект. str="abcd"; // Ошибочный оператор. str[3]="e"; // Допустимый оператор.

При желании вы можете защитить от изменения и сам объект, и указатель на него:

const char * const str="asdf";

Аналогичным образом можно защитить от изменения и параметры функции:

char *StrCopy(char *p, const char *str);

Упрощение введения новых типов данных

В языке Си для введения нового типа нужно использовать ключевое слово typedef. В языке Си++ новый тип данных вводит в программу и любое описание структуры (struct) или объединения (union). Имя структуры или объединения в языке Си++ можно использовать как имя типа переменной без ключевых слов struct и union, что требуется в языке Си. Например:

struct StPoint { int x,y; }; // Как в Си. StPoint Point; // В Си: struct StPoint Point;

Ввод/вывод

В Си++ обычно используются свои методы ввода/вывода, хотя можно использовать и старые методы языка Си. Стандартный ввод (по умолчанию с клавиатуры) языка Си++ осуществляется объектом cin, а вывод (по умолчанию на экран) — объектом cout. Использовать эти объекты очень легко: достаточно включить стандартный заголовочный файл iostream.h — и можно пользоваться операторами ввода и вывода Си++ << и >> (которые изначально были в Си только операциями побитового сдвига). Данный метод ввода/вывода называется потоковым. Он имеет богатые возможности, но здесь мы ограничимся только рассмотрением измененного примера програм-

56

Часть 2. Язык Си++

57

 

 

мы, находящей корни квадратного уравнения.

#include<iostream.h>

#include<math.h>

#include<conio.h> void main() {

float a,b,c,d; clrscr();

cout << "a="; cin >> a; //printf("a="); scanf("%f",&a);

cout << "b="; cin >> b; //printf("b="); scanf("%f",&b);

cout << "c="; cin >> c; //printf("c="); scanf("%f",&c);

if (a) { d=b*b-4*a*c;

if (d<0) cout << "Решений нет\n"; else {

if (d==0)

cout << "x=" << (b/(2*a)) << endl; else

cout <<"x1="<<((-b-sqrt(d))/(2*a)) <<", x2="<<((-b+sqrt(d))/(2*a))<<endl;

}

} else {

if (b) cout << "x=" << (-c/b) << endl; else if (a) cout << "Решений нет\n";

else cout << "Любое x\n";

}

getch();

}

Видно, что все вызовы функции printf были заменены на cout, а scanf — на cin. Теперь нет необходимости писать %f и подобные конструкции, т.к. объекты cout и cin берут определение передаваемого в них типа на себя. Можно последовательно вводить (cin) или выводить (cout) список переменных (можно разного типа) в одном операторе, для этого перед каждой переменной или константой надо поставить >> или << соответственно. Манипулятор endl переводит на новую строку. Зачастую можем считать, что его выполнение

57

58

Си и Си++

 

 

аналогично выводу символа ‗\n‘.

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

Положение описания переменной

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

#include <iostream.h> void main(void) {

cout << "Введите k: "; int k;

cin >> k;

}

В программах на Си++ довольно часто можно встретить следующую конструкцию:

for (int i=0; i<n; i++) . . .;

Контроль типов

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

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

Вставляемые функции

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

58

Часть 2. Язык Си++

59

 

 

inline int abs(int a) { return a>0 ? a : -a; }

. . .

int r=abs(x2-x1);

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

Следует отметить, что во вставляемую функцию нельзя включать циклы и текст на ассемблере.

Переопределяемые функции

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

//Длина вектора на прямой. float length (float x)

{return fabs(x); }

//Длина вектора на плоскости. float length (float x, float y)

{return sqrt(x*x+y*y); }

//Длина вектора в пространстве.

float length (float x, float y, float z)

{ return sqrt(x*x+y*y+z*z); }

//Длина строки символов

int length (char *s)

{ return strlen(s); }

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

Параметры функции по умолчанию

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

59

60

Си и Си++

 

 

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

Рассмотрим функцию root, вычисляющую выражение n a x b :

double Root(double x,double n,double a,double b){ return pow(a*x+b,1/n);

// pow — возведение в степень

}

Зададим значения аргументов по умолчанию в прототипе функции, написанном до описания самой функции. Однако можно прототип не писать, а значения аргументов по умолчанию написать в описании самой функции. Прототип функции для нашего примера может иметь следующий вид:

double Root (double x, double n=2, double a=1, double b=0);

или просто:

double Root (double, double=2, double=1, double=0);

хотя так труднее читается.

Приведем несколько вариантов вызова данной функции:

// квадратный корень из 1*9+0 (n=2 a=1 b=0)

Root(9);

// =3

//

кубический корень из 1*27+0 (a=1 b=0)

Root(27,3);

//

=3

//

кубический корень из 3*9+0 (b=0)

Root(9,3,3);

//

=3

// кубический корень из 1*20+7 Root(20,3,1,7); // =3

//Однако нельзя написать Root(20,3,,7);

Ссылки на переменные

В большинстве языков программирования параметры передаются

60