Pull to refresh
14
0
Send message
В чем нарушается то?

${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} и готовый лог
Соединить логи в один можно там, где работает линковщик.
Внимательно изучив код, я понял, что немного ошибся — это никакая не оптимизация, первый проход — холостой — делается для расчета длины, а второй для операций над строкой, о чем и написано в комментарии (которые мой внутренний парсер давно привык игнорировать)
/*
* 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 со стороны оптимизатора.
Поясню:
Происходит ли здесь присваивание?
x = x;


Суммирование?
y = x + 0;


Разыменование?
&a->b
Проблема в неверной постановке вопроса:
«разыменовывание нулевого указателя» однозначно по стандарту является неопределенным поведением.
А вопрос на самом деле заключается в следующем: считается ли конструкция "&a->b" разыменованием указателя a.
А будет ли неопределённое поведение в таком гипотетическом случае?
static int podhd_try_init(struct usb_interface *interface,
        struct usb_line6_podhd * volatile podhd)
{
  int err;
  struct usb_line6 *line6 = &podhd->line6;

  if ((interface == NULL) || (podhd == NULL))
    return -ENODEV;
  ....
}
template<class T>
typename std::enable_if<std::is_base_of<Base1,T>::value,void>::type
execute(const T&);

template<class T>
typename std::enable_if<std::is_base_of<Base2,T>::value,void>::type
execute(const T&);
P.S.: Атомарные типы впрочем не нужны в данном случае.
P. S. Несложно из приведённого кода догадаться, что это «одноразовый» барьер: как только через него пройдут все потоки, вы не сможете повторно использовать этот же экземпляр класса в качестве барьера.

Несложно упростить код до многоразового:
class barrier {
 const unsigned int threadCount;
 std::atomic<unsigned int>threadsWaiting;
 std::condition_variable waitVariable;
 std::mutex mutex;
public:
 barrier(unsigned int n) : threadCount(n) {
  threadsWaiting = 0;
}
barrier(const barrier &) = delete;
 void wait() {
  std::unique_lock<std::mutex> lock(mutex);
  if (threadsWaiting.fetch_add(1) >= threadCount - 1) {
   threadsWaiting.store(0);
   waitVariable.notify_all();
 }
 else {
  waitVariable.wait(lock);
 }
}
};
Проблема в том, что значение по умолчанию Шредингера находится в суперпозиции — оно одновременно есть, когда мы пытаемся вызвать функцию, опуская его; и его нет, когда мы пытаемся наблюдать его.
Чтобы ваш код заработал, надо чтобы для func и other_func вызывались разные версии шаблонной функций. Этого можно достичь, например, завернув каждую функцию в свой функциональный объект:
call([](auto&&... args){ func(std::forward<decltype(args)>(args)...); });
call([](auto&&... args){ other_func(std::forward<decltype(args)>(args)...); });


А можно просто отказаться от плохой практики использования значения по умолчанию в пользу перегрузок и вызова функций согласно их сигнатурам:
void func(int x);
void func() {
    func(0);
}

...

call(static_cast<void(&)()>(func)); // static_cast необходим для выбора перегруженного варианта
call(static_cast<void(&)(int)>(func),10);
Именно. Но я этого не предлагаю, напротив, говорю, что такой вариант невозможен, а без него именованные параметры превратятся в дополнительную препроцессорную магию, когда имена вроде как есть и используются, но вроде как и нет, так как средствами языка невозможно узнать о их существовании или отсутствии (например при помощи sfinae). Какая-то квантовая механика, а не программирование.
Допустим:
void func(int x=0);

А теперь попробуем:
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 предложен вариант без утери обратной совместимости, но использование такого варианта может принести вместо удобства головную боль: например автор используемой третьесторонней библиотеки решит переименовать параметры, или вообще убрать их имена — имеет полное право. А про переносимость таких программ можно будет вообще забыть.

Information

Rating
Does not participate
Location
Москва и Московская обл., Россия
Date of birth
Registered
Activity