将char*传递给期望unsigned char*的方法

8

我正在开发一些嵌入式设备,该设备有一个SDK。它有一个方法如下:

MessageBox(u8*, u8*); // u8 is typedefed unsigned char when I checked

但我在他们的示例中看到了调用代码,例如:

MessageBox("hi","hello");

传递字符指针时没有进行类型转换,这能够得到好的定义吗?我询问这个问题是因为我在代码上运行了一些工具,并且它抱怨上述的不匹配:

messageBox("Status", "Error calculating \rhash");
diy.c  89  Error 64:  Type mismatch (arg. no. 1) (ptrs to signed/unsigned)
diy.c  89  Error 64:  Type mismatch (arg. no. 2) (ptrs to signed/unsigned)


有时我会得到不同的意见,这更让我感到困惑。 所以归纳起来,按照上述方法使用他们的API,是否存在问题? 它会崩溃程序吗?

还有一件事情,很高兴听到正确的方法,如何将字符串传递给期望unsigned char*的SDK方法而不会导致约束违规?


@dasblinkenlight:我很想帮忙,但不幸的是我现在无法访问该设备 :( - user4954224
好的,对于那些打印0的系统,这段示例代码可以编译和运行;而对于那些打印-128的系统,则会发出警告或错误提示。 - Sergey Kalinichenko
@dasblinkenlight:我既没有错误也没有警告(错误来自在线lint)。我更加好奇的是,如果它是/不是未定义行为。 - user4954224
3
嵌入式设备是否使用CodeWarrior作为编译器?我模糊地记得在该编译器中有一种将字符串选项设置为无符号字符的选项。 - StilesCrisis
@chqrlie:我得检查一下,但我相信 api everywhere 使用 u8*... 有良好的定义,我的意思是没有未定义的行为等等...(当你说容易出错时,这仍然让我感到困惑,为什么?) - user4954224
显示剩余20条评论
2个回答

4

这是一种约束违规,从技术上讲,它并不好定义,但在实践中,这并不是一个问题。然而,为了消除这些警告,您应该将这些参数强制转换。在您的代码中大量使用丑陋的强制转换不是最佳选择,您可以定义一个内联函数来解决这个问题:

static inline unsigned char *ucstr(const char *str) { return (unsigned char *)str; }

在需要传递字符串给(错误地)采用unsigned char *参数的API时,使用该函数:

messageBox(ucstr("hi"), ucstr("hello"));

这样做可以避免警告,同时保持一定的类型安全性。

另外需要注意的是,messageBox 应该接收 const char * 类型的参数。这个SDK使用了有问题的规范。


1
当编译失败时,它如何被定义为良好的?[链接](http://ideone.com/r2OO4L) - Sergey Kalinichenko
2
我认为将类型转换隐藏在“可爱”的小函数下并不是最好的做法,这是一个不好的习惯。用户应该始终知道他们当前使用的类型。 - pascx64
1
@Dan Korn:如果函数期望使用Pascal字符串,那么它当然不会起作用,但是OP在相关问题中表示生产代码似乎正常工作。我怀疑该函数仅支持非ASCII字符集,并且API设计人员认为指定这些扩展字符应为无符号是明智的。这只是一种假设,如果我是正确的,那么这是一个糟糕的决定。 - chqrlie
1
@User30015:你的目标设备是什么?它运行在什么架构上?你使用的编译器是什么?没有100%的确定性。无论你传递一个 char * 还是一个 unsigned char * 都没有影响。但是如果数组包含1MB的字符串呢?我无法确定这是否会在messageBox()的实现中触发未定义的行为。 - chqrlie
1
@giorgi:const char *是指向以NUL字节结尾的字节数组的指针,不能被修改。如果接收此指针作为参数的函数不修改数组内容并正确处理范围在0..127之外的字节值(或者没有字节在此范围之外),那么这是可以的。我不知道如何在没有白板或一些内存转储的情况下更清楚地解释这一点。 - chqrlie
显示剩余20条评论

0
问题在于实现定义了charunsigned还是signed
对于没有错误的编译器,那些实际上将char定义为unsigned的编译器将不会出错。其中一些(特别是那些实际上是C++编译器的编译器,其中charunsigned char是不同类型)将发出警告。对于这些编译器,将指针转换为unsigned char *是安全的。

编译器报告错误的将是那些实际上使用了signed字符的编译器。如果编译器(或主机)使用ASCII或类似的字符集,并且字符串中的字符是可打印的,则将字符串转换为unsigned char *(或更好地转换为避免从字符串字面量中删除constness的const unsigned char *)在技术上是安全的。然而,这些转换对于使用不同字符集的实现或包含非可打印字符的字符串(例如,类型为signed char且为负数的值以及大于127的unsigned char值)可能是不安全的。我说可能是不安全的,因为发生什么取决于被调用函数的操作 - 例如它是否检查单个字符的值?它是否检查字符串中单个字符的单个位?如果被调用的函数设计良好,则后者是接受指向unsigned char *的指针的原因之一。

因此,你需要做的就是对目标机器以及它的 char 和 unsigned char 类型进行假设,并了解函数对其参数的处理方式。最通用的方法(适用于所有字符集,无论 char 是 signed 还是 unsigned)是创建一个辅助函数,将 char 数组复制到另一个 unsigned char 数组中。该辅助函数的工作方式将取决于你如何(以及是否需要)处理带有负值的 signed char 值的转换。

这并不是很准确,即使普通的char是无符号的,charunsigned char仍然是不同的类型。标准规定编译器必须发出诊断消息。 - M.M
有标准规定的内容,也有编译器实际执行的内容,Matt。由于某种原因,并非所有C编译器都会在将char *传递到期望unsigned char *的位置时进行诊断(除了警告之外,这通常是可选的并且默认情况下被禁用)。 - Peter
@MattMcNabb:那你怎么说?我会在获得设备访问权限后尝试检查CHAR_MIN,以检查char是否为有符号的。但是就像我所说的,如果我没有错,在他们的许多函数中,他们直接将char*传递给消息框-而我需要确定,因为我已经使用了该SDK来开发我的软件。他们(以及我)甚至将无符号字符指针传递给字符串方法,如strcpy、sprintf等,而不需要强制转换;还有atoi,参见我的另一个问题。那么所有这些操作都是正确的,不会导致程序崩溃吗? - user4954224
@Peter,一些编译器(如Borland C++)允许将char *传递给期望unsigned char *的函数而不发出警告。 - M.M
@User30015 嗯,事实是SDK给了你一个糟糕的API,所以你必须与之一起工作。你的代码中将不得不有一些丑陋的转换或宏。 - M.M
@User30015:正如我已经多次写过的那样,将char *甚至const char *传递给期望unsigned char *的API是不严谨的,但本质上不会引起任何问题。编译器应该会发出警告,但仍会生成无害的代码。在您的环境中,这样的警告似乎默认被禁用了:它鼓励了不严谨性,但没有危害后果。您可以添加强制转换或内联类型包装器,但这些并不是必需的,这样做可能会引入新的错误,所以不要担心它,让代码保持原样。这是我的最终答案。 - chqrlie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接