Внимательно изучив код, я понял, что немного ошибся — это никакая не оптимизация, первый проход — холостой — делается для расчета длины, а второй для операций над строкой, о чем и написано в комментарии (которые мой внутренний парсер давно привык игнорировать)
/*
* First round: compute the length and allocate memory.
* Second round: copy the text.
*/
Так как выделение памяти происходит исходя из расчетной длины, «неоптимизированный» вариант с выделением памяти заранее невозможен.
А разделение на две процедуры породило бы дублирование кода в обе процедуры, что грозит ошибками в случае его модификации. Однако, сделать его более «качественным» можно было бы, например, заменой абстрактного int round на bool length_calculation_only.
Это уже похоже на ошибку. Анализатор говорит о лишней проверке, но на самом деле проблема прежде всего в другом. Указатель retval инициализирован 0, и я не нашел ни одного места в этой функции, где бы его значение менялось. При этом в него делается много раз делается strncpy. После чего его внезапно решили проверить на NULL.
char_u *retval = NULL;
...
for (round = 1; round <= 2; ++round)
{
...
if (lnum < 0 || submatch_mmatch->endpos[no].lnum < 0)
return NULL;
...
if (s == NULL)
break;
...
if (round == 2)
vim_strncpy(retval, s, len);
...
if (round == 2)
{
STRCPY(retval, s);
retval[len] = '\n';
}
...
if (round == 2)
STRCPY(retval + len, s);
...
if (round == 2)
retval[len] = '\n';
...
if (round == 2)
STRNCPY(retval + len, reg_getline_submatch(lnum),
submatch_mmatch->endpos[no].col);
...
if (round == 2)
retval[len] = NUL;
...
if (retval == NULL)
{
retval = lalloc((long_u)len, TRUE);
if (retval == NULL)
return NULL;
}
}
Доступ по указателю осуществляется только на втором раунде, а аллокация на первом. Такая вот агрессивная оптимизация, чтобы не делать лишней аллокации, если до второго раунда дело не дойдет. Ошибки здесь нет.
в следующий стандарт языка программирования C будут добавлены средства ООП, а именно — классы
Первое предложение добавить классы в C было еще в 93-м году — N298. Затем в 95-ом — N424, N445-447. Если вы внимательно следите за предложениями для WG14 и WG21, то могли бы заметить, что далеко не всякое предложение попадает в стандарт, а некоторые хотелось бы и вовсе «развидеть».
If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined.102)
Among the invalid values for dereferencing a pointer by the unary * operator are a null pointer, an
address inappropriately aligned for the type of object pointed to, and the address of an object after the
end of its lifetime
В пояснении приведено конкретное исключение из этого правила, которое не включает в себя обращение к полям структуры.
Это неизбежно, когда ненароком ставят рядом два довольно-таки разных языка (более эпичный вариант лишь Java/JavaScript). ;)
Я мог бы с вами согласиться на 100%, если бы все четыре основных компилятора не были бы C/C++.
Тут нельзя делать заключение о значении указателя на основе лишь синтаксиса, поскольку нет разыменовывания — интерпретация выражения не выходит за рамки арифметики указателей.
В стандарте нигде не определено, считать ли такой случай разыменованием или нет. Аргументом к тому, что «считать», может быть пример ниже про виртуальное наследование. А так как в стандарте ничего не сказано, про то, что данный случай «не считается разыменованием», это и называется «неопределенным поведением», так как стандартом оно не определено.
Все действия, помимо тривиальных GET, POST, PUT, DELETE, выносятся в отдельную коллекцию:
POST /missiles/{id}/actions/launch
POST /missiles/{id}/actions/sell
Нет ничего неправильного в том, чтобы писать небезопасный код — не нужно платить за то, что не используется, если аргумент, приводящий к UB, по логике программы не может быть использован, обработкой исключений можно безболезненно пожертвовать.
struct usb_line6 *line6 = &podhd->line6;
Таким образом здесь мы соглашаемся, что podhd!=nullptr, и подобный аргумент не должен быть использован при вызове функции. Неопределенного поведения в этой строке нет.
if ((interface == NULL) || (podhd == NULL))
Однако дальнейшая проверка podhd уже является неопределенным поведением, так как существуют два конкурирующих способа осуществить эту проверку, возможно дающих разный ответ: проверить значение указателя, или воспользоваться ранее выведенной аксиомой, что podhd!=nullptr.
Это только с точки зрения компилятора/оптимизатора взятие адреса в данном случае не является разыменованием, соответственно и UB. Однако с точки зрения стандарта текст программы нужно читать буквально: есть "->" — есть разыменование, а если по указателю может быть 0 — то и неопределенное поведение. Собственно конкретная работа оптимизатора никак не описана в стандарте и, соответственно, является UB. А для соответствия стандарту от компилятора требуется строго ожидаемого поведения от буквально прочитанного кода, поэтому неопределенность оптимизатора никогда не проявляется в корректном коде. А если в этом коде присутствует формальный UB (даже если с точки зрения компилятора это не так), можно ожидать проявления UB со стороны оптимизатора.
Проблема в неверной постановке вопроса:
«разыменовывание нулевого указателя» однозначно по стандарту является неопределенным поведением.
А вопрос на самом деле заключается в следующем: считается ли конструкция "&a->b" разыменованием указателя a.
P. S. Несложно из приведённого кода догадаться, что это «одноразовый» барьер: как только через него пройдут все потоки, вы не сможете повторно использовать этот же экземпляр класса в качестве барьера.
Проблема в том, что значение по умолчанию Шредингера находится в суперпозиции — оно одновременно есть, когда мы пытаемся вызвать функцию, опуская его; и его нет, когда мы пытаемся наблюдать его.
Чтобы ваш код заработал, надо чтобы для func и other_func вызывались разные версии шаблонной функций. Этого можно достичь, например, завернув каждую функцию в свой функциональный объект:
Именно. Но я этого не предлагаю, напротив, говорю, что такой вариант невозможен, а без него именованные параметры превратятся в дополнительную препроцессорную магию, когда имена вроде как есть и используются, но вроде как и нет, так как средствами языка невозможно узнать о их существовании или отсутствии (например при помощи sfinae). Какая-то квантовая механика, а не программирование.
template<class F, class ... Args>
auto call(F f, Args&& ... args) {
return f(std::forward<Args>(args)...);
}
...
call(func);
Или даже:
auto f = func;
f();
Значения по умолчанию практически ничем не отличаются от макросов, и всего-лишь делают замену на уровне препроцессора с:
func();
на:
func(0);
Так вот, то о чем вы говорите — это не уровень языка, а уровень препроцессора, производящего еще одну неявную замену, которую будет так же невозможно заметить средствами языка, как и для аргументов по умолчанию.
Неужели c++ недостаточно твердо стоит на двух костылях макросов и значений по умолчанию, что его еще нужно подпереть третьим?
Конечно в N4172 предложен вариант без утери обратной совместимости, но использование такого варианта может принести вместо удобства головную боль: например автор используемой третьесторонней библиотеки решит переименовать параметры, или вообще убрать их имена — имеет полное право. А про переносимость таких программ можно будет вообще забыть.
${CC} ${CCFLAGS} -c -o $@ $<
=>
PVS-Studio -cc=gcc — ${CCFLAGS} -c -o $@ $<
На выходе получим $@ и $@.pvs
${LD} -o ${TARGET} ${OBJS} ${LIBS}
=>
PVS-Studio -ld=gcc — -o ${TARGET} ${OBJS} ${LIBS}
На выходе получаем ${TARGET} и готовый лог
Так как выделение памяти происходит исходя из расчетной длины, «неоптимизированный» вариант с выделением памяти заранее невозможен.
А разделение на две процедуры породило бы дублирование кода в обе процедуры, что грозит ошибками в случае его модификации. Однако, сделать его более «качественным» можно было бы, например, заменой абстрактного int round на bool length_calculation_only.
Доступ по указателю осуществляется только на втором раунде, а аллокация на первом. Такая вот агрессивная оптимизация, чтобы не делать лишней аллокации, если до второго раунда дело не дойдет. Ошибки здесь нет.
Первое предложение добавить классы в C было еще в 93-м году — N298. Затем в 95-ом — N424, N445-447. Если вы внимательно следите за предложениями для WG14 и WG21, то могли бы заметить, что далеко не всякое предложение попадает в стандарт, а некоторые хотелось бы и вовсе «развидеть».
Вы вырываете из контекста:
В пояснении приведено конкретное исключение из этого правила, которое не включает в себя обращение к полям структуры.
Я мог бы с вами согласиться на 100%, если бы все четыре основных компилятора не были бы C/C++.
В стандарте нигде не определено, считать ли такой случай разыменованием или нет. Аргументом к тому, что «считать», может быть пример ниже про виртуальное наследование. А так как в стандарте ничего не сказано, про то, что данный случай «не считается разыменованием», это и называется «неопределенным поведением», так как стандартом оно не определено.
POST /missiles/{id}/actions/launch
POST /missiles/{id}/actions/sell
Таким образом здесь мы соглашаемся, что podhd!=nullptr, и подобный аргумент не должен быть использован при вызове функции. Неопределенного поведения в этой строке нет.
Однако дальнейшая проверка podhd уже является неопределенным поведением, так как существуют два конкурирующих способа осуществить эту проверку, возможно дающих разный ответ: проверить значение указателя, или воспользоваться ранее выведенной аксиомой, что podhd!=nullptr.
Происходит ли здесь присваивание?
Суммирование?
Разыменование?
«разыменовывание нулевого указателя» однозначно по стандарту является неопределенным поведением.
А вопрос на самом деле заключается в следующем: считается ли конструкция "&a->b" разыменованием указателя a.
Несложно упростить код до многоразового:
А можно просто отказаться от плохой практики использования значения по умолчанию в пользу перегрузок и вызова функций согласно их сигнатурам:
А теперь попробуем:
Или даже:
Значения по умолчанию практически ничем не отличаются от макросов, и всего-лишь делают замену на уровне препроцессора с:
на:
Так вот, то о чем вы говорите — это не уровень языка, а уровень препроцессора, производящего еще одну неявную замену, которую будет так же невозможно заметить средствами языка, как и для аргументов по умолчанию.
Неужели c++ недостаточно твердо стоит на двух костылях макросов и значений по умолчанию, что его еще нужно подпереть третьим?