Читаем Программирование. Принципы и практика использования C++ Исправленное издание полностью

Правильно ли, что функция do_resources3() передает (предположительно) открытый файл обратно как возвращаемое значение? Правильно ли, что функция do_resources3() освобождает память, передаваемую ей как аргумент p? Мы также добавили действительно коварный вариант использования глобальной переменной var (очевидно, указатель). В принципе передача ресурсов в функцию и из нее является довольно распространенной и полезной практикой, но для того чтобы понять, корректно ли выполняется эта операция, необходимо знать стратегию управления ресурсами. Кто владеет ресурсом? Кто должен его удалять/освобождать? Документация должна ясно и четко отвечать на эти вопросы. (Помечтайте.) В любом случае передача ресурсов изобилует возможностями для ошибок и представляет сложность для тестирования.

  Обратите внимание на то, что мы (преднамеренно) усложнили пример управления ресурсами, использовав глобальную переменную. Если в программе перемешано несколько источников ошибок, ситуация может резко ухудшиться. Как программисты мы стараемся избегать таких ситуаций. Как тестировщики — стремимся найти их.

<p id="AutBody_Root520"><strong>26.3.3.3. Циклы</strong></p>

  Мы уже рассматривали циклы, когда обсуждали функцию binary_search().

Большинство ошибок возникает в конце циклов.

• Правильно ли проинициализированы переменные в начале цикла?

• Правильно ли заканчивается цикл (часто на последнем элементе)?

Приведем пример, который содержит ошибку.

int do_loop(const vector& v) // плохая функция

                                  // неправильный цикл

{

  int i;

  int sum;

  while(i<=vec.size()) sum+=v[i];

  return sum;

}

Здесь содержатся три очевидные ошибки. (Какие именно?) Кроме того, хороший тестировщик немедленно выявит возможности для переполнения при добавлении чисел к переменной sum.

  Многие циклы связаны с данными и могут вызвать переполнение при вводе больших чисел.

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

char buf[MAX];    // буфер фиксированного объема

char* read_line() // опасная функция

{

  int i = 0;

  char ch;

  while(cin.get(ch) && ch!='\n') buf[i++] = ch;

  buf[i+1] = 0;

  return buf;

}

Разумеется, вы не написали бы ничего подобного! (А почему нет? Что плохого в функции read_line()?) Однако эта ошибка, к сожалению, является довольно распространенной и имеет разные варианты.

// опасный фрагмент

gets(buf);       // считываем строку в переменную buf

scanf("%s",buf); // считываем строку в переменную buf

  Поищите описание функций gets() и scanf() в своей документации и избегайте их как чумы. Под словом “опасная” мы понимаем, что переполнение буфера является инструментом для взлома компьютеров. В настоящее время реализации выдают предупреждение об опасности использования функции gets() и ее аналогов.

<p id="AutBody_Root521"><strong>26.3.3.4. Ветвление</strong></p>

  Очевидно, что, делая выбор, мы можем принять неправильное решение. Из-за этого инструкции if и switch являются одними из основных целей для тестировщиков. Существуют две проблемы, которые необходимо исследовать.

• Все ли возможные варианты предусмотрены?

• Правильные ли действия связаны с правильными вариантами выбора?

Рассмотрим следующую бессмысленную функцию:

void do_branch1(int x, int y) // плохая функция

    // неправильное использование инструкции if

{

  if (x<0) {

    if (y<0)

      cout << "Большое отрицательное число \n";

    else

      cout << "Отрицательное число \n";

  }

  else if (x>0) {

    if (y<0)

      cout << "Большое положительное число \n";

    else

      cout << "Положительное число \n";

  }

}

Наиболее очевидная ошибка в этом фрагменте заключается в том, что мы забыли о варианте, в котором переменная x равна нулю. Сравнивая числа (положительные или отрицательные) с нулем, программисты часто забывают о нем или приписывают неправильной ветви (например, относят его к отрицательным числам). Кроме того, существует более тонкая (хотя и распространенная) ошибка, скрытая в этом фрагменте: действия при условиях (x>0 && y<0) и (x>0 && y>=0) каким-то образом поменялись местами. Это часто случается, когда программисты пользуются командами “копировать и вставить”.

Перейти на страницу:

Похожие книги

Programming with POSIX® Threads
Programming with POSIX® Threads

With this practical book, you will attain a solid understanding of threads and will discover how to put this powerful mode of programming to work in real-world applications. The primary advantage of threaded programming is that it enables your applications to accomplish more than one task at the same time by using the number-crunching power of multiprocessor parallelism and by automatically exploiting I/O concurrency in your code, even on a single processor machine. The result: applications that are faster, more responsive to users, and often easier to maintain. Threaded programming is particularly well suited to network programming where it helps alleviate the bottleneck of slow network I/O. This book offers an in-depth description of the IEEE operating system interface standard, POSIX (Portable Operating System Interface) threads, commonly called Pthreads. Written for experienced C programmers, but assuming no previous knowledge of threads, the book explains basic concepts such as asynchronous programming, the lifecycle of a thread, and synchronization. You then move to more advanced topics such as attributes objects, thread-specific data, and realtime scheduling. An entire chapter is devoted to "real code," with a look at barriers, read/write locks, the work queue manager, and how to utilize existing libraries. In addition, the book tackles one of the thorniest problems faced by thread programmers-debugging-with valuable suggestions on how to avoid code errors and performance problems from the outset. Numerous annotated examples are used to illustrate real-world concepts. A Pthreads mini-reference and a look at future standardization are also included.

David Butenhof

Программирование, программы, базы данных