Студопедия

КАТЕГОРИИ:

АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция

Задание на лабораторную работу № 2




Название работы: Изучение операторов циклов, условных переходов.

Цель работы: Научиться работе в среде Borland C++ 3.1. Исследовать работу операторов if, for, while.

 

1. Составить программу для решения уравнения второй степени в виде: a*x2+b*x+c=0

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

2. По справочной системе исследовать работу математических функций: pow(), log(), sqrt(), описания которых находятся в справочной системе, заголовочный файл <math.h>. Составить программу, которая демонстрировала бы работу этих функций.

Контрольные вопросы:

 

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

2. Оператор организации циклов while и do while.

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

4. Оператор организации ветвления switch.

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


Лабораторная работа №3
Указатели. Структурированные типы данных



Теоретическая часть

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

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

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

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

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

int *а, *b, *с;

char *d;

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

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

*у = 7;

*х * = 5;

(*z)++;

Первое из них заносит число 7 в ячейку памяти по адресу у, второе увеличивает значение по адресу х в пять раз, третье добавляет единицу к содержимому ячейки памяти с адресом z. В последнем случае круглые скобки необходимы, так как операции * и ++ с одинаковым приоритетом выполняются справа налево (см. табл. 1.2). В результате если, например, *z=5, то (*z)++ приведет к тому, что *z=6, а *z++ всего лишь изменит сам адрес z (операция ++ выполняется над адресом z, а не над значением *z по этому адресу).

Указатели можно использовать как операнды в арифметических операциях. Если у - указатель, то унарная операция y++; увеличивает его значение; теперь оно является адресом следующего элемента. Указатели и целые числа можно складывать. Конструкция y+n (у - указатель, n- целое число) задает адрес n-ro объекта, на который указывает у. Это справедливо для любых объектов (int, char, float и т. п.); транслятор будет масштабировать приращение адреса в соответствии с типом, определенным из соответствующего объявления.

Любой адрес можно проверить на равенство (= =) или неравенство (!=) со специальным значением NULL, которое позволяет определить ничего не адресующий указатель.

Массивы.

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

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

int a[100];

char b[30];

float с[42];

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

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

int а[2][3];

Тогда элементы массива а будут размещаться в памяти следующим образом: а[0][0],а[0][1],а[0][2],а[1][0],а[1][1],а[1][2]. Имя массива а - это константа, которая содержит адрес его первого элемента (для нашего примера - а[0][0]). Предположим, что a= 1000. Тогда адрес элемента а[0][1] будет равен 1002 (элемент типа int занимает в памяти 2 байт), адрес следующего элемента а[0][2]- 1004 и т. п. Что же произойдет, если выбрать элемент, для которого не выделена память. К сожалению, компилятор не следит за этим. В результате возникнет ошибка и программа будет работать неверно.

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

int a[5];

определяет массив из пяти элементов а[0],а[1],а[2],а[3],а[4],. Если объект *у объявлен как

int *у

то оператор у=&а[0]; присваивает переменной у адрес элемента а[0]. Если переменная у указывает на очередной элемент массива а, то y + 1 указывает на следующий элемент, причем здесь выполняется соответствующее масштабирование для приращения адреса с учетом длины объекта (для типа int - 2 байт, long - 4 байт, double - 8 байт и т. п.). Так как само имя массива есть адрес его нулевого элемента, то инструкцию y=&a[0]; можно записать ив другом виде: у = а;. Тогда элемент а[i] можно представить как *(а+1). С другой стороны, если у - указатель, то следующие две записи: y[i] и *(у+i) эквивалентны. Между именем массива и соответствующим указателем есть одно важное различие. Указатель - это переменная и y=a; или y++; - допустимые операции. Имя же массива - константа, поэтому конструкции вида а = у; a++; использовать нельзя, так как значение константы постоянно и не может быть изменено.

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

Если указатели адресуют элементы одного массива, то их можно сравнивать (отношения вида <, >, = =, != и другие работают правильно). В то же время нельзя сравнивать либо использовать в арифметических операциях указатели на разные массивы (соответствующие выражения не приводят к ошибкам при компиляции, но в большинстве случаев не имеют смысла). Как и выше, любой адрес можно проверять на равенство или неравенство со значением NULL. Указатели на элементы одного массива можно также вычитать. Тогда результатом будет число элементов массива, расположенных между уменьшаемым и вычитаемым объектами.

Язык С позволяет инициализировать массив при объявлении. Для этого используется следующая форма:

тип имя_массива[...]...[...] = {список значений};

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

int a[5]={0,1,2,3,4};

char c[7]={‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’, ‘g’};

int b[2][3]={1,2,3,4,5,6};

b[0][0]=1, b[0][1]=2, b[0][2]=3, b[1][0]=4, b[1][1]=5, b[1][2]=6,

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

char *m[5];. Здесь m[5] - массив, содержащий адреса элементов типа char.

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

Язык С не поддерживает отдельный строковый тип данных, но он позволяет определить строки двумя различными способами. В первом используется массив символов, а во втором - указатель на первый символ массива. Объявление char a[10]: указывает компилятору на необходимость резервирования места для максимум 10 символов. Константа а содержит адрес ячейки памяти, в которой помещено значение первого из десяти объектов типа char. Процедуры, связанные с занесением конкретной строки в массив а, копируют ее по одному символу в область памяти, на которую указывает константа а, до тех пор, пока не будет скопирован нулевой символ, оканчивающий строку. Когда выполняется функция типа printf («%s», а);, ей передается значение а, т. е. адрес первого символа, на который указывает а. Если первый символ нулевой, то работа функции printf заканчивается, а если нет, то она выводит его на экран, прибавляет к адресу единицу и снова начинает проверку на нулевой символ. Такая обработка позволяет снять ограничения на длину строки (конечно, в пределах объявленной размерности): строка может быть любой длины, до тех пор, пока есть место в памяти, куда ее можно поместить.

Второй способ определения строки - это использование указателя на символ. Объявление char *b; задает переменную b, которая может содержать адрес некоторого объекта. Однако в данном случае компилятор не резервирует место для хранения символов и не инициализирует переменную b конкретным значением. Когда компилятор встречает инструкцию вида b = «Москва»;, он производит следующие действия. Во-первых, как и в предыдущем случае, он создает в каком-либо месте объектного модуля строку Москва, за которой следует нулевой символ. Во-вторых, он присваивает значение начального адреса этой строки (адрес символа М) переменной b. Функция printf(«%s», b); работает так же, как и в предыдущем случае, осуществляя вывод символов до тех пор, пока не встретится заключительный нуль.

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

Структуры.

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

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

struct тип { тип_элемента_1 имя_элемента_1;

                тип_элемента_2 имя_элемента_2;

.............

тип_элемента_n имя_элемента_n;

}[список переменных];

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

struct date { int day;

int month;

int year; };

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

struct man { char name[30];

char fam[20];

struct date bd;

int voz; };

Определенный выше тип date включает три элемента: day, month, year, содержащий целые значения (int). Структура man включает элементы name[30], fam[20], bd и voz. Первые два - name[30] и fam[20], -это символьные массивы из 30 и 20 элементов каждый. Переменная bd представлена составным элементом (вложенной структурой) типа data. Элемент voz содержит значения целого типа (int). Теперь разрешается объявить переменные, значения которых принадлежат введенному типу:

struct man _man_[100];

Здесь определен массив _man_, состоящий из 100 структур типа man. В языке С разрешено использовать массивы структур. Структуры могут состоять из массивов и других структур.

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

_man_[i].voz = 16;

_man_[j].bd.day = 22;

_man_[i].bd.year = 1976;

При работе со структурами необходимо помнить, что тип элемента определяется соответствующей строкой объявления в фигурных скобках. Например, _man_ имеет тип man, year - является целым числом и т. п. Поскольку каждый элемент структуры относится к определенному типу, его имя может появляться везде, где разрешено использовать значения этого типа. Допускаются конструкции вида _man_[i]=_man_[j]: где man_[i] и _man_[j] - объекты, соответствующие единому описанию структуры. Другими словами, разрешается присваивать одну структуру другой по их именам.

Унарная операция & позволяет взять адрес структуры. Предположим, что задано объявление:

struct date {int d, m. у;} day;

Здесь day-это структура типа date, включающая три элемента: d, m, у. Другое объявление struct date *db: устанавливает тот факт, что db – это указатель на структуру типа date. Запишем выражение: db = &day;. Теперь для выбора элементов d, m, у структуры необходимо использовать конструкции: (*db).d, (*db).m, (*db).y. Действительно, db-это адрес структуры, *db-сама структура. Круглые скобки здесь необходимы, так как точка имеет более высокий, чем звездочка, приоритет (см. Таблицу 1 ). Для аналогичных целей в языке С предусмотрена специальная операция -> Эта операция выбирает элемент структуры и позволяет представить рассмотренные выше конструкции в более простом виде:

db -> d,

db -> m,

db -> у.

Оператор typedef.

Рассмотрим описание структуры:

struct data {int d, m. у;};

Фактически вводится новый тип данных – data. Теперь его можно использовать для объявления конкретных экземпляров структуры, например:

struct data а, b, с:

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

typedef тип имя;

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

typedef int INTEGER;

После этого можно сделать объявление:

INTEGER а, b;

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

int а, b;

Другими словами, INTEGER можно использовать как синоним ключевого слова int. При этом можно комбинировать объявления со словами (int и INTEGER, например:

INTEGER а, b;

int с. d;

Здесь объявлены четыре переменные (а, b, с, d) целого типа.

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

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

struct prim { int а:2;

unsigned b:3;

int:5;

int с: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-l байт. Переменная z будет достаточно велика, чтобы сохранять самый большой из трех приведенных типов. Таким образом, размер z будет 4 байт. В один и тот же момент времени z может иметь значение только одной из указанных переменных (ir, fr, cr).

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

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

enum seasons {spring, summer, autumn, winter};

Здесь введен новый тип данных seasons. Теперь можно объявить переменные этого типа:

enum seasons a, b, с;

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

enum seasons {spring, summer, autumn, winter} a, b, с:

Рассмотрим другое объявление:

enum days {mon, tues, wed, thur, fri, sat, sun} my_week;

Имена, занесенные в days, представляют собой константы целого типа. Первая из них (mon) автоматически устанавливается в нуль, и каждая следующая имеет значение на единицу больше, чем предыдущая (tues=l, wed=2 и т. п.). Можно присвоить константам определенные значения целого типа (именам, не имеющим их, будут, как и раньше, назначены значения предыдущих констант, увеличенные на единицу). Например:

enum days {mon = 5, tues=8, wed=10, thur, fri, sat, sun} my_week;

После этого mon=5, tues=8, wed=10, thur=11, fri=12, sat=13, sun=14.

Тип enum можно использовать для задания констант true= l и false = 0, например: enum t_f {false, true} a, b;

 

Примеры программ

Пример1. Использование индексов при работе с массивами

#include<stdio.h>

#include<conio.h>

int days[]={31,28,31,30,31,30,31,31,30,31,30,31};

void main(void)

{

int index;

extern int days[];                //использование внешней переменной

for (index=0; index< 11; index++)

printf("Месяц %d имеет %d дней.\n",index+1, days[index]);

getch();

}

 

Пример2 Использование указателей при работе с массивами Инвертирование символьной строки

#include <stdio.h>

#include <conio.h>

#include <string.h>

void main(void)

{

int n;

char str[4], *f, *k, ch;                          // Память под символы

printf ("Vvedite stroku\n");

scanf ("%s",str);

n = strlen(str);                                      // Вычисление длины строки

for (f=str, k=str+n-1; f < k; f++, k--)             /* f присваивается первый символ,

{                                                        k присваивается последний */

ch = *f;

*f = *k;

*k = ch;

}

printf ("\nInvert %s\n", str);                // Вывод инвертированной строки

getch();

}

 










Последнее изменение этой страницы: 2018-05-10; просмотров: 144.

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