Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Заземлённые указатели

Anna | 24.06.2014 | нет комментариев

pointres, gnd

Не так давным-давно, один из работников оставил наш коллектив и присоединился к компании, занимающийся разработкой программного обеспечения, связанного с встраиваемыми системами. Ничего особенного в этом нет, неизменно и всюду, кто-то уходит, а кто-то приходит. Всё зависит от числа плюшек, комфорта и предпочтений. Увлекательно другое. Человек откровенно переживает за состояние кода на новом месте работы, что в итоге и вылилось в эту совместную статью. Трудно, «легко программировать», когда знаешь, что такое статический обзор кода.

Заповедники

Мне кажется, в мире сложилась увлекательная обстановка. Что происходит, если отдел программирования, это каждого лишь маленький вспомогательный элемент, не имеющий к стержневой сфере деятельности организации прямого отношения? Появляется заповедник. Сфера деятельности организации может быть сколь желательно главной и ответственной (медицина, военная техника). Всё равно образуется болотце, где завязают новые идеи и применяются спецтехнологии 10-летней давности.

Приведу пару штрихов из переписки с одним человеком, работающим в отделе программирования на АЭС:

А он мне отвечает: Для чего нам git? Вот смотри, у меня всё в тетрадке записано.

А у вас вообще есть какой-то контроль версий?

2 человека применяют git. Остальная контора в лучшем случае нумерованные zip’ы. Правда насчет зипов, это я только про 1 человека уверен.

Умоляю крепко не пугаться. ПО, разрабатываемое на АЭС различное бывает, плюс никто аппаратную охрану не отменяет. В этом отделе занимаются сбором и обработкой статистических данных. Но всё равно, склонность заболачивания Отчетливо прослеживается. Я не знаю, почему так происходит, но это так. Причём, чем огромнее компания, тем мощней проявляется данный результат.

Хочу подчеркнуть, что застой в крупных организациях явление интернациональное. У иноземцев дела обстоят в точности также. Длинно искал, но так и не сумел обнаружить одну дюже подходящую статью. Наименование тоже не помню. Если кто-то подскажет, добавлю ссылку. В ней программист рассказывает историю, как работал в одном военном ведомстве. Ведомство было безусловно ужасно тайное и ужасно бюрократическое. Настоль тайное и бюрократичное, что в течение нескольких месяцев не могли согласовать, какие права ему выделить для работы с компьютером. В итоге, он писал программу в Notepad (не компилируя). А потом его уволили за неэффективность.

Лесничие

Возвратимся к нашему прежнему работнику. Придя на новое место работы, он испытал маленький цивилизованный шок. Трудно позже возни с инструментами статического обзора видеть, как игнорируются даже предупреждения компилятора. Вообще, это как отчужденный мир, где программируют по своим канонам и зачастую применяют свои личные термины. Огромнее каждого из его рассказов мне понравилось словосочетание «заземлённые указатели». Прослеживается близость к аппаратной части.

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

Для начала он сделал следующее. Он прочитал предупреждения компилятора. Дальше он проверил план, применяя Cppcheck. Помимо внесения исправлений, он задумался о предотвращении типовых ошибок.

Одним из первых шагов стала подготовка им документа, нацеленного на возрастание качества создаваемого кода. Ещё одним шагом может стать внедрение в процесс разработки статического анализатор кода. О PVS-Studio пока речи не идёт. Во-первых, это Linux. Во вторых продать в сходственные организации ПО дело непростое. Пока, выбор пал на Cppcheck. Это дюже отличный инструмент для первого знакомства людей с методикой статического обзора.

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

Как не нужно писать программы

Пункт N1

Пренебрежение предупреждений компилятора. При их большом списке дозволено легко упустить настоящие программные ошибки, появившиеся в опять написанном коде. Следственно следует устранять все предупреждения.

Пункт N2

В условии оператора ‘if’ происходит не проверка значения, а присваивание:

if (numb_numbc[i] = -1) { }

В данном случае код компилируется, но компилятор выдает предупреждение. Правильной будет такая запись:

if (numb_numbc[i] == -1) { }

Пункт N3

Запись вида «using namespace std;» в заголовочных файлах может приводить к тому, что будет использована эта область видимости во всех файлах, включающих данный заголовочный файл. Это может привести к тому, что будут выбраны не те функции либо возникнет раздор имен.

Пункт N4

Сопоставление знаковых и беззнаковых переменных:

unsigned int BufPos;
std::vector<int> ba;
....
if (BufPos * 2 < ba.size() - 1) { }

Помните, что при смешивании знаковых и беззнаковых переменных:

  • может случиться переполнение;
  • может появиться неизменно правдивое либо ложное условие и как следствие нерушимый цикл;
  • в знаковую переменную может быть размещено значение больше INT_MAX (и она будет иметь негативное значение);
  • переменная типа int при сложении/вычитании/… с переменной unsigned типа, тоже становится unsigned (негативные значения превращаются в крупные правильные числа);
  • прочие неожиданности и радости

Приведённый пример некорректно обрабатывает обстановку, когда массив ‘ba’ пуст. Выражение «ba.size() — 1» имеет беззнаковый тип size_t. Если в массиве нет элементов, итог выражения равен 0xFFFFFFFFu.

Пункт N5

Игнорирование применением константности может привести к неосуществимости подметить сложно устраняемые ошибки. Скажем:

void foo(std::string &str)
{
  if (str = "1234")
  {
  }
}

В данном примере оператор ‘=’ перепутан с оператором ‘==’. Если бы переменная ‘str’ была объявлена как константная, то такой код даже не скомпилировался бы.

Пункт N6

Сравниваются не строки, а указатели на строки:

сhar TypeValue [4];
...
if (TypeValue == "S") {}

Даже если в переменной TypeValue будет находиться строка «S» такое сопоставление неизменно будет возвращать ‘false’. Правильным будет применять функции для сопоставления строк ‘strcmp’ либо ‘strncmp’.

Пункт N7

Выход за границы буфера:

memset(prot.ID, 0, sizeof(prot.ID)   1);

Такой код может привести к тому, что несколько байт памяти находящейся следом за ‘prot.ID’ так же будет заполнена нулями.

Не следует путать sizeof() и strlen(). Оператор sizeof() возвращает полный размер объекта в байтах. Функция strlen() возвращает длину строки в символах (без учета терминального нуля).

Пункт N8

Неудовлетворительное заполнение буфера:

struct myStruct
{
  float x, y, h;
};
myStruct *ptr;
 ....
memset(ptr, 0, sizeof(ptr));

В данном случае нулями будет заполнена не каждая конструкция ‘*ptr’, а только N байт (N — размер указателя в данной платформе). Правильным будет такой код:

myStruct *ptr;
 ....
memset(ptr, 0, sizeof(*ptr));

Пункт N9

Некорректное выражение:

if (0 < L < 2 * M_PI) { }

С точки зрения компилятора тут нет ошибки, впрочем оно не имеет смысла, при выполнении неизменно будет получено значение ‘true’ либо ‘false’ в зависимости от операторов сопоставления и граничных условий.Компилятор выдает предупреждение на такую запись. Правильно будет записать данный код так:

 if (0 < L && L < 2 * M_PI) { }

Пункт N10

unsigned int K;
....
if (K < 0) { }
...
if (K == -1) { }

Беззнаковые переменные не могут быть поменьше нуля.

Пункт N11

Сопоставление переменной со значением, которое оно не может добиться ни при каких условиях. Пример:

short s;
...
If (s==0xaaaa) { }

О таких случаях предупреждает компилятор.

Пункт N12

Выделяем памяти через ‘new’ либо ‘malloc’ и забываем ее освободить через ‘delete’/'free’ соответственно. Скажем, может быть такой код:

void foo()
{
  std::vector<int> *v1 = new std::vector<int>;
  std::vector<int> v2;
  v2->push_back(*v1);
  ...
}

Скорее каждого, прежде в ‘v2′ сохранялся указатель на ‘std::vector<int>’. Сейчас из-за метаморфозы части кода, это не необходимо и сохраняются легко значения типа ‘int’. При этом мы не освобождаем память, которую выделили под ‘v1′, от того что прежде это было не необходимо. Для того, что бы сделать код правильным, следует добавить выражение ‘delete v1′ в конец функции. Либо применять разумные указатели.

А ещё отменнее довести рефакторинг до конца и сделать ‘v1′ локальным объектом, раз его огромнее не нужно никуда передавать:

void foo()
{
  std::vector<int> v1;
  std::vector<int> v2;
  v2->push_back(v1[0]);
  ...
}

Пункт N13

Выделение памяти через ‘new[]‘, а освобождение через ‘delete’. Либо напротив выделение — ‘new’, а освобождение через ‘delete[]‘. Отчего это нехорошо, дозволено почитать тут: “delete, new[] в C и городские легенды об их сочетании“.

Пункт N14

Применение неинициализированных переменных:

int sum;
...
for (int i = 0; i < 10; i  )
{
  sum  ;
}

В Си/Си переменная по умолчанию не инициализируется нулём. Изредка может казаться, что данный код работает. Это не так. Это легко везение.

Пункт N15

Возвращение из функции ссылки либо указателя на локальные объекты:

char* CreateName()
{
  char FileName[100];
  ...
  return FileName;
}

Позже выхода из функции ‘FileName’ будет указывать на теснее освобожденную память, от того что все локальные объекты создаются на стеке, и последующая правильная работа с ней будет немыслима.

Пункт N16

Не проверять возвращаемое значение из функций, которые могут воротить код ошибки либо ‘-1′ в случае ошибки. При некорректной работе может случиться так, что функция вернет код ошибки, но мы на него никак не отреагируем и продолжим работу, а после этого программа завершится некорректно в абсолютно непредсказуемом месте. Такие моменты дозволено длинно отлаживать позднее.

Пункт N17

Игнорирование применением особых инструментов статического и динамического обзоров, а так же написанием и применением Unit-тестов.

Пункт N18

Жадничаем поставить скобки в математических выражениях. В итоге получаем:

D = ns_vsk.bit.D_PN_ml   (int)(ns_vsk.bit.D_PN_st) << 16;

В данном случае первым будет исполнено сложение, а только после этого сдвиг налево. Смотри “Приоритет операций в языке Си/Си “. Исходя из логики программы, последовательность операций должна быть противоположной — вначале сдвиг, а для чего сложение. Схожая оплошность появляется и в таком коде:

#define A 1
#define B 2
#define TYPE A | B
if (type & TYPE) { }

Тут оплошность заключается в том, что макрос TYPE позабыли окружить круглыми скобками. Следственно вначале будет исполнено выражение ‘type & A’, а теснее после этого для чего ‘(type & A ) | B’. Итог — условие неизменно правдиво.

Пункт N19

Выход за границы массива:

int mas[3];
mas[0] = 1;
mas[1] = 2;
mas[2] = 3;
mas[3] = 4;

Выражение ‘mas[3] = 4;’ обращается к несуществующему элементу массива, от того что при объявлении массива ‘int mas[N]‘ индексация его элементов допустима в промежутке [0...N-1].

Пункт N20

Перепутаны приоритеты логических операций ‘&&’ и ‘||’. Оператор ‘&&’ имеет больше высокий приоритет, следственно в таком условии:

if (A || B && C) { }

Вначале выполнится ‘B &&C’, а после этого оставшаяся часть выражения. Это может не соответствовать нужной логике работы программы. Зачастую предполагается, что логические выражения выполняются слева-направо. О таких подозрительных местах так же предупреждает компилятор.

Пункт N21

Итог присваивания не будет иметь результата за пределами функции:

void foo(int *a, int b)
{
  If (b == 10)
  {
    *a = 10;
  }
  else
  {
    a = new int;
  }
}

Указателю ‘a’ не может быть присвоено другое значение адреса, для этого следовало бы записать объявление функции в таком виде:

void foo(int *&a, int b) {....}

либо:

void foo(int **a, int b) {....}

Список рекомендуемой литературы:

  1. ВЕРЕВКА ДОВОЛЬНОЙ ДЛИНЫ, ДАБЫ ВЫСТРЕЛИТЬ СЕБЕ В НОГУ. Правила программирования на С и С . Ален И. Голуб;
  2. Эталоны программирования на С . 101 правило и рекомендация. Герб Саттер, Андрей Александреску;
  3. Идеальный код. С. Макконнелл;
  4. Склизкие места Си . Стефан К. Дьюхэрст;
  5. Результативное применение C . 50 рекомендаций по совершенствованию ваших программ и планов. Скот Майерс.

Завершение

Особенных итогов у меня нет. Знаю только, что где-то в одном определенном месте, сейчас обстановка с разработкой ПО станет отменнее. Это отрадно.

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

Вот и получается необычная картина. Молодой фрилансер может исполнять работу больше высококачественно (вследствие умениям: TDD, постоянная интеграция, статический обзор, система контроля версий, …), чем программист 10 лет проработавший на РЖД/АЭС/(подставить что-то большое). Известность всевышнему, это вдалеке не неизменно так. Но всё-таки что-то такое есть.

Отчего меня это грустит? Туда бы продавать PVS-Studio. А там даже не догадываются о существовании и пользе таких инструментов. :)

Источник: programmingmaster.ru

Оставить комментарий
БАЗА ЗНАНИЙ
СЛУЧАЙНАЯ СТАТЬЯ
СЛУЧАЙНЫЙ БЛОГ
СЛУЧАЙНЫЙ МОД
СЛУЧАЙНЫЙ СКИН
НОВЫЕ МОДЫ
НОВЫЕ СКИНЫ
НАКОПЛЕННЫЙ ОПЫТ
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB