Почему "ворчит" компилятор?

Модератор: Модераторы разделов

Ответить
MiK13
Сообщения: 1164
ОС: Linux Debian

Почему "ворчит" компилятор?

Сообщение MiK13 »

Есть в программе массив "строк" и операция копирования строки из предыдущего элемента в текущий:

Код: Выделить всё

char fmt[1000][16];
  ...
  strncpy(fmt[ii],fmt[ii-1],15);
  fmt[ii-1][15]=0;
Вроде всё нормально. Под каждый элемент отведено 16 байтов. Копируется не больше 15 и, на всякий случай, в конец добавляется нулевой байт.
Но при компиляции выскакивает:

Shell

gcc -std=c99 -Wall -O3 -o view_udp view_udp.c
view_udp.c: In function ‘main’:
view_udp.c:62:7: warning: ‘strncpy’ accessing 15 bytes at offsets [0, 16000] and [0, 16000] may overlap up to 15 bytes at offset [0, 14] [-Wrestrict]
62 | strncpy(fmt[ii],fmt[ii-1],15);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Что не нравится компилятору?
Раньше (в предыдущих версиях Debian, сейчас 11) программа компилировалась без предупреждений.
Спасибо сказали:
Аватара пользователя
Zer0
Сообщения: 479
ОС: Void, Slackware

Re: Почему "ворчит" компилятор?

Сообщение Zer0 »

По-моему, правильнее так:

Код: Выделить всё

fmt[ii-1][15]='\0';
Memento mori ... сделай бэкап.
Спасибо сказали:
Аватара пользователя
Bizdelnick
Модератор
Сообщения: 20752
Статус: nulla salus bello
ОС: Debian GNU/Linux

Re: Почему "ворчит" компилятор?

Сообщение Bizdelnick »

Нельзя копировать перекрывающиеся строки с помощью strncpy (см. man strncpy), надо использовать memmove.
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:
Акаролибр
Сообщения: 104

Re: Почему "ворчит" компилятор?

Сообщение Акаролибр »

Компилятор ворчит по делу, он корректно обнаружил логическую ошибку в программе.
MiK13 писал:
03.02.2023 05:13
Раньше (в предыдущих версиях Debian, сейчас 11) программа компилировалась без предупреждений.
Резработчики компилятора молодцы.

Напишем тестовый пример и посмотрим, есть ли ошибки в компиляторе.

Код: Выделить всё

user@localhost:~$ cat view_udp.c 
#include <string.h>
#include <stdio.h>

int main(void)
{
    char fmt [1000][16];
    // Сдвигаем строки вверх на одну
    for (int ii = 1000 - 1; ii > 0; --ii)
    {
        printf ("%d <- %d\n", ii, ii-1 );
        // char *strncpy(char *dest, const char *src, size_t n);
        strncpy(fmt [ii], fmt [ii-1], 15);
        fmt[ii][15]=0;
    }
    // Сдвигаем строки вниз на одну
    for (int ii = 0; ii < 1000 - 1; ++ii)
    {
        printf ("%d -> %d\n", ii+1, ii );
        // char *strncpy(char *dest, const char *src, size_t n);
        strncpy(fmt [ii+1], fmt [ii], 15);
        fmt[ii+1][15]=0;
    }        
    return 0;
}
Проверяем:

Код: Выделить всё

user@localhost:~$ gcc -std=c99 -Wall -O3 -o view_udp view_udp.c
user@localhost:~$ file view_udp
view_udp: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=81831d375ac4158d2f688fa431b32b8596b82baa, for GNU/Linux 3.2.0, not stripped
user@localhost:~$ du -b view_udp
16664   view_udp
user@localhost:~$
Теперь мы знаем, что в компиляторе ошибки нет. Где же она есть?

Зададим топикстартеру уточняющие вопросы:
1) не перепутан ли порядок передачи аргументов в strncpy ?
2) почему в верхней строке заполняется элемент массива с индексом ii, а в нижней строке ноль добавляется в ДРУГОЙ элемент массива?
3) почему автор постеснялся привести заголок цикла?
4) было ли бы правильно сказать нам версию компилятора, вместо версии Debian, а то вдруг мы на Gentoo?

Для справки:

Код: Выделить всё

user@localhost:~$ gcc --version
gcc (Debian 10.2.1-6) 10.2.1 20210110
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Код: Выделить всё

user@localhost:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description:    Debian GNU/Linux 11 (bullseye)
Release:        11
Codename:       bullseye
Спасибо сказали:
MiK13
Сообщения: 1164
ОС: Linux Debian

Re: Почему "ворчит" компилятор?

Сообщение MiK13 »

Акаролибр писал:
03.02.2023 14:48
Компилятор ворчит по делу, он корректно обнаружил логическую ошибку в программе.
Если программа работает как надо, то в чём именно ошибка? (За исключением того, что 0 не туда заносится, но это на работу не влияет)
Акаролибр писал:
03.02.2023 14:48
Резработчики компилятора молодцы.
С этим я согласен. Учитывая то, что он делает :)
Правда в нём есть, как я думал, ошибка, но, судя по man gcc, фича.

Shell

mik13@MiK13:/run/shm$ cat ti.c
#include <stdio.h>
int a[3],i;
int main() {
a[3]=3; a[4]=4; a[5]=5; a[6]=6;
for(i=0;i<7;i++) printf("%d %d\n",i,a[i]);
}
mik13@MiK13:/run/shm$ gcc -o ti ti.c -Wall
mik13@MiK13:/run/shm$
В массиве всего 3 элемента, я записываю числа в 4-й, 5-й, 6-й, 7-й элементы, а компилятор никак на это не реагирует.
Правда, я нашёл, что он начинает реагировать, если включена, как минимум, -O2.
Довольно странно.
Акаролибр писал:
03.02.2023 14:48
Напишем тестовый пример и посмотрим, есть ли ошибки в компиляторе.
...
Проверяем:

Код: Выделить всё

user@localhost:~$ gcc -std=c99 -Wall -O3 -o view_udp view_udp.c
user@localhost:~$ file view_udp
view_udp: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=81831d375ac4158d2f688fa431b32b8596b82baa, for GNU/Linux 3.2.0, not stripped
user@localhost:~$ du -b view_udp
16664   view_udp
user@localhost:~$
Теперь мы знаем, что в компиляторе ошибки нет. Где же она есть?

Зададим топикстартеру уточняющие вопросы:
1) не перепутан ли порядок передачи аргументов в strncpy ?
2) почему в верхней строке заполняется элемент массива с индексом ii, а в нижней строке ноль добавляется в ДРУГОЙ элемент массива?
3) почему автор постеснялся привести заголок цикла?
4) было ли бы правильно сказать нам версию компилятора, вместо версии Debian, а то вдруг мы на Gentoo?
Я не сомневался, что в компиляторе ошибки нет. По крайней мере в пределах того, что мне приходится делать.
(правда, странно, что для компилятора задание двоичных констант сделали, а формат для него не предусмотрели)
По поводу вопросов.
1) Порядок не перепутан. нужно скопировать строку в текущий элемент из предыдущего
2) Это действительно ошибка. Нужно ноль заносить в элемент с индексом ii. Исправил. Но она на работу не влияет. "Ошибка" (в принципе) есть ниже. Но так как эта программу я написал для себя, не продукт, я дополнительный контроль не вводил (кстати, он вызывает аналогичное предупреждение).
3) Просто меня интересовало что явилось основанием для компилятора для предупреждения.
А текст цикла такой:

Код: Выделить всё

  for(i=optind+1;i<argc;i++) {                              // Для остальных параметров:
    ii=i-optind-1;
    if(*argv[i]=='.') {                                     // Если параметр "."
      ofs[ii]=ofs[ii-1]+step;                               //  Смещение -- на 4 больше предыдущего
      strncpy(fmt[ii],fmt[ii-1],15);                        //  Формат -- как у предыдущего
      fmt[ii][15]=0;
    } else {                                                // Иначе
      if(*argv[i]==':') ofs[ii]=ofs[ii-1]+4;                //  Если в начале параметра ':'
      else ofs[ii]=strtol(argv[i],&ac,0);
      if(*ac) ac++;
      strcpy(fmt[ii],ac); step=4;
      if(strstr(ac,"h")) step=2;
      if(strstr(ac,"hh")) step=1;
      if(strstr(ac,"c")) step=1;
      if(strstr(ac,"F")) step=8;
    }
  }
В этом цикле в элементы fmt[ii] записывается формат, по которому надо будет вывести значение по смещению, записанному в ofs[ii].
Параметры, передаваемые программе, имеют формат <смещение>:<формат>. И если в качестве параметра используется точка, то формат просто копируется из предыдущего. С соответствующей коррекцией смещения.
Акаролибр писал:
03.02.2023 14:48
Для справки:
...
У меня всё то же самое
Спасибо сказали:
MiK13
Сообщения: 1164
ОС: Linux Debian

Re: Почему "ворчит" компилятор?

Сообщение MiK13 »

Zer0 писал:
03.02.2023 08:30
По-моему, правильнее так:

Код: Выделить всё

fmt[ii-1][15]='\0';
Формально да. Но, учитывая, что компилятор сам при присваивании делает преобразование типов, я не вижу смысла указывать '\0' вместо простого 0.
Bizdelnick писал:
03.02.2023 14:27
Нельзя копировать перекрывающиеся строки с помощью strncpy (см. man strncpy), надо использовать memmove.
Довольно странно.
Адреса fmt[ii] и fmt[ii-1] отличаются на 16. Я копирую максимум 15 байтов. То есть никакого перекрытия быть не может.
Или это предупреждение сделано для общего случая?

Что касается требования использовать именно memmove, а не memcpy, для случаев копирования перекрывающихся областей памяти, то это, в принципе понятно. И раньше при

Код: Выделить всё

 char s[32] = "0123456789";
 memcpy(s+1,s,6);
можно было получить в s "0000000789", но сейчас всё копируется корректно, то есть получается "0012345789"
Спасибо сказали:
math
Сообщения: 276
Статус: Ъ участник
ОС: Artix (= Arch without systemd)

Re: Почему "ворчит" компилятор?

Сообщение math »

char fmt[1000][16] — это разве не массив из 16 элементов? (Я когда-то очень давно зарёкся использовать многомерные массивы в C, поэтому не знаю.)
Спасибо сказали:
Аватара пользователя
Bizdelnick
Модератор
Сообщения: 20752
Статус: nulla salus bello
ОС: Debian GNU/Linux

Re: Почему "ворчит" компилятор?

Сообщение Bizdelnick »

Вообще да, непонятно, почему такая ошибка. Но у Вас таки возможен выход за границу массива, если первый же параметр будет '.' (ii = 0).
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:
Аватара пользователя
ormorph
Сообщения: 2600
ОС: Gentoo

Re: Почему "ворчит" компилятор?

Сообщение ormorph »

math писал:
03.02.2023 21:24
char fmt[1000][16] — это разве не массив из 16 элементов? (Я когда-то очень давно зарёкся использовать многомерные массивы в C, поэтому не знаю.)
Он самый, массив указателей на массивы из 16 элементов, нумерация начинается с нуля, а тут длина составляет для 15 байт, соответственно предупреждение правильное, так как копирует не полностью а с 0 до 14. Только вот почему идет предупреждение, скорее всего особенность сборки gcc. Тут смотреть:

Shell

$ gcc -v
Спасибо сказали:
MiK13
Сообщения: 1164
ОС: Linux Debian

Re: Почему "ворчит" компилятор?

Сообщение MiK13 »

math писал:
03.02.2023 21:24
char fmt[1000][16] — это разве не массив из 16 элементов? (Я когда-то очень давно зарёкся использовать многомерные массивы в C, поэтому не знаю.)
Это, насколько я помню, было в фортране. Там сначала изменялся первый индекс.
В си, как и во многих других языках, быстрее изменяется последний индекс
char fmt[1000][16] это массив, в котором 1000 элементов, причём, каждый состоит из 16 элементов типа .
Bizdelnick писал:
03.02.2023 21:43
Вообще да, непонятно, почему такая ошибка. Но у Вас таки возможен выход за границу массива, если первый же параметр будет '.' (ii = 0).
А так же, если ii станет равно 1000. Правда, добиться этого будет гораздо сложнее :)
Но удивляет другое. Такая же ошибка будет возможна и при замене strncpy на memcpy, однако компилятор при этом предупреждения не выдаёт.
Интересно, что на оператор ниже, strcpy(fmt[ii],ac); gcc никаких предупреждений не выдаёт. Хотя опасность этой функции такая же, как и gets(s), которая в man gets помечена как устаревшая. И в описании указано Never use this function
И когда я попытался использовать эту функцию, компилятор выдал
view_udp.c:39:3: warning: implicit declaration of function ‘gets’; did you mean ‘fgets’? [-Wimplicit-function-declaration]
И ещё
view_udp.c:(.text.startup+0x1e): предупреждение: the `gets' function is dangerous and should not be used.
(странно, что компилятор сказал "warning", а линкер "предупреждение" :) )
Спасибо сказали:
Аватара пользователя
Bizdelnick
Модератор
Сообщения: 20752
Статус: nulla salus bello
ОС: Debian GNU/Linux

Re: Почему "ворчит" компилятор?

Сообщение Bizdelnick »

MiK13 писал:
04.02.2023 00:40
Интересно, что на оператор ниже, strcpy(fmt[ii],ac); gcc никаких предупреждений не выдаёт. Хотя опасность этой функции такая же, как и gets(s)
Не такая же. Когда Вы используете strcpy, Вы можете точно знать, что в источнике находится строка, не выходящая за границы приёмника (а можете и не знать). В случае gets Вы этого не знаете никогда, потому что она работает с пользовательским вводом.
Добавлено (01:47):
MiK13 писал:
04.02.2023 00:40
А так же, если ii станет равно 1000.
Да, это тоже надо проверять.
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:
MiK13
Сообщения: 1164
ОС: Linux Debian

Re: Почему "ворчит" компилятор?

Сообщение MiK13 »

Bizdelnick писал:
04.02.2023 01:41
MiK13 писал:
04.02.2023 00:40
Интересно, что на оператор ниже, strcpy(fmt[ii],ac); gcc никаких предупреждений не выдаёт. Хотя опасность этой функции такая же, как и gets(s)
Не такая же. Когда Вы используете strcpy, Вы можете точно знать, что в источнике находится строка, не выходящая за границы приёмника
Согласен. Когда пишется программа, обычно заранее определяются размеры объектов, с которыми приходится работать.
Bizdelnick писал:
04.02.2023 01:41
(а можете и не знать).
Как в данном случае. Я копирую часть строки из аргумента, переданного программе. А тут, в принципе, можно указать параметр любой длины.
Хотя в реальности он вряд ли будет больше 7-8 знаков (обычно меньше)
Спасибо сказали:
Аватара пользователя
Bizdelnick
Модератор
Сообщения: 20752
Статус: nulla salus bello
ОС: Debian GNU/Linux

Re: Почему "ворчит" компилятор?

Сообщение Bizdelnick »

MiK13 писал:
05.02.2023 16:18
Как в данном случае. Я копирую часть строки из аргумента, переданного программе. А тут, в принципе, можно указать параметр любой длины.
Выходит, компилятор недостаточно умён, чтобы понять, что Вы используете strcpy неправильно. Возможно, какой-нибудь продвинутый статический или динамический анализатор и понял бы.
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:
MiK13
Сообщения: 1164
ОС: Linux Debian

Re: Почему "ворчит" компилятор?

Сообщение MiK13 »

Bizdelnick писал:
05.02.2023 19:31
Выходит, компилятор недостаточно умён, чтобы понять, что Вы используете strcpy неправильно. Возможно, какой-нибудь продвинутый статический или динамический анализатор и понял бы.
Пока надобности в этом нет. Но на всякий случай я заменил strcpy на strncpy с занесением нуля в последний байт.
Узнал, что кроме strncpy существует функция strlcpy, которая имеет аналогичные параметры, но копирует не более (n-1) байтов и в конце заносит нулевой байт. Но вот в <string.h> её нет.
Правда, есть в <bsd/string.h> (и еще в ряде для других архитектур), но в какой библиотеке она есть, не знаю,
Что касается предупреждения в этой программе, то оно пропало после замены strncpy на memcpy.
Но вылезло в одном старом проекте.
Там есть структура с такими элементами:

Код: Выделить всё

struct SharedDataMain {                     /// dd:0x101
      ...
  struct In_Prot prot[6];                   // 000028+20*6 Протокол, отображаемый на экране
      ...
  uint16_t n_prot;                          // 0008A8+2 Ном. новой записи в протоколе для оператора
      ...
Структура In_Prot имеет размер 20 байтов. n_prot++ указывает на место, куда надо занести следующую запись (и указывает сколько записей занято). Так как элементов всего 6, то я перед занесением делаю проверку:

Код: Выделить всё

  if(dd->n_prot>5) {                                       // Если заполнено изображение протокола на экране
    memcpy(dd->prot,dd->prot+1,sizeof(struct In_Prot)*5);  // "Поднять" список событий
    dd->n_prot--;                                          // Оставить указатель строки протокола на последнюю строку
  }
  dd->prot[dd->n_prot++]=prot;                             // Занести очередное событие в протокол
То есть, если уже занято все 6 мест, то я копирую в начало sizeof(struct In_Prot)*5=100 байтов, начиная с элемента prot[1].
Я не вижу, какие тут могут быть перекрытия. Но компилятор выдаёт:

Shell

write_logs.c:425:5: warning: ‘memcpy’ accessing 100 bytes at offsets 40 and 60 overlaps 80 bytes at offset 60 [-Wrestrict]
425 | memcpy(dd->prot,dd->prot+1,sizeof(struct In_Prot)*5); // "Поднять" список событий
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Почему? Может быть сейчас какие-то новые технологии копирования появились?
Но на всякий случай заменил этот memcpy на

Код: Выделить всё

for(i=0;i<5;i++) dd->prot[i]=dd->prot[i+1];
-- предупреждения нет. А разница, на мой взгляд -- вместо копирования сразу 100 байт, выполнить пять копирований по 20 байт.
Спасибо сказали:
Аватара пользователя
Bizdelnick
Модератор
Сообщения: 20752
Статус: nulla salus bello
ОС: Debian GNU/Linux

Re: Почему "ворчит" компилятор?

Сообщение Bizdelnick »

MiK13 писал:
09.02.2023 22:14
Правда, есть в <bsd/string.h> (и еще в ряде для других архитектур), но в какой библиотеке она есть, не знаю
libbsd
И ещё в libite есть.
MiK13 писал:
09.02.2023 22:14
Что касается предупреждения в этой программе, то оно пропало после замены strncpy на memcpy.
Зря. Вероятно, дело всё-таки в выходе за границы массива. Вы бы с ним лучше разобрались.
MiK13 писал:
09.02.2023 22:14
Я не вижу, какие тут могут быть перекрытия.
Ну как же, Вы копируете элементы с 1 по 5 в ячейки с 0 по 4. Перекрываются с 1 по 4 ячейки. Это типичный случай, когда надо использовать memmove.
MiK13 писал:
09.02.2023 22:14
А разница, на мой взгляд -- вместо копирования сразу 100 байт, выполнить пять копирований по 20 байт.
Правильно, но по одной ячейке за раз (1 в 0, …, 5 в 4), без перекрытия.
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:
MiK13
Сообщения: 1164
ОС: Linux Debian

Re: Почему "ворчит" компилятор?

Сообщение MiK13 »

Bizdelnick писал:
09.02.2023 23:25
libbsd
И ещё в libite есть.
Большое спасибо. Поставил libite-dev и libite5 и с опцией -lite программа нормально собралась и работает как надо
Bizdelnick писал:
09.02.2023 23:25
MiK13 писал:
09.02.2023 22:14
Что касается предупреждения в этой программе, то оно пропало после замены strncpy на memcpy.
Зря. Вероятно, дело всё-таки в выходе за границы массива. Вы бы с ним лучше разобрались.
На данный момент в этом нет необходимости. Эта программа "для личного пользования". Она просто выводит содержимое полей UDP пакета в заданном формате (может быть есть какая-то системная программа с аналогичными функциями?)
Параметром программы задаётся формат для printf и, чтобы не дублировать один и тот же формат для ряда последовательных полей, я использую в качестве параметра ".". Поэтому пересылки из элемента[-1] в реальности быть не может. Да и 1000 форматов я никогда задавать не буду.
Bizdelnick писал:
09.02.2023 23:25
MiK13 писал:
09.02.2023 22:14
Я не вижу, какие тут могут быть перекрытия.
Ну как же, Вы копируете элементы с 1 по 5 в ячейки с 0 по 4. Перекрываются с 1 по 4 ячейки. Это типичный случай, когда надо использовать memmove.
Всё определяется тем, как копируется одна область памяти в другую.
Раньше это было реализовано приблизительно так:

Код: Выделить всё

void *memcpy(void *dest, const void *src, size_t n) {
  for(int i=0;i<n;i++) dest[i]=src[i];
  return dest;
В результате копирование "вниз" проходило нормально, а вот копирование "вверх" приводило к размножению первого элемента.
Я написал простую программу

Код: Выделить всё

#include <stdio.h>
#include <string.h>

#define N 20
int m[N],i;

int main() {
  for(i=0;i<N;i++) m[i]=i+10;
  for(i=0;i<N;i++) printf("%3d",m[i]);; putchar('\n');
  memcpy(m,m+1,(N-1)*4);
  for(i=0;i<N;i++) printf("%3d",m[i]);; putchar('\n');
  memcpy(m+1,m,(N-1)*4);
  for(i=0;i<N;i++) printf("%3d",m[i]);; putchar('\n');
}
И вот результат её работы при использовании компилятора 4.3.2 (Debian 5)

Код: Выделить всё

$ ./tm
 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 29
 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11
А вот результат той же программы при использовании компилятора 10.2.1 (Debian 11)

Код: Выделить всё

$ ./tm
 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 29
 11 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
Как они этого добились -- не знаю. Возможно, сравнивают dest и src и в зависимости от того, что больше, выполняют копирование в разных направлениях.
Кстати, я видел что-то подобное в какой-то системе лет 30 назад. На мой взгляд это эффективнее, чем, как memmove, копировать сначала src во временную область (и выделять память для неё), а потом из неё копировать в dest.
Спасибо сказали:
Аватара пользователя
Bizdelnick
Модератор
Сообщения: 20752
Статус: nulla salus bello
ОС: Debian GNU/Linux

Re: Почему "ворчит" компилятор?

Сообщение Bizdelnick »

MiK13 писал:
10.02.2023 11:10
На данный момент в этом нет необходимости.
Это точно куда более осмысленно, чем борьба с предупреждениями компилятора методом тыка.
MiK13 писал:
10.02.2023 11:10
Параметром программы задаётся формат для printf
Этого делать нельзя ни при какой погоде, см. CWE-134. Об этом Вам компилятор тоже должен был сообщить (опция -Wformat-security).
MiK13 писал:
10.02.2023 11:10
Всё определяется тем, как копируется одна область памяти в другую.
Не определяется. Это как раз неопределённое поведение.
MiK13 писал:
10.02.2023 11:10
Как они этого добились -- не знаю.
Это зависит не от версии компилятора, а от версии libc. И закладываться на то или иное поведение нельзя. Разработчики flash в своё время так сделали, а граблями по лбу получили, конечно, пользователи.
Добавлено (14:35):
Вообще есть хорошее правило: забудьте про memcpy и всегда используйте memmove, не ошибётесь.
Brian W. Kernighan, Rob Pike. The Practice of Programming писал(а):The ANSI C standard defines two functions: memcpy, which is fast but might overwrite memory if source and destination overlap; and memmove, which might be slower but will always be correct. The burden of choosing correctness over speed should not be placed upon the programmer; there should be only one function. Pretend there is, and always use memmove.
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:
Акаролибр
Сообщения: 104

Re: Почему "ворчит" компилятор?

Сообщение Акаролибр »

Так, меня вроде бы "отпустило" из забана.

Хочу сказать, что с выходом за границы и диагностикой этого было бы интересно разобраться, однако топикстартер привёл код, недостаточный для воспроизведения проблемы.

Очевидно, ему лень привести "минимально необходимый пример". Ну если ему лень, то и нам лень.
Спасибо сказали:
MiK13
Сообщения: 1164
ОС: Linux Debian

Re: Почему "ворчит" компилятор?

Сообщение MiK13 »

Акаролибр писал:
10.02.2023 22:55
Хочу сказать, что с выходом за границы и диагностикой этого было бы интересно разобраться, однако топикстартер привёл код, недостаточный для воспроизведения проблемы.
Я очень сомневаюсь, что там может быть что-то интересное.
После замены strncpy на memcpy и после добавления контроля код этого фрагмента выглядит так:

Код: Выделить всё

  for(i=optind+1;i<argc;i++) {                       // Для остальных параметров:
    ii=i-optind-1;
    if(ii>=1000) break;                              //+ Может быть задано не больше 1000 форматов
    if(*argv[i]=='.') {                              // Если параметр "."
      if(ii==0) {                                    //+ Если не было задано ни одного формата
        ofs[ii]=0; fmt[ii][0]=0;                     //+ установить вывод пустой строки
      } else {
        ofs[ii]=ofs[ii-1]+step;                      //  Смещение -- на 4 больше предыдущего
        memcpy(fmt[ii],fmt[ii-1],15);                //  Формат -- как у предыдущего
        fmt[ii][15]=0;
      }
    } else {
      if (verb) printf("for %d[%s]:\n",i,argv[i]);   // Иначе
      ofs[ii]=strtol(argv[i],&ac,0);
      if(*ac) ac++;
      strcpy(fmt[ii],ac); step=4;
      if(strstr(ac,"h"))  step=2;
      if(strstr(ac,"hh")) step=1;
      if(strstr(ac,"c"))  step=1;
      if(strstr(ac,"F"))  step=8;
    }
  }
Спасибо сказали:
Акаролибр
Сообщения: 104

Re: Почему "ворчит" компилятор?

Сообщение Акаролибр »

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

Часто бывает что люди не понимают очевидных вещей. И тут вопрос - надо ли им разъяснять, или "если надо объяснять, то не надо объяснять"?
Спасибо сказали:
Аватара пользователя
Bizdelnick
Модератор
Сообщения: 20752
Статус: nulla salus bello
ОС: Debian GNU/Linux

Re: Почему "ворчит" компилятор?

Сообщение Bizdelnick »

MiK13 писал:
13.02.2023 12:24
После замены strncpy на memcpy и после добавления контроля код этого фрагмента выглядит так
И всё же каков смысл замены strncpy на memcpy? Просто методом тыка найденный способ компилятор не ругаться? Проще вообще отключить предупреждения с таким подходом. Вы же ничего не исправили.
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:
MiK13
Сообщения: 1164
ОС: Linux Debian

Re: Почему "ворчит" компилятор?

Сообщение MiK13 »

Акаролибр писал:
13.02.2023 12:37
Фрагмент кода нельзя просто так взять и скомпилировать. Его надо дописывать и додумывать. Откуда берутся неопределённые переменные, что поступает на вход программы, как программа компилируется.
А что тут додумывать?
Я в начале написал, что fmt -- массив из 1000 16-байтных элементов.
Из текста можно догадаться, что ofs -- массив целых чисел аналогичного размера, i и ii -- целые числа.
optind -- известная переменная, ac -- ссылка на строку.
Входные данные -- аргументы, передаваемые программе.
Про формат аргументов я уже писал: <смещение>:<формат_для_printf>
Bizdelnick писал:
13.02.2023 13:05
И всё же каков смысл замены strncpy на memcpy?
В принципе да. Хотя, по идее, memcpy должен быть немного эффективнее, чем strncpy. Хотя, в данном случае это не играет никакой роли
Bizdelnick писал:
13.02.2023 13:05
Просто методом тыка найденный способ компилятор не ругаться?
Проще вообще отключить предупреждения с таким подходом. Вы же ничего не исправили.
Да. Только избавился от предупреждения. Меня это устраивает.
А глобально отключать это предупреждения -- можно пропустить предупреждение в другом месте. И, к тому же, надо makefile исправлять.
Спасибо сказали:
Аватара пользователя
olecya
Сообщения: 900
ОС: debian, fedora (i3-wm)

Re: Почему "ворчит" компилятор?

Сообщение olecya »

MiK13 писал:
13.02.2023 17:52
Да. Только избавился от предупреждения. Меня это устраивает.
А глобально отключать это предупреждения -- можно пропустить предупреждение в другом месте.
Вот например что было у меня:

Shell

make
cc -Wall -Wextra -Iinclude -c -o pix.o pix.c
pix.c: In function ‘main’:
pix.c:165:38: warning: this statement may fall through [-Wimplicit-fallthrough=]
165 | flag = (flag)? false: true;
| ^
pix.c:167:25: note: here
167 | case 'r':
| ^~~~
gcc -Wall -Wextra -Iinclude hash.o ldir.o magic.o perm.o pix.o wch.o -o pix -lncursesw -lmagic
Но это предупреждение ложно, там все так и задумано. Вставляю атрибут [[fallthrough]];

Shell

make
cc -Wall -Wextra -Iinclude -c -o pix.o pix.c
gcc -Wall -Wextra -Iinclude hash.o ldir.o magic.o perm.o pix.o wch.o -o pix -lncursesw -lmagic
Нужно найти подходящий атрибут для подавления предупреждения в вашем случае.
Добавлено (19:17):
Вот нашла. Чуть сложнее чем атрибут, но всеядное. Включаем подавление:

Код: Выделить всё

_Pragma("GCC diagnostic push")
_Pragma("GCC diagnostic ignored \"-Wunused-parameter\"")
Вместо -Wunused-parameter я вставляю свое, которое мне выдал компилятор -Wimplicit-fallthrough
И закрываю подавление после проблемного участка или строчки:

Код: Выделить всё

_Pragma("GCC diagnostic pop")
Обратите внимание. Строки не заканчиваются точкой с запятой.
Добавлено (19:40):
Альтернатива кажется выглядит аккуратней:

Код: Выделить всё

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic pop
И у меня в виме подсвечивается как комментарий.
Спасибо сказали:
MiK13
Сообщения: 1164
ОС: Linux Debian

Re: Почему "ворчит" компилятор?

Сообщение MiK13 »

olecya писала:
13.02.2023 18:52
Добавлено (13.02.2023 19:17):
Вот нашла. Чуть сложнее чем атрибут, но всеядное. Включаем подавление:

Код: Выделить всё

_Pragma("GCC diagnostic push")
_Pragma("GCC diagnostic ignored \"-Wunused-parameter\"")
Вместо -Wunused-parameter я вставляю свое, которое мне выдал компилятор -Wimplicit-fallthrough
И закрываю подавление после проблемного участка или строчки:

Код: Выделить всё

_Pragma("GCC diagnostic pop")
Обратите внимание. Строки не заканчиваются точкой с запятой.
Спасибо. Буду знать, что есть и такая форма записи прагмы.
olecya писала:
13.02.2023 18:52
Добавлено (19:40):
Альтернатива кажется выглядит аккуратней:

Код: Выделить всё

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic pop
И у меня в виме подсвечивается как комментарий.
Я знаком с такой формой записи прагмы. Но пока использовал её только для установки выравнивания.
А про #pragma GCC diagnostic ещё не слышал. Спасибо.
А с fallthriugh я встречался. Причём, даже возник конфликт. Один компилятор на этот "провал" никак не реагировал, а другой ворчал.
Вставлял __attribute__ ((fallthrough));, так старому компилятору это не нравилось. Пришлось делать разные makefile, а в программе вставлять

Код: Выделить всё

    #ifdef _TS_FALL
    __attribute__ ((fallthrough));
    #endif
в нужном case
Спасибо сказали:
Аватара пользователя
Bizdelnick
Модератор
Сообщения: 20752
Статус: nulla salus bello
ОС: Debian GNU/Linux

Re: Почему "ворчит" компилятор?

Сообщение Bizdelnick »

MiK13 писал:
13.02.2023 22:43
А с fallthriugh я встречался. Причём, даже возник конфликт. Один компилятор на этот "провал" никак не реагировал, а другой ворчал.
Начиная с C23 в стандарте есть атрибут fallthrough: https://en.cppreference.com/w/c/language/attributes/fallthrough
MiK13 писал:
13.02.2023 22:43
Пришлось делать разные makefile, а в программе вставлять
Привязка к компилятору в Makefile — зло. В том числе и указание в нём специфичных для компилятора опций.
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:
MiK13
Сообщения: 1164
ОС: Linux Debian

Re: Почему "ворчит" компилятор?

Сообщение MiK13 »

Bizdelnick писал:
14.02.2023 01:30
Привязка к компилятору в Makefile — зло. В том числе и указание в нём специфичных для компилятора опций.
А что делать?
Есть несколько проектов. Причём, в одном стоит Debian 6, 32-битный (или даже 5, не помню уже)
Где-то заказчик потребовал RHEL. Сейчас думаем поставлять на Debian-11.
Программа одна (на уровне исходника), периодически что-то в неё добавляется, но так, чтобы функциональность не менялась.
Для старой системы приходится компилировать на компьютере с Lenny, а там компилятор не понимает атрибут fallthrough.
Вот я и решил в makefile добавить две строчки:
TFALL = -D_TS_FALL
#TFALL =
И, на одном компьютере они такие, на другом знак комментария на другой строке.
Спасибо сказали:
Аватара пользователя
Bizdelnick
Модератор
Сообщения: 20752
Статус: nulla salus bello
ОС: Debian GNU/Linux

Re: Почему "ворчит" компилятор?

Сообщение Bizdelnick »

MiK13 писал:
17.02.2023 20:12
А что делать?
Писать Makefile переносимо, если это невозможно — использовать переносимые системы сборки.
MiK13 писал:
17.02.2023 20:12
Для старой системы приходится компилировать на компьютере с Lenny, а там компилятор не понимает атрибут fallthrough.
Решается условным определением макросов. В Makefile ничего прописывать ради этого не надо.
Типа такого:

Код: Выделить всё

#ifndef __has_attribute
# define __has_attribute(x) 0
#endif

#ifndef __has_c_attribute
# define __has_c_attribute(x) 0
#endif

#if __has_c_attribute(fallthrough)
# define FALLTHROUGH [[fallthrough]]
#elif __has_attribute(fallthrough)
# define FALLTHROUGH __attribute__ ((fallthrough))
#else
# define FALLTHROUGH
#endif
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:
Ответить