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

www.testent.ru

Структура программы на языке СИ

Программа на языке Си это текстовый файл с расширением. c

Текст программы имеет определенную структуру:

1. заголовок

2. включение необходимых внешних файлов

3. ваши определения для удобства работы

4. объявление глобальных переменных

Перед использованием переменной в Си её необходимо объявить! Т.е. указать компилятору какой тип данных она может хранить и как она называется.

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

5. описание функций - обработчиков прерываний

6. описание других функций используемых в программе

7. функция main - это единственный обязательный пункт !

Это не жесткий порядок, а ориентировочный!

Иногда п. 6 - это прототипы функций, а сами функции описываются полностью после п. 7.

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

Функция - имеет { "тело" } в фигурных скобках. Тело - это код на Си определяющий то, что делает функция. Знак «;» после функции не ставится.

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

main(){

... какой то код программы ...

вызов функции_1; /* программа перейдет в функцию_1 строка программы */

// будет выполнятся после

// возврата из функции_1

... какой то код программы ...

}

Функции могут вызываться не только из main(), но и из других функций.

№4

Приведе́ние ти́па (type conversion) — преобразование значения переменной одного типа в значение другого типа. Выделяют явное и неявное приведения типов.

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

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

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

Неявное приведение

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

В языке C:

double d; // вещественный тип

long l; // целый тип

int i; // целый тип

if (d > i) d = i;

if (i > l) l = i;

if (d == l) d *= 2;

Каждый раз при выполнени операции сравнения или присваивания переменные разных типов будут приведены к единому типу. Следует с осторожностью использовать неявное приведение типа. При переводе числа из вещественного типа в целочисленный, дробная часть отсекается. Обратное приведение из целочисленного типа к вещественному также может привести к понижению точности, что связано с различным представлением вещественных и целочисленных чисел на машинном уровне. К примеру, вещественный тип single стандарта IEEE 754 не может точно представить число 16777217, в то время как 32-битный целочисленный тип может. Это может привести к ситуациям, когда сравнение на равенство одного и того же числа, представленного типами (int и single) будет выдавать ложный результат (числа не равны друг другу).

[править]Явное приведение типа

[править]В языке C

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

int X;

int Y = 200;

char C = 30;

X = (int)C * 10 + Y; //переменная С приведена к типу int.

Грубых ошибок в данном примере от автоматического приведения типов не произойдет, так как переменная C, которая имеет тип char будет приведена к типу int, так как здесь идет речь о «повышении» типа переменной к старшему в выражении(переменная С перед присваиванием неявно приводится к типу переменной Y). Но возможен один нюанс - в зависимости от машинной реализации типа char при преобразовании char в int может получиться отрицательное число, потому рекомендуется использовать явное преобразование, а если все-таки есть преобразования типа char в int, char объявлять как беззнаковый(unsigned char).

7

Условный оператор if ... else. Форма оператора следующая:

if ( условие )

оператор1

else

оператор2

Если условие истинно, то выполнятся первый оператор, иначе второй. Если в условии отрабатываются несколько операторов, то они помещаются фигурные скобки. Вторая часть (else) может опускаться если нам не нужно обрабатывать ложное условие. Допускается вложение условных операторов. При сравнении на равенство необходимо помнить, что a = b пишется как a == b (двойное равно, иначе мы получим присваение, т.е. a пример значение равное b). Другие операции сравнения: больше (>), меньше (<), не равно (!=), логическая операция И (&&), логическая операция ИЛИ (||).

Пример на Си (запустите для различных значений переменных a и b):

// Условный оператор - поиск минимального из двух чисел

printf("Минимальное из значений %i и %i равно ", a, b);

int a = 1;

int b = 2;

if ( a < b )

printf("%d", a);

else

printf("%d", b);

if ( a == b )

printf(" (оба числа одинаковые)");

printf("\n");

Далее рассмотрим структуры цикла.

Итерационный цикл - цикл управляемый счетчиком.

Структура следующая:

for ( инициализация; условие; операция ) {

операторы

}

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

Рассмотрим на примере вычисления факториала числа:

printf("вычисление факториала\n");

const int N = 5;

int i, f = 1;

for (i = 1; i <= N; i++) {

f *= i;

printf("%d! = %d\n", i, f);

}

В качестве операции в примере используется постфиксная форма интремирования (увеличение переменной на 1). Цикл повторяется до тех пор, пока i меньше или равно N. Операция f *= i (присваение с умножением) эквивалентна форме f = а * i.

Цикл с предусловием - цикл управляемый условием.

Структура следующая:

while ( условие ) {

операторы

}

Цикл выполняется пока условие истинно (условие проверяется в начале). Пример:

// Цикл с предусловием

printf("вычисление суммы чисел\n");

i = 0;

f = 0;

while (i < 5) {

f += ++i;

printf("S(%d) = %d\n", i , f);

}

Операция f += ++i эквивалентна двум операторам i++; f = f + i;

Цикл с постусловием - цикл управляемый условием.

Структура следующая:

do {

операторы

} while ( условие )

Цикл выполняется пока условие истинно (условие проверяется в конце, т.е. тело цикла выпольняется по крайней мере один раз). Пример:

// Цикл с постусловием

printf("вычисление квадратов\n");

i = 0;

do {

f = i * i;

printf("%d^2 = %d\n", i, f);

i++;

} while ( f < 25 );

Полный текст программы со всеми рассмотренными конструкциями:

#include "stdio.h"

int main() {

int a = 1;

int b = 2;

// Условный оператор - поиск минимального из двух чисел

printf("Минимальное из значений %i и %i равно ", a, b);

if ( a < b )

printf("%d", a);

else

printf("%d", b);

if ( a == b )

printf(" (оба числа одинаковые)");

printf("\n");

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

printf("вычисление факториала\n");

const int N = 5;

int i, f = 1;

for (i = 1; i <= N; i++) {

f *= i;

printf("%d! = %d\n", i, f);

}

// Цикл с предусловием

printf("вычисление суммы чисел\n");

i = 0;

f = 0;

while (i < 5) {

f += ++i;

printf("S(%d) = %d\n", i , f);

}

// Цикл с постусловием

printf("вычисление квадратов\n");

i = 0;

do {

f = i * i;

printf("%d^2 = %d\n", i, f);

i++;

} while ( f < 25 );

return 0;

}

Оператор выбора switch. Форма оператора следующая:

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

case константа_1 : операторы ; break;

...

case константа_n : операторы ; break;

default : операторы ; break;

}

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

Следующий пример показывает работу оператора выбора.

int a = 2, b = 5;

...

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

switch (i) {

case 0 : c = a + b; break;

case 1 : c = a - b; break;

case 2 : c = a * b; break;

case 3 : c = (float) a / b; break;

default : c = 0; break;

}

printf("Результат: i = %i, c = %f\n", i, c);

}

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

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

if ( i == 0 )

c = a + b;

else

if ( i == 1 )

c = a - b;

else

if ( i == 2 )

c = a * b;

else

if ( i == 3 )

c = (float) a / b;

else

c = 0;

printf("Результат: i = %i, c = %f\n", i, c);

}

Результат выполнения кода будет одинаковым для обоих примеров (блок default выполняется во всех случаях, когда i не равно 0, 1, 2, 3):

Результат: i = 0, c = 7.000000

Результат: i = 1, c = -3.000000

Результат: i = 2, c = 10.000000

Результат: i = 3, c = 0.400000

Результат: i = 4, c = 0.000000

Результат: i = 5, c = 0.000000

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

i = 2;

j = 0;

switch (i) {

case 0 : i = 5; j++;

case 1 : i = 0; j++;

case 2 : i++; j++;

case 3 : i += 2; j++;

case 4 : i = 0; j++;

case 5 : i = i - 2; j++;

default : j++;

}

printf("Результат: i = %i, выборов: %i\n", i, j);

Счетчик j подсчитывает реальное число исполненых блоков case. В начале i = 2 и первый выполняемый блок case 2. В этом блоке i увеличивается на 1 и проверка продолжается, т.ч. следующим выполняется блок case 3 (i становиться равным 4), далее case 4 (i зануляется) и блок по умолчанию (он выполняется всегда, если до этого не был выполнен оператор break). Окончательно получаем:

Результат: i = -2, выборов: 5

Полный текст примера для оператора switch

#include "stdio.h"

int main(){

int i, j;

int a = 2, b = 5;

float c;

printf("Пример 1а:\n");

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

switch (i) {

case 0 : c = a + b; break;

case 1 : c = a - b; break;

case 2 : c = a * b; break;

case 3 : c = (float) a / b; break;

default : c = 0; break;

}

printf("Результат: i = %i, c = %f\n", i, c);

}

printf("Пример 1б:\n");

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

if ( i == 0 )

c = a + b;

else

if ( i == 1 )

c = a - b;

else

if ( i == 2 )

c = a * b;

else

if ( i == 3 )

c = (float) a / b;

else

c = 0;

printf("Результат: i = %i, c = %f\n", i, c);

}

printf("Пример 2:\n");

i = 2;

j = 0;

switch (i) {

case 0 : i = 5; j++;

case 1 : i = 0; j++;

case 2 : i++; j++;

case 3 : i += 2; j++;

case 4 : i = 0; j++;

case 5 : i = i - 2; j++;

default : j++;

}

printf("Результат: i = %i, выборов: %i\n", i, j);

return 0;

}

Операторы break и continue. Использование оператора break мы уже видели на примере структуры switch. Оператор break - это выход из цикла или конструкции switch. Пример (цикл по i от 0 до 4, но при i = 2 происходит выход из цикла):

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

if ( i == 2 ) break;

printf("Индекс: i = %i\n", i);

}

Результат:

Индекс: i = 0

Индекс: i = 1

Оператор continue - переход на конец цикла (т.е. пропуск всех операторов от continue до конца структуры цикла). Пример (цикл по i от 0 до 4, но при i = 2 происходит переход на конец цикла):

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

if ( i == 2 ) continue;

printf("Индекс: i = %i\n", i);

}

Результат:

Индекс: i = 0

Индекс: i = 1

Индекс: i = 3

Индекс: i = 4

Полный текст примера использования операторов break и continue:

#include "stdio.h"

int main() {

int i;

printf("Пример для break:\n");

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

if ( i == 2 ) break;

printf("Индекс: i = %i\n", i);

}

printf("Пример для continue:\n");

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

if ( i == 2 ) continue;

printf("Индекс: i = %i\n", i);

}

return 0;

}

Оператор перехода

В языке С определены четыре оператора перехода: return, goto, break и continue. Операторы return и goto можно использовать в любом месте внутри функции. Операторы break и continue можно использовать в любом из операторов цикла. Как указывалось ранее в этой главе, break можно также использовать в операторе switch.

Оператор return

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

Стандарт С89 допускает наличие оператора return без значения, даже если тип функции отличен от void. В этом случае функция возвращает неопределенное значение. Но что касается языков С99 и C++, если тип функции отличен от void, то ее оператор return обязательно должен иметь значение. Конечно, и в программе на С89 отсутствие возвращаемого значения в функции, тип которой отличен от void, является признаком плохого стиля!

Общая форма оператора return следующая:

return выражение;

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

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

Функция, определенная со спецификатором void, не может содержать return со значением. Так как эта функция не возвращает значения, в ней не может быть оператора return, возвращающего значение. Более подробно returnрассматривается в главе 6.

Оператор goto

Кроме goto, в языке С есть другие операторы управления (например break, continue), поэтому необходимости в применении goto практически нет. В результате чрезмерного использования операторов goto программа плохо читается, она становится "похожей на спагетти". Чаще всего такими программами недовольна администрация фирм, производящих программный продукт. То есть оператор goto весьма непопулярен, более того, считается, что в программировании не существует ситуаций, в которых нельзя обойтись без оператора goto. Но в некоторых случаях его применение все же уместно. Иногда, при умелом использовании, этот оператор может оказаться весьма полезным, например, если нужно покинуть глубоко вложенные циклы[1]. В данной книге оператор goto рассматривается только в этом разделе.

Для оператора goto всегда необходима метка. Метка — это идентификатор с последующим двоеточием. Метка должна находится в той же функции, что и goto, переход в другую функцию невозможен. Общая форма оператора goto следующая:

goto метка;

.

.

.

метка:

Метка может находиться как до, так и после оператора goto. Например, используя оператор goto, можно выполнить цикл от 1 до 100:

x = 1;

loop1:

x++;

if(x<=100) goto loop1;