2010-12-30 3 views
3

я следующий код:Преобразование целых чисел с разной мощностью

// ---- third party library code ---- 
struct Point { int16_t x, y; }; 
void third_party_library__getPoint(Point*); 
void third_party_library__setPoint(const Point*); 

// ---- my library code ---- 
void move_point(int dx, int dy) 
{ 
    Point pt; 
    third_party_library__getPoint(&pt); 
    pt.x += dx; 
    pt.y += dy; 
    third_party_library__setPoint(&pt); 
} 

Line pt.x += dx; дает предупреждение
conversion from 'int' to 'int16_t', possible loss of data

Что я должен делать?

  1. отключить предупреждение для тех линий
  2. переместить источник предупреждения интерфейса: сделать dx и dy int16_t, поэтому тот, кто будет использовать move_point функции будет иметь дело с этой проблемой.
  3. только литые dx и dy до int16_t.
  4. добавьте assert(dx <= 0x7FFF && dx >= -0x8000 && "too large 'dx' value") и надеемся, что он ударит, когда я запустил отладочную версию.
+2

Определенно не 1. –

ответ

2

Для меня есть 3 связанные, но разные точки здесь:

А. Deal с переполнением

Фактически, здесь есть две части: конверсия переполнение и дополнение overflo ш. Даже если вы берете int16_t в качестве входных данных, все еще может быть переполнение на этапе +=, и вам нужно решить, что с ним делать (конечно, вы можете просто проигнорировать его, но это тоже выбор, с четко определенными последствия).

B. Информ пользователей об ограничениях

По выборе (3) вариант - переход к интерфейсу, вы бы информировать пользователей о первом перелива, но не о втором. Кроме того, это не - единственный способ сообщить пользователям. Иногда лучший способ состоит в том, чтобы иметь раздел «ограничений» в вашей документации, чтобы сразу это прояснить.

C. Избегайте предупреждение

После того, как вы определились с (А), вы можете сделать компилятор счастливым, делая преобразование в соответствии с тем, что вы решили.


Ваши (1) и (3) - это, по сути, одно и то же, хотя люди не любят подавление предупреждений. Ответы на эти вопросы Избегать, но не Сделка и не Информировать.

(2) игнорирует второе переполнение и отправляет первый пользователю. Так оно и делает Избегите и частично Сообщите, игнорируя при этом половину Сделка и оставив вторую половину пользователям.

(4) только о Информ, но не имеет ничего общего с Deal и Избегайте.


Что мне делать? Сначала я решил бы, как Сделка с переполнением - в зависимости от приложения она может быть совсем другой, т. Е. Это может быть стандартное поведение переполнения или усечение до максимально возможного значения. Тогда я подумал о том, как правильно Сообщите моим пользователям - это зависит от способа организации документов, если утверждения/исключения приемлемы и т. Д. Наконец, я бы Избегайте предупреждения в соответствии с этим, проверяя переполнение и действующий в соответствии с правилами Сделка.

4

Вы должны сделать dx и dy int16_t, если вы их бросите, вы просто скрываете его, а человек, использующий ваш код, не увидит эти предупреждения и не узнает о проблеме. Так что пусть человек с другой стороны справится с предупреждениями, по крайней мере, они уверены в функциональности.

+0

Это на самом деле гораздо лучшее предложение для моего, предполагая, что он может это сделать. Вы не всегда можете. –

1

Предполагая, что вы точно знаете, что на самом деле здесь нет ошибки (переменная никогда не будет иметь бит за бит 15, о которой вы заботитесь), я бы пошел с некоторым вариантом «кастинга».

То, что я обычно делаю в этом случае для двух целочисленных типов является конструктором:

pt.x += int16_t(dx); 

Это должно сделать это довольно ясно, кто читает этот код, что да, действительно вы на самом деле хотите, чтобы отбрасывать любые биты прошлое 16-й.

+0

Разве это не техническая функция, отличная от функционального стиля, а не вызов конструктора? – Puppy

+0

С абстрактной точки зрения, это одно и то же. :) –

0

добавить утверждают (дй < = 0x7FFF & & дх> = -0x8000 & & "слишком большое 'ая' значение") и надеюсь, что это будет хит, когда я буду работать отладка версии.

Ну, очевидно, вы должны обработать «потенциальную» ошибку (я бы назвал ее полной ошибкой, кстати).

Для того, чтобы принять предупреждение (зная, что только 16-разрядные номера будут переданы вашей функции), вы можете добавить актерский состав, который выражает то, что вы делаете, когда выбрасываете 16 старших бит в любом случае.

Все это сказало, предполагая, что у вас 32 или 64-битная платформа.

1

Если ваша реализация не справляется с использованием int16_t, вы должны взять int16_t.

1

Я предпочитаю , однако лучший выбор зависит от контекста (например, соображений производительности).

  1. отключить предупреждение для тех линий

    Это плохо, потому что отключение предупреждений добавляет грязи в код (например,Синтаксис для отключения предупреждений зависит от компилятора)

  2. перемещать источник предупреждения интерфейса: сделать ое и д int16_t, поэтому тот, кто будет использовать функцию move_point будет иметь дело с этой проблемой

    Это плохо, потому что вы могли бы хотите обновить свою стороннюю библиотеку в будущем без изменения интерфейса вашей библиотеки

  3. просто введите dx и dy в int16_t.

    Это плохо, потому что это может привести к переполнению (очевидно).

    BTW только с использованием pt.x += int16_t(dx) не будет «исправлять» предупреждение; если вы хотите, чтобы скрыть предупреждение, используйте pt.x = int16_t(pt.x + dx) (или какой-либо альтернативный синтаксис произнесения)

  4. добавить Assert (дй < = 0x7FFF & & дх> = -0x8000 & & «слишком большое„ое“значение») и надеется, что он ударит, когда я запустил отладочную версию.

    Это какой-то волосатый синтаксис, но я думаю, что это наименьшее зло (его единственным недостатком, кроме синтаксиса, является производительность, и мы, вероятно, можем его игнорировать). Вы можете также использовать следующий синтаксис (избегая магических чисел):

    assert(int16_t(dx) == dx);