Читаем Программирование полностью

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

int ai[] = { 1, 2, 3, 4, 5, 6 };        // массив из шести чисел

                                        // типа int

int ai2[100] = { 0,1,2,3,4,5,6,7,8,9 }; // остальные 90 элементов

                                        // инициализируются нулем

double ad[100] = { };             // все элементы инициализируются нулем

char chars[] = { 'a', 'b', 'c' }; // нет завершающего нуля!

Обратите внимание на то, что количество элементов в массиве ai равно шести (а не семи), а количество элементов в массиве chars равно трем (а не четырем), — правило “добавить нуль в конце” относится только к строковым литералам. Если размер массива не задан явно, то он определяется по списку инициализации. Это довольно полезное правило. Если количество элементов в списке инициализации окажется меньше, чем количество элементов массива (как в определениях массивов ai2 и ad), остальные элементы инициализируются значениями, предусмотренными для данного типа элементов по умолчанию.

<p id="AutBody_Root335"><strong>18.5.4. Проблемы с указателями</strong></p>

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

• Обращение по нулевому указателю.

• Обращение по неинициализированному указателю.

• Выход за пределы массива.

• Обращение к удаленному объекту.

• Обращение к объекту, вышедшему из области видимости.

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

  Не обращайтесь к памяти с помощью нулевого указателя.

int* p = 0;

*p = 7;   // Ой!

Очевидно, что в реальной программе это может произойти, если между инициализацией и использованием указателя размещен какой-то код. Чаще всего эта ошибка возникает при передаче указателя p функции или при получении его в результате работы функции. Мы рекомендуем никуда не передавать нулевой указатель, но, уж если вы это сделали, проверьте указатель перед его использованием. Например,

int* p = fct_that_can_return_a_0();

if (p == 0) {

  // что-то делаем

}

else {

  // используем р

  *p = 7;

}

и

void fct_that_can_receive_a_0(int* p)

{

  if (p == 0) {

    // что-то делаем

  }

  else {

    // используем р

    *p = 7;

  }

}

Основными средствами, позволяющими избежать ошибок, связанных с нулевыми указателями, являются ссылки (см. раздел 17.9.1) и исключения (см. разделы 5.6 и 19.5).

  Инициализируйте указатели.

int* p;

*p = 9; // Ой!

В частности, не забывайте инициализировать указатели, являющиеся членами класса.

  Не обращайтесь к несуществующим элементам массива.

int a[10];

int* p = &a[10];

*p = 11;     // Ой!

a[10] = 12;  // Ой!

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

  Не обращайтесь к памяти с помощью удаленного указателя.

int* p = new int(7);

// ...

delete p;

// ...

*p = 13;  // Ой!

Инструкция delete p или код, размещенный после нее, может неосторожно обратиться к значению *p или использовать его косвенно. Все эти ситуации совершенно недопустимы. Наиболее эффективной защитой против этого является запрет на использование “голых” операторов new, требующих выполнения “голых” операторов delete: выполняйте операторы new и delete в конструкторах и деструкторах или используйте контейнеры, такие как Vector_ref (раздел Д.4).

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже