【问题标题】:Pass integer address in method expecting unsigned char pointer在期望无符号字符指针的方法中传递整数地址
【发布时间】:2014-07-22 11:51:55
【问题描述】:

最近我有代码(在 C 中),其中我将 int 的地址传递给期望 pointer to unsigned char 的函数。这无效吗?这是 UB 还是什么?

例如,

void f(unsigned char*p)
{
// do something
}

// Call it somewhere
int x = 0; // actually it was uint32_t if it makes difference
f(&x);

我确实收到了警告...在 Xcode 中编译

【问题讨论】:

  • @EJEHardenberg 这与这里有什么关系?
  • @Sathish:你的意思是f((unsigned char*)&x);,但问题是如果不使用演员表会发生什么?
  • @dmcr_code 如果省略演员表,则代码格式错误。
  • @dmcr_code 不要将“语义”与“句法”混淆。也许您的编译器可以理解代码(尽管 C 标准不要求它这样做),并且很可能它将代码视为有强制转换。但是,如果一个格式错误的程序编译,它会自动具有未定义的行为。此外,一旦启用将警告视为错误的编译器标志,例如 GCC 的 -Werror,代码将无法编译。某些特定的代码段是否使用某些特定的编译器和标志进行编译是无关紧要的,也不是代码正确性的衡量标准。
  • @dmcr_code:来自 gcc 的警告可能意味着代码不严格符合。该标准允许实现编译尽可能多的无效程序,只要它编译所有有效程序(并在某些情况下提供“诊断消息”(对于 gcc,例如警告 is 诊断)案例,例如对于您的代码)。所以编译器允许拒绝编译你的代码并且需要给出诊断。

标签: c


【解决方案1】:

int *unsigned char * 不被视为兼容类型,因此隐式转换将发出诊断。但是,标准确实允许在不同指针之间进行显式转换,但要遵守两条规则(C11 第 6.3.2.3 节):

  1. 将“指向A 的指针”类型转换为“指向B 的指针”类型并转换回“指向A 的指针”将产生相同的原始指针。 (即,如果pint * 类型,那么(int *)(double *)p 将产生p
  2. 将任何指针转换为char * 将指向对象的最低可寻址字节。

因此,在您的情况下,显式 (unsigned char *) 强制转换将产生一个没有任何未定义行为的符合程序。

【讨论】:

    【解决方案2】:

    需要演员表,请参阅 C11 (n1570) 6.5.2.2 p.2:

    […] 每个参数都应该有一个类型,这样它的值可以分配给一个对象,该对象具有其对应参数类型的非限定版本。

    这里指的是赋值规则,相关部分是(同上。6.5.16.1 p.1)

    应满足以下条件之一:

    […]

    • 左操作数具有原子、限定或非限定指针类型,并且(考虑到左操作数在左值转换后将具有的类型)两个操作数都是指向兼容类型的限定或非限定版本的指针,并且由left 具有 right 指向的类型的所有限定符。

    […]

    而且unsigned charint 不兼容。

    这些规则都出现在“约束”部分,其中“应”表示编译器必须给出“诊断消息”(参见 C11 5.1.1.3)并且可能会停止编译(或其他任何超出该诊断的所有内容)严格来说,超出了 C 标准的范围)。您的代码是违反约束的示例。

    其他违反约束的例子是调用带有错误数量参数的(原型和非可变)函数,在doubles 上使用按位运算符,或者在同一范围内重新声明具有不兼容类型的标识符,同上。 5.1.1.3 第 2 页:

    示例

    实现应为翻译单元发出诊断:

        char i;
        int i;
    

    因为在本国际标准中的措辞将构造的行为描述为既是约束错误又导致未定义行为的情况下,应诊断约束错误。

    语法违规一视同仁。

    所以,严格来说,你的程序是无效的

    int foo(int);
    int main() {
        It's my birthday!
        foo(0.5 ^ 42, 12);
    }
    

    只要它提供至少一个诊断(例如警告),一个符合要求的实现就可以很好地编译,可能编译为具有未定义行为的程序。

    例如gcc,警告是一种诊断(您可以使用-pedantic-errors 将语法和约束违规转化为错误)。

    格式不正确的术语可用于指代语法或约束违规,C 标准不使用该术语,但参见。 C++11 (n3242):

    1.3.9

    格式错误的程序

    格式不正确的程序

    1.3.26

    格式良好的程序

    根据语法规则、可诊断语义规则和单一定义规则构造的C++程序。

    撇开语言律师的态度不谈,您的代码可能总是根本不编译(这应该足以进行强制转换),或者显示预期的行为。

    【讨论】:

      【解决方案3】:

      C11,§6.5.2.2:

      2 每个参数都应具有一个类型,以便可以将其值分配给一个对象,该对象具有其对应参数类型的非限定版本。

      §6.5.16.1 根据约束列表描述分配,包括

      左操作数具有原子、限定或非限定指针类型,并且(考虑到左操作数在左值转换后将具有的类型)两个操作数都是指向兼容类型的限定或非限定版本的指针,并且由left 具有 right 指向的类型的所有限定符

      intunsigned char 不是兼容的类型,因此程序格式不正确,标准甚至不保证它会编译。

      【讨论】:

      • 约束部分中的“shall”表示:给出诊断(并且允许实现停止编译),而不是UB。
      • @mafso:那是 UB 还是不是 UB? (当页面上的所有答案都不同时,这会令人困惑!:))
      【解决方案4】:

      虽然有些人会说“根据标准,这是未定义的行为”,但事实上发生的情况如下(通过示例回答):


      安全:

      void f(char* p)
      {
          char r, w = 0;
          r = p[0]; // read access
          p[0] = w; // write access
      }
      
      ...
      
      int x = 0;
      f((char*)&x); // the casting is just in order to emit the compilation warning
      

      只要您使用p[i] 访问内存,此代码是安全的,其中0 <= i <= sizeof(int)-1


      不安全:

      void f(int* p)
      {
          int r, w = 0;
          r = p[0]; // read access
          p[0] = w; // write access
      }
      
      ...
      
      char x[sizeof(int)] = {0};
      f((int*)&x); // the casting is just in order to emit the compilation warning
      

      这段代码是不安全的,因为尽管分配的变量足够大以容纳int,但它在内存中的地址不一定是sizeof(int) 的倍数。因此,除非编译器(以及底层硬件架构)支持未对齐的加载/存储操作,否则如果该变量在内存中的地址确实没有正确对齐,则会在运行时发生内存访问冲突。

      【讨论】:

      • 从技术上讲,第二个示例是未定义的行为(“如果结果指针未正确对齐引用的类型,则行为未定义。”- C11 6.3.2.3 第 7 条)
      • @DrewMcGowen:好的,这就是我从“有人会说...”开始的原因。
      • @barakmanos:第一个例子是 UB 怎么样?这就是我感兴趣的;接受一个答案很奇怪,有些人会说一件事而其他人会说其他的
      • @dmcr_code: 应该没问题,只要您不尝试使用i >= sizeof(int) 访问p[i]
      • @dmcr_code:不。memcpy 一次复制一个字节,这是非常安全的。但另一方面,*(int*)x = y 是不安全的,因为编译器将赋值编译为 int-store 操作而不是 byte-store 操作。我回答中的第二段代码也证明了同样的危险。
      猜你喜欢
      • 2012-05-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多