指针,铸造和不同的编译器

时间:2014-02-14 17:04:21

标签: c pointers gcc casting

我现在正在学习ANSI C编程语言课程并尝试从讲师的幻灯片中运行此代码:

#include<stdio.h>

int main()
{
    int a[5] = {10, 20, 30, 40, 50};
    double *p;

    for (p = (double*)a; p<(double*)(a+5); ((int*)p)++)
    {
        printf("%d",*((int*)p));
    }

    return 0;
}

不幸的是它不起作用。在MacOS,XCode,Clang上我收到错误:"Assignment to cast is illegal, lvalue casts are not supported"并且在Ubuntu gcc上我收到下一个错误:"lvalue required as increment operand"

我怀疑问题是编译器,因为我们学习ANCI C并且它有自己的要求,可以暴力其他标准。

1 个答案:

答案 0 :(得分:5)

关于((int*)p)++

(int *) p的结果是一个值。这与左值不同。左值(可能)指定一个对象。例如,在int x = 3;之后,名称x指定我们定义的对象。我们通常可以在表达式中使用它,例如y = 2*x,然后将x用于其值。但我们也可以在分配中使用它,例如x = 5,然后x用于对象。

表达式(int *) p需要p并将其转换为指向int的指针。结果只是一个值。它不是左值,因此它不代表可以修改的对象。

++运算符修改对象。所以它只能应用于左值。由于(int *) p不是左值,因此++无法应用于它。

幻灯片中的代码,如您所示,是不正确的,我不希望它在任何C实现中工作。 (C确实允许实现进行许多扩展,但扩展C以允许此操作将是不寻常的。)

关于(double*)a(int*)p

C允许您将指向对象的指针转换为指向不同类型对象的指针。有关于此的各种规则。一个重要的一点是结果指针必须与它指向的类型具有正确的对齐方式。

对象具有各种对齐要求,这意味着它们必须放在内存中的特定地址。通常,char个对象可能有任何地址,int个对象必须是四个字节的倍数,double个对象必须是八个字节的倍数。这些要求从C实现到C实现各不相同。我将使用这些值进行说明。

当您将正确的double *转换为int *时,我们知道结果指针是4的倍数,因为它以8的倍数开始(假设上述要求)。这是一个安全的转换。将int *转换为double *时,可能会出现错误的对齐方式。特别是,给定a的数组int,我们知道a[0]a[1]必须与double不正确对齐,因为,如果其中之一它们是8个字节的倍数,另一个必须从8的倍数开始关闭4个字节。因此,此代码中int *double *的转换不是由C标准定义的。

它们可能适用于许多C实现,但您不应该依赖它们。

C规则还规定,当指向对象的指针转换回其原始类型时,结果等于原始指针。因此,如果遵循了对齐规则,示例代码中的往返转换就可以了:您可以将int *转换为double *并返回int *,前提是已对齐要求得到遵守。

如果double *已用于访问double,则会违反别名规则。通常,当访问一种类型的对象时,C不会定义行为,就好像它是另一种类型一样。有一些例外,特别是字符类型。但是,除了对齐问题之外,简单地来回转换指针而不使用它们来访问对象是可以的。