и т.д. Протестируйте программу на разных значениях.
Представление целых чисел и их преобразование еще будет рассматриваться в разделе 25.5.3. По возможности ограничивайтесь немногими типами данных, чтобы минимизировать вероятность ошибок. Например, используя только тип
double
и избегая типа
float
, мы минимизируем вероятность возникновения проблем,
связанных с преобразованием
double
—
float
. Например, мы предпочитаем использовать только типы
int
,
double
и
complex
(см. раздел 24.9) для вычислений,
char
— для символов и
bool
— для логических сущностей. Остальные арифметические типы мы используем только при крайней необходимости.
24.2.1. Пределы числовых диапазонов
Каждая реализация языка C++ определяет свойства встроенных типов в заголовках
<limits>
,
<climits>
и
<limits.h>
, чтобы программисты могли проверить пределы диапазонов, установить сигнальные метки и т.д. Эти значения перечислены в разделе Б.9.1. Они играют очень важную роль для создания низкоуровневых инструментов. Если они вам нужны, значит, вы работаете непосредственно с аппаратным обеспечением, хотя существуют и другие приложения. Например, довольно часто возникают вопросы о тонкостях реализации языка, например: “Насколько большим является тип
int
?” или “Имеет ли знак тип
char
?” Найти определенные и правильные ответы в системной документации бывает трудно, а в стандарте указаны только минимальные требования. Однако можно легко написать программу, находящую ответы на эти вопросы.
cout << "наибольшее число типа int: " << INT_MAX << endl;
cout << "наименьшее число типа int: " << numeric_limits<int>::min
<< '\n';
if (numeric_limits<char>::is_signed)
cout << "тип char имеет знак n";
else
cout << "тип char не имеет знака\n";
cout << "char с минимальным значением: "
<< numeric_limits<char>::min <<'\n';
cout << "минимальное значение типа char: "
<< int(numeric_limits<char>::min) << '\n';
Если вы пишете программу, которая должна работать на разных компьютерах, то возникает необходимость сделать эту информацию доступной для вашей программы. Иначе вам придется “зашить” ответы в программу, усложнив ее сопровождение.
Эти пределы также могут быть полезными для выявления переполнения.
24.3. Массивы
Массив (array) — это последовательность, в которой доступ к каждому элементу осуществляется с помощью его индекса (позиции). Синонимом этого понятия является вектор (vector). В этом разделе мы уделим внимание многомерным массивам, элементами которых являются тоже массивы. Обычно многомерный массив называют матрицей (matrix). Разнообразие синонимов свидетельствует о популярности и полезности этого общего понятия. Стандартные классы
vector
(см. раздел Б.4),
array
(см. раздел 20.9), а также встроенный массив (см. раздел A.8.2) являются одномерными. А что если нам нужен двумерный массив (например, матрица)? А если нам нужны семь измерений? Проиллюстрировать одно- и двухмерные массивы можно так.
Массивы
имеют фундаментальное значение в большинстве вычислений, связанных с так называемым “перемалыванием чисел” (“number crunching”). Наиболее интересные научные, технические, статистические и финансовые вычисления тесно связаны с массивами.
Часто говорят, что массив состоит из строки столбцов.
Столбец — это последовательность элементов, имеющих одинаковые первые координаты (х– координаты). Строка — это множество элементов, имеющих одинаковые вторые координаты (y– координаты).
24.4. Многомерные массивы в стиле языка С
В качестве многомерного массива можно использовать встроенный массив в языке С++ . В этом случае многомерный массив интерпретируется как массив массивов, т.е. массив, элементами которого являются массивы. Рассмотрим пример.
int ai[4]; // 1-мерный массив
double ad[3][4]; // 2-мерный массив
char ac[3][4][5]; // 3-мерный массив
ai[1] = 7;
ad[2][3] = 7.2;
ac[2][3][4] = 'c';
Этот подход наследует все преимущества и недостатки одномерного массива.
• Преимущества
• Непосредственное отображение с помощью аппаратного обеспечения.
• Эффективные низкоуровневые операции.
• Непосредственная языковая поддержка.
• Проблемы
• Многомерные массивы в стиле языка являются массивами массивов(см. ниже).
• Фиксированные размеры (например, фиксированные на этапе компиляции). Если хотите определять размер массива на этапе выполнения программы, то должны использовать свободную память.
• Массивы невозможно передать аккуратно. Массив превращается в указатель на свой первый элемент при малейшей возможности.
• Нет проверки диапазона. Как обычно, массив не знает своего размера.
• Нет операций над массивами, даже присваивания (копирования).
Встроенные массивы широко используются в числовых расчетах. Они также являются основным источником ошибок и сложностей. Создание и отладка таких программ у большинства людей вызывают головную боль. Если вы вынуждены использовать встроенные массивы, почитайте учебники (например, The C++ Programming Language, Appendix C, p. 836–840). К сожалению, язык C++ унаследовал многомерные массивы от языка C, поэтому они до сих пор используются во многих программах.
Большинство фундаментальных проблем заключается в том, что передать многомерные массивы аккуратно невозможно, поэтому приходится работать с указателями и выполнять явные вычисления, связанные с определением позиций в многомерном массиве. Рассмотрим пример.
void f1(int a[3][5]); // имеет смысл только в матрице [3][5]
void f2(int [ ][5], int dim1); // первая размерность может быть
// переменной
void f3(int [5 ][ ], int dim2); // ошибка: вторая размерность
// не может быть переменной
void f4(int[ ][ ], int dim1, int dim2); // ошибка (совсем
// не работает)
void f5(int* m, int dim1, int dim2) // странно, но работает