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

Предоплата всего

Подписываем
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
Предоплата всего
Подписываем
16
Лекции по дисциплине «Информатики и программирование»
Автор – Шульга Т.Э.
В языках программирования существует множество различных типов данных. На предыдущих лекциях нами были рассмотрены только основные типы данных языка программирования С. Для того, что чтобы разобраться в многообразии типов данных вводят различные классификации. Рассмотрим две такие классификации.
Первая из них - классификация по строению элемента. В соответствии с ней все типы модно разделить на скалярные (простые), которые не имеют внутренней структуры и структурированные, которые состоят из элементов других типов. Иными словами, cскалярный (простой) тип данных позволяют хранить только одно значение, а структурированный тип данных позволяют хранить совокупности значений. Все рассмотренные в предыдущей теме типы данных являются скалярными. В общем случае к скалярным типам относятся числовые и символьные типы, указатели, перечисления. К структурированным типам относят, например, массивы, структуры, классы.
Вторая классификация в качестве критерия использует способ создания типа данных. В соответствии с этим критерием, все типы данных делятся на встроенные (стандартные, определенные в стандарте языка) и определяемые программистом. К встроенным типам обычно относят числовые и символьные типы данных, логический тип. К типам, определяемым программистом, относят, например, перечисления, структуры, классы.
В данной лекции рассмотрим такие типы данных как указатели, ссылки, перечисления, массивы, строки.
Указатели – это переменные, значениями которых служат адреса участков памяти, выделенных для объектов конкретных типов. Различают три вида указателей – указатели на объекты, на функции и на void. Кроме того, различают указатели-переменные и указатели-константы. В простейшем случае определение указателя-переменной на некоторый объект имеет вид
тип *[const] имя [инициал_выр];
где звездочка относится непосредственно к имени. Например,
char *t1, *t2, t3; // t1, t2 -неинициализированные указатели на объекты типа int (то есть в переменных t1, t2 может храниться адрес объекта типа int), a t3 – переменная типа int;
const int *t1; указатель на константу целого типа.
В качестве инициализирующего выражения указателя должно использовать константное выражение, частными случаями которого являются
Кроме того, инициализировать указатель можно с помощью операции динамического выделения участка памяти функцией malloc (из библиотеки stdlib.h). Например,
char *p5= maloc(1) или char *p5= maloc(sizeof(char)); //выделили память для перемененной типа char и связали указатель p с эти участком памяти.
Таким образ выделенная память должна быть освобождена с помощью функции free. В противном случае одна будет занята до перезагрузки операционной системы (Это проблема называется проблемой «динамического мусора»). Например, free(p5);
Основная операция над указателем – разыменование, унарная операция ‘*’ (получение значения через указатель). Эта операция также называется косвенным обращением или обращением по адресу. Например,
char d=*p3;// значением переменной c2 будет значение того участка памяти, с которым связан указатель p, то есть c=d= ‘a’.
С помощью операции разыменования инициализированного указателя можно не только получать, но и изменять содержимое участка памяти, с которым связан указатель. Например,
*p= ‘b’; // значением переменной с и d стал символ ‘b’.
Разыменование нулевого указателя приводит к ошибки времени выполнения, так как данная область памяти недоступна. Разыменование неинициализированного указателя приведет либо к ошибке времени выполнения, либо к получению случайных данных из памяти. Поэтому рекомендуется всегда инициализировать указатель при описании, а перед разыменованием проверять его на NULL.
Над указателями-переменными можно также осуществлять операции присваивания, сложение с константой, вычитание, --, ++, сравнение, приведение типов.
Если указатель описан со спецификатором const (то есть является константой-указателем), то он должен быть инициирован и его значения нельзя будет изменить с помощью приведенных выше операций. Например,
char * const p6=p5;
Указатель на void применяется в тех случаях, когда конкретный тип объекта, адрес которого следует хранить, не определен. Таким указателям можно присвоить значение указателя любого типа, а также сравнивать его с любыми указателями.
Ссылка представляет собой синоним имени, уже существующего объекта, и поэтому не занимает место в памяти (в отличии от указателя). Формат описания:
тип & имя инициализатор;
Наличие инициализатора обязательно. После инициализации ссылке не может быть присвоено другое значение. Например,
int a=10;
int& a1=a;
printf(“%d%d”,a,a1);
a1+=2;
printf(“%d%d”,a,a1);
Значением ссылки является адрес объекта, на который указывает ссылка, и, по сути ссылка представляет указатель, который не надо разыменовывать. В основном ссылки используются при работе с функциями.
Перечислимый тип по существу описывает целые константы (типа int), которым приписаны уникальные и удобные для использования имена.
Формат описания:
emun [имя типа] {список констант}
где константы могут быть инициализированы обычным образом.
Например,
enum digit {one=1,two,three};
Здесь one, two, three – произвольным образом выбранные программистом идентификаторы для обозначения констант 1,2,3. После такого определения в программе наряду, например, с константой 1 можно использовать ее обозначение one. Фактически такая запись равносильно записи
const int one=1;
const int two=2;
const int three=3;
Обычно перечислимый тип используется для задания числовых рядов или стандартных рядов, таких как месяцы, дни недели.
Если в определении перечислимых констант опустить знаки “=” и не указывать числовых значений, то они будут присваиваться идентификаторам по умолчанию следующим образом: самый левый в фигурных скобках идентификатор получит значение 0, а каждый последующий увеличивается на 1. Имена перечислимых констант должны быть уникальными, а их значения могут совпадать.
Имя типа указывают, если требуется вводить в программе переменные этого типа. Например
digit d1,d2;
Компилятор обычно обеспечивает, чтобы эти переменные принимали значения только из списка констант.
Массив - самая распространенная структура данных, реализованная практически во всех языках программирования. Массив – это именованная последовательность однотипных элементов, расположенных в памяти компьютера последовательно. Тип элементов массива называют базовым. К любому элементу массива можно обращаться произвольным образом, так как он имеет определенный номер, называемый индексом.
Описание массива в общем случае имеет формат:
[класс памяти] [const] тип имя [константное выражение]={список инициализирующих элементов};
Константное выражение представляет собой количество элементов массива (размерность массива). Если массив объявлен без инициализирующего списка, то задание размерности массива обязательно. Например,
int a[10]; - массив из 10 целых чисел
int n=10; int a[n]; - недопустимо, так как n – не константа
int a[]; - недопустимо, так как нет ни размерности ни инициализирующего списка.
Предпочтительнее задавать размерность массива с помощью именованных констант. Например,
const int n=10;
int mas[n];
Когда массив объявлен без указания размера, но при этом инициализирован списком, его размер вычисляется путем подсчета числа элементов этого списка. Например
int a []={1,2,3,4};//размерность массива равна 4
Явная инициализация массива разрешена только при его определении и возможна двумя способами: либо с указанием размера массива в квадратных скобках, либо без его явного указания, например,
int а[6]={1,2,3,4};//массив из 6 целых чисел с инициализацией первых четырех, остальные будут обнулены.
char str1[]={‘a’, ‘b’, ‘c’};// массив из 3 элементов типа char
Число элементов в инициализирующем списке должно быть меньше и равно указанной размерности массива, то есть запись int a[3]={1,2,3,4}; недопустима.
Доступ к элементам массива осуществляется с помощью комбинации имя массива + индекс элемента следующими двумя способами.
Работа с элементами массива организовывается обычно в цикле.
Для ввода и вывода элементов массива а можно использовать записи
scanf(“%d%d%d”, &a[0], &a[1], &a[3]);
printf (“%d%d%d”, a[0],a[1],a[3]);
соответственно, при условии, что количество элементов точно известно и невелико. На практике так поступают редко.
При выполнении команды printf(“%d”,a); на экране появится адрес памяти, по которому расположен первый элемент массива a. Комадна scanf("%d",a) считает данные с клавиатуры в первый элемент массива.
Исключение составляют только массивы символов (char), для которых функции printf и scanf работают по-другому.
Например, пусть описан массив
char s[255]=”Hello”;/*обратите внимание, что массив типа char допустимо инициализировать не только списком элементов в скобках {}, но и строковой константой*/
printf(“%s”,s); //на экран выведется, не адрес памяти, а вся строка
scanf("%s",s); // с клавиатуры считаются все символы до первого пробельного разделителя.
Функция malloc, используемая для инициализации указателей, позволяет определять массив еще одним способом (это так называемый динамический массив):
int k;
scanf(“%d”, &k);
int *a=malloc(k*sizeof(int);
Память, зарезервированная под динамический массив, должна освобождаться явным образом функцией free, например free(a);
Многомерные массивы описываются как массивы массивов, например,
int a2[3][2] //массив из 3 массивов, содержащих по 2 целых элемента.
Для обращения к элементу двумерного массива используется два индекса, например, a2[i][j]. Для работы с двумерными массивами используется конструкция вложенных циклов.
Задание для самостоятельной работы! Придумать, как двумерный массив задать динамически.
В языке С тип данных «строка» не определен, но определено понятие С-строка. С-строка – это массив символов, заканчивающийся ноль-симолом. Ноль-симовол – это символ с кодом, равным нулю, что записывается в виде esp-последовательности ‘\0’.
В С строка может быть описана двумя способами:
Например,
char str1[]= “adc”; //массив из 4 элементов типа char, элемент str2[3]= ‘\0’ – признак конца строки.
Например,
char *str2= “abc”; // указатель на строковую константу
Для строк не определено специальных операций, они обрабатываются как обычные массивы. Однако в отличии от массивов других типов, можно считать все символы строки с клавиатуры можно с помощью одной функции scanf (“%s”, имя_символьного_массива). Следует помнить, что в этом случае считается только символы по первого пробельного разделителя. Кроме того, всю строку можно вывести на экран с помощью одной функции printf (“%s”, имя_символьного массива).
Например
char s[]="Hello";
printf("%s",s);// на экран выведется слово «Hello», а для массивов других типов в данном случае выведется только адрес памяти первого элемента.
scanf("%s",&s);//
// считывается строка для первого пробельного разделителя, для массивов других типов считается первый элемент.
Если на клавиатуре набрать строку «мама мыла раму», то в массив запишется только слово «мама», то есть в массиве будет 5 элементов со значениями ‘м’, ‘а’, ‘м’, ‘а’, ‘\0’.
printf("%s",s);//на экран выведется вся строка «мама»,
Кроме того строку можно считать с помощью функции gets(имя_символьного массива); Однако, она не контролирует выход за пределы массива.
При выводе символьного массива с помощью функции printf из памяти выводятся подряд все символы, начиная с первого и до тех пор, пока не встретится символ ‘\0’. Поэтому, если символьный массив инициализирован списком (а не строковой константой) или заполнятся в цикле ответственность за то, чтобы последний символ был символом конца строки, ложится на плечи программиста.
Например,
char s[]={‘m’, ‘a’, ‘m’};
printf("%s",s);///на экран выведется mamЁ@kj^^ и т.п.
Существует функции стандартной библиотеки, которые позволяют копировать, сравнивать, объединять строки, выделять подстроки, определять длину строки, считывать строки с клавиатуры и файла. (Они описаны в заголовочных файлах stdio.h, stdlib.h, string.h)
Например,
#include <stdio.h>
#include <string.h>
main ()
{
const int n1=20;
int n2=20;
char str1[n1];
char *str2=malloc (n2);
gets(str1);//считываем строку
printf("Первая строка %s\n",str1);
scanf (“%s”,str2);//считываем строку до первого пробельного разделителя
printf("Вторая строка %s\n",str2);
printf("Длина строк: %d %d\n", strlen(s1), strlen(s2));
if(!strcmp(s1, s2)) printf("Строки равны\n");
strcat(s1, s2);// объединяем строки
printf("Объединенная строка: %s\n", s1);
}