当 C(或 C++)中的“unsigned long”和“unsigned int”都是 32 位宽时,使用它们是否存在可观察到的差异?

发布于 2024-12-20 12:45:13 字数 792 浏览 1 评论 0原文

我使用的是带有编译器的 MPC56XX(嵌入式系统),其 intlong 均为 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++ 问题都感兴趣。

编辑:我知道重新定义时将 intsint32 混合可能会产生不同的结果。但我们不允许直接使用原始的 C 类型,只能使用 typedef 的类型。
我正在寻找使用常量、一元/二元运算符、强制转换等的示例(表达式或片段),并根据更改的类型定义具有不同但正确的编译结果。

I'm using an MPC56XX (embedded systems) with a compiler for which an int and a long are both 32 bits wide.

In a required software package we had the following definitions for 32-bit wide types:

typedef   signed int sint32;
typedef unsigned int uint32;

In a new release this was changed without much documentation to:

typedef   signed long sint32;
typedef unsigned long uint32;

I can see why this would be a good thing: Integers have a conversion rank between short and long, so theoretically extra conversions can apply when using the first set of definitions.

My question: Given the above change forced upon us by the package authors, is there a situation imaginable where such a change would change the compiled code, correctly leading to a different result?

I'm familiar with the "usual unary conversions" and the "usual binary conversions", but I have a hard time coming up with a concrete situation where this could really ruin my existing code. But is it really irrelevant?

I'm currently working in a pure C environment, using C89/C94, but I'd be interested in both C and C++ issues.

EDIT: I know that mixing int with sint32 may produce different results when it's redefined. But we're not allowed to use the original C types directly, only the typedef'ed ones.
I'm looking for a sample (expression or snippet) using constants, unary/binary operators, casts, etc. with a different but correct compilation result based on the changed type definition.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

山有枢 2024-12-27 12:45:13

在 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 更改后,不再存在精确匹配(两个函数都不需要很长时间),并且编译器会失败,因为它将无法确定要调用哪个重载。

In C++ you may run into issues with function overloading. Say you had the following:

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;
}

Prior to the typedef definition change, the value 6 would be printed. After the change the value 4 would be printed. While it's unlikely that an overload would have behavior that's this different, it is a possibility.

You could also run into issues with overload resolution. Assume you had two functions with the following definitions:

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

and were calling the functions with:

sint32 x;
func(x);

Prior to the change, the function call was unambiguous, func(int) would be an exact match. After the typedef change, there is no longer an exact match (neither function takes a long), and the compiler fails since it will not be able to determine which overload to invoke.

杯别 2024-12-27 12:45:13

这可能会导致一些微妙的问题,因为默认情况下文字数字是 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)

而它工作得很好前。

因此,在使用文字的地方可能会出现一些意外的重载。如果您总是使用命名(因此类型化)常量,那么应该没问题。

It might lead to subtle issues because literal numbers are int by default.

Consider the following program:

#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}}
}

This leads to an error because the call to newp is now ambiguous:

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)

whereas it worked fine before.

So there might be some overloads surprises where literals were used. If you always use named (and thus typed) constants, you should be fine.

漫漫岁月 2024-12-27 12:45:13

如果在需要指向 int/long 的指针的地方使用了指向 sint32/uint32 的指针(反之亦然),并且它们不匹配 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;
}

If a pointer to sint32/uint32 is used where a pointer to int/long is expected (or vice versa) and they don't match int with int or long with long, you may get a warning or error at compile time (may in C, guaranteed in 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;
}
踏雪无痕 2024-12-27 12:45:13

标准中没有任何内容允许代码安全地将 32 位 intlong 视为可互换的。给定代码:

#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;
}

编译器有权假设,因为 p1p2 被声明为不同的类型(一个为 int ,另一个为 < code>long),它们不可能指向同一个对象(不调用未定义行为)。对于任何输入字符,如果上述程序需要执行任何操作(即通过导致 i1i2 不相等来避免未定义行为的操作),则该程序将是需要输出 1234。由于严格别名规则,编译器有权对“P”、“E”、“J”或“O”等字符执行任何操作,这会导致 i和j接收匹配值;因此它也可以输出 1234。

虽然许多 intlong 均为 32 位的编译器有可能(事实上很可能)实际上将它们视为严格别名规则的等效类型,标准中没有任何内容强制要求此类行为。

Nothing in the Standard would allow code to safely regard a 32-bit int and long as interchangeable. Given the code:

#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;
}

A compiler would be entitled to assume that because p1 and p2 are declared as different types (one as int and the other long), they cannot possibly point to the same object (without invoking Undefined Behavior). For any input character were the above program would be required to do anything (i.e. those which would avoid Undefined Behavior by causing i1 and i2 to be unequal), the program would be required to output 1234. Because of the Strict Aliasing Rule, a compiler would be entitled to do anything it likes for characters like 'P', 'E', 'J', or 'O' which would cause i and j to receive matching values; it could thus output 1234 for those as well.

While it's possible (and in fact likely) that many compilers where both int and long are 32 bits will in fact regard them as equivalent types for purposes of the Strict Aliasing Rule, nothing in the Standard mandates such behavior.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文