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 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++.