Г.Тяпичев «Начальный курс быстрого программирования на СИ++». Приложение 4.

Приложение 4. 1

Некоторые конструкции языков Си и Си ++. 1

Основные понятия языка. 1

Объявления и типы данных. 2

Как вводить и выводить информацию. 2

Форматный вывод данных. 3

Форматный ввод данных. 3

Операторы и выражения. 4

Переменные и константы. 4

Операции языка Си. 4

Преобразование типов. 7

Указатели и операции с ними. 7

Операторы организации циклов. 8

Структурированные типы данных. 9

Функции. 13

Другие возможности языка Си. 14

Приложение 4

Некоторые конструкции языков Си и Си ++

Основные понятия языка

При написании программы применяются определенные символы, составляющие алфавит языка. Алфавит включает латинские прописные и строчные буквы, цифры и специальные знаки. К таким знакам, например, относятся: точка (.), запятая (,), двоеточие (:), точка с запятой (;) и др. Внутреннее представление символов алфавита в персональном компьютере осуществляется на основе определенной системы кодирования. Соответствие между каждым символом и его кодом задается в вид специальной кодовой таблицы. На нее разработан стандарт ASCII и поэтому коды символов называют ASCII.

Различают видимые и управляющие символы. Для представления каждого символа в персональном компьютере используется один байт, поэтому их общее число равно 28= 256. Напомним, что каждый байт состоит из восьми битов.

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

1.     Они должны начинаться с буквы латинского алфавита (a…, z, A…, Z) или с символа подчеркивания (_).

2.     В них могут использоваться буквы латинского алфавита, символ подчеркивания и цифры (0, …., 9). Использование других символов в идентификаторах запрещено.

3.     В языках Си и Си++  буквы нижнего регистра (a, …, z), применяемые в идентификаторах, отличаются от букв верхнего регистра (A, …, Z). Это означает, что следующие идентификаторы считаются разными: name, NaMe и т. п.

4.     Идентификаторы могут иметь любую длину (в Borland C++ оно может устанавливаться при настройке среды и имеет максимальное значение – 32). 

5.     Идентификаторы для новых объектов не должны совпадать с ключевыми словами языка и именами стандартных функций из библиотеки.

В программах на языках Си и Си++ важная роль отводится комментариям. Они повышают наглядность и удобство чтения программ. Комментарии обрамляются символами /* и */. Их можно записывать в любом месте программы.

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

 

Объявления и типы данных

Программы оперируют с различными данными, которые могут быть простыми и структурированными. Простые данные – это целые и вещественные числа, символы и указатели (адреса объектов в памяти). Целые числа не имеют, а вещественные имеют дробную часть. Структурированные данные – это массивы, записи и файлы; они будут рассмотрены позже.

Всякая переменная величина или функция в программе сначала должна быть «объявлена», а затем «описана» и далее может применяться. Объявление переменной или функции необходимо для выделения для нее необходимого количества памяти. Описание устанавливает свойства объекта: его тип (например, целый), размер (например, 4 байт) и т. п.

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

В таблице П4.1 приведены данные по всем типам переменных.

Таблица П4.1

Тип

Размер

Диапазон

char

8

 –128127

signed char

8

–128127

unsigned char

8

0255

int

16

-32768 … 32767

signed int

16

-32768 … 32767

short int

16

-32768 … 32767

signed chort int

16

-32768 … 32767

unsigned int

16

0 … 65535

unsigned short int

16

0 … 65535

long int

32

-2147483648 … 2147483647

signed long int

32

-2147483648 … 2147483647

unsigned long int

32

0 … 4294967295

float

32

3.4е-38 … 3.4е+38

double

64

1.7е-308 … 1.7е+308

long double

80

3.4е-4932 … 1.1е+4932

 

Как вводить и выводить информацию

Самый простой механизм ввода – чтение по одному символу из стандартного входного потока (с клавиатуры) с помощью функции getchar(). Она имеет следующий прототип:

int getchar(void);

       Здесь определен тип единственного аргумента (void) и тип возвращаемого функцией значения (int).

Другая функция – putchar(x) выдает значение переменной x в стандартный выходной поток (на экран дисплея). Функция putchar() имеет прототип:

int putchar(int);

Объявления getchar() и putchar() сделаны в стандартном включаемом файле stdio.h.

Форматный вывод данных.

 Функция printf() обеспечивает форматный вывод. Ее можно записать в следующем формальном виде:

printf («управляющая строка», аргумент _1, аргумент _2, …);

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

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

% <символ преобразования>

На месте <символ преобразования> могут быть записаны:

c – значением аргумента является символ;

d или l—значением аргумента является десятичное целое число;

e – значением аргумента является вещественное десятичное число в экспоненциальной форме вида 1.23е + 2;

E – значением аргумента является вещественное десятичное число в экспоненциальной форме вида 1.23Е + 2;

f – значением аргумента является вещественное десятичное число с плавающей точкой;

g (или G) – используется, как e или f, и исключает вывод незначащих нулей;

o – значением аргумента является восьмеричное целое число;

s – значением аргумента является строка символов (символы строки выводятся до тех пор, пока не встретится символ конца строки или же не будет выведено число символов, заданное точностью);

u – значением аргумента является беззнаковое целое число;

x – значением аргумента является шестнадцатеричное целое число с цифрами 0, ….., 9, a, b, c, d, e, f;

X – значением аргумента является шестнадцатеричное целое число с цифрами 0, …..9, A, B, C, D, E, F;

p – значением аргумента является указатель;

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

Функция printf() также используется для ввода управляющих символов.

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

\a – для кратковременной подачи звукового сигнала;

\b – для перевода курсора влево на одну позицию;

\f – для подачи формата;

\n – для перехода на новую строку;

\r – для возврата каретки;

\t – горизонтальная табуляция;

\v – вертикальная табуляция;

\\ -- вывод символа \;

\¢ -- вывод символа ¢;

\” – вывод символа “;

\? – вывод символа ?.

Форматный ввод данных.

 Функция scanf() обеспечивает форматный ввод. Ее действия похожи на  действия функции printf(), но используются только для ввода.

Операторы и выражения

Переменные и константы.

Все переменные до их использования должны быть объявлены. При этом задается тип, а затем идет список из одной или более переменных этого типа, разделенных запятыми. Например:

int a, b, c, d;

char x, y;

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

Вещественные, например, числа 123.456,  5.61е-8. Они могут снабжаться суффиксом F, например, 123.456F,   5.61e-8F;

Целые, например, число 125;

Длинные целые, в конце записи которых добавляется буква (суффикс) L, например, 361327L;

Беззнаковые, в конце записи которых добавляется буква U, например, 62125U;

Восьмеричные, в которых перед первой значащей цифрой записывается нуль (0), например, 071;

Шестнадцатеричные, в которых перед первой значащей цифрой записывается пара символов нуль-икс (0x), например, 0x1F2;

Символьные – единственный символ, заключенный в одинарные кавычки, например, ¢g¢, ¢2¢, ¢.¢ и т. п. Символы, не имеющие графического представления, можно записывать, используя специальные комбинации, например, \n (для новой строки), \0 (код которого 00000000). Эти комбинации выглядят как два символа, хотя фактически это один символ. Так же можно представить любой двоичный образ одного байта: ¢NNN¢, где NNN – от одной до трех восьмеричных цифр.

Допускается и шестнадцатеричное задание кодов символов, которое представляется в виде ¢\x2B¢, ¢\x36¢ и т. п.

Строковые – последовательность из нуля символов или более, заключенная в двойные кавычки, например: «Это строковая константа». Кавычки не входят в строку, а лишь ограничивают ее. Строка представляет массив из перечисленных элементов, в конце которого помещается байт с символом \0. Таким образом, число байт, необходимых для хранения строки, на единицу превышает число символов между двойными кавычками;

Константное выражение, состоящее из одних констант, которое вычисляется во время трансляции (например: a = 60+30);

Типа long double, в конце записи которых добавляется буква L, например: 1234567.89L.

Операции языка Си

Охарактеризуем основные операции языка Си. Для задания каждой из них используются определенные знаки (см. табл. П4.2) или операторы. Сначала рассмотрим один из них – оператор присваивания (=). Выражение вида

x = y;

присваивает переменной x значение переменной y. Оператор «=» разрешается использовать многократно в одном выражении, например:

x = y = z = 100;

Здесь всем трем переменным (x, y, z) будет присвоено значение 100.

Различают унарные и бинарные операции. У первых из них один операнд, а у вторых – два. Начнем их рассмотрение с операций, отнесенных к одной из следующих традиционных групп:

1.     Арифметические операции.

2.     Логические операции и операции отношения.

3.     Операции с битами.

Арифметические    операции  задаются следующими операторами (см. табл. П4.2).

Таблица П4.2

Знак операции

Назначение операции

Порядок выполнения

( )

Вызов функции

Слева направо

[ ]

Выделение элемента массива

 

.

Выделение элемента записи

 

®

Выделение элемента записи

 

!

Логическое отрицание

Справа налево

~

Поразрядное отрицание

 

-

Изменение знака

 

++

Увеличение на единицу

 

--

Уменьшение на единицу

 

&

Взятие адреса

 

*

Обращение по адресу

 

(тип)

Преобразование типа

 

sizeof

Определение размера в байтах

 

*

Умножение

Слева направо

/

Деление

 

%

Определение остатка от деления

Слева направо

+

Сложение

Слева направо

-

Вычитание

Слева направо

<<

Сдвиг влево

Слева направо

>>

Сдвиг вправо

 

<

Меньше, чем

Слева направо

<=

Меньше или равно

Слева направо

>

Больше, чем

Слева направо

>=

Больше или равно

Слева направо

= =

Равно

Слева направо

!=

Не равно

Слева направо

&    

Поразрядное логическое «И»

Слева направо

^

Поразрядное исключающее «ИЛИ»

Слева направо

|

Поразрядное логическое «ИЛИ»

Слева направо

&

Логическое «И»

Слева направо

||

Логическое «ИЛИ»

Слева направо

?:

Условная (тернарная) операция

Слева направо

=

Присваивание

Слева направо

zn=

Здесь  zn – любая бинарная операция, например a *=b;

Слева направо

,

Операция запятая

Слева направо

Расшифровка записей из предпоследней строки табл. П4.2 дана в таблице П4.3.

Таблица Г.3. Сокращенная форма оператора присваивания

Например, допустимы:

Что эквивалентно:

a += b;

a = a + b;

a –= b;

a = a – b;

a <<= b;

a = a << b;

a  &= b;

a = a & b;

a ^= b;

a = a ^ b;

a |= d;

a = a | b;

 

Логические операции отношения  задаются следующими операторами (см. табл. П4.2). Традиционно эти операции должны давать одно из двух значений: истину и ложь. В языке Си принято следующее правило: истина – это любое ненулевое значение; ложь – это нулевое значение. Выражения, использующие логические операции и операции отношения, возвращают 0 для ложного значения и 1 – для истинного. Ниже приводится таблица истинности для логических операций:

x        y        x&y            x&y           !x

0        0        0               0             1

0        1        0               1             1

1        0        0               1             0

1        1        1               1             1

 

Битовые операции   можно применять к переменным, имеющим типы int, char, а также их варианты (например, long). Их нельзя применять к переменным типам float, double, long (или более сложным типам). Эти операции задаются следующими операторами: ~,  <<,  >>,  &,  ^,  |. Ниже приводится таблица истинности для битовых операций ~, &, |,  ^;

x    y    ~x     x&y      x|y      x ^ y

 0   0      1      0          o          0

0    1      1      0          1          1

1    0      0      0          1          1

1    1      0      1          1          0

Рассмотрим примеры операций сдвига. В выражении

a = b << c;

производится сдвиг значения b влево на бит; в освободившиеся (справа) биты b заносятся нули. Если b = 310 = 00112, c = 2, то после выполнения операции a = 11002 = 1210. Это эквивалентно умножению числа 3 сначала на 2 и потом еще раз на 2. В выражении

a = b >> c;

 

Таблица П4.4. Побитовые логические операции языка Си

Операции

Наименование

Назначение операций

-

Обращение

Формирование обратного кода числа; поразрядно: 1 ® 0, 0 ® 1

<<, >>

Сдвиги

Эквивалентно умножению (делению) на 2n, где n – количество сдвигов

&

И

Поразрядное логическое умножение

^

ИСКЛЮЧАЮЩЕЕ ИЛИ

Сложение разрядов по модулю 2

|

ИЛИ

Поразрядное логическое сложение

 

В языке предусмотрены две нетрадиционные операции инкремента (++) и декремента (--). Они соответственно предназначены для увеличения и уменьшения на единицу значения операнда. Операции ++ и -- можно записывать как перед операндом, так и после него.

Еще одним отличием языка является то, что выражение вида a = a + 5; можно записывать в другой форме: a + = 5;. Вместо знака + можно использовать и символы других бинарных операций (см. табл. П4.3).

Таблица П4.5. Примеры встроенных функций для арифметических типов данных

Обращение

Возвращаемый результат

abs(x)

Модуль аргумента, x -- целое

fabs(x)

Модуль аргумента, x -- вещественное

atan(x)

Арктангенс (радианы)

sin(x)

Синус (угол в радианах)

sinh(x)

Синус гиперболический

cos(x)

Косинус (угол в радианах)

cosh(x)

Косинус гиперболический

tan(x)

Тангенс (угол в радианах)

tanh(x)

Тангенс гиперболический

exp(x)

Экспонентаех

log(x)

Логарифм натуральный

log10(x)

Логарифм десятичный

sgrt(x)

Корень квадратный аргумента

pow(x,y)

Значение x в степени y

fmod(x, y)

Остаток от деления двух чисел: x на y

floor(x)

Ближайшее меньшее целое, <= x

ceil(x)

Ближайшее большее целое, >= x

 

Преобразование типов

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

Если один из операндов в выражении имеет тип long double, то остальные тоже преобразуются к типу long double.

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

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

В противном случае, если один из операндов в выражении имеет тип unsigned long, то остальные тоже преобразуются к типу unsigned long.

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

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

В противном случае все операнды преобразуются к типу int. При этом тип char преобразуется в int со знаком; тип unsigned char в int, у которого старший байт всегда нулевой; тип signed char в int, у которого в знаковый разряд передается знак из char; тип short в int (знаковый или беззнаковый).

Указатели и операции с ними

 Указатели – это переменные, показывающие место или адрес памяти, где расположены другие объекты (переменные, функции и т. п.). Так как указатель содержит адрес некоторого объекта, то через него можно обращаться к этому объекту.

Унарная операция & дает адрес объекта, поэтому оператор y = &x; присваивает адрес x переменной y. Операцию & нельзя применять к константам и выражениям; конструкции вида &(x + 7) или &28 недопустимы.

Унарная операция * воспринимает свой операнд как адрес некоторого объекта и использует этот адрес для выборки содержимого, поэтому оператор z = *y; присваивает z значение переменной, записанной по адресу y. Если y = &x; z = *y; то z = x.

Объекты, состоящие из знака * и адреса (например, *a), необходимо объявлять. Делается это, например, так:

int *a, *b, *c;

char *d;

       Объявление вида char *d; говорит о том, что значение, записанное по адресу d, имеет тип char.

Указатели могут встречаться и в выражениях. Если y – указатель на целое, т.е. имело место объявление int *y; то *y может появиться там же, где и любая другая переменная, не являющаяся указателем.

Операторы организации циклов

Циклы организуются, чтобы повторить некоторую инструкцию или группу инструкций определенное число раз. В языке Си три оператора цикла:

q      for, while  и  do-while.

Первый из них формально записывается в виде

for(выражение _1; выражение _2; выражение _3){тело цикла};

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

Выражение 1 присваивает начальное значение управляющей переменной, выражение 3 изменяет его на каждом шаге, а выражение 2 – проверяет, не достигло ли оно граничного значения, устанавливающего необходимость выхода из цикла. Любое из трех выражений в цикле for может отсутствовать, однако точка с запятой должна оставаться. Таким образом,

for(;;) {…}

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

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

q      while (выражение) {тело цикла};

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

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

q      Do тело цикла while(выражение);

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

Допускается вложенность одних циклов в другие, т. е. в теле любого цикла могут появляться операторы for, while и do-while.

В теле цикла могут использоваться новые операторы: break и continue. Первый из них обеспечивает немедленный выход из цикла. Оператор continue вызывает прекращение очередной и начало следующей итерации.

Операторы организации условных и безусловных переходов

 Для организации условных и безусловных переходов в программе на языке Си используются операторы: ifelse, switch и goto. Первый из них записывается в следующем формальном виде:

if(проверка условия) {инструкции 1;} else {инструкция 2;}

Если условие в скобках принимает истинное значение, то выполняется инструкция 1, а если ложное – то инструкция 2. Если вместо одной необходимо использовать несколько инструкций, то они заключаются в фигурные скобки. В операторе if слово else может и отсутствовать. В этом случае, если условие в скобках принимает истинное значение, то выполняется инструкция _1, а если ложное, то инструкция _1 пропускается. Таким образом, инструкция _2 будет выполнена всегда.

Оператор switch позволяет выбрать одну из нескольких альтернатив. Он записывается в следующем формальном виде:

switch (выражение)

{case константа 1: вариант 1; break;

. .  .   .  .  .  .  .  .  .  .

case константа n-1: вариант n-1; break;

default: вариант n;}

Здесь вычисляется значение целого выражения в скобках (его иногда называют селектором) и оно сравнивается со всеми константами (константными выражениями). Все константы должны быть различными. При совпадении выполняется соответствующий вариант (одна или несколько инструкций). Вариант с ключевым словом default реализуется, если ни один другой не подошел (слово default может и отсутствовать). Если default отсутствует, а все результаты сравнения отрицательны, то ни один вариант не выполняется. Для прекращения последующих проверок после успешного выбора некоторого варианта используется оператор break, обеспечивающий немедленный выход из переключателя switch.

Допускаются вложенные конструкции switch.

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

q      goto метка;

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

Структурированные типы данных

Массивы.

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

Массив состоит из многих элементов одного и того же типа. Ко всему массиву целиком можно обращаться по имени. Кроме того, можно выбирать любой элемент массива. Для этого необходимо задать индекс, который указывает его относительную позицию. Число элементов массива начинается при его объявлении и в дальнейшем не меняется. Если массив объявлен, то к любому его элементу можно обратиться следующим образом: указать имя массива и индекс элемента в квадратных скобках. Массивы объявляются так же, как и переменные:

int a[100];

char b[30];

float c[42];

В первой строке объявлен массив a из 100 элементов целого типа: a[0], a[1], …a[99] (индексация всегда начинается с нуля). Во второй строке элементы массива b имеют тип char, в в третьей –float.

Двумерный массив представляется как одномерный, элементы которого тоже массивы. Например, объявление char a[10][20]; задает такой массив. По аналогии можно установить и большее число измерений. Элементы двумерного массива хранятся по строкам, т. е. если проходить по ним в порядке их расположения в памяти, то быстрее всего изменяется самый правый индекс. Например, обращение к девятому элементу пятой строки запишется так: a[5][9]. Пусть задано объявление:

В языке Си существует «сильная» взаимосвязь между указателями и массивами. Любое действие, которое достигается индексированием массива, можно выполнить и с помощью указателей, причем последний вариант будет быстрее. Объявление

int a[5];

определяет массив из пяти элементов a[0], a[1], a[2], a[3], a[4].

Строки символов

 Язык Си не поддерживает отдельный строковый тип данных, но он позволяет определить строки двумя различными способами. В первом используется массив символов, а во втором – указатель на первый символ массива. Объявление char a[10]; указывает компилятору на необходимость резервирования места для максимум 10 символов. Константа a содержит адрес ячейки памяти, в которой помещено значение первого из десяти объектов типа char.

Второй способ определения строки – это использование указателя на символ. Объявление char *b; задает переменную b, которая может содержать адрес некоторого объекта. Однако в данном случае компилятор не резервирует место для хранения символов и не инициализирует переменную b конкретным значением.

Тип строк AnsiString

В языке C++ и в C++ Builder задействован тип строк AnsiString, который реализован как класс, объединенный в файле vel/dstring.h. Это строки с нулевым символом в конце. При объявлении переменные типа AnsiString инициализируются пустыми строками.

Для AnsiString определены операции отношения –=, !=, >, <=, ,=. Сравнение производится с учетом регистра.

Для AnsiString определены операции присваивания =, += и операция склеивания строк (конкатенации) +. Определена также операция индексации [ ]. Индексы начинаются с 1. Например, если S1= «Привет», то S1[1] будет ¢П¢, S1[2] будет ¢р¢ и т.д.

Класс AnsiString имеет множество методов, подробно рассмотренных в [1]. Не останавливаясь сейчас на их перечислении. Расмотрим только некоторые примеры применения типа AnsiString.

Тип AnsiString используется для ряда свойств компонентов C++ Builder. Например, для таких, как свойства Text окон редактирования, свойства Caption меток и разделов меню и т. д.

Рассмотрим некоторые примеры работы с AnsiString. Следующий оператор демонстрирует конкатенацию (склеивание) двух строк:

Label1->Caption = Edit1->Text + ¢  ¢ + Edit2->Text;

В данном случае в свойстве Label1->Caption отображается текст, введенный пользователем в окне редактирования Edit1, затем записывается символ пробела, а затем – текст, введенный в окне редактирования Edit2.

Сравнение данного кода с приведенным в предыдущем разделе для типов строк (char*), как мне кажется, показывает большую прозрачность действий со строками AnsiString.

Полный перечень этих методов вы найдете в [1].

В заключение отметим только метод, позволяющий переходить от типа AnsiString к типу (char*). Несмотря на то, что применение AnsiString практически всегда удобнее (char*), такие переходы приходится делать при передаче параметров в некоторые функции, требующие тип параметра (char*).

Преобразование строки AnsiString в строку (char*) осуществляется функцией c_str() без параметров, возвращающей строку с нулевым символом в конце, содержащую текст той строки AnsiString, к которой она применена. Например, если вы имеете строки S1 и S2 типа AnsiString, которые хотите передать в функцию типа (char*), то запись должна выглядеть следующим образом:

q       для строки S1:   S1. c str();

q      для строки S2:    S2.c_str();

Возможно и обратное преобразование строки (char*) в строку AnsiString. Для этого используется функция

AnsiString(char*S)

Которая возвращает строку типа AnsiString, содержащую текст, записанной в строке S, являющейся аргументом функции. 

 

Структуры

Структура – это объединение одного или более объектов (переменных, массивов, указателей, других структур и т. п. ). Как и массив, она представляет собой совокупность данных. Отличием является то, что к ее элементам необходимо обращаться по имени и что различные элементы структуры не обязательно должны принадлежать одному типу.

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

Struct тип (тип элемента 1 имя элемента 1;

.  .  .  .  .  .  .  .  .  .

Тип элемента n имя элемента n;);

Именем элемента может быть любой идентификатор. Как и выше, в одной строке можно записывать через запятую несколько идентификаторов одного типа. Рассмотрим пример:

Struct date {int day;

             Int month;

             Int year;};

Следом за фигурной скобкой, заканчивающей список элементов, могут записываться переменные данного типа, например: struct date {…} a, b, c; (при этом выделяется соответствующая память). Описание без последующего списка не выделяет никакой памяти; оно просто задает форму структуры. Введенное имя типа позже можно использовать для объявления структуры, например: struct days;. Теперь переменная days имеет тип date. При необходимости структуры можно инициализировать, помещая за объявлением список начальных значений элементов.

Оператор typedef

В языке Си введено специальное средство, позволяющее назначать имена новым типам данных. Таким средством является оператор typedef. Он записывается в следующем виде:

typedef тип имя;

Здесь «тип» – любой разрешенный тип данных и «имя» – любой разрешенный идентификатор.

Рассмотрим пример:

typedef int INTEGER;

После этого можно при объявлении переменных вместо int писать INTEGER:

INTEGER a, b;

Битовые поля

 Особую разновидность структур представляют поля. Поле – это последовательность соседних битов внутри одного целого значения. Оно может иметь тип signed int либо unsigned int и занимать от 1 до 16 битов. Поля размещаются в машинном слове в направлении от младших к старшим разрядам. Например, структура:

struct prym {int ×a:2; unsigned b:3; int:5;

int c:1; unsigned d:5;} I, j;

Обеспечивает размещение данных в двух байтах (в одном слове). Если бы последнее поле было задано так: unsigned d:6;, то оно размещалось бы не в первом слове, а в разрядах 0 ¸ 5 второго слова.

В полях типа signed крайний левый бит является знаковым. Например, такое поле шириной 1 бит может только хранить значения – 1 и 0, так как любая ненулевая величина будет интерпретироваться как – 1.

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

Смеси

 Смесь – это некоторая переменная, которая может хранить (в разное время) объекты различного типа и размера. В результате появляется возможность работы в одной и той же области памяти с данными различного вида. Для описания смеси используется ключевое слово union, а соответствующий синтаксис аналогичен записям. Пусть задано объявление:

union r (int ir, float fr, char cr;) z;

Здесь ir имеет размер 2 байт, fr – 4 байт и cr – 1 байт. Переменная z будет достаточно велика, чтобы сохранять самый большой из трех приведенных типов. Таким образом, размер z будет 4 байт. В один и тот же момент времени z может иметь значение только одной из указанных  (ir, fr, cr).

Файлы

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

Файловая система позволяет преобразовывать файлы в единое абстрактное логическое устройство, называемое потоком. Существует два типа потоков: текстовые и двоичные.

Прежде чем читать или записывать информацию в файл, он должен быть открыт. Это можно сделать с помощью библиотечной функции fopen. Она берет внешнее представление файла (например, C:\MY_FILE.TXT) и связывает его с внутренним логическим именем, которое используется далее в программах. Логическое имя – это указатель на требуемый файл. Его необходимо объявлять; делается это, например, так:

FILE *lst;

Здесь FILE – имя типа, описанное в стандартном определении stdio.h, lst – указатель на файл. Обращение к функции fopen в программе производится выражением:

lst = fopen (имя файла, вид использования файла);

Вид использования файла может иметь вид:

q      r – открыть существующий файл для чтения;

q       создать новый файл для записи  (если файл с указанным именем существует, то он будет переписан);

q      a—дополнить файл (открыть существующий файл для записи информации, начиная с конца файла, либо создать файл, если он не существует);

q      rb – открыть двоичный файл для чтения;

q      wb – создать двоичный файл для записи;

q      ab – дополнить двоичный файл;

q      rt – открыть текстовый файл для чтения;

q      wt – создать текстовый файл для записи;

q      at  дополнить текстовый файл;

q      rt  открыть существующий файл для записи и чтения;

q      w+ -- создать новый файл для записи и чтения;

q      a+ -- дополнить или создать файл с возможностью записи и чтения;

q      r + b – открыть двоичный файл для записи и чтения;

q      w +  создать двоичный файл для записи и чтения;

q      a + – дополнить двоичный файл с предоставлением возможности записи и чтения.

После окончания работы с файлом он должен быть закрыт. Это делается с помощью библиотечной функции fclose(). Она имеет следующий прототип:

int fclose(FILE *lst);

При успешном завершении функции fclose() возвращает значение нуль.

Любое другое значение говорит об ошибке.

Рассмотрим другие библиотечные функции, используемые для работы с файлами (все они описаны в файле stdio.h):

1.        Функция putc() записывает символ в файл и имеет следующий прототип:  int putc(int c, FILE *lst);  здесь lst—указатель на файл, возвращенный функцией fopen(), c  символ для записи (переменная c имеет тип int, но используется только младший байт). При успешном завершении putc() возвращает записанный символ, в противном случае возвращается константа EOF. Она определена в файле stdio.h и имеет значение – 1.

2.        Функция getc() читает символ из файла и имеет следующий прототип:  int getc (FILE *lst);  здесь lst  указатель на файл, возвращенный функцией fopen(). Эта функция возвращает прочитанный символ. Соответствующее значение определяется типом int, но старший байт равен нулю. Если достигнут конец файла, то getc возвращает значение EOF.

3.        Функция feof() определяет конец файла при чтении двоичных данных и имеет следующий прототип: int feof (FILE *lst);  здесь lst – указатель на файл, возвращенный функцией fopen(). При достижении конца файла возвращается ненулевое значение, в противном случае возвращается 0.

4.        Функция fputs() записывает строку символов в файл. Она отличается от функции puts только тем, что в качестве второго параметра должен быть записан указатель на переменную файлового типа. Рассмотрим пример: fpyts (“Example”, lst); При возникновении ошибки возвращается значение EOF.

5.        Функция fgets() читает строку символов из файла. Она отличается от функции gets только тем, что в качестве второго параметра должен быть записан указатель на переменную файлового типа. Рассмотрим пример: fgets (str, lst); Функция возвращает указатель на строку при успешном завершении и константу NULL в случае ошибки либо достижения конца файла.

6.        Функция fprintf() выполняет те же действия, что и функция printf(), но работает с файлом. Ее отличием является то, что в качестве первого параметра задается указатель на переменную файлового типа. Рассмотрим пример: fscanf (lst, ”%x”, a).

7.        Функция fscanf() выполняет те же действия, что и функция scanf(), но работает с файлом. Ее отличием является то, что в качестве первого параметра задается указатель на переменную файлового типа. Рассмотрим пример: fscant (lst, “%”, &a);. При достижении конца файла возвращается значение EOF.

8.        Функция fseek() позволяет выполнять чтение и запись с произвольным доступом и имеет следующий прототип:  int fseek (FILE *lst, long count, int access); Здесь lst – указатель на файл, возвращенный функцией fopen(), count – номер байта относительно заданной начальной позиции, начиная с которого будет выполняться операция, access – способ задания начальной позиции. Переменная access может принимать следующие значения: 0 – начальная позиция задана в начале файла; 1 – начальная позиция считается текущей; 2 – начальная позиция задана в конце файла. При успешном завершении возвращается нуль, при ошибке – ненулевое значение.

9.        Функция ferror() позволяет проверить правильность выполнения последней операции при работе с файлом и имеет следующий прототип: int ferror (char *file _name); В случае ошибки возвращается ненулевое значение, в противном случае возвращается нуль.

10.     Функция remove() удаляет файл и имеет следующий прототип: int remore (char *file _name); Здесь file _name – указатель на строку со спецификацией файла. При успешном завершении возвращается нуль, в противном случае возвращается ненулевое значение.

11.     Функция rewind() устанавливает указатель текущей позиции в начало файла и имеет следующий прототип: void rewind (FILE *lst); В языке Си открываются пять стандартных файлов со следующими логическими именами: stdin  для ввода данных из стандартного потока (по умолчанию с клавиатуры); stdout – для вывода данных в стандартный выходной поток (по умолчанию на экран дисплея; stderr  файл для вывола сообщений об ошибках (всегда связан с экраном дисплея); stdprn – для вывола данных на принтер; stdaus – для ввода и вывода данных в коммуникационный канал.

Перечисляемый тип данных

Перечисляемый тип данных предназначен для описания объектов из некоторого заданного множества. Он определяется ключевым словом enum. Рассмотрим пример: enum seasins a, b, c;

Каждая из них (a, b, c) может принимать одно из четырех значений: spring, summer, autumn и winter. Эти переменные можно было объявить сразу при описании типа: enum seasons {spring, summer, autumn, winter} a, b, c;

Функции

Общие сведения

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

Return выражение;

Таких операторов в подпрограмме может быть несколько и тогда они фиксируют соответствующие точки выхода. Вызвавшая функция может при необходимости игнорировать возвращаемое значение. После слова return можно ничего не записывать; в этом случае вызвавшей функции никакого значения не передается. Управление передается вызвавшей функции и в случае выхода «по концу» (последняя закрывающаяся фигурная скобка).

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

A = fun (b, c);

Здесь b и  аргументы, значения которых передаются в вызываемую подпрограмму. Если описание функции начинается так:

void fun (int b, int v)

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

void fun (int I, int j)

То вместо имени b в вызвавшей функции для того же аргумента в функции fun будет использовано имя I, в вместо  cj. Пусть обращение к функции имеет вид

a = fun (&b, &c);

Здесь подпрограмме передаются адреса передаваемой переменной b и c. Поэтому прототип функции должен быть, например, таким:

void fun (int *k, int *c)

Теперь k получает адрес передаваемой переменной b, а c—адрес передаваемой переменной c. В результате в вызвавшей программе  это переменная целого типа, а в вызванной программе c – это указатель на переменную целого типа. Если в вызове записаны те же имена, что и в списке параметров, но они записаны в другом порядке, то все равно устанавливается соответствие между 1-ым именем в списке и 1-м именем в вызове.

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

 

Библиотечные функции

В системах программирования подпрограммы для решения часто встречающихся задач объединяются в библиотеки. К числу таких задач относятся вычисление математических функций, ввод-вывод данных, обработка строк, взаимодействие со средствами операционной системы и т. п. Использование библиотечных подпрограмм избавляет пользователя от необходимости разработки соответствующих средств и предоставляет ему дополнительный сервис.

Таблица П4.6. Примеры встроенных функций с символами и строками

Вызов

Назначение функции

getchar()

Ввод одного символа с клавиатуры

putchar()

Вывод одного символа на экран

gets()

Ввод строки с клавиатуры до символа \n

puts()

Вывод строки на экран

strlen(st)

Определяет длину строки st

strcat(st1, st2)

Объединяет 2 строки в одну строку (STRing conCATention)

strcmp(st1, st2)

Сравнение двух строк (STRing CoMParison)

strcpy(copy, orig)

Копирование одной строки (orig) в другую (copy)

 

 

Включенные в библиотеки функции на языке Си поставляются вместе с системой программирования. Их объявления даны в файлах *.h. Поэтому в начале программы с библиотечными функциями должны встречаться строки вида:

#include <включенный_файл_типа_h>

Существуют также средства для расширения и создания новых библиотек с программами пользователя.

Другие возможности языка Си

Препроцессор

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

Директива:

#define идентификатор подстановка

вызывает замену в последующем тексте названного идентификатора на текста подстановка (обратите внимание на отсутствие точки с запятой в конце этой команды). Если директива имеет вид:

# define идентификатор (идентификатор, …, идентификатор) подстановка,

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

Опишем другие директивы препроцессора. Первая из них include уже встречалась ранее. Ее можно использовать в двух формах:

#include «имя файла»

#include <имя файла>

Действие обеих команд сводится к включению в программу файлов с указанным именем. Первая из них загружает файл из текущего либо заданного в качестве префикса директория. Вторая команда осуществляет поиск файла в стандартных местах, определенных в системе программирования (см. команду Directories меню Options Borland C++). Если файл, записанный в двойных кавычках, не найден в указанном директории, то поиск будет продолжен в поддиректориях, заданных для команды #include < …>. Директива #error записывается в следующей форме:

# error сообщение _об _ошибке

Если она встречается в тексте программы, то компиляция прекращается и на экран дисплея выводится сообщение об ошибке. Эта команда в основном применяется на этапе отладки. Заметим, что сообщение об ошибке не надо заключать в двойные кавычки.

Следующая группа директив позволяет избирательно компилировать части программы. Этот процесс называется условной компиляцией. В нее входят директивы #if, #else, #elif, #endif, #ifdef, #ifndef. Основная форма записи команды #if представляется в виде

#if константное_выражение

… последовательность_инструкций …

#endif

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

Действие директивы #else подобно действию команды else в языке Си, например:

#if константное_выражение

… последовательность_инструкций_1 …

#else

… последовательность_инструкций_2 …

#endif

Здесь если константное выражение истинно, то выполняется последовательность инструкций 1, а если ложно – последовательность инструкций 2.

Директива #elif означает действие типа «else if». Основная форма ее использования представляется в виде

#if константное_выражение

… последовательность_инструкций …

#elif константное_выражение_1

… последовательность_инструкций_1 …

………………….

#elif константное_выражение_n

… последовательность_инструкций_n

#endif

Эта форма подобна конструкции языка Си вида: ifelse ifelse  if

Директива

#ifdef идентификатор

устанавливает, определен ли в данный момент указанный идентификатор, т. е. входил ли он в команду вида # define.

Строка вида

# ifndef идентификатор

проверяет, не определен ли в данный момент указанный идентификатор. За любой из этих команд может следовать произвольное число строк текста, возможно, содержащих инструкцию #else (# elif использовать нельзя) и заканчивающихся строкой #endif. Если проверяемое условие истинно, то игнорируются все строки между #else и #endif, а если ложно, то строки между проверкой и #else (если слово #else нет, то #endif). Приведенные инструкции (#if, #ifdef, #ifndef) могут «вкладываться одна в другую.

 

Для получения большего количества информации обращайтесь к учебникам по C/C++.