Типы данных в си. Типы данных

Типы данных в си. Типы данных

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

При динамической типизации переменная связывается с типом на момент инициализации. Получается, что переменная в разных участках кода может иметь разные типы. Динамическую типизацию поддерживают Java Script, Python, Ruby, PHP.

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

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

x = 1 + “2”; //ошибка — нельзя прибавить к числу символьный знак

Пример слабой типизации.

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

Классы памяти

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

Классы памяти:

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

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

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

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

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

  1. Все переменные внутри блока не являются переменными, соответственно, если предполагается использование именно этого класса памяти, то спецификатор auto не указывается.
  2. Все функции, объявленные вне блока или функции, являются по умолчанию глобальными, поэтому спецификатор extern не обязателен.

Для указания простых типов указываются спецификаторы int, char, float или double. К переменным могут подставляться модификаторы unsigned (беззнаковый), signed (знаковый), short, long, long long.

По умолчанию все числа являются знаковыми, соответственно, могут находиться в диапазоне только положительных чисел. Чтобы определить переменную типа char как знаковую, пишется signed char. Long, long long и short указывают, как много места в памяти отводится для хранения. Наибольшее — long long, наименьшее — short.

Char — самый маленький тип данных в Си. Для хранения значений выделяется всего 1 байт памяти. Переменной типа character обычно присваиваются символы, реже — цифры. Символьные значения берутся в кавычки.

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

Явное преобразование беззнаковой переменной задается так:

Неявное выглядит так:

Float и double определяют числа с точкой. Числа float представляются в виде -2.3 или 3.34. Double используется для большей точности — после разделителя целой и дробной части указывается больше цифр. Этот тип занимает больше места в памяти, чем float.

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

Логический тип Bool

Применяется в проверках условий и циклах. Имеет всего два значения:

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

Булевые значения могут преобразовываться в значение типа int. True эквивалентно единице, false — нулю. Преобразование типов предусмотрено только между bool и int, в противном случае компилятор выдаст ошибку.

if (x) { //Error: «Cannot implicitly convert type ‘int’ to ‘bool"»

if (x != 0) // The C# way

Строки и массивы

Массивы относятся к сложными типам даным в Си. ЯП не работает со строками так же, как это делает Джаваскрипт или Руби. В Си все строки являются массивами элементов символьного значения. Строки оканчиваются нулевым байтом “

Программист говорит:

Здравствуйте! Я прочел вашу статью. Мне было очень грустно и одновременно смешно. Особенно убивает эта ваша фраза: «Так как переменную типа char часто используют как массив, то определяют количество возможных значений». 😆 😆 😆
Я не смеюсь над вами. Создание сайта это действительно подвиг. Я лишь хочу поддержать вас советом и указать несколько ошибок.

1. Значение переменной типа char присваивается так:

Вот здесь:

Char a = *"A";

Происходит разадресация указателя на массив и в результате возвращается значение первого элемента массива т.е. ‘A’

2. Обнуление происходит так:

Char a = NULL;
char b = {};

//А так очищается строка в теле программы

"" -- этот символ называется ноль-терминатор. Он ставится в конце строки. Вы сами того не зная заполнили этим символом массив s1 из вашей статьи. А ведь можно было присвоить этот символ только нулевому элементу массива.

3. Смело пользуитесь терминологией.
Знак = это операция присваивания.
Знак * операция разадресации.
Я имею в виду вот этот фрагмент статьи: "Настолько всё оказалось просто, перед знаком = нужно было поставить знак * и нужно было объявить номер элемента (ноль соответствует первому)"

Поймите меня правильно, статья в нынешнем виде не может существовать. Не поленитесь, перепишите ее.
На вас лежит большая ответственность! Я серьезно. Страницы вашего сайта попали в первую страницу выдачи Яндекса. Много людей уже начали повторять за вами ошибки.

Удачи! Вы справитесь!

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

Я буду рад исправить и другие ошибки. поправить неточности, если они выскочат. Я ценю вашу помощь. спасибо.Я это давно знаю, просто трудно перечитывать 200 статей постоянно, чтобы что-то исправить. А некоторые грубые типы так пишут, что даже зная, что лучше исправить, исправлять совсем не охота.
С вашим char b = {}; Это не обнуление совсем. проверили бы хотя б.
если говорить о нулевом символе "" ; Я хорошо знал, когда заполнял им строку и цель была в том, чтобы показать настоящее очищение, а не видимое на глаз, ибо в строку входит мусор, который иногда мешает. Вы бы с терминами сами поаккуратнее, "символ нуль-терминации" или просто "нулевой символ", не терминатор))) А символ-терминатор звучит просто круто.

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

Я буду рад исправить и другие ошибки. поправить неточности, если они выскочат. Я ценю вашу помощь. спасибо.

Здравствуйте еще раз!
Хочу пояснить. Термином "ноль-терминатор" (terminator с англ. ограничитель) пользовался мой преподаватель в ВУЗе. Видимо это old school!
Что касается обнуления строк.
char b = {}; Это действительно обнуление. Весь массив заполнен нулями. Не верите -- проверьте!
Если рассматривать строку в её естественном, бытовом смысле, то "пустой" будет та строка в которой нет ни одного символа. Поэтому в 99.9% случаев достаточно поставить в начало нулевой символ. Обычно обработка строки идет до первого нулевого символа а какие символы идут за ним уже не важно. Я понимаю что вы хотели обнулить строку. Просто решил предложить проверенный временем классический вариант.

:
Когда "Обычно обработка строки идет до первого нулевого символа а какие символы идут за ним уже не важно" - да, строка обнуляется
Если рассматривать "реальное обнуление всех ячеек строки (о котором писал я)" - нет, не обнуление и, даже, первый символ не нулевой. Я этим вариантом проверял. MinGW(CodeBlock) - весь массив отдает символ "a"
не думаю, что это повод для споров.

Типы данных

Типы данных имеют особенное значение в C#, поскольку это строго типизированный язык. Это означает, что все операции подвергаются строгому контролю со стороны компилятора на соответствие типов, причем недопустимые операции не компилируются. Следовательно, строгий контроль типов позволяет исключить ошибки и повысить надежность программ. Для обеспечения контроля типов все переменные, выражения и значения должны принадлежать к определенному типу. Такого понятия, как "бестиповая" переменная, в данном языке программирования вообще не существует. Более того, тип значения определяет те операции, которые разрешается выполнять над ним. Операция, разрешенная для одного типа данных, может оказаться недопустимой для другого.

В C# имеются две общие категории встроенных типов данных: типы значений и ссылочные типы . Они отличаются по содержимому переменной. Концептуально разница между ними состоит в том, что тип значения (value type) хранит данные непосредственно, в то время как ссылочный тип (reference type) хранит ссылку на значение.

Эти типы сохраняются в разных местах памяти: типы значений сохраняются в области, известной как стек , а ссылочные типы - в области, называемой управляемой кучей .

Давайте разберем типы значений.

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

В C# определены девять целочисленных типов: char, byte, sbyte, short, ushort, int, uint, long и ulong . Но тип char применяется, главным образом, для представления символов и поэтому рассматривается отдельно. Остальные восемь целочисленных типов предназначены для числовых расчетов. Ниже представлены их диапазон представления чисел и разрядность в битах:

Целочисленные типы C#
Тип Тип CTS Разрядность в битах Диапазон
byte System.Byte 8 0:255
sbyte System.SByte 8 -128:127
short System.Int16 16 -32768: 32767
ushort System.UInt16 16 0: 65535
int System.Int32 32 -2147483648: 2147483647
uint System.UInt32 32 0: 4294967295
long System.Int64 64 -9223372036854775808: 9223372036854775807
ulong System.UInt64 64 0: 18446744073709551615

Как следует из приведенной выше таблицы, в C# определены оба варианта различных целочисленных типов: со знаком и без знака. Целочисленные типы со знаком отличаются от аналогичных типов без знака способом интерпретации старшего разряда целого числа. Так, если в программе указано целочисленное значение со знаком, то компилятор C# сгенерирует код, в котором старший разряд целого числа используется в качестве флага знака. Число считается положительным, если флаг знака равен 0, и отрицательным, если он равен 1.

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

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

Так, если значение нужно сохранить без знака, то для него можно выбрать тип uint , для больших значений со знаком - тип long , а для больших значений без знака - тип ulong . В качестве примера ниже приведена программа, вычисляющая расстояние от Земли до Солнца в сантиметрах. Для хранения столь большого значения в ней используется переменная типа long:

Using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { class Program { static void Main(string args) { long result; const long km = 149800000; // расстояние в км. result = km * 1000 * 100; Console.WriteLine(result); Console.ReadLine(); } } }

Всем целочисленным переменным значения могут присваиваться в десятичной или шестнадцатеричной системе обозначений. В последнем случае требуется префикс 0x:

Long x = 0x12ab;

Если возникает какая-то неопределенность относительно того, имеет ли целое значение тип int, uint, long или ulong, то по умолчанию принимается int. Чтобы явно специфицировать, какой другой целочисленный тип должно иметь значение, к числу можно добавлять следующие символы:

Uint ui = 1234U; long l = 1234L; ulong ul = 1234UL;

U и L можно также указывать в нижнем регистре, хотя строчную L легко зрительно спутать с цифрой 1 (единица).

Типы с плавающей точкой

Типы с плавающей точкой позволяют представлять числа с дробной частью. В C# имеются две разновидности типов данных с плавающей точкой: float и double . Они представляют числовые значения с одинарной и двойной точностью соответственно. Так, разрядность типа float составляет 32 бита, что приближенно соответствует диапазону представления чисел от 5E-45 до 3,4E+38. А разрядность типа double составляет 64 бита, что приближенно соответствует диапазону представления чисел от 5E-324 до 1,7Е+308.

Тип данных float предназначен для меньших значений с плавающей точкой, для которых требуется меньшая точность. Тип данных double больше, чем float, и предлагает более высокую степень точности (15 разрядов).

Если нецелочисленное значение жестко кодируется в исходном тексте (например, 12.3), то обычно компилятор предполагает, что подразумевается значение типа double. Если значение необходимо специфицировать как float, потребуется добавить к нему символ F (или f):

Float f = 12.3F;

Десятичный тип данных

Для представления чисел с плавающей точкой высокой точности предусмотрен также десятичный тип decimal , который предназначен для применения в финансовых расчетах. Этот тип имеет разрядность 128 бит для представления числовых значений в пределах от 1Е-28 до 7,9Е+28. Вам, вероятно, известно, что для обычных арифметических вычислений с плавающей точкой характерны ошибки округления десятичных значений. Эти ошибки исключаются при использовании типа decimal, который позволяет представить числа с точностью до 28 (а иногда и 29) десятичных разрядов. Благодаря тому что этот тип данных способен представлять десятичные значения без ошибок округления, он особенно удобен для расчетов, связанных с финансами:

Using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { class Program { static void Main(string args) { // *** Расчет стоимости капиталовложения с *** // *** фиксированной нормой прибыли*** decimal money, percent; int i; const byte years = 15; money = 1000.0m; percent = 0.045m; for (i = 1; i

Результатом работы данной программы будет:

Символы

В C# символы представлены не 8-разрядным кодом, как во многих других языках программирования, например С++ , а 16-разрядным кодом, который называется юникодом (Unicode) . В юникоде набор символов представлен настолько широко, что он охватывает символы практически из всех естественных языков на свете. Если для многих естественных языков, в том числе английского, французского и немецкого, характерны относительно небольшие алфавиты, то в ряде других языков, например китайском, употребляются довольно обширные наборы символов, которые нельзя представить 8-разрядным кодом. Для преодоления этого ограничения в C# определен тип char , представляющий 16-разрядные значения без знака в пределах от 0 до 65 535. При этом стандартный набор символов в 8-разрядном коде ASCII является подмножеством юникода в пределах от 0 до 127. Следовательно, символы в коде ASCII по-прежнему остаются действительными в C#.

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

Объявления переменной имеет следующий формат:

[спецафикатор-класа-памяти] спецификатор-типа описатель [=инициатор] [,описатель [= инициатор] ]...

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

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

Инициатор - задает начальное значение или список начальных значений, которые (которое) присваивается переменной при объявлении.

Спецификатор класса памяти - определяется одним из четырех ключевых слов языка СИ: auto, extern, register, static, и указывает,каким образом будет распределяться память под объявляемую переменную, с одной стороны, а с другой, область видимости этой переменной, т.е., из каких частей программы можно к ней обратиться.

1.2.1 Категории типов данных

Ключевые слова для определения основных типов данных

Целые типы: Плавающие типы: char float int double short long double long signed unsigned

Переменная любого типа может быть объявлена как немодифицируемая. Это достигается добавлением ключевого слова const к спецификатору-типа. Объекты с типом const представляют собой данные используемые только для чтения, т.е. этой переменной не может быть присвоено новое значение. Отметим, что если после слова const отсутствует спецификатор-типа, то подразумевается спецификатор типа int. Если ключевое слово const стоит перед объявлением составных типов (массив, структура, смесь, перечисление), то это приводит к тому, что каждый элемент также должен являться немодифицируемым, т.е. значение ему может быть присвоено только один раз.

Const double A=2.128E-2; const B=286; (подразумевается const int B=286)

Примеры объявления составных данных будут рассмотрены ниже.

1.2.2. Целый тип данных

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

Таблица 6

Отметим, что ключевые слова signed и unsigned необязательны. Они указывают, как интерпретируется нулевой бит объявляемой переменной, т.е., если указано ключевое слово unsigned, то нулевой бит интерпретируется как часть числа, в противном случае нулевой бит интерпретируется как знаковый. В случае отсутствия ключевого слова unsigned целая переменная считается знаковой. В том случае, если спецификатор типа состоит из ключевого типа signed или unsigned и далее следует идентификатор переменной, то она будет рассматриваться как переменная типа int. Например:

Unsigned int n; unsigned int b; int c; (подразумевается signed int c); unsigned d; (подразумевается unsigned int d); signed f; (подразумевается signed int f).

Отметим, что модификатор-типа char используется для представления символа (из массива представление символов) или для объявления строковых литералов. Значением объекта типа char является код (размером 1 байт), соответствующий представляемому символу. Для представления символов русского алфавита, модификатор типа идентификатора данных имеет вид unsigned char, так как коды русских букв превышают величину 127.

Следует сделать следующее замечание: в языке СИ не определено представление в памяти и диапазон значений для идентификаторов с модификаторами-типа int и unsigned int. Размер памяти для переменной с модификатором типа signed int определяется длиной машинного слова, которое имеет различный размер на разных машинах. Так, на 16-ти разрядных машинах размер слова равен 2-м байтам, на 32-х разрядных машинах соответственно 4-м байтам, т.е. тип int эквивалентен типам short int, или long int в зависимости от архитектуры используемой ПЭВМ. Таким образом, одна и та же программа может правильно работать на одном компьютере и неправильно на другом. Для определения длины памяти занимаемой переменной можно использовать операцию sizeof языка СИ, возвращающую значение длины указанного модификатора-типа.

Например:

A = sizeof(int); b = sizeof(long int); c = sizeof(unsigned long); d = sizeof(short);

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

Например:

0xA8C (int signed); 01786l (long signed); 0xF7u (int unsigned);

1.2.3. Данные плавающего типа

Для переменных, представляющих число с плавающей точкой используются следующие модификаторы-типа: float, double, long double (в некоторых реализациях языка long double СИ отсутствует).

Величина с модификатором-типа float занимает 4 байта. Из них 1 байт отводится для знака, 8 бит для избыточной экспоненты и 23 бита для мантиссы. Отметим, что старший бит мантиссы всегда равен 1, поэтому он не заполняется, в связи с этим диапазон значений переменной с плавающей точкой приблизительно равен от 3.14E-38 до 3.14E+38.

Величина типа double занимает 8 бит в памяти. Ее формат аналогичен формату float. Биты памяти распределяются следующим образом: 1 бит для знака, 11 бит для экспоненты и 52 бита для мантиссы. С учетом опущенного старшего бита мантиссы диапазон значений равен от 1.7E-308 до 1.7E+308.

Float f, a, b; double x,y;

1.2.4. Указатели

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

спецификатор-типа [ модификатор ] * описатель.

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

В качестве модификаторов при объявлении указателя могут выступать ключевые слова const, near, far, huge. Ключевое слово const указывает, что указатель не может быть изменен в программе. Размер переменной объявленной как указатель, зависит от архитектуры компьютера и от используемой модели памяти, для которой будет компилироваться программа. Указатели на различные типы данных не обязательно должны иметь одинаковую длину.

Для модификации размера указателя можно использовать ключевые слова near, far, huge.

Unsigned int * a; /* переменная а представляет собой указатель на тип unsigned int (целые числа без знака) */ double * x; /* переменная х указывает на тип данных с плавающей точкой удвоенной точности */ char * fuffer ; /* объявляется указатель с именем fuffer который указывает на переменную типа char */ double nomer; void *addres; addres = & nomer; (double *)addres ++; /* Переменная addres объявлена как указатель на объект любого типа. Поэтому ей можно присвоить адрес любого объекта (& - операция вычисления адреса). Однако, как было отмечено выше, ни одна арифмитическая операция не может быть выполнена над указателем, пока не будет явно определен тип данных, на которые он указывает. Это можно сделать, используя операцию приведения типа (double *) для преобразования addres к указателю на тип double, а затем увеличение адреса. */ const * dr; /* Переменная dr объявлена как указатель на константное выражение, т.е. значение указателя может изменяться в процессе выполнения программы, а величина, на которую он указывает, нет. */ unsigned char * const w = &obj. /* Переменная w объявлена как константный указатель на данные типа char unsigned. Это означает, что на протяжение всей программы w будет указывать на одну и ту же область памяти. Содержание же этой области может быть изменено. */

1.2.5. Переменные перечислимого типа

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

Объявление перечисления начинается с ключевого слова enum и имеет два формата представления.

Формат 1. enum [имя-тега-перечисления] {список-перечисления} описатель[,описатель...];

Формат 2. enum имя-тега-перечисления описатель [,описатель..];

Объявление перечисления задает тип переменной перечисления и определяет список именованных констант, называемый списком-перечисления. Значением каждого имени списка является некоторое целое число.

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

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

В первом формате 1 имена и значения перечисления задаются в списке перечислений. Необязательное имя-тега-перечисления, это идентификатор, который именует тег перечисления, определенный списком перечисления. Описатель именует переменную перечисления. В объявлении может быть задана более чем одна переменная типа перечисления.

Список-перечисления содержит одну или несколько конструкций вида:

идентификатор [= константное выражение]

Каждый идентификатор именует элемент перечисления. Все идентификаторы в списке enum должны быть уникальными. В случае отсутствия константного выражения первому идентификатору соответствует значение 0, следующему идентификатору - значение 1 и т.д. Имя константы перечисления эквивалентно ее значению.

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

1. Переменная может содержать повторяющиеся значения.

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

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

4. Значение может следовать за последним элементом списка перечисления.

Enum week { SUB = 0, /* 0 */ VOS = 0, /* 0 */ POND, /* 1 */ VTOR, /* 2 */ SRED, /* 3 */ HETV, /* 4 */ PJAT /* 5 */ } rab_ned ;

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

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

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

1.2.6. Массивы

Массивы - это группа элементов одинакового типа (double, float, int и т.п.). Из объявления массива компилятор должен получить информацию о типе элементов массива и их количестве. Объявление массива имеет два формата:

спецификатор-типа описатель [константное - выражение];

спецификатор-типа описатель ;

Описатель - это идентификатор массива.

Спецификатор-типа задает тип элементов объявляемого массива. Элементами массива не могут быть функции и элементы типа void.

Константное-выражение в квадратных скобках задает количество элементов массива. Константное-выражение при объявлении массива может быть опущено в следующих случаях:

При объявлении массив инициализируется,

Массив объявлен как формальный параметр функции,

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

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

Int a; /* представлено в виде матрицы a a a a a a */ double b; /* вектор из 10 элементов имеющих тип double */ int w = { { 2, 3, 4 }, { 3, 4, 8 }, { 1, 0, 9 } };

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

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

Если при обращении к некоторой функции написать s, то будет передаваться нулевая строка массива s.

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

Пример объявления символьного массива.

char str = "объявление символьного массива";

Следует учитывать, что в символьном литерале находится на один элемент больше, так как последний из элементов является управляющей последовательностью "\0".

1.2.7. Структуры

Cтруктуры - это составной объект, в который входят элементы любых типов, за исключением функций. В отличие от массива, который является однородным объектом, структура может быть неоднородной. Тип структуры определяется записью вида:

struct { список определений }

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

тип-данных описатель;

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

Struct { double x,y; } s1, s2, sm; struct { int year; char moth, day; } date1, date2;

Переменные s1, s2 определяются как структуры, каждая из которых состоит из двух компонент х и у. Переменная sm определяется как массив из девяти структур. Каждая из двух переменных date1, date2 состоит из трех компонентов year, moth, day. >p>Существует и другой способ ассоциирования имени с типом структуры, он основан на использовании тега структуры. Тег структуры аналогичен тегу перечислимого типа. Тег структуры определяется следующим образом:

struct тег { список описаний; };

где тег является идентификатором.

В приведенном ниже примере идентификатор student описывается как тег структуры:

Struct student { char name; int id, age; char prp; };

Тег структуры используется для последующего объявления структур данного вида в форме:

struct тег список-идентификаторов;

struct studeut st1,st2;

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

Struct node { int data; struct node * next; } st1_node;

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

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

St1.name="Иванов"; st2.id=st1.id; st1_node.data=st1.age;

1.2.8. Объединения (смеси)

Объединение подобно структуре, однако в каждый момент времени может использоваться (или другими словами быть ответным) только один из элементов объединения. Тип объединения может задаваться в следующем виде:

Union { описание элемента 1; ... описание элемента n; };

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

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

Объединение применяется для следующих целей:

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

Интерпретации основного представления объекта одного типа, как если бы этому объекту был присвоен другой тип.

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

Union { char fio; char adres; int vozrast; int telefon; } inform; union { int ax; char al; } ua;

При использовании объекта infor типа union можно обрабатывать только тот элемент который получил значение, т.е. после присвоения значения элементу inform.fio, не имеет смысла обращаться к другим элементам. Объединение ua позволяет получить отдельный доступ к младшему ua.al и к старшему ua.al байтам двухбайтного числа ua.ax .

1.2.9. Поля битов

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

Struct { unsigned идентификатор 1: длина-поля 1; unsigned идентификатор 2: длина-поля 2; }

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

Struct { unsigned a1: 1; unsigned a2: 2; unsigned a3: 5; unsigned a4: 2; } prim;

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

1.2.10. Переменные с изменяемой структурой

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

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

Struct figure { double area,perimetr; /* общие компоненты */ int type; /* признак компонента */ union /* перечисление компонент */ { double radius; /* окружность */ double a; /* прямоугольник */ double b; /* треугольник */ } geom_fig; } fig1, fig2 ;

В общем случае каждый объект типа figure будет состоять из трех компонентов: area, perimetr, type. Компонент type называется меткой активного компонента, так как он используется для указания, какой из компонентов объединения geom_fig является активным в данный момент. Такая структура называется переменной структурой, потому что ее компоненты меняются в зависимости от значения метки активного компонента (значение type).

Отметим, что вместо компоненты type типа int, целесообразно было бы использовать перечисляемый тип. Например, такой

Enum figure_chess { CIRCLE, BOX, TRIANGLE } ;

Константы CIRCLE, BOX, TRIANGLE получат значения соответственно равные 0, 1, 2. Переменная type может быть объявлена как имеющая перечислимый тип:

enum figure_chess type;

В этом случае компилятор СИ предупредит программиста о потенциально ошибочных присвоениях, таких, например, как

figure.type = 40;

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

Struct { общие компоненты; метка активного компонента; union { описание компоненты 1 ; описание компоненты 2 ; ::: описание компоненты n ; } идентификатор-объединения; } идентификатор-структуры;

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

Struct { /* общая информация */ char name ; /* имя */ int age; /* возраст */ char sex; /* пол */ /* метка активного компонента */ /* (семейное положение) */ enum merital_status ins; /* переменная часть */ union { /* холост */ /* нет компонент */ struct { /* состоит в браке */ char marripge_date; char spouse_name; int no_children; } marriage_info; /* разведен */ char date_divorced; } marital_info; } health_record; enum marital_status { SINGLE, /* холост */ MARRIGO, /* женат */ DIVOREED /* разведен */ } ;

Обращаться к компонентам структуры можно при помощи ссылок:

Helth_record.neme, helth_record.ins, helth_record.marriage_info.marriage_date .

1.2.11. Определение объектов и типов

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

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

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

Однако надо помнить, что некоторые комбинации модификаторов недопустимы:

Элементами массивов не могут быть функции,

Функции не могут возвращать массивы или функции.

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

Для интерпретации сложных описаний предлагается простое правило, которое звучит как "изнутри наружу", и состоит из четырех шагов.

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

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

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

4. Интерпретировать спецификатор типа.

Int * (* comp ) (); 6 5 3 1 2 4

В данном примере объявляется переменная comp (1), как массив из десяти (2) указателей (3) на функции (4), возвращающие указатели (5) на целые значения (6).

Char * (* (* var) ()) ; 7 6 4 2 1 3 5

Переменная var (1) объявлена как указатель (2) на функцию (3) возвращающую указатель (4) на массив (5) из 10 элементов, которые являются указателями (6) на значения типа char.

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

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

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

Typedef double (* MATH)(); /* MATH - новое имя типа, представляющее указатель на функцию, возвращающую значения типа double */ MATH cos; /* cos указатель на функцию, возвращающую значения типа double */ /* Можно провести эквивалентное объявление */ double (* cos)(); typedef char FIO /* FIO - массив из сорока символов */ FIO person; /* Переменная person - массив из сорока символов */ /* Это эквивалентно объявлению */ char person;

При объявлении переменных и типов здесь были использованы имена типов (MATH FIO). Помимо этого, имена типов могут еще использоваться в трех случаях: в списке формальных параметров, в объявлении функций, в операциях приведения типов и в операции sizeof (операция приведения типа).

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

спецификатор-типа абстрактный-описатель;

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

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

1.2.12. Инициализация данных

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

Формат 1: = инициатор;

Формат 2: = { список - инициаторов };

Формат 1 используется при инициализации переменных основных типов и указателей, а формат 2 - при инициализации составных объектов.

Переменная tol инициализируется символом "N".

const long megabute = (1024 * 1024);

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

static int b = {1,2,3,4};

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

static int b = { { 1,2 }, { 3,4 } };

При инициализации массива можно опустить одну или несколько размерностей

static int b]; Примеры:
 int a, b, c;
 double x, y;
 char ch;
 Есть некоторые негласные правила, т.е исполнения который является хорошим тоном, но делать это не обязательно:
  1. каждое объявление переменных нового типа начинается с новой строки;
  2. из названия переменной должно быть понятно зачем она и что в ней будет храниться(хотя иногда из-за таких вот информативных названий скорость написания кода падает, т.к некоторых заносит и они называют переменные целыми предложениями);
  3. поэтому появляется правило: название переменной не должно быть слишком длинным;
  4. после объявления переменной очень желательно в комментариях пометить зачем она;
  5. необходимо разделять имена переменных пробелами.
 Оператор объявления переменных с инициализацией имеет следующий синтаксис: тип имя1[=значение1][, имя2[=значение2][,...]]; Примеры:
 int a=26, b=032, c=0x1A;
 double x=2.5e2,y=0x1.ffe-3;
 char ch=’Z’;

Константы в СИ

 В языке СИ присутствует три вида констант:
  1. целочисленные,
  2. вещественные,
  3. символьные.
 Целочисленные константы
  1. Десятичная константа указывается десятичным числом в обычной форме.
  2. Восьмеричная константа указывается числом, начинающимся с цифры ноль и содержащим цифры 0...7.
  3. Шестнадцатеричная константа указывается целым числом с префиксом 0x или 0X, содержащим цифры 0...9 и буквы латинского алфавита a...f, A...F.
 Вещественные константы записываются в десятичной или шестнадцатеричной системе исчисления. Позиция запятой указывается точкой, экспонента указывается после латинской буквы e (или E). Символьным константам предшествует символ \, это так называемое "экранирование" . В языке СИ присутствуют специальные символы:
  ‘\’’ – одинарная кавычка,
  ‘\”’ – двойная кавычка,
  ‘\\’ – обратный слеш,
  ‘\?’ – знак вопроса,
  ‘\a’ – звуковой сигнал,
  ‘\b’ – удаление символа,
  ‘\f’ – прокрутка страницы,
  ‘\n’ – перевод строки,
  ‘\r’ – возврат каретки в начало строки,
  ‘\t’ – горизонтальная табуляция,
  ‘\v’ – вертикальная табуляция.

В языке СИ можно также создавать переменные, имеющие константное значение (их значение нельзя изменить). Объявление таких «переменных» имеет следующий синтаксис: const тип имя1=значение1[,имя2=значение2[,...]]; Примеры:
  const unsigned int x=80, y=25;
  const double pi=3.1415;

Оператор создания типов данных в СИ

 Оператор typedef используется для создания пользовательских типов данных, синтаксис его использования: typedef имя_старого_типа имя_нового_типа; Пример:
  typedef unsigned int word;
 В СИ согласно стандарту, определение типов может производиться практически в любом месте программы(т.е нет строго определенного блока для определения типов данных).Функция для определения размера типа, или переменной какого-либо типа: sizeof, она возвращает количество занимаемых байт в памяти. Пример:
  sizeof(int) //вернет 4
  sizeof(char) //результат 1
  sizeof(double) // вернет 8


© 2024 beasthackerz.ru - Браузеры. Аудио. Жесткий диск. Программы. Локальная сеть. Windows