November 2019

S M T W T F S
      12
34 5 678 9
10111213141516
17181920212223
24252627282930

Style Credit

Expand Cut Tags

No cut tags
Sunday, January 30th, 2005 10:48 pm


Я привык, что структуры можно сравнивать с помощью memcmp(). Сегодня нарвался на неприятность.
Вот кусок программы (без проверок кодов возврата, чтоб понятнее было):
struct termios ios, ios2;
tcgetattr(scfd, &ios);
// тут меняем флажочки в ios
tcsetattr(scfd, TCSANOW, &ios);
// (1)
tcgetattr(scfd, &ios2);
if( memcmp(&ios, &ios2, sizeof(ios)) != 0) {
    fprintf(stderr,"Warning: not all parameters successfully set by tcsetattr()\n");
}


Стабильно получаю это сообщение об ошибке.
Печатаю все элементы ios и ios2 поштучно - всё нормально, совпадают.
После десяти минут поисков ошибки в программе и в своей ДНК, начинаю печатать уже побайтно.
Обнаруживаю стабильное несовпадение в 49-51 байтах. Ага, это всё от выравнивания по границе слова.
Попробовал в точку (1) добавить ios2=ios1. Помогло.

Структура выглядит так:
typedef unsigned char   cc_t;
typedef unsigned int    speed_t;
typedef unsigned int    tcflag_t;

#define NCCS 32
struct termios
  {
    tcflag_t c_iflag;           /* input mode flags */
    tcflag_t c_oflag;           /* output mode flags */
    tcflag_t c_cflag;           /* control mode flags */
    tcflag_t c_lflag;           /* local mode flags */
    cc_t c_line;                /* line discipline */
    cc_t c_cc[NCCS];            /* control characters */
    speed_t c_ispeed;           /* input speed */
    speed_t c_ospeed;           /* output speed */
  };


Несовпадающие байты - это вовсе даже не в конце, а в середине. После c_cc (начинается со смещения 17 и заканчивается на 48) и перед c_ispeed (смещение 52).

На данной конкретной архитектуре i386 структура копируется не поэлементно, а с помощью rep movsl, но это implmentation dependent. На PowerPC в этом месте вызывается memcpy(&ios2,&ios,60). А вообще говоря, никто этого не обещал.

Так что же, структуры таки надо всегда сравнивать поэлементно, или стандарт предусматривает при копировании структур и копирование всех байтов в пределах sizeof()?
Sunday, January 30th, 2005 08:59 pm (UTC)
А ты в точку (1) поставь не присваивание, а memcpy, и всякие сомнения в соответствии стандарту исчезнут.
Sunday, January 30th, 2005 09:13 pm (UTC)
Это идеологически неправильно. Если рассматривать структуру как абстрактный тип данных, то надо пользоваться присваиванием. Хотя тогда и для сравнения надо использовать не memcmp, а ==, но оно на структурах не работает :(
Sunday, January 30th, 2005 09:19 pm (UTC)
Вот именно, раз уж ты сравниваешь при помощи memcmp, то и копировать правильно через memcpy. В C++ лучше отношение к абстрактным типам данных, можно свой оператор == определить. Там умолчальный оператор присваивания определяется как поэлементное присваивание, а как в простом C -- не помню.
Monday, January 31st, 2005 07:28 am (UTC)
Агащазблин. Переименовал файл в .cpp, скомпилировал явно g++. На месте присваивания та же фигня:
        cld
        movl    $15, %ecx
        leal    -152(%ebp), %edi
        leal    -88(%ebp), %esi
        rep
        movsl


P.S. Если там вместо присваивания написать memcpy и включить -O, то получается ровно то же самое :) А если -O не писать, тогда честно вызывается memcpy.
Sunday, January 30th, 2005 09:35 pm (UTC)
хоть си и похожи на язык высокого уровня, это всё-таки по-прежнему скорее набор макросов для ассемблера, и настоящие абстрактные типы данных сям чужды. можно писать на ++, но если надо ковырять всякие termios, проще всё-таки привыкнуть к bzero(&dst,sizeof dst)
Monday, January 31st, 2005 08:15 am (UTC)
Чтобы рассматривать структуру как абстрактный тип данных, нужно переходить на С#.
Там в труктуры ты бы перекрыл интерфейс IComparable и она бы сравнивалась так, как реализовано в твоём методе.
А вот такое байт вообще лучше забыть, если оперируешь с абстракциями.
А в C++ приходится помнить и что такое стэк, и что такое регистр, и даже что такое деструктор.
Monday, January 31st, 2005 09:33 am (UTC)
А что, уже есть кросс-комплятор C# под PowerPC? У меня основная задача - там, а на i386 я только предварительно отлаживаюсь :)
Monday, January 31st, 2005 09:47 am (UTC)
C# & PowerPC - это грустно.
Но и без мака у C++ ещё много применений пока.
Я к тому что используя C++ нужно быть готовым к машинным ньюансам.
Monday, January 31st, 2005 10:09 am (UTC)
Какой мак? Это интегрированный контроллер IBM STB 04500. К тому же, под линуксом :)
Изначально речь шла о C, а не о C++.
Sunday, January 30th, 2005 09:05 pm (UTC)
memset/bzero при создании структуры - и никаких бед.
есть, конечно, #pragma pack, но чистить память перед использованием полезно всё равно.
Sunday, January 30th, 2005 09:17 pm (UTC)
Это да, но вопрос немножко не в этом. Получается, что две переменные, одинаковым образом полностю набитые одинаковыми данными, не проходят сравнение посредством memcmp. А посредством == они в C не сравниваются.
Sunday, January 30th, 2005 09:23 pm (UTC)
ну да, это нормально.

You should also be careful about using memcmp to compare objects that can contain "holes", such as the padding inserted into structure objects to enforce alignment requirements, extra space at the end of unions, and extra characters at the ends of strings whose length is less than their allocated size. The contents of these "holes" are indeterminate and may cause strange behavior when performing byte-wise comparisons. For more predictable results, perform an explicit component-wise comparison.
Monday, January 31st, 2005 06:37 am (UTC)
Вот этого я и боялся :(
"For more predictable results, perform an explicit component-wise comparison."
Спасибо.