【问题标题】:Is the signedness of char an interface issue?char 的签名是接口问题吗?
【发布时间】:2011-04-29 14:34:39
【问题描述】:

假设我有一个函数

void foo(char *)

在内部,它需要将其输入视为一个以 NUL 结尾的字节块(例如,它是字符串上的哈希函数)。我可以在函数中将参数转换为unsigned char*。我也可以将声明更改为

void foo(unsigned char *)

现在,鉴于 charsigned charunsigned charthree different types,根据 C 中术语“接口”的任何合理定义,这是否会构成接口更改?

(此问题旨在解决另一个问题提出的discussion。我有我的意见,但在有人投票成为“赢家”之前不会接受答案。)

【问题讨论】:

  • 如果您只是针对 C 提出问题,为什么还要用 C++ 标记它?
  • @sbi:如果您实际查看他的链接,它会引用 C++ 标准并提供 C++ 示例代码。 OP 似乎只是假设这在 C 标准中是相同的。
  • 将链接更改为指向 C 标准基本原理的引用。
  • 我在这里看不到任何真正的答案。一个真正的答案应该引用处理函数指针类型的标准部分。

标签: c interface unsigned-char signedness


【解决方案1】:

根据 ISO/IEC 9899:TC3,

  • 通过不兼容类型的表达式调用函数是未定义的行为 (6.5.2.2 §9)
  • 兼容的函数类型必须具有兼容的参数类型(6.7.5.3 §15)
  • 兼容的指针类型必须指向兼容的类型(6.7.5.1 §2)
  • charsigned charunsigned char 是不同的基本类型(6.2.5 §14),因此不兼容(6.2.7 §1),第 35 页的脚注 35 中也明确提到了这一点

所以是的,这显然是对编程接口的更改。

但是,由于 char *signed char *unsigned char * 在 C 语言的任何合理实现中都具有相同的表示和对齐要求,因此二进制接口将保持不变。

【讨论】:

  • 我知道charsigned charunsigned char 是不同/不兼容的基本类型。但是char *signed char *unsigned char * 是不兼容的指针类型吗?我知道关于类型表示的部分明确定义了通过任一类型的指针访问相同类型的有符号和无符号版本,只要该值符合有符号类型的正范围,以及其他保证(例如unsigned char * 可以用来访问任何东西)。
  • +1 并被接受。感谢您提供标准的指针(有符号或无符号;)。
【解决方案2】:

不,不是。对客户端代码的任何更改都是微不足道的(特别是如果它只是为了避免警告),实际上在几乎任何 C 实现中你会发现你甚至不需要重新编译,因为指向 char* 和 @ 987654323@ 将在调用约定中以完全相同的方式传递。

【讨论】:

  • 您在这里做出某些假设并不总是成立。如果您接受客户端代码需要更改,即使您认为这是微不足道的(在这种情况下,我认为这可能不是微不足道的),您也接受 is界面变化。
  • @Tim:确切地说,尽管我希望人们会为我的两个答案之一投票 +1,而不是为其中一个投票 -1。两者都为 -1 也是可以接受的。
  • 我选择这个。 6.3.2.3 中的第 7 句明确允许在指向不同对象类型的指针之间进行转换,只要没有对齐问题,这里就不会发生这种情况。它通过关于“字符类型”的声明继续进行。因此,对于这里的情况,任何有效的调用仍然有效。编译器之后可能会发出一些不错的警告,但仅此而已。
  • 哦,是的。对不起。没有看到它们都是你的答案。 :-)
【解决方案3】:

我选择“C——以上都不是”。

虽然这不是对您实际提出的问题的直接回答,但对我来说,正确的解决方案似乎相当简单明了:您不应该真正使用上述任何方法。

至少在 IMO,您有充分的理由不这样做,您的函数应该接受 void * 或(最好)void const *。您正在寻找的基本上是一个不透明的指针,而这正是void * 提供的。用户不需要了解您的实现的内部结构,并且由于任何其他指针类型都会隐式转换为 void *,因此它也是少数不会破坏任何现有代码的可能性之一。

【讨论】:

  • 这不是对引发这个问题的另一个问题的回答吗?这个问题是,“你会称之为界面变化吗?”,而不是“我应该怎么做?”
  • 你为什么要把它改成void *?这不太安全,并且会在用户没有警告时伤害用户。
  • @mathepic:当且仅当您可以定义函数对其有意义的类型时,类型安全才有意义。他说他将内容简单地视为字节,这表明它与类型无关。
  • @Steve:也许——我没看到/看过那个。我没有费心回答是否,因为这让我觉得非常明显:当然是。你可以争辩说它太了,不能太在意,但它显然不管这一点。
  • “说,它是字符串上的散列函数” - 它不是通用的。
【解决方案4】:

是的。先前编译的客户端代码将不再编译(或者无论如何可能会生成新的警告),因此这是一个重大更改。

【讨论】:

  • +1:当然,这取决于“接口”的定义,但如果它需要重新编译,那么您已经将抽象泄露给了 API 的用户。
  • @Oli,为什么需要重新编译?在C中它没有。 char*unsigned char* 对齐相同。 (当然会重新编译我的代码,但没有什么可以强迫它。)
  • @Jens:你是对的。我想我的意思是“如果它要求客户端在重新编译之前修改他们的代码”。
【解决方案5】:

char* 是否隐式转换为 unsigned char*?

  • 是的 - 你没有破坏你的界面
  • 不 - 你泄露了实现
    详情。

【讨论】:

  • 要从这个 POV 中回答,您还需要 void (*)(unsigned char*) 隐式转换为 void (*)(char*)(并在调用时工作),因为有人可能写过 void (*pfoo)(char*) = &foo;。我有点困惑 C 允许什么,因为它发生了......
  • @Steve Jessop:这可能是个问题。但是,编写一个简单地传递参数的函数将非常简单。
  • 真的。但是,对于背景,最初的问题提出了答案“将签名更改为unsigned char*,“将char* 转换为@ 987654326@ in foo”,并将cast the char` 转换为unsigned,一旦您阅读了它”。如果您最终提供了一个包装函数来弥补对foo 签名的更改,我认为这意味着首先使用foo 会更好。我认为最初的争论是关于一件小事——这个新的 foo 是旧的兼容替代品吗?
猜你喜欢
  • 2011-06-11
  • 1970-01-01
  • 1970-01-01
  • 2020-08-21
  • 1970-01-01
  • 2011-03-01
  • 2011-12-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多