将不同类型的指针传递给函数 (C)

passing a pointer of different type to a function (C)

提问人:Sasha 提问时间:10/3/2023 更新时间:10/3/2023 访问量:58

问:

假设一个来自通用库的函数在任何情况下都想使用长整数(比如 64 位),但在我的程序中我想使用短整数(比如 32 位)。然后我遇到如下情况:

void f(long unsigned int *a) {
  *a = 10;
}

void main(void) {
  unsigned int b;
  f(&b);
  return;
}

我是否正确理解这实际上不是一个好主意,并且函数 f 将覆盖另外 32 位(内存中的位),写入其中(当编译器从 到 时)?但是,如果我想象正确,以下内容不会覆盖(因为编译器从 转换为):b0unsigned int *long unsigned int *long unsigned intunsigned int

long unsigned int f(void) {
  return 10;
}

void main(void) {
  unsigned int b;
  b = f();
  return;
}

这是对的吗?

第二个实现有一个缺点,以防函数返回计算的其他部分对我来说很方便,而这只是计算的额外细节......10

C 指针 类型 参数传递 大小

评论

0赞 Weather Vane 10/3/2023
long unsigned int始终至少与 一样大。因此,由于有可能对某些东西造成损坏,因此不要这样做。在评论中的附属问题中,它可能但不寻常地与它的大小相同(始终至少至少为 16 位),因此在这种情况下,它会更加危险。如果函数指针的类型小于传递的类型,则不会设置其所有字节。unsigned intunsigned charunsigned int
1赞 Weather Vane 10/3/2023
void f(long unsigned int *a)需要指向 的指针。其他任何事情都可能“有效”,但都是未定义的行为。long unsigned int

答:

2赞 Ted Lyngmo 10/3/2023 #1

我是否正确理解,这实际上不是一个好主意

是的,这是正确的。

...并且函数 f 将覆盖另外 32 位(内存中 b 后面的位),将 0 写入其中(当编译器从 到 时)?unsigned int *long unsigned int *

这是一种可能性。也可能有比 AN 更严格的对齐要求,因此它甚至可能在走到那一步之前就崩溃了。unsigned long int*unsigned int*

如果你有这样的接口,你通常会做的是提供正确类型的变量,然后将其分配给所需类型的变量:

void f(unsigned long int *a) {
  *a = 10;
}

void main(void) {
  unsigned long int tmp;
  f(&tmp);
  // if (tmp > UINT_MAX) ... // possible error check
  unsigned int b = tmp;
}

评论

0赞 Sasha 10/3/2023
谢谢!事实上,我已经考虑过你的建议,但认为也许它有点不优雅,并且有更好的解决方案,但我想可能不是......
1赞 Ted Lyngmo 10/3/2023
@Sasha 不客气。另一种方法是使用您正在使用的 API 中使用的任何类型。
2赞 Eric Postpischil 10/3/2023 #2

关键问题是用于访问对象的类型。在 中,参数声明为 。这意味着,在 里面,指的是 .特别是,表示“将值 10 写入 .falong unsigned int *af*along unsigned int*a = 10;long unsigned int

在 中,定义为 。然后将 an 的地址传递给 。mainbunsigned int bf(&b)unsigned intf

所以说将 a 的字节写入实际上是 .*a = 10long unsigned intunsigned int

如果 是 64 位且是 32 位,则表示名义上尝试写入的位数多于对象的位数。它可能会写入对象外部的内存,这可能会损坏程序用于其他目的的数据。它还可能尝试访问进程有效地址空间之外的内存,这将导致陷阱,从而结束程序的执行。long unsigned intunsigned intf

但是,无论内存访问的细节如何,此代码的行为都不是由 C 标准定义的。C 2018 6.5 7 指定了有关应使用哪种类型访问对象的规则。对于 ,这些规则允许使用以下类型访问它:unsigned int

  • unsigned int,
  • unsigned int使用限定符(例如 or ),constvolatile
  • int (signed int),带或不带限定符,
  • 以上述项之一为成员的数组、结构或联合类型,或
  • 字符类型。

由于以上都不是,因此不符合规则。即使宽度相同,它也是不兼容的不同类型的,所以它不符合规则。long unsigned intlong unsigned intunsigned intunsigned int

即使它是相同的宽度,因此名义上不会访问比实际拥有的更多的内存,违反 6.5 7 中的规则意味着编译器可能会在优化时对程序做出“不正确”的推断,然后优化可以以您意想不到的方式转换您的程序。*a = 10b

评论

0赞 Sasha 10/3/2023
感谢您的详细回答。但是,更详细地说,即使在我的代码中我将 long unsigned int 更改为 unsigned char,如果我理解正确的话,仍然有一些有趣的观点。我认为 (CMIYW),第一个代码将写入 的第一个字节,而第二个代码将这样做,再加上放入剩余的三个字节......b0
1赞 Eric Postpischil 10/3/2023
@Sasha:当对象本身没有字符类型时,使用字符类型访问对象是出于特殊目的。这个答案中提到它是因为它是别名规则允许的,但这并不意味着它将填充对象的其余部分或服务于您的目的。