Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Философия программирования на C++: типы данных


Типы данных

Добрый день, уважаемые подписчики!

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

Типы данных определяют способы использования памяти в написанных вами программах. Каждый тип данных указывает компилятору, какой размер имеет созданный блок памяти и как с ним работать. В С++ имеется 4 основных типов данных (встроенных). Тип char предназначен для хранения символов и занимает как правило 1 байт, тип int предназначен для хранения целых чисел и занимает минимум 2 байта (обычно на 32-разрядных процессорах 4 байта), типы float и double хранят вещественные числа с одинарной и двойной точностью. Ну и теперь сразу простенький пример:

#include "iostream"
using namespace std;
int main() {
    //Определение без инициализации
    int i;
    char c;
    float f;
    double d;
    //Определение с инициализацией
    char symbol = 'a', smb2 = '_';
    int temp = 100;
    int number = -2000;
    double test = 1.4e100;
    float pi = 3.141592;
    //Вывод на экран
    cout << "sizeof(symbol) = " << sizeof(symbol) << ", " << "symbol = " << symbol << endl;
    cout << "sizeof(number) = " << sizeof(number) << ", " << "number = " << number << endl;
    cout << "sizeof(test) = " << sizeof(test) << ", " << "test = " << test << endl;
    cout << "sizeof(pi) = " << sizeof(pi) << ", " << "pi = " << pi << endl;
}

Вот что будет выведено на экран (32-рязрядный компьютер, компилятор MSVC++):

sizeof(symbol) = 1, symbol = a
sizeof(number) = 4, number = -2000
sizeof(test) = 8, test = 1.4e+100
sizeof(pi) = 4, pi = 3.14159

Тут же расскажу о комментариях. Комментарии в С++ начинают с символов "//" и заканчиваются концом строки, еще их можно записывать между "/*" и "*/" (при этом можно комментарии записывать на несколько строк). Комментарии никак не интерпретируются компилятором, он просто заменяет их на символ пробела. Многие программисты знают о комментариях, но большинство из них почему-то не придают им особого значения. Я очень рекомендую писать комментарии во всех своих программах (и учиться их писать лучше с самого начала - что именно в них писать, познается на практике). Выгода от них очень большая при отладке. Кроме того не стоит забывать о том, что невозможно помнить программу всю жизнь, и если вы к ней когда-либо еще вернетесь, вы без комментариев просто не сможете вспомнить что делает такой-то участок кода. Ну и важнее всего то, что чаще всего ваши исходники кто-нибудь будет читать, и разобраться в них проще всего будет, если написаны все необходимые комментарии. Советую писать их так, чтобы ваш код смог прочитать практически любой программист (работающий в той же области). В общем, пишите комментарии, и у вас будет гораздо меньше проблем и головной боли :).

Дальше по программе определяются 4 переменные с различными типами данных (грубо говоря, переменная - это имя некоторого участка памяти для компилятора, тип переменной - размер данного участка и правила его интерпретации, подробнее - в интернет :) ). После этого определяются некоторые переменные, но им в самом начале присваиваются некоторые значения (обратите внимание: можно объявлять сразу несколько переменных с одним типом данных через запятую). Если переменные не инициализировать некоторым начальным значением, то вообще говоря (по стандарту) их значение не определено (чаще всего там находится "мусор", который находился в данном участке памяти). Интуитивно все здесь должно быть ясно, кроме, пожалуй, инициализации переменной test. Запись 1.4e100 означает, что в test присвоится значение 1.4, умноженное на 10 в степени 100. Это просто одна из форм записей вещественного числа.

Далее показано, как можно выводить на экран значениях этих типов данных. Теперь о новых ключевых словах: sizeof() возвращает целое число типа int - число байт, которые занимает указанная переменная (или указанный тип данных). Подробно рассказывать не буду, поэкспериментируйте. Далее модификатор endl выводит в поток вывода символ завершения строки и переводит курсор на новую строку.

Теперь поговорим о некоторых правилах хорошего программирования. Я в примере привел много различных вариантов имен переменных (о правилах наименования переменных можно узнать путем экспериментов или прочитав где-нибудь в интернете). Согласитесь, что понять, что хранится в переменной, проще, когда переменная имеет имя вроде number, а не просто i. По имени number сразу видно, что в переменной хранится число. Поэтому советую давать переменным осмысленные имена, отражающие хранимые ею данные. Дальше вы поймете, как стоит их называть. Но следует помнить, что есть исключения. Скажу пару слов о венгерской нотации. Это некоторое соглашение наименования переменных. Например, int iSize. Суть ее в том, чтобы добавлять перед именем переменной некоторые символы, говорящие о типе переменной и о хранимой ею информации. Насколько я знаю, некоторые программисты пользуются данной нотацией (в основном пришедшие к С++ из низкоуровневого программирования, где она была оправдана). Но я бы не советовал использовать ее, поскольку она ухудшает читаемость кода (а это очень важный параметр). Ну и как правило в С++, как объектно-ориентированном языке, это просто излишне (и если вдруг изменится тип переменной, придется менять во всей программе имя данной переменной). Просто советую помнить всегда, что нужно код писать элегантно и удобно для чтения. Это очень важно (и в финансовом плане :) ).

Кратко расскажу о спецификаторах short и long, signed и unsigned. Первые два меняют минимальное и максимальное значение, поддерживаемое данным типом данных. Но возможна ситуация (теоретически), что во всех случаях они будут равны (например, short int, int, long int могут занимать 64 бита каждый на процессоре с 64-разрядным словами). Также отмечу, что тип long float не поддерживается. Вот некоторые примеры типов данных: long double, long int, long, short, long long. Поэкспериментируйте с ними (как в приведенном выше примере). Ну а спецификаторы signed и unsigned просто указывают на то, что данный тип со знаком или без знака. Например, тип signed short может принимать значения от -32768 до 32767, а unsigned short от 0 до 65535.

Теперь о приведении основных типов. В С++ возможно присваивать значения одного типа другому (пока я подразумеваю только встроенные типы данных). Например, тип int можно записать в double. Можно даже наоборот, но возможна потеря данных. Кроме того не стоит забывать о размерах типов, ведь на 64-разрядном компьютере тип int может занимать 8 байт, поэтому он просто "не уместится" в double, что приведет к частичной потере информации. Пример.

#include "iostream"
using namespace std;
int main() {
    unsigned long long numberx64 = -1;    //64-битная переменная
    double temp = numberx64;
    int numberx32 = temp;
    double temp2 = numberx32;
    unsigned long long recover = temp;
    //Вывод на экран
    cout << "numberx64 = " << numberx64 << endl;
    cout << "temp = " << temp << endl;
    cout << "recover = " << recover << endl;
    cout << "numberx32 = " << numberx32 << endl;
    cout << "temp2 = " << temp2 << endl;
}

Компилятор должен выдать минимум 3 предупреждения о возможной потере данных. Запустите и посмотрите что получится. Просто немного опишу, что делается в программе. В начале мы беззнаковой переменной unsigned long long передаем значение -1 (кстати, все числа имеют тип int). Поэтому в numberx64 записывается максимально возможное значение (для подробностей нужно знать представление чисел в памяти, если об этом необходимо будет рассказать, пишите, в течение недели отвечу). Данный тип занимает 8 байт в памяти (как и тип double). Но, присваивая его переменной типа double, теряются 2-3 младших разряда (так как тип double хранит 15 значащих цифр в числе). Что в итоге получится - смотрите сами.

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

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

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

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


Рушан Сулейманов, asm_89@mail.ru

Пишите свои вопросы и пожелания (а также критику :) ), постараюсь ответить всем.


В избранное