2010-10-23 40 views
4

假设我有一个功能char是接口问题的签名吗?

void foo(char *) 

其中,在内部,需要治疗其输入为NULL结尾的字节数(比如,它是对字符串的哈希函数)的块。我可以在该函数中将参数投射到unsigned char*。我也可以改变声明

void foo(unsigned char *) 

。现在,因为charsigned charunsigned charthree different types,这将构成一个接口的改变,在C中的“接口”一词的任何合理的定义是什么?

(这个问题是为了解决另一个问题,提出了discussion。我有我的意见,但直到一个出来作为一个“胜利者”别人的票不会接受一个答案。)

+2

为什么你用C++标记这个,如果你只是提出C的问题? – 2010-10-23 15:02:28

+0

删除C++标记。 – Puppy 2010-10-23 15:05:40

+0

@DeadMG:击败我吧! ':)' – sbi 2010-10-23 15:06:44

回答

4

根据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中也明确提到过

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

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

+0

我知道'char','signed char'和'unsigned char'是不同/不兼容的基本类型。但是'char *','signed char *'和'unsigned char *'不兼容的指针类型?我知道类型表示的部分使得它可以通过任何类型的指针访问相同类型的带符号和无符号版本,只要该值符合签名类型的正范围以及其他保证(如'unsigned char *'可以用来访问任何东西)。 – 2010-10-24 01:35:14

+0

@R:对,你错过了那一步(现在修正了......) – Christoph 2010-10-24 07:22:29

+0

+1并且接受。感谢标准的指针(有符号或无符号)。 – 2010-10-25 11:07:11

3

是它是。之前编译的客户端代码将不再编译(或者无论如何可能会产生新的警告),所以这是一个突破性的改变。

+1

+1:当然,这取决于“接口”的定义,但是如果需要重新编译,那么您已经将抽象泄露给API的用户。 – 2010-10-23 15:01:52

+1

@Oli,为什么需要重新编译?在C中它没有。 'char *'和'unsigned char *'对齐相同。 (很确定*我*会重新编译我的代码,但没有任何东西强制它。) – 2010-10-23 15:21:02

+0

@Jens:你是对的。我想我的意思是“如果它需要客户端在重新编译之前修改它们的代码”。 – 2010-10-23 15:22:39

0

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

+0

你正在做某些假设,这并不总是成立。如果您接受客户端代码需要进行更改,即使您认为这很简单(在这种情况下,我认为这可能不是微不足道的),但您接受*有接口更改。 – Tim 2010-10-23 15:14:00

+0

@Tim:虽然我希望人们会为我的两个答案中的一个投票+1,而不是-1。 -1两个也是可以接受的。 – 2010-10-23 15:15:41

+0

我去买这个。只要不存在对齐问题,6.3.2.3中的句子7明确允许在指向不同对象类型的指针之间进行转换,这在这里不会发生。并继续发表关于“一种字符类型”的声明。所以对于这里的情况来说,任何有效的呼叫仍然如此。之后,编译器可能会发出一些很好的警告,但仅此而已。 – 2010-10-23 15:16:14

0

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

  • 是 - 你没有破坏你的界面
  • 没有 - 你已经泄露实施
    细节。
+1

为了回答这个POV,你还需要'void(*)(unsigned char *)'将隐式转换为'void(*)(char *)'(并在调用时工作),因为有人可能写了'void(* pfoo)(char *)= &foo;'。我有点困惑,C允许,因为它发生了...... – 2010-10-23 15:32:42

+0

@Steve Jessop:这可能是一个问题。然而,编写一个简单地传递参数的函数将是非常微不足道的。 – Puppy 2010-10-23 16:17:42

+0

为真。但是,对于背景,原始问题抛出了答案:“将签名更改为'unsigned char *',”将char *'转换为'foo中的'unsigned char *'',并将'char'转换为'unsigned'一旦你读过它“。如果你最终提供了一个包装函数来弥补'foo'签名的改变,我认为这意味着在'foo'中投射首先会更好。我认为最初的观点是一个很小的问题 - 这个新的foo是否替代旧的? – 2010-10-23 16:25:14

2

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

虽然这不是直接回答你实际上问的问题,到了形势的正确的解决方案似乎相当简单而明显的对我说:你真不该使用任何上述的。

至少国际海事组织,你有一个很好的理由,否则,你的职能应该接受void *或(最好是)void const *。你要找的基本上是一个不透明的指针,这正是void *提供的。用户不需要知道任何有关您的实现的内部,因为任何其他指针类型将隐式转换为void *,这是少数可能性之一,不会破坏任何现有的代码。

+0

这不是对其他引发这个问题的问题的回答吗?这个问题是,“你会称之为界面变化吗?”,而不是“我该怎么做?” – 2010-10-23 15:36:53

+0

你为什么要将它改为'void *'?这种类型安全性较低,并且会在用户获得_no warning_时伤害用户。 – alternative 2010-10-23 16:54:26

+0

@mathepic:当且仅当您可以定义函数有意义的类型时,类型安全才有意义。他说他只是将内容视为字节,这表明它与类型无关。 – 2010-10-23 18:53:49