字符指针类型之间的转换

时间:2019-08-01 09:20:02

标签: c pointers casting language-lawyer undefined-behavior

给予

unsigned long long int strtoull (const char* str, char** endptr, int base);

这有效吗?

unsigned char *s = "123";
unsigned char *t;
unsigned long long n = strtoull(s, &t, 0);

通常无法接受指向一种类型的指针并将其转换为另一种类型的指针,严格来说charunsigned char是不同的类型,因此在以上代码显然是合理的,我想确保它不是标准的严格字母所定义的行为。

2 个答案:

答案 0 :(得分:2)

关于类型,没有别名问题,有两个原因。 C 2018 6.5 7中的别名规则允许访问具有字符类型的对象(并且unsigned charchar都是字符类型),并且它们还允许访问具有符号或符号类型的对象。与对象的有效类型相对应的无符号类型(您正在使用unsigned charchar进行操作。)

但是,将unsigned char *作为参数const char *的使用违反了有关兼容类型的规则,对unsigned char **参数使用char **参数的违反了。在第一种情况下,如果将显式强制转换插入参数类型,则转换由C 2018 6.3.2.3 7定义:“当将对象的指针转换为字符类型的指针时,结果指向对象的最低寻址字节。”

在第二种情况下,我看不到C标准定义了一种将unsigned char **转换为char **并具有定义结果的方法。 (转换本身是允许的,但是C标准未指定结果的值,除了转换回unsigned char **时产生的结果等同于原始指针)。

答案 1 :(得分:1)

如果编译器因为将char**转换为unsigned char**而导致程序中断的可能性,则可以解决类型差异的问题。

#include <stdlib.h>

unsigned long long foo(const unsigned char* const p)
{
  const char* const s = (const char*)p;
  char* t = NULL;
  const unsigned long long n = strtoull(s, &t, 0);

  return n;
}

在这个简单的示例中,我可能会省略s的声明,而仅在需要的地方使用强制类型转换,而完全没有必要声明t。如果您经常使用别名并将它们保存为击键,则将它们声明为别名更有意义。 (此外,对于字符类型和void*以外的其他类型,您还希望将别名作为函数参数,以使其不会出现在与原始别名相同的范围内,并且违反严格的别名规则。)

如果您需要传递end_ptr以外的NULL值,则可能是您使用存储的值来继续解析。要么将其传递给另一个期望strtoull()的函数,例如char*,要么可以安全地转换为unsigned char*,即在指向兼容类型的指针之间进行的转换。混叠规则。这不会引起(char**)&uchar_ptr的任何问题。

该代码实际上将在不进行任何显式强制转换的情况下进行编译,但是强制转换使别名更加明确,即是故意的(并抑制了编译器警告)。