//Оглавление

Типы данных

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

Индейцы из племени Палехо считают один, два, много. Собственно десять для них это и есть бесконечность.
На Руси число 10 000 называлось «тьма» и тоже служило последним пределом естественного, т.e. соотносимого с какой-либо деятельностью счета.
Впрочем, еще в XII веке новгородский дьякон Кирик в своем «Учении, им же ведати человеку числа всех лет» подсчитал, что в прошедших от сотворения мира 6644 годах содержится: месяцев  — 79 728, недель — 346 673, дней — 2 426 721.
В умозрительных построениях, так называемый «великий счет» под «тьмой» понимался нынешний миллион 106. Далее шли: легион 1012 (именно — «тьма тем»), леодр 1024, ворон 1048 и, наконец, колода 1096 «более же сего не бывает». Колода в полной записи выглядит как единица с девяносто шестью нулями.

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

Естественно, что в программировании, как в  практике следует рассматривать лишь конечные множества, ибо infinitum actu non datur — «актуальная бесконечность не дана». Фундаментальными типами данных языков С++ и С# можно считать числовой, логический, символьный, и адресный.

Восемь ваххабит — один ваххабайт

Совокупность свойств, согласно которым объекты группируются в тип, определяет набор допустимых значений и операций над объектами такого типа. В свою очередь, набор допустимых значений определяет количество памяти необходимое для хранения экземпляра данного типа в памяти. Например, допустимые значения типа данных «положительное целое разрядности n» находятся в интервале [0-2n]
Для хранения любого экземпляра данного типа требуется n байт памяти.

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

В заключении, пара примеров. Вышеупомянутый тип данных «положительное целое разрядности n», это кольцо над подмножеством [0-2n] множества целых чисел. Строка — набор символов одного или нескольких алфавитов с операциями конкатенации и разделения, которые для чисел не определены, и сравнения. Читатель может развлечься и определить собственный тип данных, например, «кружка пива».

Преобразование типов

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


int _tmain(int argc, _TCHAR* argv[])
{
  double f = 9.45;
  int n1 = 9.45;   // warning C4244: 'initializing' : conversion from 'double' to 'int', possible loss of data
  int n2 = f;      // warning C4244: 'initializing' : conversion from 'double' to 'int', possible loss of data
  return 0;
}

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


class Program
{
  static void Main(string[] args)
  {
    double f = 9.45;  
    int n1 = 9.45;   // error CS0266: Cannot implicitly convert type 'double' to 'int'.
    int n2 = var;    // error CS0266: Cannot implicitly convert type 'double' to 'int'.
  }
}

Типы данных в C и C++

В языке С++ равно как и в С, четыре фундаментальных типа данных: целые и вещественные числа, указатели и void.
Полный набор целых и вещественных чисел зависит от компилятора, но в целом все выглядит примерно так. Целочисленные типы — char, bool, short, int, long. Вещественные, они же числа с плавающей точкой, — float и double.
Размер каждого из вышеперечисленных типов в байтах, зависит от системы, поэтому, для определения размера конкретного типа следует использовать оператор sizeof. Другими словами размер типа int равен sizeof(int), а не четырем байтам.

Числовые типы данных в C и C++

Несмотря на кажущуюся похожесть, разница между ними довольна существенна, так как результат операций принадлежит разным подмножествам, для целых чисел результат принадлежит подмножеству целых чисел, для вещественных - подмножеству вещественных чисел. Результат операции a/b  где значение a = 3 а b = 2, для целых чисел будет равен 1, для вещественных 1,5. Замечу, что результат операции определяется типом переменной, в которой сохраняется значение операции, и если он не совпадает с типом, возвращаемым операцией, производиться неявное  преобразование типа, если в вышеприведенном примере a и b вещественные переменные, в том случае если результат операции деления двух вещественных чисел, являющийся так же вещественным числом, сохраняется в целочисленную переменную, то при этом производиться преобразование типа, т.е.выборка из множества значений целых чисел, наиболее подходящего, в некоторых случаях такие преобразования ведут к потере данных. В некоторых ситуациях такая потеря не имеет значения, в некоторых - недопустима, именно поэтому компилятор генерирует предупреждение. В пользовательских типах данных, можно запретить неявное преобразование типа, декларируя конструктор типа как explicit.
Все вышесказанное относиться к языку С++, С# требуют явного указания преобразования при преобразовании "с возможными" потерями т.е. в подмножество, пользовательские типы неявно преобразуются только к суперклассам.

Числовые типы данных в C# и Java

В языке Java отсутствует тип unsigned. Другими словами все числовые типы данных в Java знаковые, видимо, именно требование совместимости с Java, является причиной, по которой интерфейсы списков и массивов ?? в C# декларированы как принимающие знаковое целое, соответственно при индексации массивов, всегда придется писать абсолютно бессмысленные проверки на неотрицательность индекса.

Boolean

В явном виде тип boolean есть только в языках Java и C#. В языках С и С++ тип boolean представляет собой обычное число int. В качестве булевого типа может выступать любой тип данных, который неявно приводиться к целому


class Veritas
{
public:
  Veritas():_vino(2)
  {}
  operator bool(){
    return _vino;}

private:
  int _vino;	
};

void в C и С++

Тип void, «он сука, хитрый» обозначение ничего, аналог пустого множества. Данный тип используется только для денотации отсутствия возвращаемого значения у функции. В языке С, а также в С++, С#, Java  нет разделения на процедуры и функции, так, как это сделано, например, в Pascal или Modula. Таким образом, с одной стороны в языке имеются только функции, которые обязаны возвращать значение, а с другой зачастую возвращаемого значения нет, и возвращать нечего. Можно, конечно, возвращать что в голову придет, но это значит привносить излишнюю путаницу, попробуй пойми, какое значение возвращать из функции. Не использование возвращаемых функциями значений, порождает большие проблемы.
Так вот, в тех случаях когда возвращаемого значения нет, функция возвращает "ничего", этим самым «ничем» и является void, аналог пустого множества в математике void. C точки зрения английского языка это вполне естественная языковая конструкция, к тому же, очень по-Кэрролловски. Это – хорошо. Значение void возвращается либо явно, вызовом оператора return; либо неявно, при передачи управления из функции. Возвращение "ничего" это очередной постмодернистский бред С++.

Повторюсь, тип void можно использовать только в качестве возвращаемого функцией значения. Нельзя декларировать переменную типа void, соответственно, нельзя передать void в качестве параметра это вызовет ошибку компиляции. Хотя чисто теоретически, в каждой программе, в каждой функции — бесконечное количество переменных типа void. Они попросту везде :-)
Так как нет типа, то не определен и размер такого типа, соответственно вызов оператора sizeof(void) так же вызовет ошибку компиляции. Можно предположить, что sizeof(void) это 0, но добывать 0 таким способом это уже перебор, даже для С++ :-)


void t;				// error C2182: 't' : illegal use of type 'void'
int szVoid = sizeof(void); 	// error C2070: 'void': illegal sizeof operand

void procedure()
{
	// ...
	return;
}

void f_voidValue()
{
	int i = 0;
	bool	bValid  = true;

	// первый вариант
	if (bValid)
	{
		procedure();
		return;
	}

	// второй вариант, гораздо компактнее, однако компилироваться это будет
	// только в Visual Studio начиная с Visual Studio 2003.
	if (i == 0)
		return procedure();
}


void в C#

System.Void размерный тип FCL.

Указатели

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

Указатели в C и C++

Указателем в С++ фактически является числовая переменная, значение которой трактуется как адрес (потому и указатель).Соответственно, указатель может указывать на переменную, функцию или вообще на хер пойми что. Указатель, как и любая переменная имеет свой собственный адрес. Собственно указатель может содержать адрес себя самого. Так как адрес – это число то фактически указателем может является любая целочисленная переменная, вещественная переменная будет приведена к целому (округление или трактование байтов разница между static_cast reinterpret_cast). В свою очередь указатель может трактоваться как просто некое число.
Однако, как уже упоминалось выше, над указателями определены операции сложения с константой вычитания и выбора. Однако, несмотря на то что сложение указателе операция  достаточно бессмысленная в С++ можно сложить два указателя.
Размер указателя, точно так же как и размер числового типа, определяется с помощью оператора sizeof. Размер указателя на int равен sizeof(int*).


int  i = 5;
int  value;
int *pValue = &i;

value = 2;		// присвоение константы
value = i;		// присвоение значения другой переменной
value = *pValue;	// присвоение значения указателя

value = 2;	  	// присвоение константы
00411FE9  mov       dword ptr [value],2 
value = i;	  	// присвоение значения другой переменной
00411FF0  mov       eax,dword ptr [i] 
00411FF3  mov       dword ptr [value],eax 
value = *pValue;	// присвоение значения указателя
00411FF6  mov       eax,dword ptr [pValue] 
00411FF9  mov       ecx,dword ptr [eax] 
00411FFB  mov       dword ptr [value],ecx

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