【问题标题】:Casting between character pointer types字符指针类型之间的转换
【发布时间】:2019-12-09 20:30:32
【问题描述】:

给定

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 是不同的类型,所以虽然上面的代码有某种意义显然是合理的,我想确保它不是严格标准的未定义行为。

【问题讨论】:

  • 为什么一开始就使用unsigned char * 作为字符串呢?这是来自使用它的遗留代码吗?
  • @Someprogrammerdude 除此之外,至少某些版本的ctype.h isxxxxx 对于负数是未定义的,因此如果输入字节有很多使用ctype.h 和普通char* 的程序是未定义的高位设置。
  • 我通常只在实际需要时才进行这种转换(即当调用isalnum 等时)。作为奖励,您可以避免这个问题。
  • 我不确定“标准的严格字母”,但就像你说的,它通常会起作用。尝试使用 -pedantic -Wall 进行编译,看看你的编译器是怎么想的。如果您仍然担心,我会将内容转换为 void* 以通过它,然后根据需要在 strtoull() 中重新转换。
  • @SanderDeDycker “仅在实际需要时(即调用 isalnum 等时)进行转换。”这里的应用不清楚。这篇文章是关于 pointer 转换的。 is...(int ch) 都是关于 int

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


【解决方案1】:

没有关于类型的别名问题,原因有两个。 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 ** 时会产生与原始指针等效的东西。)

【讨论】:

    【解决方案2】:

    如果编译器可能因为将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**)&amp;uchar_ptr 的任何问题。

    代码实际上会在没有任何显式强制转换的情况下编译,但强制转换更清楚地表明别名是有意的(并抑制编译器警告)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-06-16
      • 1970-01-01
      • 1970-01-01
      • 2016-08-31
      • 2011-02-01
      • 2020-06-15
      相关资源
      最近更新 更多