Иллюстрированный самоучитель по практике программирования

Стилевое единство и идиомы

Выражение do-while используется гораздо реже, чем for и while, поскольку при его применении тело цикла исполняется как минимум один раз вне зависимости от условий, которые проверяются не в начале цикла, а в конце. Во многих случаях это становится причиной появления ошибки, ждущей своего часа, как, например, в следующем варианте цикла для getchar:

Иллюстрированный самоучитель по практике программирования › Стиль › Стилевое единство и идиомы

В этом случае в переменную будет записано мусорное значение, поскольку проверка производится уже после вызова putchar. Цикл do-while стоит применять только в тех случаях, когда тело цикла обязательно должно быть выполнено хотя бы один раз; некоторые примеры подобных ситуаций мы рассмотрим несколько позже.

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

Иллюстрированный самоучитель по практике программирования › Стиль › Стилевое единство и идиомы

Место в памяти выделяется для элементов в количестве nmemb – от iArray[0] до iArray[nmemb-1 ], но, поскольку в условии цикла проверка производится на "меньше или равно" (=<), цикл вылезет за границу массива и испортит новой записью то, что хранится за пределами выделенной памяти. К сожалению, ошибки подобного типа нередко можно обнаружить только после того, как данные уже разрушены.

В С и C++ есть также идиомы для выделения места под строки и для работы с ними, а код, который их не использует, зачастую таит в себе ошибку:

Иллюстрированный самоучитель по практике программирования › Стиль › Стилевое единство и идиомы

Никогда не следует употреблять gets, поскольку не существует способа ограничить размер вводимой информации. Это ведет к проблемам, связанным с безопасностью, к которым мы вернемся в главе 6; там мы увидим, что всегда лучше использовать fgets. Однако существует и еще одна проблема: функция strlen вычисляет размер строки, не учитывая завершающего строку символа ' \0', а функция strcpy его копирует. Таким образом, выделяется недостаточно места, и strcpy пишет за пределами выделенной памяти. Стандартной формой в C++ является следующая:

р = malloc(strlen(buf)+1);
strcpy(p, buf);

…или:

р = new char[strlen(buf)+1];
strcpy(p, buf);

Таким образом, если вы не видите +1, то стоит проверить все еще раз.

В Java такой проблемы нет, поскольку строки там не представляются в виде массивов, оканчивающихся на 0. Кроме того, индексы массивов проверяются, так что в Java выйти за границы массива невозможно.

В большинстве сред С и C++ предусмотрена библиотечная функция strdup, которая создает копию строки, используя для этого malloc и strcpy, что гарантирует от обсуждавшейся чуть выше ошибки. К сожалению, strdup не входит в стандарт ANSIС.

Кстати говоря, ни в первой, ни в окончательной версии не проверяется значение, возвращаемое функцией malloc. Мы опустили эту проверку для того, чтобы сфокусировать ваше внимание на теме данного раздела, однако в настоящей программе значения, возвращаемые функциями malloc, realloc, strdup, а также другими функциями, выделяющими память, должны в обязательном порядке проверяться.

Если Вы заметили ошибку, выделите, пожалуйста, необходимый текст и нажмите CTRL + Enter, чтобы сообщить об этом редактору.