хабраиндекс
822,98
28 августа 2013 в 11:44

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

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) { }

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

Пункт 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. А там даже не догадываются о существовании и пользе таких инструментов. :)
+76
41506
229
Andrey2008 530,6 G+

комментарии (134)

+11
alexeyknyshev, #
Если в массиве нет элементов, результат выражения равен 0xFFFFFFFFu.


У меня тоже происходит FFFFFFFFFu, когда компилится некоторый код и g++ орёт:
«предупреждение: comparison between signed and unsigned integer expressions [-Wsign-compare]»

Это зло, ребята. Обращаюсь ко всем плюсовикам: «Правьте такой код сразу же, чтобы сэкономить время и нервы клиентам и сотрудникам.»
Наболело…
+66
datacompboy, #
Не могу удержаться, спрошу: вы сообщили об этом разработчику? а когда будет версия под линукс? а почему так дорого?
+7
barker, #
если это ирония, то не все поняли)
+1
ivan_kolmycheck, #
Повышаем градус долбанутости: когда можно будет купить через Steam?
+3
datacompboy, #
тогда уж предлагаю на хамблбамбл выйти.
0
ivan_kolmycheck, #
Да, я как-то о такой возможности не подумал. ^_^
+3
ivan_kolmycheck, #
В догонку: когда версия под Android?
0
wholeman, #
А на чём Вы под Android'ом пишете? Хороший редактор для C++ посоветуйте, а то я что-то ничего не нашёл.
0
AraneusAdoro, #
Лично я пользуюсь TextWarrior и C4Droid (просто С), но это далеко не идеал.
0
ivan_kolmycheck, #
Ни на чём. У меня вообще нет девайсов под андроидом, только у жены CM 10.1 (4.2) @ Samsung Galaxy Gio. =)
+20
datacompboy, #
особенно меня радует, когда предупреждение компилятора на
if (a=5) {… }
фиксят через
if ((a=5)) {… }

А там надо было ==…
+7
ploop, #
Когда так фиксят — это клиника :)

А вот подобные опечатки сам постоянно допускаю. Особенно, когда часто переключаешься между C++ кодом и PL/SQL, отлаживая проект.
Поэтому не могу писать код вообще, если маячит хоть один варнинг.
+2
UseRifle, #
чтобы не делать таких опечаток, нужно взять за правило менять переменную и константу местами 5 == a
+3
ploop, #
Не всегда там a==5 или 5==а. Есть выражения чуть сложнее:
if ((i!=j)&&(settingsModel->item(i,0)->text()==settingsModel->item(j,0)->text()))
0
alexeyknyshev, #
Это же Qt код, если я не ошибаюсь. А вот такой случай, когда «проблема» в Qt, т.к данный хранятся в QVariant и метод имеет сигнатуру text(): QString, а не text(): const QString / text(): const QString &, что позволило бы отловить случаи присваивания вместо сравнения.

Но если пишешь код сам, то лучше возвращать const объекты / ccылки. Это ещё и возвращаемых значение из операторов + / — / * касается, Скотт Майерс плохого не посоветует.

С++ ошибок не прощает, он беспощаден.
0
ploop, #
Да, Qt.
Код достался по наследству, требует громадного рефакторинга, который, разумеется, одному не потянуть.
+1
int19h, #
>> лучше возвращать const объекты / ccылки

Лучше не надо. Это сломает move optimization в C++11.
0
alexeyknyshev, #
Но точно не сломает RVO (Return Value Optimization), что не уступает move-semantic'е по эффективности.
+2
int19h, #
Область применимости RVO намного уже. Например, если вы напишете что-то вроде vec.push_back(foo()), и foo возвращает const-объект, то как минимум одна копия вам гарантирована. А без const это будет move.
+5
NobodyCallMeChicken, #
Нет ничего хуже таких выражений в незнакомом коде. В чем проблема добавить пару булевых переменных и сделать его более осмысленным. Например вместо
if (x!=j && is_action_avaible(x, y) && action_type != prev_action_type)

const bool is_field_free = x!=j;
const bool is_current_action_valid = is_action_avaible(x, y);
const bool is_this_new_action = action_type != prev_action_type;
if (is_field_free && is_current_action_valid && is_this_new_action) {
}

Такой код будет доступнее для понимания, более приближен к человеческому языку и в перспективе лишен таких вот ошибок.
+1
ploop, #
Это ещё цветочки, там весь метод на пять строк, так что проблем с пониманием не возникает. Но есть такие мозгодробильные методы, на пять экранов вот таких строк:
Скрытый текст
dbfFile.PutField(dbfFile.GetFieldNo(dbfRecord[count].FieldName), QString::number(round(i_map.value().at(cl).at(count).toDouble()*d_prec)/d_prec,'g',8).toLocal8Bit().data() );

(это одна строка, если что)
Человек писал явно в аврале, не думая о наследниках.
Тем более я не программист, и в C/C++ чистый самоучка. Но больше некому тащить проект.
0
Mrrl, #
Человек писал явно в аврале, не думая о наследниках.

Больше похоже на строчку из черновой версии кода — когда его нужно написать, не теряя контекста, чтобы не забыть, что откуда берётся и что когда надо сделать. В этом случае просто не успеваешь вспомнить, как называется функция, выполняющая
QString::number(round(someNumber*d_prec)/d_prec,'g',8).toLocal8Bit().data()

(или какой там фрагмент является часто используемым), и быстрее написать её сразу по коду — тем более, что она делает — понятно с первого взгляда. Потому что поиск или выделение метода — серьёзное переключение контекста. Причёсывание кода, как всегда, оставляем на «после того, как заработает», но тогда, к сожалению, возникают другие задачи, которые нужно сделать вчера.
В аврале такая ситуация возникает с большей вероятностью, но и при рутинной работе она не удивительна.
0
ploop, #
Да понятно, но от этого не легче.
В конкретном случае этот один громадный метод, на 650 таких вот строк, с циклами и условиями 5-7 уровней вложенности. Простыня, уезжающая за правый край экрана. Но можно же было его разбить на более мелкие части? Тем более есть куски (и довольно много) повторяющегося кода, вынеси которые, ещё пополам можно сократить.
0
VolCh, #
Часто такие условия накапливаются постепенно и перестаешь замечать, что оно становится нечитаемым, если хоть ненадолго выйти из контекста.

Ну и есть противники такого подхода, считающие, что это напрасное выделение памяти и тактов процессора.
+2
NobodyCallMeChicken, #
Компилятор с этим справится, так что напрасно ничего не выделяется.
0
VolCh, #
Я сейчас не про конкретный язык, а вообще про принцип. А так не каждый компилятор справится, да и не для каждого языка есть компилятор.
+2
int19h, #
Обычно в тех языках, в которых компиляторы не справляются с оптимизацией подобного кода — и уж тем более в таких, где компилятора нет — базовый уровень производительности настолько ниже, что микрооптимизации вроде инлайнинга переменных никакого значения иметь не будут. А там, где считают каждый такт — т.е. C и C++ — это свернет в изначальное выражение любой компилятор, выпущенный в последние 15 лет.
0
VolCh, #
Тем не менее, те кто так считает формально правы — потребление ресурсов увеличивает. А на довод «микрооптимизации вроде инлайнинга переменных никакого значения иметь не будут» возражают в стиле «копейка рубль бережет» и тоже сложно поспорить.
+1
int19h, #
Потребление ресурсов в случае использования вменяемого оптимизирующего компилятора это не увеличивает ни на грамм (если только вы не о времени компиляции, но и там это пренебрежимо малая величина).

А с «копейка рубль бережет» спорить очень легко в том случае, если выбор языка изначально предполагает сорение «деньгами» просто в силу дизайна реализации (например, PHP или Python) — встречный аргумент там очень простой, предложить переписать все на плюсах, т.к. экономия сразу будет куда серьезней.
0
VolCh, #
На плюсах будет дольше. Собственно аргументы за невыделения сложных выражений (или даже простых типа x!=j в примере выше по треду):
— меньше жрет ресурсов
— главное, писать быстрее, хоть тупо символы считать, хоть считать время затраченное на придумывание имен
0
int19h, #
Писать быстрее только в режиме write-only. Если потом этот код переписывать, или даже дебажить, то все эти попытки сэкономить десяток символов отольются во много зря потраченного времени.
0
Mrrl, #
Как раз превратить x!=j в is_field_free может быть полезно. А вот два остальных условия ничего к читабельности не прибавляют, в именах переменных там написано то же, что и в самих выражениях.
0
VolCh, #
Не сказал бы, что то же самое.
+1
AraneusAdoro, #
Йода-стайл. А читаемость? Это же неестественно. Это как «Если 18 — это ваш возраст» (if (18 == age)).

Внизу уже написали.
+1
dimzon541, #
Поэтому просто приучитесь всегда вне зависимости от языка программирования писать if(5==a) — константа СЛЕВА.
+33
MikhailS, #
У меня у одного после прочтения статьи остаётся вопрос: «кто же такие эти мистические заземлённые указатели?»?) А то заинтриговали же…
+2
Andrey2008, #
Я думаю легко догадаться, что имеется в виду. Как я понимаю, имеются в виду объявление указателей, которые сразу обнулены (на всякий случай): float * f = NULL;
+1
lagranzh, #
Вот думаю, что ровно наоборот. Во дыделения памяти (и после освобождения памяти), переменной присваивается валидное значение, «что бы не падало», в случае ошибки.

float *f = &default_float;
+3
ProstoTyoma, #
Мне кажется, это очень опасно. Вместо падения будет неочевидное неадекватное поведение.
+5
lagranzh, #
Конечно. Но мы же не оцениваем, хорошее это решение, или плохое. Мы отгадываем, что такое «заземленные указатели».
0
Andrey2008, #
В принципе, как я понимаю, «заземлять» можно не только указатели. Вот заземлённый int:
int A = 0;
:-)
0
WST, #
Думаю, в случае указателей аналогия уместнее, так как указатели могут содержать в себе опасность :)
+4
vsespb, #
Нет. Заземлённые — это только там где «NULL». NULL созвучно с Зануление. А Зануление это почти Заземление (в электрике).
НЛО прилетело и опубликовало эту надпись здесь
+1
VolCh, #
Если объявление с инициализацией сильно разнесены друг от друга и такие моменты в коде постоянно, то вероятность в следующий раз объявить переменную, но забыть её проинициализировать, сильно повышается.
НЛО прилетело и опубликовало эту надпись здесь
0
VolCh, #
Лучше инициализировать непосредственно перед использованием, чем рассчитывать, что а) переменная была проинициализирована когда-то раньше и б) её значение не изменилось.
НЛО прилетело и опубликовало эту надпись здесь
0
VolCh, #
А где невозможно — инициализировать непосредственно перед первым использованием.
0
datacompboy, #
это когда ноль на массу
+16
Singerofthefall, #
В стиле Йоды мастера сравнения рулят:
if( 5 == a ) {...}
+5
savostin, #
прокатывает константами с только, что жалко
+6
burjui, #
Может, всё-таки стоит читать предупреждения компилятора? Никогда не использовал Yoda conditions, т.к. не вижу смысла городить уродливый костыль для решения проблемы, которая обнаруживается ещё до запуска приложения штатным инструментом разработчика. Читается неестественно — «если пять равно a...», работает только с константами. Напоминает танец для вызова дождя, а не серьёзное решение.

Вопрос вначале скорее к Singerofthefall.
+1
dimzon541, #
1 ) Этому приему более 10 лет. Вы уверены что все компиляторы подо все платформы лет этак 10-15 назад выдавали варнинги?

2) Помимо C/C++ есть еще куча языков с аналогичными граблями. Yoda conditions работает везде.

3) Это всего навсего привычка которая реально помогает жить. Хуже точно не будет.

4) То что это «уродливо» всего лишь ваше субъективное мнение, на качество кода и его стабильность негативно не влияет.

Поймите правильно старого программиста. Если вас спросить цвета радуги 99% вы на автомате вспомните «каждый охотник...» Точно так-же я когда пишу код не задумываясь ставлю константу слева…
+2
VolCh, #
Хуже точно не будет.

Главный для меня аргумент.
+1
Flammar, #
Помимо C/C++ есть еще куча языков с аналогичными граблями. Yoda conditions работает везде.
Для Java, при сравнении строк, это практически «стандартная хитрость»: вместо
if( aa != null &&  aa.equals("qwerty")) {...} 
писать
if( "qwerty".equals(aa)) {...}

Не знал, что это так называется…
0
VolCh, #
Грабли не аналогичные, но название тоже подходит, да
+3
int19h, #
Чего только люди не придумают, чтобы не добавлять в язык перегрузку операторов…
+1
burjui, #
Ну, так сейчас — не 10-15 лет назад. Уже можно пользоваться современными компиляторами, я гарантирую это. Про качество кода можно даже не говорить после того, как для вас это стало субъективным мнением.
0
Singerofthefall, #
А почему вы решили, что я использую Yoda conditions — только потому что я про них вспомнил? Я вообще работаю со включенным treat warnings as errors (ну, в моем случае это QMAKE_CXXFLAGS += -Werror, но суть одна).
+14
Hertz, #
Такое ощущение, что в конторе даже с языком знакомы понаслышке, а о стандарте вообще не знают. :-)
+2
Andrey2008, #
Боюсь, Вы не так далеки от истины. Забыл написать ещё про одно полезных дело, который сделал этот человек. Уговорил руководство закупить для отдела разных книг по Си++.
+9
olekl, #
Теперь ему еще останется уговорить эти книги прочитать…
0
sfghelios, #
У них отключен интернет?
+4
Andrey2008, #
Вы будете плакать.
0
sfghelios, #
И дома тоже?

Как же они живут без однокласников и вконтактов (опционально фейсбуков, твиттеров, линкединов и т.п.)!!! ))))
НЛО прилетело и опубликовало эту надпись здесь
+1
denim, #
печально. cppcheck мы вкидываем на билд-машину вместе с плагином для jenkins'а. на выходе имеем актуальную информацию после каждого коммита. -Wall объявлен обязательным флагом и за игнор ворнингов бьем по рукам
+2
Mezomish, #
>-Wall объявлен обязательным флагом и за игнор ворнингов бьем по рукам

Только -Wall -Wextra -Werror, только хардкор! %)
0
EugeneGavrin, #
$ gcc -Wall -Wextra -Werror -std=c99   -pedantic
$ gcc -Wall -Wextra -Werror -std=gnu99 -pedantic
$ gcc -Wall -Wextra -Werror -std=c89   -pedantic
$ gcc -Wall -Wextra -Werror -std=gnu89 -pedantic

+18
viklequick, #
if(flag.ToString().Length == 5) { // flag is false
    ...
}


«На любом языке можно писать на фортране» (Ц) 8-)
+2
mifki, #
string sSelectedCalendarDate = cartItems.ElementAt(0).DateMeal.Day.ToString() + '/' +
cartItems.ElementAt(0).DateMeal.Month.ToString() + '/' +
cartItems.ElementAt(0).DateMeal.Year.ToString();

string sCurrenSystemTime = DateTime.Now.TimeOfDay.ToString();
string sOrderDateTime = sSelectedCalendarDate + ' ' + sCurrenSystemTime;

DateTime dtOrderDateTime = DateTime.Parse(sOrderDateTime);
0
Eddy_Em, #
Столько пунктов…
А между тем, все можно существенно упростить, если всегда в опции компилятора добавлять -Wall -Werror
УМВР.
// кстати, конструкции вида if((a = b)){...} сам часто использую. Это удобнее, чем писать a = b; if(a){...}
+4
DmitryMe, #
Если аналогичную вещь сделать в Visual C++, то перестает компилироватьca любой проект, включающий в себя заголовки Microsoft SDK, которые нужны для использования Windows API.
+1
int19h, #
Надо понимать, что /Wall в VC++ включает в себя т.н. «informational warnings», которые не предполагают наличия проблемы. Например, там выдаются варнинги о том, что в структуру добавлен padding. Настоящий аналог gcc -Wall в VC++ — это /W4. С ним все стандартные заголовочные файлы, в т.ч. WinSDK, компилируются молча.
0
DmitryMe, #
MSDN говорит следующее:
Level 4 displays all level 3 warnings and informational warnings.
т.е. уровень 4 объединяет в себя «informational warnings». Выключены по умолчанию предупреждения самых разных уровней, например, C4906 имеет уровень 1, выключено по умолчанию и очень даже предполагает наличие неопределенного поведения.
0
int19h, #
Сам по себе каст строкового литерала к char* неопределенного поведения не предполагает. Оно возникнет только в том случае, если вы попытаетесь потом через этот неконстантный указатель литерал поменять.

В целом, да, я был несколько некорректен. Но суть та же — /Wall включает в себя большое количество варнингов, которые таковыми по сути не являются, и на практике собирать с ним смысла не имеет — базовым должен являться /W4. А если там чего-то не хватает, то это уже надо включать выборочно.
+4
EvgeniyRyzhkov, #
Боюсь, это лишь теория. Поднимите руку те, у кого на хотя бы среднем проекте -Wall? Средний — это от мегабайта исходников.
+5
encyclopedist, #
В том проекте, который сейчас использую (OpenFOAM): исходников ~ 60 МБ, флаги
-Wall -Wextra -Wno-unused-parameter -Wold-style-cast -Wnon-virtual-dtor -O3
При компиляции вылезает ещё несколько предупреждений на неиспользуемые переменные, и вроде всё.
+2
Andrey2008, #
Уважаю.
0
encyclopedist, #
Я не автор, я только пишу дополнения для своих целей. Так что уважение направляется авторам. Но там в коде другие особенности: например, широкое использование #include для кусков кода, своя самописная система сборки…
0
bigfatbrowncat, #
Строго и правильно всё, кроме -O3. Я бы побоялся в продакшн включать экспериментальные оптимизации gcc…
0
Mezomish, #
Респект! У меня тоже непереносимость варнингов, я еще и -Werror включаю, чтобы не пропустить ненароком. Однажды в релизном билде забыл его убрать, и было весело, когда ворнинг вылез из Qt-шного хедера, и проект перестал собираться на некоторых платформах :)
+2
AoD314, #
В своих проектах стараюсь всегда собирать с флагами:

-Werror -Wall -Wextra -pedantic -Weffc++ -Wconversion -Wsign-conversion -Wold-style-cast -Wunreachable-code -Woverloaded-virtual -Wctor-dtor-privacy -Wnon-virtual-dtor -Wenum-compare
+1
dmitriy_t, #
Если речь про gcc то -Wunreachable-code удалили вроде пару лет назад и по-моему он тупо игнориться сейчас.

-Weffc++ полезно иногда включать, но в больших проектах ни у кого не хватает духу его оставить — задалбывают параноидальные ворнинги про необходимую инициализацию всего в конструкторе. Там сейчас собственно есть предложение разделить Weffc++ на несколько отдельных частей, надеюсь к 5-му gcc разделят :).

Ещё кстати -Wshadow полезен.

Есть ещё много флажков но они не одинаково полезны для всех случаев.
0
phprus, #
"-Wall -Wextra" и порядка 8Мб своих исходников на С++. К сожалению, из-за использования некоторых сторонних библиотек и древних компиляторов (включая gcc 4.1.2), которые выдают некоторые предупреждения, которые реально ложные срабатывания флаг -Werror не используется. Однако, все предупреждения тщательно анализируются и либо игнорируются с соответствующей записью куда следует, либо исправляются, а от древних gcc я очень надеюсь избавиться в ближайшее время ибо у следующего нужного компилятора gcc 4.3 с ложными срабатываниями все куда лучше (лично не встречал).
+1
gribozavr, #
LLVM, Clang. -Wall -pedantic.
0
int19h, #
Visual Studio подойдет? Там не везде /W4, но в очень многих плюсовых модулях таки да.
0
khim, #
Да даже на больших: достаточно вводить его постепенно, по модулям.

Скажем Chromium — там делоко не один мегабайт исходников, но большая часть модулей компилируется именно в таком режиме.

Собственно единственная отговорка была до GCC 4.6, когда не было "#pragma GCC diagnostic push"/"#pragma GCC diagnostic pop" (которые необходимы для постепенного введения -Wall -Werror в проекте где в header'ах много… добра).
0
SVlad, #
Если в проекте с самого начала используется этот флаг — то не проблема. А вот потом вводить — проблема.
+5
olekl, #
Чтобы убедиться в том, что в непрофильных компаниях в отделе программирования болото, достаточно взглянуть на то, что производители различной и порой даже хорошей техники поставляют в виде прилагающегося софта. Из того, с чем приходилось сталкиваться: Никон, Кенон, Моторола, Нокия, НР… Так что увы :(
+2
bigfatbrowncat, #
Кстати, да. И это вообще очень стыдно, так как эти приложения — не мат. моделирование, в котором действительно важнее всего результат. Они — пользовательские. И жуткий глючный интерфейс в сочетании с общей нелогичностью и убогостью совсем не вяжется с отличным дизайном телефона или фотоаппарата.
+5
Veliant, #
Скриншот в тему
image
0
Andrey2008, #
Не удержался. Написал забавную заметку на эту тематику: После подключения бесперебойника программа больше не работает. Приглашаю всех почитать.
–1
Goodkat, #
Никон, Кенон, Моторола, Нокия, НР
SONY!!!!1
+17
bigfatbrowncat, #
Но всё равно, тенденция заболачивания четко прослеживается. Я не знаю, отчего так происходит, но это так.


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

На протяжении учёбы вполне естественно оказалось, что по части программирования (и в том числе математических методов физического моделирования) я был одним из лучших. И там же выяснились следующие факты:

1. Физики рассматривают компьютер как большой многофункциональный калькулятор, позволяющий построить график зависимости Эта от Тэта при Гамма стремящемся в бесконечность. Причем, очевидным образом, для них цель — график, а вовсе не та программа, которая его рисует.
2. Как следствие из этого факта, программист — это не профессия. Программист — это просто тот человек, который умеет пользоваться Большим Калькулятором, чтобы построить означенный график. Каким способом график будет построен, не имеет значения. Совсем. Как-как вы сказали? Статический анализ? Контроль версий? Окститесь, родные! C++ — язык для программистов. Физики пишут на Фортране!
3. Как следствие из предыдущего пункта, человек, стремящийся посвятить свою жизнь написанию программ физ. моделирования, даже универсальных, даже очень и очень крутых — не более, чем приложение к калькулятору. Он и не человек вовсе, а так… И это, кстати, распространялось не только на меня (куда уж мне, убогому), а даже и на лучшего численника-расчётчика в НИИ, который преподавал у нас численные методы и который, когда я пришел к нему делать курсовую, сказал мне почти открытым текстом: «Вас будут презирать, приготовьтесь терпеть.»

Терпеть мне не захотелось и я после окончания ушел из моделирования в области, где программистов не считают людьми второго сорта. Надеюсь, этот пример объясняет, почему инициативы типа ввода статического анализа даже на сравнительно крупных (до 20-30 человек) проектах по мат. моделированию — гиблое дело. Там просто может не найтись человека, который знает, что это такое. А если такой человек и найдётся, его, скорее всего затопчут, потому что нафиг не нужны эти новомодные программерские прибамбасы. Сто лет без них жили — и дальше проживём.

И тем, кто не заскучал, второй пример. Мой отец, находясь в пенсионном возрасте, тем не менее работает в очень крупном оборонно-инженерном предприятии, здесь же, в Нижнем (самом крупном в городе и одном из крупнейших в стране — те, кто в теме опять же угадают ;) ). Он всю жизнь программировал на Фортране. Начинал еще с перфокарт. Я не виню его за то, что не учит C++. Ему это уже 10 лет назад было поздно — он еще неплохо держится. Но на предприятии, где 2/3 сотрудников что-то как-то программируют, приняты следующие меры безопасности:

1. Никакого интернета. Совсем. Нужна литература — иди в местную библиотеку. Stack Overflow? А что это? Даже если ты хочешь отправить письмо электронной почтой, ты должен написать заявление начальнику, где ты объяснишь, кому это письмо и для чего. Интернет «под расписку» есть только у избранных. Слава богу хоть внутренняя сеть есть.

2. Никаких административных прав на рабочем компьютере. Возможно это правило разумно для офисного планктона, но мне трудно себе представить программиста, которого бы оно устроило.

3. (не относится к делу, просто иллюстрация) Нельзя даже пронести телефон с камерой (а где вы теперь видели другие)?

В результате даже молодняк пишет на Фортране, причем реально грамотных в программировании — единицы. Знаю, потому что подтягивал по программированию одного парня лет 25, который был мне отцом зарекомендован как многообещающий.

Мой вердикт: там 80-е годы. Даже при том, что там неплохо платят, я не пойду туда ни за какие каврижки.

Вот такие два примера из жизни интеллектуальной элиты. Никого не хочу очернить — люди хорошо делают своё дело и так, но иногда смотря, с какими ветряными мельницами порой воюет отец, которого я недавно (слава богу!) смог-таки пересадить в git, сердце сжимается. Никакого ООП в проекте под миллион строк кода, никакого статического анализа.

Просто люди имеют свойство быть очень консервативными в областях, не являющихся их основным «коньком».
+1
DmitryMe, #
Как-как вы сказали? Статический анализ? Контроль версий? Окститесь, родные! C++ — язык для программистов. Физики пишут на Фортране!
Особенно печально, что при таком восприятии контроль версий каким-то непостижимым образом оказывается связан с C++, который «для программистов». Вот в Википедии у каждой статьи есть история — по сути, тот же контроль версий. Видимо, Википедия тоже «для программистов».
+2
bigfatbrowncat, #
Для этих людей (особенно для старшего поколения «за 40», которое руководит) принцип работы Википедии — еще большая магия, чем для большинства веб-разработчиков устройство двигателя подлодки или ядерного реактора. И это не грустно и не радостно. Это — просто разделение обязанностей.

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

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

Разумеется, большинство из них, являясь очень интеллигентными людьми, даже не отдают себе отчёта в факте своего презрения. Сознательно они будут вам говорить, что программирование — очень важная и сложная наука.
+4
VolCh, #
Статический анализ? Контроль версий? Окститесь, родные! C++ — язык для программистов. Физики пишут на Фортране!
В результате даже молодняк пишет на Фортране
Никакого ООП в проекте под миллион строк кода, никакого статического анализа.

Почему-то мне кажется, что вы во власти каких-то стереотипов находитесь, слово FORTRAN для вас как красная тряпка. А ведь поддержка ООП появилась в FORTRAN больше 20 лет назад (стандарт FORTRAN 90) и с тех пор не раз улучшалась. Инструменты контроля версий были даже в «русифицированных» клонах Unix на «русифицированных» клонах (ЕС, СМ и т. п.) «серьезных» компьютеров типа PDP-11 (и в начале 90-х нас заставляли ими пользоваться на лабах по FORTRAN 77 на СМ-1420), и современные никто не запрещает использовать. Инструменты статического анализа для FORTRAN тоже существуют, хотя бы предупреждения компилятора.

Описанные вами проблемы не в языке, а в мышлении. Люди осваивают минимально необходимый набор знаний и больше ничего изучать и использовать для «управления большим калькулятором» не хотят.
+1
JustAMan, #
Аллергия, да :) Не совсем беспочвенная, на мой взгляд (знаю эту конкретную ситуацию изнутри, привет, bigfatbrowncat! ;) )

Что касается болота и мышления… как говорится, что посеешь — то и пожрёшь пожнёшь, в основном там работают «старые кадры» с уже существующими наработками, а молодые и перспективные редко туда идут (не только в «крупном оборонном предприятии», но и в других НИИ, например, под эгидой Нижегородского Университета), по крайней мере на полную ставку. Соответственно и времени на обучение людей правильному написанию не остаётся, надо за минимально потраченное время добавить то-то и то-то в расчеты, а не перепахивать весь проект.
0
VolCh, #
А не надо перепахивать весь проект, если есть желание ввести современную культуру разработки. Например, научить базовым командам Git, Mercurial или SVN можно за 15 минут. ООП тоже можно внедрять постепенно, как и TDD. Язык тут явно не причем. Вон в НАСА используют FORTRAN весьма активно с использованием TDD оценивая базовое введение в 1-2 дня.
0
bigfatbrowncat, #
Я знаю про ООП-расширения Фортрана. Приделаны они, на мой взгляд, весьма и весьма косо. Что понятно, так как когда этот язык создавался, о том, что такое ООП, никто даже не задумывался (поправьте, если я неправ).

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

Да, я субъективен. Мне C++ намного намного ближе. Там хотя бы грамматика почти контекстно независимая.
0
bigfatbrowncat, #
Хорошо, уточню. Пишут на фортране стандарта F77, как и подобает любому продвинутому разработчику 80-х годов.
+4
huankun, #
В Си/Си++ переменная по умолчанию не инициализируется нулём.


В Си++ локальная переменная по умолчанию не инициализируется нулём
0
Amomum, #
Я очень стараюсь убирать все ворнинги компилятора, но довольно часто ничего не могу поделать с двумя:
statement is unreachible и pointless comparison between unsigned value and zero. И вот почему.

Я в коде (embedded) часто использую самопальный assert, который сводится к следующему
do { if(! (statement) ) { while(1); } } while(0)
так я сразу в отладчике вижу, на какой строчке остановилось выполнение. Но если этот ассерт вставлен, например, в одной из веток switch'a — компилятор ругается.

Второй ворнинг в основном вылезает из шаблонов, когда при проверке одно из шаблонных значений оказывается нулем.
А из-за чудного компилятора, который выдает ворнинг на коды в .h-файлах при КАЖДОМ ВКЛЮЧЕНИИ этого .h-файла, вывод иногда просто весь в ворнингах.
Это меня очень огорчает, но что делать я не очень представляю. Не давить же эти ворнинги?
+1
Paul, #
Вроде любой компилятор выдает ворнинги на код из хедера при каждом включении хедера. Другое дело, что если хедер системный или 3rd-party, который не поменяешь, то можно перед его инклудом поткнуть прагму, которая ворнинг давит, а после — прагму, которая возвращает настройку нотификации о ворнингах в режим «как было».
0
Amomum, #
Ах, если бы все компиляторы поддерживали эти прагмы :)
Но может быть есть какой-то другой способ код писать?
0
khim, #
А какие компиляторы их не поддерживают?

Просто интересно. GCC научился с версии 4.6, остальные распространённые компиляторы (MSVC, Clang) вроде всегда умели…
+1
encyclopedist, #
Так речь идёт же о встраиваемых системах. А там компиляторы могут очень необычными, от производителя процессора.
0
Amomum, #
На самом деле, я был не прав на счет конкретно своего (keil arm compiler), просто мне было лень хорошенько погуглить.
Я вчера потратил пару часов и смонстрячил более-менее переносимое решение. По-крайней мере, сторонние хедеры я могу заключать в прагмы.

Но вот что делать со сторонними сишниками — пока не знаю.
0
Riateche, #
Предупреждения, порождаемые сторонними заголовочными файлами, нужно глушить с помощью pragma warning (см., например, эту стутью). При этом для остального кода предупреждения остаются включенными. А описанный пример с while(1), насколько я понял, употребляется только для отладки. В таком случае предупреждение, наоборот, полезно, так как помогает не забыть убрать отладочный код.
+3
Semisonic, #
Мда. За окном 2013-й год, беспилотные космические вездеходы давно уже бороздят просторы не только Большого театра, но и марсианскую поверхность. А статья, рассказывающая, как не путать сравнение и присваивание и чем чревато сравнение С-строк по указателям, выходит в топ Хабра.

Простите мне мой скепсис, но я склоняюсь к тому чтобы согласиться со Станиславским. Ибо после всех постов о мегадостижениях вашего программного продукта у меня в голове не укладывается, как кто-то, уходя от вас, стал бы устраиваться на работу в такое место. Не поверю, что люди могут дойти до такой степени отчаяния и при этом хотеть жить на этом свете. Либо вы своим работникам совсем не платите, и ради денег они готовы на самые извращённые формы рабства =).
+1
bigfatbrowncat, #
Возможно, просто мы имеем дело с перфекционистом, который пошёл в глухой угол в надежде сделать мир лучше
0
VolCh, #
В условии оператора 'if' происходит не проверка значения, а присваивание:
if (numb_numbc[i] = -1) { }

Кстати, предупреждает ли о возможности подобной ошибки анализаторы, если она встречается не в if, а в циклах? Что-то вроде
nLines = 0;
file->open();
while (str = file->readLine()) {
  nLines++;
}

Для себя так и не решил, является ли подобный код допустимым или следует его разворачивать во что-то вроде
nLines = 0;
file->open();
str = file->readLine();
while (str) {
  nLines++;
  str = file->readLine();
}

или
nLines = 0;
file->open();
for(str = file->readLine(); str; str = file->readLine()) {
  nLines++;
);

Смущает двойное повторение в коде file->readLine() при попытке избавиться от присваивания в проверке условия цикла, особенно в варианте с for, который вроде бы больше для этого подходит. Кажется, что ухудшает читаемость. Кто как думает?
+1
Andrey2008, #
Кстати, предупреждает ли о возможности подобной ошибки анализаторы, если она встречается не в if, а в циклах?

Да. Например, PVS-Studio предупреждает в некоторых случаях. Но тут надо соблюдать компромисс между нахождением ошибок и количеством ложных срабатываний. Слишком это частый приём.
+2
Mrrl, #
Я последнее время пишу так:
nLines=0;
for(;;){
  str=file->readLine();
  if(!str) break;
  nLines++;
}

0
VolCh, #
Кстати, да, лучше чем мои выглядит, хотя если переписать под обычные правила, то уже не так:
nLines=0;
for(;;){
  str=file->readLine();
  if(!str) { 
    break;
  }
  nLines++;
}

0
burjui, #
Потому что эти «обычные» правила — отстой.

nLines = 0;

while (true)
{
    str = file->readLine();

    if (str != NULL)
        ++nLines;
    else
        break;
}
0
VolCh, #
Отстой или нет, но может просто коммит не пройти.
0
Envy, #
А поясните пожалуйста 21 пункт, почему если мы присваиваем переменной a новый адрес не будет никакого эффекта на значение которое хранится по данному адресу
0
Andrey2008, #
Имелось в виду другое. Программисты путают. Они думают, что раз вот так *a = 10; можно поменять значение данных по указателю, то можно изменить и само значение указателя вне функции. Думают так они не всегда, но видимо бывает, что забываются. :)
0
rsa2048, #
По поводу пункта 15 можно найти такую красоту:

\Microsoft SDKs\Windows\v7.0A\Include\WinSock.h

char FAR * PASCAL FAR inet_ntoa (__in struct in_addr in);

The string returned by inet_ntoa resides in memory that is allocated by Windows Sockets. The application should not make any assumptions about the way in which the memory is allocated. The data is guaranteed to be valid until the next Windows Sockets function call within the same thread, but no longer. Therefore, the data should be copied before another Windows Sockets call is made.
0
bigfatbrowncat, #
Ах, WinAPI, WinAPI… Сколько счастливых ночей было проведено за выискиванием причин «Access violation at address 123455FF read at address 00000000»…
+1
mifki, #
Ну так сплошь и рядом делается, что такого…
0
kyb27, #
Каким боком пример 4 иллюстрирует проблему одновременного использования знакового и беззнакового типа? BufPos unsigned int, size_t скорее всего unsigned long, имеется ошибка в логике, при чем здесь знаковость?
0
Andrey2008, #
Ошибка в голове. Люди не осознают, что смешивая int и unsigned, они получают unsigned со всеми вытекающими последствиями. Я вот тоже нередко встречал людей, которые думают что 1u — 2 < 0
0
egorF, #
Статья про notepad проскакивала в thedailywtf.
0
vit1251, #
> Я не знаю, отчего так происходит, но это так. Причём, чем больше компания, тем сильнее проявляется этот эффект.

Вы глубоко заблуждаетесь. Могу вам с уверенностью сказать, что есть большие компании в которых готовы двигаться вперед,
но для этого требуется проводить тренинги по новейшим технологиям и кроме того иметь отдел R&D. А вот теперь на пальцах
пересчитай компании в которых есть R&D и как уже было сказано в статье команды, которые прибиты к какой-то отрасли просто
реально загнивают, так как сами по себе сидят на жопе ровно в своем сформированном годами штате и ничего им для счастья
не нужно (а вот на сколько хорошо это или плохо не нам судить, так как если их устраивает ZIP архив, то не надо лезть со своими
вениками мужики и без вас разберутся, а то и напишут какой-нить Zit или Zubversion)
0
Mrrl, #
Но ведь отдел R&D в какой-нибудь физической лаборатории должен заниматься, скорее, средствами фиксации результатов экспериментов и алгоритмами обработки этих результатов, а не системами контроля версий? То же и при АЭС — там им надо, к примеру, разрабатывать и настраивать какие-нибудь системы контроля своего оборудования и электростанции в целом. Хотя, с большей вероятностью, этот отдел будет где-нибудь при министерстве, а на станции они будут ездить в командировки.
0
xaizek, #
Пункт N20 точно правильный? Просто то, что там написано противоречит логике ленивых вычислений в моём понимании. Больше похоже, что имелось в виду группировка выражений (мол "(A || (B && C))" вместо "((A || B) && C)").

Код для проверки
#include <iostream>

class C
{
public:
    C(char name, bool val)
        : name(name)
        , val(val)
    {
    }

    operator bool() const
    {
        std::cout << name << std::endl;
        return val;
    }

private:
    const char name;
    const bool val;
};

int
main(void)
{
    C a('a', false), b('b', true), c('c', true);
    if (a || b && c)
    {
    }
    return 0;
}


0
Andrey2008, #
Вы всё как-то слишком усложняете. Читать эту статью надо думая о простом. :)
0
xaizek, #
Согласен, так и читал до 20-го пункта. А вот после него думал о приоритетах операций и возможной дезинформации людей, работающих со встраиваемыми системами. Они ведь и поверить на слово могут, а результатом окажеться, что так и не будут понимать как работает их код, чего хотелось бы избежать.

Ну и меня сильно удивил сам факт того, что никто из читателей не указал на неточность.
+1
Andrey2008, #
Ну глупость там написана, глупость. :) Бывает. Поправил.
+1
xaizek, #
Спасибо. Теперь я снова смогу спать спокойно :-)

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