Автор Анна Евкова
Преподаватель который помогает студентам и школьникам в учёбе.

ПОНЯТИЕ ПЕРЕМЕННОЙ В ПРОГРАММИРОВАНИИ. ВИДЫ И ТИПЫ ПЕРЕМЕННЫХ (Простые и сложные типы данных)

Содержание:

Введение

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

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

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

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

В курсовой работе переменные будут рассмотрены на основе языка программирования Си. Это компилируемый статически типизированный язык программирования общего назначения. Он был разработан в 1969-1973 годах сотрудником Bell Labs Деннисом Ритчи. К 1973 году на языке Си была переписана большая часть ядра Unix, а сам язык оказал существенное влияние на всю индустрию разработки программного обеспечения. В том числе, синтаксис языка Си впоследствии стал основой для таких языков программирования, как C++, C#, Java.

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

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

Данная курсовая работа состоит из трех глав.

В первой главе будет подробно рассмотрено понятие переменной.

Во второй главе мы рассмотрим простые и сложные типы данных и особенности работы с ними.

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

  1. Понятие переменной в программировании

1.1. Переменные

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

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

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

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

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

Аналогом ячеек памяти в языках программирования высокого уровня являются переменные.

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

После того, как переменной будет присвоено некоторое значение, то саму переменную можно использовать в дальнейших вычислениях. Например, в следующем участке кода в переменную X заносится значение 9, в переменную Y заносится значение -13. А в переменной c сохраняется значение сложения X и Y, равное -4.

X = 9;

Y = -13;

Sum = X + Y;

Значения переменных X и Y могут быть изменены, они могут быть считаны с клавиатуры от пользователя, или быть результатом других выражений. В переменную Sum будет занесен результат вычисления, основанный на значениях переменных X и Y. Таким образом, использование переменных в программировании позволяет писать универсальные программы. Программы, которые не зависят от конкретных значений, и могут запускаться и получать результат для различных начальных данных. При том, что внесения изменений в код программы не требуется.

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

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

На рисунке 1 изображена схема хранения значения переменных в ячейках памяти.

Рисунок 1. Хранение значений переменных в памяти

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

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

Например, пускай у нас есть две переменные X и Y. И мы хотим получить сумму двух этих чисел в отдельную переменную. Для имени результирующей переменной лучше выбрать значение sum, нежели абстрактное z.

sum = X + Y;

z = X + Y;

При таком подходе при разработке более крупных приложений намного легче ориентироваться в переменных, а не путаться в бесконечных A, B, C, A1, что способствует повышению скорости и качества разработки.

В языке Си существует некоторый набор требований к имени переменной.

Имя переменной не может являться ключевым словом языка. Ключевыми словами называются предопределенные идентификаторы, которые имеют специальное значение для компилятора языка Си. Список ключевых слов языка Си приведен в приложении 1.

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

Ниже приведены несколько примеров с корректными именами переменных:

«a», «abc», «Abc», «_aaa», «var1», «_var_12»

Ниже приведены несколько примеров с не корректными именами переменных:

«123», «awd awd», «переменная1»

Также нужно помнить, что язык Си является регистрозависимым языком. Это значит, что переменные с идентификаторами «abc» и «ABC», «var1» и «vaR1», «countItems» и «countitems» являются различными переменными и могут содержать в себе разные значения.

1.2. Типы данных переменных

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

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

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

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

Тип переменной задает следующие параметры:

  • размер ячейки;
  • способ кодирования содержимого ячейки;
  • допустимые операции преобразования над значением переменной.

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

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

В языке Си имеется четыре базовых типа:

  • char – символьный тип;
  • int – целый тип;
  • float – вещественный тип одинарной точности;
  • double – вещественный тип двойной точности.

Подробнее про каждый тип данных будет рассказано во второй главе.

1.3. Хранение переменных в памяти компьютера

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

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

Стеком называется такая область оперативной памяти, которая создаётся для каждого потока. Стек организован и работает в порядке LIFO (Last In, First Out, «последним пришел, первым вышел»). Другими словами, последний добавленный в стек элемент, в частности кусок памяти, будет первым в очереди на вывод из стека при получении данных. Каждый раз, когда функция программы объявляет новую переменную, она добавляется в стек, а когда эта переменная пропадает из области видимости (например, это происходит в тот момент, когда функция заканчивается), она автоматически удаляется из стека. В момент, когда стековая переменная освобождается, эта область памяти становится доступной для использования других стековых переменных. [1]

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

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

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

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

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

Указатели - это переменные, чьи значения являются адресами других переменных. Создавая указатель, программист указывает на местоположение памяти в куче, что задаёт начальное значение переменной и говорит программе, где получить доступ к этому значению. Из-за динамической природы кучи ЦП не принимает участия в контроле над ней; в языках без сборщика мусора (C, C++) разработчику нужно вручную освобождать участки памяти, которые больше не нужны. Если этого не делать, могут возникнуть утечки и фрагментация памяти, что существенно замедлит работу кучи.

Стек, в сравнении с кучей, работает быстрее. Связано это с тем, что в куче переменные разбросаны по памяти, а не доступны на верхушке стека. Некорректное управление памятью в куче приводит к замедлению её работы; тем не менее, эти недостатки не уменьшают важности кучи при разработке программ — если есть необходимость работать с динамическими или глобальными переменными, лучше воспользоваться кучей. [1]

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

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

В Си любая переменная должна быть объявлена раньше, чем она будет использована; обычно все переменные объявляются в начале функции перед первой исполняемой инструкцией. В объявлении описываются свойства переменных. [3]

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

[<спецификатор_класса_памяти>] [<модификатор(ы)>] <тип> <имя1>[=<инициализатор1>], <имя2>[=<инициализатор2>],…, <имяN>[=<инициализаторN>];

Элемент <спецификатор_класса_памяти> является необязательным и может принимать одно из доступных значений из ключевых слов:

  • auto;
  • register;
  • static;
  • extern.

Подробнее про них будет рассказано в параграфе «Классы памяти».

Элемент <модификатор(ы)> является необязательным.

Обычно переменной соответствует стандартная длина в соответствии с обозначенным типом, обусловленная аппаратными особенностями используемого вычислительного устройства. Например, для компьютеров IBM PC стандартная длина слова в памяти составляет 2/4 байта. Соответственно, тип переменной, к примеру, int определяет соответствующую интерпретацию - первый бит содержит разряд знака, а остальные 15/31 бит будут содержать значение числа.

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

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

  • short;
  • long;
  • signed;
  • unsigned;
  • const;
  • volatile.

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

Модификатор long расширяет переменную int c двух до четырех байт.

Модификаторы signed и unsigned задают интерпретацию доступных для типа данных значений, как, соответственно, знаковую и беззнаковую. Данный модификатор возможно использовать только для типов int и char.

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

Например, нельзя использовать модификатор signed не для типов int и char. Также, невозможно объявить переменную одновременно знаковой и беззнаковой – «signed unsigned».

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

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

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

Модификатор volatile является полной противоположностью для модификатора const. Он сообщает компилятору о том, что значение, которое принимает переменная может быть изменено не только внутри самой программы, но также и каким-либо внешним воздействием со стороны операционной системы. Например таким как программа прерываний или порт ввода-вывода.

Элемент <тип> является обязательным и должен принимать один из допустимых типов данных.

При этом тип при описании переменной может быть не указан, если объявлены модификаторы. В таком случае компилятор присваивает переменной тип int, и применяет указанные модификаторы. Например, возможно такое объявление:

unsigned a;

Элементы вида <имяN> являются идентификатором переменной. Через них в дальнейшем будет выполняться взаимодействие с переменной внутри программы.

Элементы вида [=<инициализаторN>] являются необязательными и могут выполнять изначальную инициализацию значений переменной. В качестве инициализатора может выступать любое допустимое значение, например число или строка, другая переменная, результат выполнения функции.

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

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

int a; // объявление целочисленной переменной

unsigned b; // объявление беззнаковой целочисленной переменной

unsigned int b; // аналогично предыдущей строке

char g = 'Q', k = 'm'; // объявление двух символьных переменных с инициализацией значений

const float pi=3.14155926; // объявление и инициализация константной вещественной переменной. В дальнейшем коде значение переменной pi не может быть изменено.

Если переменной не присвоено начальное значение, то в случае глобальной переменной её значение заполняется нулями, а для локальной переменной начальное значение будет неопределённым.[12] После объявления переменной, участок оперативной памяти, на который ссылается переменная, может хранить «мусорное» значение – некие случайные данные, которые остались в области памяти.

1.5. Области видимости переменных

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

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

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

Например, рассмотрим следующий пример кода:

int globalVar = 1;

void foo() {

printf("foo: %d\n", globalVar);

}

void bar(int globalVar) {

printf("bar: %d\n", globalVar);

}

int main() {

foo();

bar(2);

getch();

}

В результате выполнения данной программы будет выведено

foo: 1

bar: 2

Почему так происходит? В самом начале объявляется глобальная переменная globalVar, и инициализируется значением 1. Теперь данная переменная доступна из любого участка кода, и функция foo получает доступ к переменной globalVar, и выводит её значение, равное единице.

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

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

int main() {

{

int y = 30;

}

printf("%d", y);

}

В данном примере объявлена локальная переменная y, и данный пример не скомпилируется, потому что локальная переменная существует только внутри своего блока, у не доступна в строке printf("%d", y) при выводе.

Видимость объявленных переменных в программе определяется следующими правилами:

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

1.6. Классы памяти переменных

Как было указано в параграфе объявления переменных, переменной при объявлении можно не только указать тип данных, модификатор, имя и начальное значение. Также для переменной можно указать спецификатор класса памяти. [4]

Рассмотрим доступные в языке Си классы памяти.

Автоматический класс памяти. Принимает значение auto. Является классом, который используется для переменных автоматически, если не указан другой класс. Такие переменные располагаются на стеке, а область видимости переменных ограничена своим блоком. Спецификатор auto уведомляет компилятор о том, что локальная переменная, перед именем которой он стоит, создаётся при входе в блок и разрушается при выходе из блока. Все переменные, определённые внутри функции, являются автоматическими по умолчанию, и поэтому ключевое слово auto используется крайне редко. Например, следующие объявления переменных дадут одинаковый результат:

int a;

auto int a;

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

register int x = 20;

register int y = 30;

Так как регистровая переменная не имеет адреса, то к ней не применима операция взятия адреса, это вызовет ошибку во время компиляции. Аргументы функции также могут быть заданы как register. Внутри функции они будут вести себя также, как и регистровые переменные.

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

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

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

Класс памяти extern обычно используется в двух случаях:

  • если используемая переменная в программе объявляется ниже, чем ссылка на неё;
  • если переменная объявляется в другом модуле.

Пример использования внешней переменной:

#include <stdio.h>

int x=145; //Описание внешней переменной

main()

{

extern int x,y;

printf("x=%d y=%d \n",x,y);

}

int y=541; //Описание внешней переменной

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

2. Простые и сложные типы данных

2.1. Целочисленные типы

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

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

Беззнаковые целые числа представлены в виде последовательности битов в диапазоне от 0 до 2n-1, где n-количество занимаемых битов.

Знаковые целые числа представляются в диапазоне от -2n-1 до +2n-1-1. При этом старший бит знаковых чисел отводится для знака числа (0 соответствует положительному числу, 1 – отрицательному).

Основные типы и размеры целочисленных данных представлены в таблице 1.

Таблица 1. Целочисленные типы

Количество бит

Беззнаковый тип

Знаковый тип

8

unsigned char

char

16

unsigned short

short

32

unsigned int

int

64

unsigned long int

long int

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

  • сравнение. Сюда относятся сравнение на равенство и не равенство, отношения порядка (больше/меньше, больше/меньше или равно);
  • инкремент и декремент – арифметическое увеличение и уменьшение числа на единицу;
  • арифметические операции – сложение, вычитание, умножение, деление, получение остатка от деления;
  • инверсия и получение знака.

Также переменным целочисленного типа могут быть применены побитовые операции – сдвиги, побитовое «И», «ИЛИ» и другие. [6]

2.2. Вещественные типы

Вещественный тип в языке Си предназначен для представления действительных чисел. Вещественные числа представляются в разрядной сетке машины в нормированной форме.

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

12345,678 = 1,2345678·104

Число 0,009876 в нормированной форме можно представить как

0,009876 = 9,876·10-3

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

Значащие разряды числа, стоящие в нормированной форме после разделителя целой и дробной части, называются мантиссой числа.

В общем случае вещественное число в разрядной сетке вычислительной машины можно представить в виде 4 полей. Графическое изображение полей представлено на рисунке 2.

Вещественное число в разрядной сетке

Рисунок 2

  • знак — бит, определяющий знак вещественного числа (0 для положительных чисел, 1 — для отрицательных);
  • порядок — определяет степень 2, на которую требуется умножить число в нормированной форме. Поскольку степень 2 для числа в нормированной форме может быть как положительной, так и отрицательной, нулевой степени 2 в представлении вещественного числа соответствует величина сдвига, которая определяется как
    2n-1, где n — количество разрядов, отводимых для представления степени числа;
  • целое — бит, который для нормированных чисел всегда равен 1, поэтому в некоторых представлениях типов этот бит опущен и принимается равным 1;
  • мантисса — значащие разряды представления числа, стоящие после разделителя целой и дробной части в нормированной форме.

В языке Си представлены три основных типа представления вещественных чисел. Они приведены в таблице 2.

Таблица 2. Представление вещественных типов

Тип

Обозначение в Си

Кол-во бит

Биты степени

Мантисса

Сдвиг

простое

float

32

30…23

22…0

127

двойной точности

double

64

62…52

51…0

1023

двойной расширенной точности

long double

80

78…64

62…0

16383

2.3 Символьный тип

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

Фактически, тип char является целочисленным однобайтным типом. Значение переменной типа char в программе, например при выводе, может выводиться и как целое, и как символ. Например, код ниже:

#include <stdio.h>

int main()

{

char c = 66;

printf("as int %d. as char %c", c, c);

return 0;

}

выведет следующий результат:

as int 66. as char В

2.4. Логический тип

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

  • истина — true;
  • ложь — false.

2.5. Структуры

Структура – это объединенное в единое целое множество поименованных элементов данных. В отличии от массива, всегда состоящего из однотипных элементов, компоненты структуры могут быть разных типов и все должны иметь различные имена. [11]

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

struct <имя> {

<тип1> <поле1>;

...

<типN> <полеN>;

};

После ключевого слова struct идет имя структуры, после в фигурных скобках перечисляются поля структуры. Элементы <типN> и <полеN> описывают, соответственно, тип и имя соответствующего поля.

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

struct point_t {

int x;

int y;

};

void main() {

struct point_t A = { 5, 15 };

A.x = 10;

A.y = 20;

}

В данном примере структура A сначала была проинициализирована, после чего были изменены значения её полей.

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

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

Память для объединения выделяется по размеру самого длинного из полей. Когда определяется значение одного из полей – то оно затирает предыдущее значение любого поля. [10]

Пример объявление и использования объединений представлен ниже:

union myUnion

{

int x;

double d;

};

void main(void)

{

union myUnion uUnion = {150};

uUnion.d = 123.12;

}

2.7. Массивы

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

double ar[100];

Именем массива выступает ar, массив объявляется на 100 элементов, и все элементы доступны через индексы от 0 до 99 включительно. Для доступа к конкретному индексу используется синтаксис ar[5].

Каждый из элементов массива принадлежит к типу, указанному при описании массива, в нашем примере типу double. [9]

2.8. Приведение типов

Операция приведения типа в языке Си временно изменяет тип переменной с одного на другой. Ниже приведен формат операции приведения типа:

(<типДанных>)<значение>

Элемент <типДанных> может быть любым типом данных языка Си, например int или float. Элемент <значение> — это любая переменная, литерал или выражение.

Предположим, что целочисленная переменная age содержит значение 6, следующая строка конвертирует это значение в значение с плавающей точкой 6.0:

(float)age;

Целочисленная переменная age не превращается в переменную типа float, а лишь временно приводится к этому типу только для вычисления значения этого выражения. В остальных участках программного кода, где тип переменной age не приводится, эта переменная так и остается целочисленной. [5]

3. Указатели

Указатель — переменная, содержащая адрес объекта. Указатель не несет информации о содержимом объекта, а содержит сведения о том, где размещен объект. [7]

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

int *pointerToInteger;

Здесь объявляется переменная pointerToInteger.

Тип данной переменной – указатель на переменную типа int.

Рассмотрим две переменные: первая - целочисленная переменная x. Вторая - указатель на целочисленную переменную.

int x;

int *p;

Чтобы получить адрес переменной, нужно перед ее именем написать амперсанд.

p = &x;

Данная конструкция будет выполняться справа налево. Сначала с помощью оператора &, примененного к переменной x, будет получен адрес x. Затем адрес x будет сохранен в указателе p.

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

int y = *p;

Такая операция в русском языке называется разыменование.

В данном примере с помощью оператора * мы получим то значение, которое находится в памяти по адресу p. Затем мы сохраним его в переменную y. В итоге получится, что значения x и y совпадают. [2]

Одной из более распространенных конструкций с использованием указателей являются массивы. Результатом использования указателей для массивов являются меньшее количество используемой памяти, а также высокая производительность. Если был создан массив values, который содержит 100 целочисленных переменных, можно создать указатель с именем valuesPtr, который будет использоваться для доступа к значениям элементов массива. Например

int *valuesPtr;

valuesPtr = values;

В данном случае указатель valuesPtr будет ссылаться на первый элемент массива values. Оператор адресации в данном случае не используется, так как компилятор языка Си трактует имя массива без индекса как указатель на массив. [8]

Для обращения к третьему элементу массива (values[3]) с помощью указателя valuesPtr можно воспользоваться следующей записью:

* (valuesPtr + 3)

В общем случае используется выражение:

* (valuesPtr + i)

где i – индекс нужного элемента массива.

Заключение

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

Язык Си предоставляет возможность программисту получения прямого доступа к ячейкам памяти, регистрам компьютера. Такое поведение частично напоминает язык низкого уровня Ассемблер. Хотя на самом деле, Си представляет более универсальные и мощные возможности для разработки программного обеспечения, решения трудных задач и создания сложных программных систем.

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

Эти элементы постоянно используются при разработке. Практически каждая строчка кода содержит как минимум одну переменную.

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

Библиография

  1. А. Краус. Основные принципы программирования: стек и куча. URL: https://tproger.ru/translations/programming-concepts-stack-and-heap (Дата обращения: 12.03.2019)
  2. А. Черный. Указатели и ссылки в С и С++ URL: http://chernyy.ru/blog/2010/04/19/pointers-and-references-in-c-and-cpp (Дата обращения: 12.03.2019)
  3. Б. Керниган, Д. Ритчи. Язык программирования Си. - Финансы и статистика, 2001
  4. В. Черемных. Классы памяти в языке Си. URL: https://it-black.ru/klassy-pamyati-v-yazyke-si/ (Дата обращения: 12.03.2019)
  5. Г. Перри, Д. Миллер. Программирование на Си для начинающих, 3-е издание - Эксмо, 2015
  6. Е. Вставская. Типы данных в языке Си. URL: https://prog-cpp.ru/c-data-types/ (Дата обращения: 12.03.2019)
  7. Е. Вставская. Типы данных в языке Си. URL: https://prog-cpp.ru/c-pointers/ (Дата обращения: 12.03.2019)
  8. К. Стефан. Программирование на языке Си. - Вильямс, 2007
  9. М. Банахан. Массивы URL: https://publications.gbdirect.co.uk/c_book/chapter5/arrays.html (Дата обращения: 12.03.2019)
  10. Объединения в Си. URL: http://www.sbp-program.ru/c/sbp-c-union.htm (Дата обращения: 12.03.2019)
  11. Подбельский В. В. Фомин С. С. Курс программирования на языке Си. Учебник - ДМК-Пресс, 2012 г.
  12. Си (язык программирования). Переменные. URL: https://ru.wikipedia.org/wiki/Си_(язык_программирования)#Переменные (Дата обращения: 12.03.2019)

Приложение

Приложение 1. Список ключевых слов языка Си

  • auto
  • continue
  • else
  • for
  • long
  • signed
  • switch
  • void
  • break
  • default
  • enum
  • goto
  • register
  • sizeof
  • typedef
  • while
  • case
  • do
  • extern
  • if
  • return
  • static
  • union
  • char
  • double
  • float
  • int
  • short
  • struct
  • unsigned