在期望无符号字符指针的方法中传递整数地址

时间:2014-07-22 11:51:56

标签: c

最近我有代码(在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中

4 个答案:

答案 0 :(得分:2)

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

  1. 将类型“指针指向A”转换为“指向B”的指针并返回“指向A的指针”将产生相同的原始指针。 (即如果p属于int *类型,那么(int *)(double *)p将产生p
  2. 将任何指针转换为char *将指向对象的最低可寻址字节。
  3. 因此,在您的情况下,显式的(unsigned char *)强制转换将产生一个符合程序而没有任何未定义的行为。

答案 1 :(得分:0)

C11,§6.5.2.2:

  

2每个参数的类型应使其值可以分配给具有相应参数类型的非限定版本的对象。

§6.5.16.1根据约束列表描述了赋值,包括

  

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

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

答案 2 :(得分:0)

虽然有些人会说"根据标准"它是未定义的行为,这是事实上发生的事情(通过一个例子回答):


<强>安全

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)的倍数。因此,除非编译器(以及底层硬件架构)支持未对齐的加载/存储操作,否则如果内存中此变量的地址确实未正确对齐,则在运行时将发生内存访问冲突。

答案 3 :(得分:0)

需要施法,参见C11(n1570)6.5.2.2第2页:

  

[...]每个参数都应具有一个类型,使其值可以分配给具有相应参数类型的非限定版本的对象。

这是指转让规则,相关部分是(同上6.5.16.1第1页)

  

以下其中一项应成立:

     

[...]

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

[...]

unsigned charint不兼容。

这些规则都出现在“约束”部分,其中“应”表示编译器必须提供“诊断消息”(参见C11 5.1.1.3)并可能停止编译(或者除此之外的所有内容)严格地说,是超出C标准的范围)。您的代码是违反约束的示例。

其他约束违规的例子是使用错误数量的参数调用(prototyped和non-variadic)函数,使用double上的按位运算符,或者在同一范围内重新声明具有不兼容类型的标识符,同上。 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 ++程序。

除了语言律师的态度之外,你的代码可能总是根本没有编译(这应该是足够的理由来进行演员表),或显示预期的行为。