Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
otvety_na_voprosy.docx
Скачиваний:
6
Добавлен:
05.06.2015
Размер:
121.33 Кб
Скачать
  1. Определение алгоритма

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

Как правило, для данного алгоритма можно выделить 7 характеризующих его параметров:

  1. совокупность возможных исходных данных;

  2. совокупность возможных результатов;

  3. совокупность возможных промежуточных результатов;

  4. правило начала;

  5. правило непосредственной переработки;

  6. правило окончания;

  7. правило извлечения результата.

  8. Классификация языков

  1. Классификация языков

Высокий уровень: Ада, Модула-2, Паскаль, COBOL, Фортран, Бейсик.

Средний уровень: С++, С, FORTH, Макро Ассемблер.

Низкий уровень: Ассемблер.

Структурированные: Паскаль, Ада, С++, С, Модула-2.

Неструктурированные: Фортран, Бейсик, cobol.

  1. Модификаторы доступа

С имеет 2 типа модификаторов, которые используются для контроля за способом доступа или модификации переменных. Эти модификаторы называются const и volatile.

Переменные типа constне могут изменяться операторами программы. Например:

constinta;

создаст целочисленную переменную, называемую а, которая не может быть модифицирована в программе. Она может использоваться в других типах выражений. Переменная с модификатором const получает своё значение или при инициализации, или каким-либо аппаратно-зависимым способом. Например, нижеприведённая строка присваивает count значение 100:

constintcount= 100;

Помимо инициализации константа не может быть модифицирована программой.

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

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

const volatile unsigned char *port = 0x30;

  1. Объявление переменных

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

тип список_переменных;

  1. Локальные переменные

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

  1. Формальные параметры

/* возвращает 1, если с является частью строки s; в противном случае – 0 */

int is_in(char *s, char c)

{

while(*s)

if(*s==c) return1;

else s++;

return 0;

}

Тип данных

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

char

int

long int

short int

float

double

‘a’ ‘\n’ ‘9’

1 123 21000 -234

35000L -34L

10 -12 90

123.23F 4.34e-3F

123.23 12312.333 -0.987654

  1. Глобальные переменные

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

В следующей программе переменная count объявлена вне функций. Общепринятым является объявление глобальных переменных в начале программы.

#include<stdio.h>

void func1(void), func2(void);

int count;

int main(void)

{

count = 100;

func1();

return 0;

}

void func1(void)

{

func2();

printf(“count is %d”, count); /* выведет 100 */

}

void func2(void)

{

int count;

for(count=1; count<10; count++)

putchar(‘ ‘);

}

Хотя ни main(), ни func1() не объявляют переменную count, но они оба могут её использовать, func2() объявляет локальную переменную count. Когда func2() обращается к count, она обращается только к локальной переменной, а не к глобальной.

  1. Спецификаторы хранения

Имеется 4 спецификатора хранения, поддерживаемые в С. Это extern,static,register,auto.

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

спецификатор_хранения тип имя_переменной

extern

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

Файл 1

Файл 2

int x, y;

char ch;

int main(void)

{

}

void func1(void)

{

x = 23;

}

extern int x, y;

extern char ch;

void func22(void)

{

x = y/10;

}

void func23(void)

{

y = 10;

}

Имеется другой вариант использования extern. Когда используется глобальная переменная внутри функции, находящейся в том же файле, где происходит объявление глобальной переменной, то можно объявлять её как extern.

static

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

Статические локальные переменные

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

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

int series(void)

{

static int series_num;

series_num = series_num+23;

return(series_num);

}

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

Статические глобальные переменные

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

auto

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

register

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

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

  1. Оператор присваивания

Общий вид оператора присваивания следующий:

имя_переменной = выражение;

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

Многочисленное присваивание

С позволяет присваивать нескольким переменным одни и те же значения путём использования многочисленных присваиваний в одном операторе. Например, данный фрагмент программы присваивает переменным x, y, z значение 0:

x=y=z= 0;

В профессиональных программах переменным часто присваиваются стандартные значения с использованием данного метода.

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

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

  1. Инициализация переменных

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

тип имя_переменной = константа;

Ниже приведено несколько примеров

char ch = ‘a’;

int first = 0;

floatbalance= 123.23;

Глобальные и статические глобальные переменные инициализируются только при запуске программы. Локальные переменные инициализируются каждый раз при входе в блок, где они были объявлены. Статические локальные переменные инициализируются только один раз, а не каждый раз при входе в блок. Глобальные и статические локальные переменные инициализируются 0, если не указано инициализационное значение. Неинициализированные нестатические локальные и регистровые переменные будут иметь неопределённое значение.

  1. Константы

Cподдерживает ещё один тип констант в дополнение к предопределённым типам данных. Это строковые константы. Все строковые константы заключаются в двойные кавычки, например: “thisisatest”. Не следует путать строковые константы с символами. Одиночный символ заключается в одинарные кавычки, как например: 'a'. Строки мы рассмотрим позже.

Символьные константы с обратным слэшем

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

Код

Значение

\b

Забой

\f

Перевод формата

\n

Новая строка

\r

Возврат каретки

\t

Горизонтальная табуляция

\”

Двойная кавычка

\’

Одинарная кавычка

\0

Нулевой символ

\\

Обратный слэш

\v

Вертикальная табуляция

\a

Звонок

\N

Восьмеричная константа (N – значение)

\xN

Шестнадцатеричная константа (N – значение)

Символы с обратным слэшем следует использовать таким же образом, как и обычные символы.

  1. Операторы

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

Арифметические операторы

Приоритет выполнения арифметических операций следующий:

высший +(унарный плюс) – (унарный минус) ++ * / %

низший + (бинарные операторы)

Операторы, обладающие одним приоритетом, вычисляются компилятором слева направо. Круглые скобки в С устанавливают наивысший приоритет операций.

Операторы отношения и логические операторы

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

Операция

Значение

Пример

!

Логическое НЕ

!EOF

= =

Равно

value==0

!=

Не равно

value!=0

<

Меньше

i<count

>

Больше

i>count

> =

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

i>=count

< =

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

i<=count

||

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

!a || b

&&

Логическое И

a>8 && c<5

Таблица истинности для логических операторов образована с использованием на входах 1 и 0:

P

q

p&&q

p||q

!p

0

0

0

0

1

0

1

0

1

1

1

0

0

1

0

1

1

1

1

0

Как операторы отношения, так и логические операторы имеют более низкий приоритет по сравнению с арифметическими операторами. Это означает, что выражение 10 > 1 + 12 вычисляется как 10 > (1 + 12). Результатом будет ложь.

Ниже показаны приоритеты выполнения операторов отношения и логических операторов

высший !

> >= < <=

== !=

&&

низший ||

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

!1 && 0

даст в результате 0, поскольку ! вычисляется первым, а затем вычисляется &&. Если расставить скобки следующим образом:

!(1 && 0)

то получится истина.

Надо помнить, что все выражения с операторами отношения и логическими операторами дают в результате 0 или 1. Поэтому следующий фрагмент программы не только корректен, но и печатает число 1:

intx;

x=100;

printf(“%d”,x> 1);Битовые операторы

С поддерживает все существующие битовые операторы. Битовые операции – это считывание, установка или сдвиг битов в байте или слове, которые соответствуют стандартным типам языка С char и int. Битовые операции не могут использоваться с float, double,longdouble,voidи другими сложными типами.

Оператор

Действие

&

И

|

ИЛИ

^

Исключающее ИЛИ

~

Дополнение

>>

Сдвиг вправо

<<

Сдвиг влево

Битовые операторы И, ИЛИ, НЕ используют ту же таблицу истинности, что и их логические эквиваленты, за тем исключением, что они работают побитно. Исключающее ИЛИ имеет следующую таблицу истинности:

pqp^q

0 0 0

0 1 1

1 0 1

1 1 0

Битовое И чаще всего используется для обнуления битов. То есть любой бит установленный в 0, вызывает установку соответствующего бита в другом операнде в также в 0. Например, следующая функция читает символы из порта модема, используя функцию read_modem(), и сбрасывает бит чётности в 0.

char get_char_from_modem(void)

{

char ch;

ch = read_modem();

return(ch& 127);

}

Чётность отображается старшим битом, который устанавливается в 0 с помощью битового И, поскольку 7 младших бит равны 1.

  1. Оператор ?

С имеет очень мощный оператор, который можно использовать вместо if-else. Оператор ? имеет следующий вид:

выражение1 ? выражение2 : выражение3

Оператор ? работает следующим образом: вычисляется выражение1,если оно истинно, то вычисляетсявыражение2и всё выражение получает это значение, если ложно, то вычисляетсявыражение 3и всё выражение получает это значение. Например:

x= 10;

y=x>9 ? 100 : 200;

В данном примере y получает значение 100. Если бы х было меньше либо равно 9, то у получила бы значение 200.

  1. Операторы указания & и *

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

  1. Предоставляют быстрое обращение к элементам массива.

  2. Позволяют функциям модифицировать передаваемые параметры.

  3. Поддерживают динамические структуры данных.

Первый оператор - &.Это унарный оператор, возвращающий адрес операнда в памяти. Например:

m= &count;

помещает в mадрес переменнойcount.

Второй оператор – это *,дополняющая &. Это унарный оператор, возвращающий значение переменной по указанному адресу. Например:

q= *m;

Переменные, содержащие адреса или указатели, должны объявляться путём помещения * перед именем переменной. Например, для объявления указателя chна символ, следует написать

char*ch;

Здесь ch это не символ, а указатель на символ. Тип данных, на который указывает указатель, называется базовым типом указателя.

Ниже операторы * и & используются для занесения числа 10 в переменную target:

#include <stdio.h>

int main(void)

{

int target, source;

int *m;

source = 10;

m = &source;

target = *m;

printf(“%d”, target);

return 0

}

  1. Оператор sizeof

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

float f;

printf(“%f ”, sizeof f);

printf(“%d”, sizeof(int));

выдаёт 4 4. (Предполагается использование 32-битных целых).

  1. Операторы выбора

С поддерживает два типа оператора выбора: if и switch. Кроме того, оператор ? является иногда альтернативой if.

Условный оператор if

Синтаксис полнойформы условного оператора:

if ( логическое выражение ) оператор1;

else оператор2;

Если логическое выражение истинно, т.е. не равно нулю, то выполняется оператор1, иначе выполняется оператор2.

Синтаксис сокращеннойформы условного оператора:

if ( логическое выражение ) оператор;

Оператор выполняется только в том случае, если логическое выражение не равно нулю, т.е. истинно.

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

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

Список операторов, заключенный в фигурные скобки, называется блоком. Лесенка if-else-if

Типичной программной конструкцией является лесенка if-else-if. Она выглядит следующим образом:

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

оператор;

else if(выражение)

оператор;

else if(выражение)

оператор;

else

оператор;

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

switch

Синтаксис оператора switch:

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

{

case константное выражение: оператор или группа операторов;

break;

case константное выражение: оператор или группа операторов;

break;

case константное выражение: оператор или группа операторов;

break;

. . . . . . . .

defaulf: оператор или группа операторов;

};

Результат вычисленного выражения сравнивается с каждым из константных выражений. Если находится совпадение, то управление передается оператору, связанному с данным case. Исполнение продолжается до конца тела оператора switch или пока не встретится оператор break, который передает управление из тела switch оператору, следующему за switch. Оператор или группа операторов, стоящий после default, выполняется, если выражение не соответствует ни одному из константных выражений в case.

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

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

{

case константное выражение:

case константное выражение:

case константное выражение: оператор или группа операторов;

break;

};

  1. Оператор «запятая»

Оператор «запятая» используется для связки нескольких выражений. Левая сторона оператора «запятая» вычисляется как не выдающая значения. Значение выражения, находящегося с правой стороны, станет значением разделенного запятыми выражения. Например:

x=(y=3,y+1);

Сначала присваивается 3 переменной y, а затем 4 переменной x. Скобки нужны, поскольку оператор «запятая» имеет более низкий приоритет, чем оператор присваивания.

  1. Выражения

Выражения в С – это любая допустимая комбинация операторов, констант и переменных.

  1. Преобразования типов в выражениях.

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

1. Все переменные типа char short int преобразуются к типу int. Все переменные типа float – к типу double.

2. Если один из пары операндов имеет тип longdouble, другой операнд также преобразуется к типуlongdouble. Иначе, если один из операндов имеет типdouble, другой операнд также преобразуется кdouble. Иначе, если один из операндов имеет типlong, другой операнд также преобразуется к типуlong. Иначе, если один из операторов имеет тип unsigned, другой операнд также преобразуется к типу unsigned.

  1. Принудительные преобразования типов

Имеется возможность заставить выражение принять определённый тип с помощью оператора принудительных преобразований. Эта операция имеет следующий вид:

(тип) выражение;

где тип– это один из стандартных типов данных С или определяемый пользователем тип. Например, если необходимо, чтобы выражениеx/2 имело типfloat(частное со знаками после запятой), следует написать:

(float)x/ 2.

  1. Циклы

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

Итерационный цикл for

Стандартный вид цикла forследующий:

for(инициализация_цикла;выражение-условие;список_выражений)оператор;

Оператор forимеет три главные части:

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

  2. выражение-условие – это выражение, определяющее условие работы цикла.

  3. список_выражений – это место где определяется характер изменения переменной цикла на каждой итерации.

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

#include <stdio.h>

int main(void)

{

int x;

for(x=1; x<=100; x++) printf(“%d ”,x);

return0;

}

В данном примере x является переменной цикла, которая изменяется и проверяется на каждой итерации цикла.

Ниже приведён пример цикла for, повторяющего несколько операторов:

for(x=100; x!=65; x-=5) {

z = sqrt(x);

printf(“The square root of %d, %f”, x, z);

}

Как sqrt(), так и printf(), вызываются и выполняются пока x не равно 65.

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

Вариации цикла for

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

Например, функция reverse(). Она предназначена для копирования содержимого первого строкового аргумента во второй строковый аргумент в обратном порядке.

void reverse(char *s, char *r)

{

int i, j;

for(i=strlen(s)-1, j=0; i>=0; j++, i--)

r[i]=s[j];

r[j] = '\0';

}

Бесконечный цикл

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

for( ; ; ) printf(“this loop will run forever.\n”);

while

while(выражение-условие)

тело цикла

Тело цикла– это или пустой, или простой, или составной оператор.Выражением-условием может быть любое выражение, имеющее в качестве истины ненулевое значение. Цикл выполняется, пока условие истинно. Когда условие становится ложным, выполняется строка, следующая за циклом.

Следующий пример показывает процедуру, обрабатывающую ввод с клавиатуры, работающую пока не будет введён символ ‘A’:

void wait_for_char(void)

{

char ch;

ch = ‘\0’;

while(ch!=’A’) ch = getchar();

}

Как и цикл for, цикл while сначала выполняет проверку, то есть тело цикла может вообще не выполняться.

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

void func1(void)

{

int working = 1;

while(working) {

working = procces1();

if (working)

working = process2();

if (working)

working = process3();

}

}

do/while

Цикл do/while проверяет условие в конце. То есть, циклdo/while всегда выполняется, хотя бы один раз. Стандартный вид циклаdo/while следующий:

do{

последовательность операторов;

}while(выражение-условие);

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

Возможно, наиболее типичным использованием цикла do/while является процедура выбора пунктов меню. Когда набран корректный ответ, она возвращает значение функции. Неправильный ответ приводит к повторному вводу. Пример:

void menu(void)

{

char ch;

printf(“1. Check Spelling\n”);

printf(“2. Correct Spelling Errors\n”);

printf(“3. Display Spelling Errors\n”);

printf(“ Enter your choice: “);

do{

ch = getchar();

switch(ch) {

case ‘1’:

check_spelling();

break;

case ‘2’:

correct_errors();

break;

case ‘3’:

display_errors();

break;

}

} while(ch!=’1’&&ch!=’2’&&ch!=’3’);

}

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

  1. Оператор break

Оператор breakимеет два назначения. Первое – это окончание работы оператораswitch. Второе – это принудительное окончание цикла, минуя стандартную проверку условия.

Когда оператор breakвстречается в теле цикла, цикл завершается и выполнение программы переходит на строку, следующую за циклом. Пример

#include <stdio.h>

int main(void)

{

int x;

for(x=1; x<100; x++) {

printf(“%d ”,x);

if(x==10) break;

}

return0;

}

Данная программа выводит числа от 0 до 10 включительно и заканчивает работу, поскольку breakвызывает немедленный выход из цикла, минуя условиеx<100.

  1. Оператор continue

Работа оператора continueчем-то похоже на работу оператораbreak. Но вместо форсированного окончанияcontinueпереходит к следующей итерации цикла, пропуская оставшийся код тела цикла. Например, следующая процедура выводит только положительные числа:

do{

scanf(“%d”, &x);

if(x<0) continue;

printf(“%d “, x);

} while(x!=100);

  1. Метки и goto

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

  1. Одномерный массив

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

Стандартный вид объявления одномерного массива следующий:

типимя_переменной[размер];

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

общее число байт = sizeof(базовый тип)*число элементов

У всех массивов первый элемент имеет индекс 0. Поэтому, если написать

charp[10];

то будет объявлен массив символов из 10 элементов, причём эти элементы адресуются индексом от 0 до 9. Следующая программа загружает целочисленный массив числами от 0 до 9 и выводит его:

#include <stdio.h>

int main(void)

{

int x[10];

int t;

for(t=0; t<10; ++t) x[t] = t;

for(t=0; t<10; ++t) printf(“%d ”, x[t]);

return0;

}

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

  1. Создание указателя на массив

Можно создать указатель на первый элемент массива, указав имя массива без индекса. Пусть имеется

intsample[10];

Можно создать указатель на первый элемент, используя имя sample. Следовательно, следующий фрагмент присваивает переменной p адрес первого элемента sample:

int *p;

int sample[10];

p = sample;

Можно также получить адрес первого элемента массива с помощью оператора &. Например, sample и &sample[0] приводят к одинаковому результату. Тем не менее в профессиональных программах нет почти нигде &sample[0].

  1. Строки

Наиболее типичным представителем одномерного массива являются строки. Хотя С не определяет строкового типа, имеется поддержка строковых операций с помощью функций, имеющихся в любом языке. В С строка определяется как символьный массив произвольной длины, оканчивающийся нулевым символом. Нулевой символ определяется как '\0'. Поэтому необходимо объявлять символьные массивы на один символ больше, чем требуется для хранения самой длинной строки. Например, если необходимо объявить массив s, содержащий десятисимвольную строку, следует написать:

chars[11];

В результате этого выделяется место в конце строки для нулевого символа.

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

С поддерживает множество функций для работы со строками. Наиболее типичными являются strcpy(), strcat(), strlen(), strcmp() со следующими прототипами:

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

char *strcat(char *s1, const char *s2);

size_t strlen(const char *s1);

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

Все функции используют заголовочный файл string.h. Функция strcpy() копирует строку, на которую указывает s2, в строку на которую указываетs1. Функция возвращаетs1. Функцияstrcat() выполняет конкатенацию строки, на которую указываетs1, и строки на которую указываетs2. Она также возвращаетs1. Функцияstrlen() возвращает длину строки, на которую указываетs1. Тип данныхsize_tопределён стандартом для адресов и в настоящее время для большинства машин определяется как беззнаковое длинное целое. Функцияstrcmp() сравнивает строкиs1 иs2. Она возвращает 0, если две строки эквивалентны, значение меньше 0, если строкаs1 при лексикографическом порядке идет раньшеs2 и значение больше 0, если строкаs1 при лексикографическом порядке идет послеs2.

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

#include <string.h>

#include <stdio.h>

int main()

{

char s1[80], s2[80];

gets(s1); gets(s2);

printf(“lengths: %d %d\n“, strlen(s1), strlen(s2));

if(!strcmp(s1, s2)) printf(“The strings are equal\n”);

strcat(s1, s2);

printf(“%s\n”, s1);

return0;

}

Если при запуске программы ввести строки “hello” и “hello”, в результате получится:

lengths: 5 5

The strings are equal

hellohello

Для многих компиляторов существуют безопасные версии функций strcpy() и strcat() - это strcpy_s() и strcat_s(). Чтобы ими полноценно пользоваться необходимо подключить <string.h> и <errno.h>.

errno_t strcpy_s(char *strDestination, size_t sizeInBytes, const char *strSource );

errno_t strcat_s(char *strDestination, size_t sizeInBytes, const char *strSource);

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

  1. Двумерные массивы

С позволяет создавать многомерные массивы. Простейшим видом многомерного массива является двумерный массив. Двумерный массив – это массив одномерных массивов. Двумерный массив объявляется следующим образом:

тип имя_массива[размер второго измерения][размер первого измерения];

В следующем примере в двумерный массив заносятся числа от 1 до 12, после этого массив выводится на экран.

#include <stdio.h>

int main()

{

int t, i, num[3][4];

/* загрузка чисел */

for(t=0; t<3; ++t)

for(i=0; i<4; ++i)

num[t][i] = (t*4)+i+1;

/* вывод чисел */

for(t=0; t<3; ++t) {

for(i=0; i<4; ++i)

printf(“%d “, num[t][i]);

printf(“\n”);

}

return0;

}

В данном примере num[0][0] имеет значение 1, num[0][1] имеет значение 2, num[0][2] имеет значение 3 и так далее. num[2][3] имеет значение 12.

  1. Массивы строк

В программировании типично использование массивов строк. Например, процессор ввода в базу данных может проверять команды пользователя в строковом массиве. Для создания массива строк используется двумерный массив символов. Левый индекс определяет число строк, а правый индекс – максимальное число символов в каждой строке. Данный фрагмент кода объявляет массив из 30 строк, каждая может содержать до 79 символов включительно:

charstr_array[30] [80];

Доступ к отдельным строкам очень прост – необходимо написать имя массива только с левым индексом. Например:

gets(str_array[2]);

Данная строка эквивалентна

gets(&str_array[2][0]);

но предыдущий вариант более типичен при написании профессиональных программ.

Рассмотрим программу, использующую массив как основу простейшего текстового редактора.

#include <stdio.h>

#define MAX 100

#define LEN 255

char text[MAX][LEN];

/* простейший текстовый редактор*/

int main()

{

register int t, i, j;

for(t=0; t<MAX; t++) {

printf("%d: ", t);

gets(text[t]);

if(!*text[t]) break;/*выход по пустой строке*/

}

/*посимвольный вывод текста*/

for(i=0; i<t; i++) {

for(j=0; text[i][j]; j++)

printf("%c",text[i][j]);

printf("%c", '\n');

}

getchar();

return 0;

}

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

  1. Многомерные массивы

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

тип имя[размерN]…[размер2][ размер1];

  1. Индексация с помощью указателей

Указатели и массивы очень тесно связаны между собой. Имя массива без индекса – это указатель на первый элемент массива. Пусть имеется массив

charp[10];

тогда следующие значения идентичны:

p

&p[0]

Выражение

p== &p[0]

выдаёт истину, поскольку адрес первого элемента и адрес массива совпадают.

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

int*p,i[10];

p=i;

p[5] = 1000;

*(p+5) = 1000;

Оба оператора присваивания помещают значение 1000 в шестой элемент i. Первый оператор использует индексацию с р, а второй – арифметику указателей. Результат одинаков.

Данные способы индексации совершенно справедливы для массивов размерности 2 и более. Предположим, что а – это целочисленный массив 10 на 10. Тогда нижеприведенные операторы эквивалентны:

(int*)a

&a[0][0]

К элементу 0, 4 массива а можно обратиться или с помощью индексации массива – a[0][4], или с помощью указателя - *((int*)a+4). Аналогично к элементу 1, 2 можно обратиться или с помощью индексации массива – a[1][2], или с помощью указателя - *((int *)a+12).

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

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

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

int num[10][10];

void pr_row(int j)

{

int *p, t;

p = num[j];

for(t=0; t<10; ++t) printf(“%d “, *(p+t));

}

Данный код может быть обобщён, если передавать в качестве аргумента номер строки, длину строки и указатель на первый элемент массива:

void pr_row(int j, int row_dimension, int *p)

{

int t;

p = p + (j * row_dimension);

for(t=0; t< row_dimension; ++t)

printf(“%d“, *(p+t));

}

  1. Размещение массивов

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

int *p;

try{ p = new int[100]; }

catch(…){ puts(“Cannot allocate”); exit(1); }

В дальнейшем доступ осуществляется, как при помощи указателя. Например, доступ к 6 элементу:

p[5]

Для освобождения памяти необходимо пользоваться оператором delete.

delete[]p;

Оператор delete должен освобождать память, только ранее выделенную оператором new, иначе это может привести к краху программы.

  1. Функции

Функции – это базовые блоки С, в которых выполняются все операции. Стандартный вид функций следующий:

спецификатор_типа имя_функции(список параметров)

{

тело функции

}

Спецификатор_типаопределяет тип возвращаемого функцией значения с помощью оператора return. Это может быть любой допустимый тип.Список параметров– это разделённый запятыми список переменных, получающих значения аргументов при вызове функции. Функция может быть без параметров и в таком случае список параметров состоит из ключевого слова void.

  1. Оператор return

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

Выход из функции

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

void pr_reverse(char *s)

{

register int t;

for(t=strlen(s)-1; t>-1; t--) printf(“%c”, s[t]);

}

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

Большинство функций использует оператор return для окончания исполнения с целью возвращения значения или упрощения кода функции и увеличения его эффективности путём создания нескольких точек выхода. Например, функция, показанная ниже, возвращает или индекс первого появления подстроки, указываемой в s2, в строке, указываемой в s1, или -1, если не обнаружено совпадений:

int find_substr(char *s1, char *s2)

{

register int t;

char *p, *p2;

for(t=0; s1[t]; t++) {

p = &s1[t];

p2 = s2;

while(*p2 && *p2==*p) {

p++;

p2++;

}

if(!*p2) return t;

}

return -1;

}

Надо обратить внимание, как два оператора return упрощают функцию.

Возвращаемые значения

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

x=power(y);

if(max(x, y) > 100) printf(“greater”);

for(ch=getchar(); isdigit(ch);) …;

Функция не может стоять с левой стороны оператора присваивания. Оператор типа

swap(x,y) = 100; неправилен. Компилятор выдаст ошибку.

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

intt;

t=f();

f() +f();

Если не указано, чему присваивается возвращаемое значение, то оно просто отбрасывается. Рассмотрим следующую программу, использующую mul():

#include<stdio.h>

int mul(int a, int b);

int main(void)

{

int x, y, z;

x = 10; y = 20;

z = mul(x, y); /* 1 */

printf(“%d “, mul(x, y)); /* 2 */

mul(x, y); /* 3 */

return 0;

}

int mul(int a, int b)

{

returna*b;

}

Строка 1 присваивает возвращаемое функцией mul() значение переменнойz. В строке 2 возвращаемое значение используется функцией printf(). В строке 3 возвращаемое значение теряется.

Значения, возвращаемые функцией main()

Когда используется оператор returnвmain(), программа возвращает код завершения вызывавшему процессу. Возвращаемое значение должно быть целого типа. Большинство ОС трактуют 0 как нормальное завершение программы. Остальные значения воспринимаются как ошибки.

  1. Правила видимости для функций

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

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

Все функции в С находятся на одном уровне видимости. То есть невозможно определить функцию в функции.

  1. Аргументы функции

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

Передача по значению и передача по указателю

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

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

Рассмотрим следующую функцию:

#include<stdio.h>

int sqr(int x);

int main(void)

{

int t=10;

printf(“%d %d”, sqr(t), t);

return 0;

}

int sqr(int x)

{

x = x*x;

return x;

}

На экране появится 100 10.

Создание передачи по указателю.

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

Указатели передаются в функции, как и обычные значения. Конечно, необходимо объявлять параметр типа указатель. Пример – функция, меняющая значение двух целочисленных аргументов.

voidswap(int*x,int*y)

{ int temp;

temp = *x;

*x = *y;

*y=temp;

}

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

#include<stdio.h>

void swap(int *x, int *y);

int main(void)

{

int x, y;

x = 10;

y = 20;

swap(&x, &y);

printf(“%d %d”, x, y);

return0;

}

В данном примере переменной х присваивается значение 10, а переменной у – 20. Затем вызывается swap() с адресами х и у. Унарный оператор & используется для получения адресов переменных. Поэтому в функцию swap() передаются адреса х и у, а не их значения.

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

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

#include<stdio.h>

void display(int num[10]);

int main(void)

{ int t[10], i;

for(i = 0; i < 10; ++i) t[i] = i;

display(t);

return 0;

}

void display(int num[10])

{ int i;

for(i=0; i<10; ++i) printf(“%d “, num[i]);

}

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

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

void display(int num[ ])

{ int i;

for(i=0; i<10; ++i) printf(“%d “, num[i]);

}

где num объявлен как целочисленный массив неизвестного размера. Поскольку С не предоставляет проверку границ массива, настоящий размер массива не имеет никакого отношения к параметру.

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

void display(int *num)

{ int i;

for(i=0; i<10; ++i) printf(“%d “, num[i]);

}

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

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

intm[4][5][6][7];

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

intfunc1(intd[][5][6][7])

{

}

  1. Аргументы функции main()

По стандарту поддерживаются два агрумента main() – это argc и argv. Они позволяют передавать аргументы командной строки в программу. Аргументы командной строки – это информация, следующая за именем программы в командной строке операционной системы.

Параметр argc содержит число аргументов командной строки и является целым числом. Он всегда больше либо равен 1, поскольку имя программы квалифицируется как первый аргумент. Параметр argv – это указатель на массив символьных указателей. Каждый элемент данного массива указывает на аргумент командной строки. Все аргументы командной строки – это строки, все числа конвертируются программой во внутренний формат. Следующая программа выводит “Hello”, а затем имя пользователя, если его набрать прямо за именем программы:

#include <stdio.h>

int main(int argc, char *argv[])

{ if(argc!=2) {

printf(“You forgot to type your name\n”);

return 1;

}

printf(“Hello%s”, argv[1]);

return 0;

}

Аргументы командной строки должны отделяться пробелами или табуляциями. Запятые, точки с запятыми и им подобные символы не рассматриваются как разделители. Если необходимо передать строку, содержащую пробелы или табуляции в виде одного аргумента, то её следует заключить в двойные кавычки. Например:

“thisisatest”

Пример по использованию аргументов командной строки. Программа отсчитывает в обратном порядке от значения, указанного в командной строке и подаёт сигнал. Если в качестве второго аргумента присутствует строка “display”, то на экране будет отображаться счётчик.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

int main(int argc, char *argv[])

{ int disp, count;

if(argv<2) {

printf(“You must enter the length of count\n”);

printf(“on command line. Try again.\n”);

return 1;

}

if(argc == 3 && !strcmp(argv[2],”display”)) disp = 1;

else disp = 0;

for(count=atoi(argv[1]); count; --count)

if (disp) printf(“%d “, count);

printf(“%c”, ‘\a’);

return0;

}

  1. Возврат указателей

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

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

char *match(char c, char *s)

{ register int count;

count = 0;

while(c!=s[count] && s[count]) count++;

if(s[count]) return (&s[count]);

elsereturnNULL;

}

Функция match() пытается вернуть указатель на позицию в строке, где первый раз найден символ. Если не найдено соответствие, возвращается указатель, содержащий NULL.

  1. Рекурсия

В С функции могут вызывать сами себя. Простой пример.

int factr(int n)

{ int answer;

if(n == 1) return 1;

answer = factr(n-1)*n;

return(answer);

}

Задача о ханойских башнях.

#include <stdio.h>

void solveTowers(int count, char sourse,

char destination, char spare)

{

if (count == 1)

{

printf("Move top ring from %c to %c\n", sourse, destination);

}

else

{

solveTowers(count-1, sourse, spare,

destination);

solveTowers(1, sourse, destination, spare);

solveTowers(count-1, spare, destination,

sourse);

}

}

int main()

{

solveTowers(4,'A','B','C');

getchar();

return 0;

}

  1. Указатели на функции

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

Адрес функции получается при использовании имени функции без скобок и аргументов. Пример:

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <math.h>

#define PI 3.1415

double del2(double a, double b, double eps, double (*f)(double));

int main(void)

{

double (*p)(double);

p = sin;

double a = -PI/2, b = PI/2, eps = 0.0001;

printf("Root = %lf\n", del2(a,b,eps,p));

printf("Root = %lf\n", del2(a,b,eps,sin));

return 0;

}

double del2(double a, double b, double eps, double (*f)(double))

{

if(b<a) {

puts("Left bigger than right");

exit(0);

}

if(eps <= 0) {

puts("Eps <= 0");

exit(0);

}

if(f(a) == 0) return a;

if(f(b) == 0) return b;

while(b-a >= eps) {

double x=(a+b)/2;

if(f(a)*f(x) < 0.) a=x;

else if(f(x) == 0.) return x;

else b=x;

}

return (a+b)/2;

}

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

При объявлении указателя на функцию можно использовать прототип, оставив имена параметров пустыми.

Рассмотрим работу функции sin() в функцииdel2(). Оператор

if(f(a) == 0) return a;

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

Бывают моменты, когда выгодно передавать функции в процедуры или хранить массивы функций. Пример:

#include <stdlib.h>

#include <stdio.h>

#include <conio.h>

#include <string.h>

void enter(void), del(void); void review(void), quit(void);

int menu(void);

void (*options[])(void) = {

enter,

del,

review,

quit

};

int main(void)

{

int i;

i = menu();

(*options[i])();

getchar();

return 0;

}

int menu(void)

{

char ch;

do {

printf("1. Enter\n");

printf("2. Delete\n");

printf("3. Review\n");

printf("4. Quit\n");

printf("Select a number:");

ch = getche();

printf("\n");

}while(!strchr("1234", ch));

return ch-49;

}

void enter(void)

{ printf("In enter.");}

void del(void)

{ printf("In del.");}

void review(void)

{ printf("In review.");}

void quit(void)

{

printf("In quit.");

exit(0);

}

  1. Структуры

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

Фрагмент кода объявляет структуру, определяющую имя и адрес. Ключевое слово struct сообщает компилятору об объявлении структуры.

struct addr {

char name[30];

char street[40];

char city[20];

char region[3];

unsigned long int zip;

};

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

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

addr addr_info; //C++

struct addr addr_info; //C

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

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

struct addr {

char name[30];

char street[40];

char city[20];

char region[3];

unsigned long int zip;

} addr_info, binfo, cinfo;

объявляет структуру addr и переменные addr_info, binfo, cinfo данного типа.

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

struct {

char name[30];

char street[40];

char city[20];

char region[3];

unsigned long int zip;

} addr_info;

объявляет одну переменную addr_info с типом определённым предшествующей ей структурой.

Стандартный вид объявления структуры следующий:

structярлык{

тип имя_переменной;

} структурные переменные;

  1. Доступ к членам структуры

Доступ к отдельным членам структуры осуществляется с помощью оператора . («точка»). Например, следующий фрагмент кода присваивает члену zipструктурной переменной addr_info значение 12345:

addr_info.zip= 12345;

Ко всем членам структуры доступ осуществляется точно таким же способом. Стандартный вид доступа следующий:

имя_структуры.имя_члена

Таким образом, массив символов addr_info.nameможет использоваться вfgets():

fgets(addr_info.name,30,stdin);

Для доступа к отдельным элементам addr_info.nameможно использовать индекс. Например:

register int t;

for(t=0; addr_info.name[t]; ++t) putchar(addr_info.name[t]);

  1. Присваивание структур

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

#include<stdio.h>

intmain()

{

struct ab{

int a;

int b;

} x, y;

x.a = 10;

x.b = 20;

y = x;

printf(“Contents of y: %d %d.”, y.a, y.b);

return0;

}

После присваивания y.aиy.bбудут содержать значения 10 и 20 соответственно.

  1. Массивы структур

Для объявления массива структур следует сначала определить структуру, а затем объявить массив переменных данного типа. Например, для объявления 100-элементного массива структур типа addr, которая была ранее определена в данной лекции, следует написать:

structaddraddr_info[100];

Для доступа к отдельным структурам массива addr_info следует проиндексировать имя массива. Например, для вывода содержимого поля zip третьей структуры, следует написать:

printf(“%ld”, addr_info[2].zip);

Как и массивы переменных, массивы структур индексируются с 0.

  1. Указатели на функции

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

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

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

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

#include <stdio.h>

struct struct_type {

int a, b;

char ch;

};

void f1(struct struct_type parm);

int main(void)

{

struct struct_type arg;

arg.a = 1000;

f1(arg);

return 0;

}

void f1(struct struct_type parm)

{

printf("%d", parm.a);

}

Данная программа выводит число 1000 на экран.

Указатели на структуры

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

Для доступа к членам структуры с помощью указателя на структуру следует использовать оператор «стрелка» ->.

Пример:

struct bal {

float balance;

char name[80];

} person;

struct bal *p;

p = &person;

p->balance = 0f;

  1. Битовые поля

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

  2. Некоторые интерфейсы устройств передают информацию, закодировав биты в один байт.

  3. Некоторым процедурам кодирования необходимо получить доступ к отдельным битам в байте.

Стандартный вид объявления битовых полей следующий:

structимя структуры{

тип имя1:длина;

тип имя2:длина;

тип имяN:длина;};

Пример

struct device {

unsigned active : 1;

unsigned ready : 1;

unsigned xmt_error : 1;

} dev_code;

Битовые поля имеют некоторые ограничения. Нельзя получить адрес переменной битового поля. Переменные битового поля не могут помещаться в массив. Переходя с компьютера на компьютер, нельзя быть уверенным в порядке следования бит (слева направо или справа налево). Любой код, использующий битовые поля, зависит от компьютера.

  1. Объединения

union union_type{

int i;

charch[4];

};

  1. Перечисления

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

enumярлык {список перечислений}список переменных;

enum coin{ penny, nickel, dime, quarter, half_dollar, dollar};

coinmoney;

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

printf(“The value of quarter is %d “, quarter);

Если явно не проводить инициализацию, значение первого имени перечисления будет 0, второго 1 и т.д.

Можно определить значение одного или нескольких символов, использую инициализатор. Это делается путём помещения за символом знака равенства и целочисленного значения. При использовании инициализатора, имена, следующие за инициализированным, получают значение большее на 1, чем указанное перед этим.

enum coin{ penny, nickel, dime, quarter=100,

half_dollar,dollar};

Теперь имена получат следующие значения:

penny 0

nickel 1

dime 2

quarter 100

half_dollar 101

dollar 102

  1. Использование typedef

typedef тип имя;

typedef float balance;

  1. Ввод, вывод, потоки и файлы

Потоки и файлы

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

Текстовые потоки– это последовательности символов. В текстовых потоках некоторые символы могут преобразовываться согласно требованиям среды.

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

Файлы

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

Не все файлы имеют одинаковые возможности.

В начале работы программы по стандарту ANSI C открыты три предопределённых текстовых потока: stdin, stdout, stderr.

Поток

Устройство

stdin

Клавиатура

stdout

Экран

stderr

Экран

Консольный ввод-вывод

Функция

Операция

getchar( )

Читает символ с клавиатуры,

ожидает нажатия ввода

getche( )

Читает символ с эхом, не ожидает нажатия ввода, не определена в стандарте ANSIC, но является типичным расширением

getch( )

Читает символ без эха, не ожидает нажатия ввода, не определена в стандарте ANSIC, но является типичным расширением

putchar( )

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

puts( )

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]