Существует ли наблюдаемая разница при использовании `unsigned long` и `unsigned int` в C (или C++), когда оба имеют ширину 32 бита?

Я использую MPC56XX (встроенные системы) с компилятором, для которого int и long имеют ширину 32 бита.

В необходимом программном пакете у нас были следующие определения для 32-битных типов:

typedef   signed int sint32;
typedef unsigned int uint32;

В новой версии это было изменено без особой документации на:

typedef   signed long sint32;
typedef unsigned long uint32;

Я понимаю, почему это было бы хорошо: целые числа имеют ранг преобразования между short и long, поэтому теоретически при использовании первого набора определений могут применяться дополнительные преобразования.

Мой вопрос: Учитывая приведенное выше изменение, навязанное нам авторами пакета, можно ли представить себе ситуацию, когда такое изменение изменило бы скомпилированный код, правильно приведя к другому результату?

Я знаком с "обычными унарными преобразованиями" и "обычными двоичными преобразованиями", но мне трудно придумать конкретную ситуацию, когда это действительно может разрушить мой существующий код. Но действительно ли это не имеет значения?

В настоящее время я работаю в чистой среде C, используя C89/C94, но меня интересуют проблемы как C, так и C++.

РЕДАКТИРОВАТЬ: я знаю, что смешивание int с sint32 может привести к другим результатам, когда оно будет переопределено. Но нам не разрешено использовать исходные типы C напрямую, только те, которые определены с помощью typedef.
Я ищу образец (выражение или фрагмент), использующий константы, унарные/бинарные операторы, приведения типов и т. д. с другой, но правильный результат компиляции на основе измененного определения типа.


person Johan Bezem    schedule 08.12.2011    source источник
comment
Это все-таки разные типы   -  person Johannes Schaub - litb    schedule 08.12.2011
comment
¤ Как отмечает @JohannesSchaub-litb в своем комментарии, это разные типы. Это означает, что вы можете получить разные результаты от разрешения перегрузки, вы получите разные результаты typeid и т. д. Однако в отношении арифметических операций это то же самое, если только у вас действительно действительно очень извращенный внутренний компилятор. Ура и чт.,   -  person Cheers and hth. - Alf    schedule 08.12.2011
comment
@ AlfP.Steinbach Да, я сначала так и подумал. Но у меня есть ощущение, что может существовать аналогичная ситуация, как в другом из моих вопросов. В настоящее время я не могу придумать ни одного, но в SO есть много людей умнее меня, я бы лучше спросил.   -  person Johan Bezem    schedule 08.12.2011
comment
Когда используется int? когда используется sint32?   -  person curiousguy    schedule 10.12.2011


Ответы (4)


Это может привести к тонким проблемам, потому что по умолчанию литеральные числа равны int.

Рассмотрим следующую программу:

#include <iostream>

typedef signed short old16;
typedef signed int old32;

void old(old16) { std::cout << "16\n"; }
void old(old32) { std::cout << "32\n"; }

typedef signed short new16;
typedef signed long new32;

void newp(new16) { std::cout << "16\n"; }
void newp(new32) { std::cout << "32\n"; }

int main() {
  old(3);
  newp(3); // expected-error{{call of overload ‘newp(int)’ is ambiguous}}
}

Это приводит к ошибке, потому что вызов newp теперь неоднозначен:

prog.cpp: In function ‘int main()’:
prog.cpp:17: error: call of overloaded ‘newp(int)’ is ambiguous
prog.cpp:12: note: candidates are: void newp(new16)
prog.cpp:13: note:                 void newp(new32)

тогда как раньше работало нормально.

Таким образом, могут быть некоторые сюрпризы перегрузки, когда используются литералы. Если вы всегда используете именованные (и, следовательно, типизированные) константы, все должно быть в порядке.

person Matthieu M.    schedule 08.12.2011
comment
Почти все проблемы с переносимостью, с которыми я сталкивался в нашей встроенной системе, где у нас было одно и то же правило (без необработанных целых чисел), возникали из-за того, что числовые литералы обрабатывались как разные размеры на разных платформах. - person AShelly; 12.12.2011
comment
@AShelly: особенно ужасная проблема может возникнуть с шестнадцатеричными литералами, которые слишком велики, чтобы поместиться в int, но поместятся в unsigned int. Рассмотрим эффект longVar &= ~0x0000000080000000;. Он не будет работать с (INT_MAX+1), указанным как шестнадцатеричное значение без суффикса, но он будет работать, если для него указано десятичное значение (с суффиксом или без него), а также он будет работать с большими или меньшими степенями двойки, даже если указано как шестигранник. - person supercat; 23.07.2013

В C++ вы можете столкнуться с проблемами перегрузки функций. Скажем, у вас было следующее:

signed int func(signed int x) {
    return x + 1;
}

signed long func(signed long x) {
    return x - 1;
}

int main(void) {
    sint32 x = 5;
    std::cout << func(x) << std::endl;
}

До изменения определения typedef значение 6 будет напечатано. После изменения будет напечатано значение 4. Хотя маловероятно, что перегрузка будет вести себя по-другому, это возможно.

Вы также можете столкнуться с проблемами с разрешением перегрузки. Предположим, у вас есть две функции со следующими определениями:

void func(int x);
void func(unsigned int x);

и вызывали функции с помощью:

sint32 x;
func(x);

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

person DRH    schedule 08.12.2011
comment
Набирал точно так же, но вы были быстрее ;) Кроме того, та же проблема существует для шаблонов - например. думать о boost::is_same<old_sint32, new_sint32> приводит к чему-то ложному - person zerm; 08.12.2011

Если указатель на sint32/uint32 используется там, где ожидается указатель на int/long (или наоборот), и они не совпадают int с int или long с long, вы можете получить предупреждение или ошибку во время компиляции (может быть в C, гарантируется в C++).

#include <limits.h>

#if UINT_MAX != ULONG_MAX
#error this is a test for systems with sizeof(int)=sizeof(long)
#endif

typedef unsigned uint32i;
typedef unsigned long uint32l;

uint32i i1;
uint32l l1;

unsigned* p1i = &i1;
unsigned long* p1l = &l1;

unsigned* p2il = &l1; // warning or error at compile time here
unsigned long* p2li = &i1; // warning or error at compile time here

int main(void)
{
  return 0;
}
person Alexey Frunze    schedule 08.12.2011
comment
+1 Хороший. Однако (см. мое редактирование) нам разрешено использовать только типы typedef, а не собственные типы C, и смешивание разных типов, по крайней мере, не рекомендуется. Однако константы по определению имеют собственные типы. Там смешение разных типов неизбежно... - person Johan Bezem; 08.12.2011

Ничто в стандарте не позволяло бы коду безопасно рассматривать 32-битные int и long как взаимозаменяемые. Учитывая код:

#include <stdio.h>

typedef int i32;
typedef long si32;

int main(void)
{
  void *m = calloc(4,4); // Four 32-bit integers
  char ch = getchar();
  int i1 = ch & 3;
  int i2 = (ch >> 2) & 3;
  si32 *p1=(si32*)m + i1;
  i32 *p2=(i32*)m + i2;

  *p1 = 1234;
  *p2 = 5678;
  printf("%d", *p1);
  return 0;
}

Компилятор вправе предположить, что поскольку p1 и p2 объявлены как разные типы (один как int, а другой long), они не могут указывать на один и тот же объект (без вызова Undefined Behavior). Для любого входного символа, если вышеприведенная программа должна была бы что-то делать (т. е. те, которые позволили бы избежать неопределенного поведения, заставив i1 и i2 быть неравными), программа должна была бы вывести 1234. Из-за строгого правила псевдонимов компилятор будет иметь право делать все, что угодно, для таких символов, как «P», «E», «J» или «O», что приведет к тому, что i и j получат совпадающие значения; таким образом, он может выводить 1234 и для них.

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

person supercat    schedule 15.07.2015