C++ для начинающих. Урок 2. Примитивные типы данных

Физические и логические типы данных. В чем разница

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

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

Следует знать, что в языках C/C++ нет базового строкового типа данных, который был бы поддержан на уровне ядра языка. Этот факт несколько отличает его от языка Pascal и ряда других языков. Явно хочется упомянуть именно Pascal по той причине, что в прикладном отношении он очень близок языкам C/C++, но обладает, при этом, рядом преимуществ перед этими языками хотя и является более простым языком. В рассматриваемом контексте следует указать, что ядро современного Pascal поддерживает несколько эффективных строковых реализаций разного назначения.

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

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

  • Физические типы — типы данных со строго определенной размерностью, которая не зависит от вычислительной платформы и ее разрядности.
  • Логические типы — типы данных с неопределенной размерностью, которая зависит от вычислительной платформы и ее разрядности.

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

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

Физические типы данных в языке C++

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

Если с помощью директивы препроцессора include включить в программу на языке C++ заголовочный файл stdint.h, то в нашей программе будет доступна следующая система типов.

int8_t 8-ми битовый знаковый тип
uint8_t 8-ми битовый беззнаковый тип
int16_t 16-ти битовый знаковый тип
uint16_t 16-ти битовый беззнаковый тип
int32_t 32-х битовый знаковый тип
uint32_t 32-х битовый беззнаковый тип
int64_t 64-х битовый знаковый тип
uint64_t 64-х битовый беззнаковый тип

Здесь следует сделать замечание, что согласно традициям C++, следует выполнять включение другого заголовочного файла — cstdint. Можно попробовать включить в программу именно этот файл, однако, в моем случае, на используемых мною версиях компиляторов и библиотек, рекомендуется использование C-версии этого файла.

Вы, наверное, знаете о совместимости языков C и C++ — любая программа на языке C должна компилироваться компилятором с языка C++. Это позволяет заимствовать технологии и библиотеки из языка C в язык C++, хотя, чаще всего, такое заимствование не является рекомендуемым. Чтобы смягчить некоторые проблемы такого заимствования, в стандартную библиотеку языка C++ включены переработанные варианты заголовочных файлов из стандартной библиотеки языка C. Чтобы различать эти версии, используется некоторое формальное правило об изменении имени заголовочного файла. Во-первых, согласно правилам языка C++, заголовочные файлы стандартной библиотеки не имеют в своем имени суффикс .h. Во-вторых, к имени адаптированных файлов добавляется префикс из литеры c. В результате, например, stdio.h превращается в cstdio, а stdlib.h превращается в cstdlib и т.д.

Логические типы данных, поддерживаемые ядром C++

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

Тип Размер (Бт) Комментарий
bool 1 Булевый тип данных. Может принимать значения true и false. Поддержка данного типа в ядре может оказаться не во всех компиляторах — тип может быть создан специальными описаниями в стандартной библиотеке языка.
char
unsigned char
1 Однобайтовое целое. Знаковый и беззнаковый (unsigned) вариант. Часто с этим типом связывают понятие символа и используют для образования строк, однако, следует иметь в виду, что с точки зрения ядра языка C++, это обычное целое число (как правило, однобайтовое), для которого определен стандартный для C++ набор арифметических операций. В переменной данного типа удобно хранить коды однобайтовых символов. Это ничего не меняет с точки зрения операций, доступных для переменной данного типа, но с помощью специальных средств (например, с помощью спецификаторов вывода) имеется возможность при выводе значения переменной, интерпретировать ее не как число, а как код символа и, соответственно, выводить символ соответствующий данному коду. Для тех, кто знаком с Pascal, такая интерпретация типа char может оказаться неожиданной и к этому надо будет привыкнуть. Если вам доступна консоль Linux, то вы можете посмотреть один из вариантов простой символьной таблицы, называемой ASCII (American Standard Code for Informational Interchange), с помощью команды: man ascii. В противном случае, любые кодовые таблицы, в том числе и таблицу ASCII можно найти с помощью Google.
wchar_t 2 (Windows)
4 (*nix)
Тип «широкий символ». Образовано от сочетания wide char. Интерпретация этого типа существенно отличается на платформах *nix и Windows. Под *nix, это, как правило, 4-х байтовый тип. Поддержка данного типа реализована, скорее всего, не в ядре языка, а на уровне специальной библиотеки языка C++. Там же присутствуют и необходимые функции и классы обработки значений этого типа. Чтобы понимать назначение данного типа для решения текстовых задач в мире различных символьных кодировок, прежде всего, следует понимать, различие между кодовыми таблицами символов и способами представления этих кодовых таблиц. Например, в чем разница между понятием Unicode и понятиями UTF-8/UTF-16/UTF-32.
int
unsigned int
4 Знаковое и беззнаковое (unsigned) целое. Обычно размер этого типа выбирается на платформе так, чтобы скорость его обработки была максимальной.
short int
unsigned short int
2 Знаковое и беззнаковое (unsigned) целое. В таком виде используется достаточно редко. Удобнее использовать тип int, который обычно более эффективен на любой платформе из-за использования особенностей процессора, и, часто, может оказаться еще и более емким. Когда же нужны типы четко определяющие свою емкость независимо от платформы, то правильнее использовать искусственно созданные фундаментальные типы данных, которые были рассмотрены выше.
long int
unsigned long int
8 Знаковое и беззнаковое (unsigned) целое. Обычно используется для хранения больших целых чисел.
float 4 Вещественный тип данных. Обычно соответствует стандартному 4-х байтовому типу математического сопроцессора.
double 8 Вещественный тип данных. Обычно соответствует стандартному 8-х байтовому типу математического сопроцессора.
long double 10 Вещественный тип данных. Обычно соответствует стандартному 10-ти байтовому типу математического сопроцессора.

К таблице следует добавить, что для указания целых типов используется еще ключевое слово signed которое явно подчеркивает знаковое содержание типа данных. Используется оно редко, так как по умолчанию, все целые типы являются знаковыми. Кроме того, следует знать, что согласно стандарту языка C++, при определении таких типов как short int и long int в знаковом или беззнаковом варианте, можно опускать ключевое слово int.

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

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

Изучение заголовочных файлов cstdint и climits

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

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

#include <climits>;

Теперь следует поставить текстовый курсор на имя заголовочного файла и нажать на клавиатуре клавишу F2. Если файл доступен согласно настройкам проекта и если у вас есть права на открытие этого файла хотя бы на чтение, то файл откроется в окне редактора кода QtCreator. Далее, можно использовать клавишу F2 для дальнейшего углубления в код. По этой клавише в QtCreator производится поиск определения объекта под текстовым курсором. Чтобы вернуться назад от места последнего перехода, используйте клавиатурную комбинацию Alt+LeftArrow.

Получение размера типов

В языке C++ определен оператор получения размера сущности. Он применим как к переменной, так и к типу. Результатом операции возвращается размер в байтах для сущности переданной аргументом. Оператор называется sizeof.

Пример работы с примитивными типами данных

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

#include <iostream>

// Класс, реализующий наложение знаковой и беззнаковой переменных 
// одной емкости по одному адресу
union MyChar {
  unsigned char uch;
  signed char sch;
};

void print(const MyChar & m)
{
  int i,j;

  i = m.uch;
  j = m.sch;

  std::cout << "unsigned: " << i << ", signed: " << j << std::endl;
}

int main(int argc, char *argv[])
{
   std::cout << "Size of MyChar: " << sizeof(MyChar) << std::endl;

   MyChar m;
   std::cout << "Size of (m): " << sizeof(m) << std::endl;

   m.uch = 255;
   print(m);  

   ++m.uch;
   print(m);

   return 0;
}

Разберите, откомпилируйте и запустите представленный пример и попробуйте объяснить результаты. Обязательно поэкспериментируйте с различными константами назначая их членам m.uch или m.sch и проверяя вывод результатов.

В данном примере пришлось использовать элементы, которые не были рассмотрены ранее в нашем курсе. Это относится к описанию класса MyChar через ключевое слово union и к описанию функции print().

Ключевое слово union, в языке C++ вводит новый тип данных, называемый классом. Классы в C++ вводятся с помощью трех ключевых слов: class, struct и union. Подробности классов обсуждаются в объектно-ориентированном программировании. Здесь мы лишь скажем, что через ключевое слово union вводится совершенно особый тип отличающийся тем, что все элементы описанные в блоке union { … } размещаются по одному адресу и размер всего блока данных, в этом случае, соответствует размеру самого большого элемента данных в описании блока.

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

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

Один комментарий к “C++ для начинающих. Урок 2. Примитивные типы данных

  1. Уведомление: max

Добавить комментарий