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

Ana*_*oly 3 c gcc pointers 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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

Eri*_*hil 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 *,我们知道结果指针是四的倍数,因为它以8的倍数开始(假设上面提到的要求).这是一个安全的转换.当您将int *a 转换为a时double *,它可能具有错误的对齐方式.特别地,给定阵列aint,我们知道,无论是a[0]a[1]必须为一个被不正确地对齐double,这是因为如果它们中的一个是在8个字节的倍数,另外一个必须从八的倍数是关闭的四个字节.因此,从转换int *double *在这个代码不被C标准定义.

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

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

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