Данные (сведения необходимые для начала работы)

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

Константы и переменные

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

Для задания символических имен констант в Си очень часто используется директива препроцессора #define. Так, строка #define PI 3.14159265 задает символическое имя PI для константы 3.14159265. После этого имя PI можно использовать вместо числового значения. Препроцессор находит все вхождения слова PI в текст и заменяет их на константу.

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

Основные типы данных

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

Термины тип данных (или просто тип) переменной обозначает множество значений, которые может принимать эта переменная. Набор базовых типов данных отличается в различных языках.

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

· int

· long

· short

· unsigned

· char

· float

· double

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

float, double используются для представления чисел с десятичной точкой.

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

int x;

int y, z, t;

При описании переменных можно присваивать им начальные значения:

int maxind = 1000;

int a = 5, b = 7;

Тип также определяет, сколько памяти занимает переменная этого типа. Для различных ЭВМ число байт памяти отводимое под переменные одного типа может различаться. Для определения того числа служит функция sizeof(). Пример использования данной функции:

/*

Пример 3

Определяет сколько байт памяти занимают переменные

различных типов

*/

#include <stdio.h>

#include <stdlib.h>

int main(void) {

printf("int - %d ,байт \n",sizeof(int));

printf("long - %d ,байт \n",sizeof(long));

printf("short - %d ,байт \n",sizeof(short));

printf("unsigned - %d ,байт \n",sizeof(unsigned));

printf("char - %d ,байт \n",sizeof(char));

printf("float - %d ,байт \n",sizeof(float));

printf("double - %d ,байт \n",sizeof(double));

return EXIT_SUCCESS;

}

Резулитат выполнения программы:

int - 4 ,байт

long - 4 ,байт

short - 2 ,байт

unsigned - 4 ,байт

char - 1 ,байт

float - 4 ,байт

double - 8 ,байт

Размер памяти, занимаемой переменной некоторого типа определить диапазон значений этой переменной.

В таблице 3.1 перечислены диапазоны значений простейших типов данных.

Таблица 3.1

Диапазоны значений простейших типов данных.

Тип Диапазон значений
char от -128 до +127
unsigned char от 0 до 255
short от -32768 до +32767
unsigned short от 0 до 65535
int от – 2147483648 до + 2147483647
unsigned int (или просто unsigned) от 0 до 4294967295
long от – 2147483648 до + 2147483647
unsigned long от 0 до 4294967295
float от ±3.4e-38 до ±3.4e+38 (7 значащих цифр)
double от ±1.7e-308 до ±1.7e+308 (15 значащих цифр)

В рассмотренном примере программы встретилось слово void, которое означает "пустота". Тип void в Си обозначает отсутствие чего-либо там, где обычно предполагается описание типа. Например, функция int main(void), не принимает никакого значения.

Структуры данных

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

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

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

Массивы и записи являются структурами с произвольным доступом, подразумевая под этим, что время доступа к компонентам массива или записи не зависит от значения индекса массива или указателя поля записи.

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

Достоинство агрегирования с помощью файла заключается в том, что файл не имеет ограничения на количество составляющих его элементов и это количество может изменяться во время выполнения программы.

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

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

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

Пример структуры данных представлен на рис.3.1. В данной структуре ячейка header содержит указатель на запись, состоящую из двух полей: cur и next. Поле cur содержит курсор, содержащий индекс логически первого элемента массива reclist (в данном случае четвертый элемент массива является логически первым, см. ниже). Поле next содержит указатель на аналогичную запись, в которой поле cur содержит курсор на логически последнюю запись массива reclist (в примере это вторая запись), а поле next содержит пустой указатель. Массив reclist состоит из четырех записей с индексами от 1 до 4. Каждая запись состоит из двух полей: data и next. Поле data содержит данные (в примере это символы a,b,c,d). Логический порядок элементов массива определяется значением курсора в поле next - курсор хранит индекс логически следующего элемента массива. Так за четвертым элементом следует первый, за первым - третий, за третьим - второй. Второй элемент массива логически последний, т.к. значение поля next второго элемента равно 0, т.е. отличается от любого значения индекса массива - 1,2,3 или 4.

 
  Данные (сведения необходимые для начала работы) - student2.ru

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

3.1.4.Оператор определения имени типа typedef

Новые имена типов данных в Си можно определять, используя ключевое слово typedef. На самом деле таким способом новый тип данных не создается, а всего лишь определяется новое имя для уже существующего типа. Этот процесс может помочь сделать машинно-зависимые программы более переносимыми. Если вы для каждого машинно-зависимого типа данных, используемого в вашей программе, определяете данное вами имя, то при компиляции для новой среды придется менять только операторы typedef.

Общий вид декларации typedef (оператора typedef) такой:

typedef тип новое_имя;

где тип — это любой тип данных языка Си, а новое_имя — новое имя этого типа. Новое имя является дополнением к уже существующему, а не его заменой.

Например, для float можно создать новое имя с помощью

typedef float balance;

Это выражение дает компилятору указание считать balance еще одним именем float. Затем, используя balance, можно создать переменную типа float:

balance over_due;

Теперь, когда имя balance определено, его можно использовать и в другом операторе typedef. Например, выражение

typedef balance overdraft;

дает компилятору указание признавать overdraft в качестве еще одного имени balance, которое в свою очередь является еще одним именем float.

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

Для создания новых типов в Си можно использовать массивы, указатели и структуры. Более детально структуры, вопросы конструирования новых типов и абстрактные типы данных будут рассмотрены позднее. Сейчас рассмотрим массивы и указатели.

Массивы

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

int a[10];

char c[256];

double d[1000];

В первой строке описан массив целых чисел из 10 элементов. Подчеркнем, что нумерация в Си всегда начинается с нуля, так что индексы элементов массива изменяются в пределах от 0 до 9. Во второй строке описан массив символов из 256 элементов (индексы в пределах 0...255), в третьей - массив вещественных чисел из 1000 элементов (индексы в пределах 0...999). Для доступа к элементу массива указывается имя массива и индекс (номер) элемента в квадратных скобках, например,

a[9], c[255], d[123].

Функция sizeof() возвращает размер всего массива в байтах, а не в элементах массива. В данном примере

sizeof(a) = 10*sizeof(int) = 40,

sizeof(c) = 256*sizeof(char) = 256,

sizeof(d) = 1000*sizeof(double) = 8000.

При описании массива можно выполнить инициализацию его элементов:

double a[3]={5.2, 6.7, 3.1}};

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

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

Указатели

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

int *a, *b, c, d;

char *e;

В первой строке описаны указатели a и b на тип int и простые переменныe c и d типа int.

Для указателей определены две унарные операции:

1. присвоить указателю адрес некоторой переменной. Для этого используется операция взятия адреса, которая обозначается амперсендом &. Например, строка a = &c; указателю a присваивает значение адреса переменной c;

2. получить объект, адрес которого содержится в указателе; для этого используется операция звездочка '*', которая записывается перед указателем. (Заметим, что звездочкой обозначается также операция умножения.) Например, строка d = *a; присваивает переменной d значение целочисленной переменной, адрес которой содержится в a. Так как ранее указателю a был присвоен адрес переменной c, то в результате переменной d присваивается значение c, т.е. данная строка эквивалентна следующей: d = c;

Указатели и массивы

Между указателями и массивами существует тесная связь. Имя массива a является указателем на его первый элемент, т.е. выражения a и &(a[0]) эквивалентны. Подробнее связь между массивами и указателями будет рассмотрена в дальнейшем.

Наши рекомендации