【问题标题】:Pointer arithmetic around cast围绕演员表的指针算术
【发布时间】:2015-04-18 11:57:15
【问题描述】:

我目前正在参加 CS107 课程,该课程做出以下假设:

  • sizeof(int) == 4
  • sizeof(short) == 2
  • sizeof(char) == 1
  • 大字节序

我的教授显示了以下代码:

int arr[5];
((short*)(((char*) (&arr[1])) + 8))[3] = 100;

这是代表arr的20个字节:

|....|....|....|....|....|

我的教授说&arr[1] 点在这里,我同意。

|....|....|....|....|....|
     x

我现在明白(char*) 将指针设为 char(1 字节)的宽度,而不是 int(4 字节)的宽度。

我不明白的是+ 8,我的教授在这里指出:

|....|....|....|....|....|
                         x

但它不应该指向这里吗,因为它的大小是 char(1 字节)的 8 倍?

|....|....|....|....|....|
               x

【问题讨论】:

  • 你是对的。编写一些代码来打印指针的值,以向您的教授表明您是对的。
  • 这一切都取决于sizeof(int),不一定是4
  • @BlagovestBuyukliev 我的教授说在上课期间假设sizeof(int) 是4。抱歉,我应该提到这一点。
  • 阿列克谢 - 1,教授 - 0。
  • 有点相关:这是严格别名违规的经典示例,因此这段代码无论如何都没有任何明确定义的语义。

标签: c casting pointer-arithmetic


【解决方案1】:

让我们一步一步来。你的表达式可以这样分解:

((short*)(((char*) (&arr[1])) + 8))[3]
-----------------------------------------------------
char *base = (char *) &arr[1];
char *base_plus_offset = base + 8;
short *cast_into_short = (short *) base_plus_offset;
cast_into_short[3] = 100;

base_plus_offset 指向数组中的字节位置12cast_into_short[3] 指的是位置 12 + sizeof(short) * 3short 值,在您的情况下是 18

【讨论】:

  • 这就是为什么这样的代码不应该写在一行上。这里很清楚发生了什么。在问题的代码中,我假设教授用太多的括号混淆了自己。
  • @gnasher729:或者至少避免像(char *)(&arr[1]) 这样完全无用的括号(这似乎是为了让人们不知道演员可以采用一元表达式,如果不允许的话,无论如何,如果没有括号就不会编译;这个“澄清”的疑问甚至不能用优先级来表达)。虽然我同意在这种情况下可能应该拆分它。
  • @mafso:第二对括号也没有用,因此该表达式的非货物崇拜版本将是:((short *) ((char *) &arr[1] + 8))[3]
【解决方案2】:

这里有一些代码可以显示系统上哪个字节被修改,以及正在发生的事情的细分:

#include <stdio.h>

int main( int argc, char* argv[] )
{
    int arr[5];
    int i;

    for( i = 0; i < 5; i++ )
        arr[i] = 0;

    printf( "Before: " );

    for( i = 0; i < sizeof(int)*5; i++ )
        printf( "%2.2X ", ((char*)arr)[i] );

    printf( "\n" );

    ((short*)(((char*) (&arr[1])) + 8))[3] = 100;

    printf( "After: " );

    for( i = 0; i < sizeof(int)*5; i++ )
        printf( "%2.2X ", ((char*)arr)[i] );
    printf( "\n" );

    return 0;
}

从最里面开始:

指向 (arr + 4) 的 int 指针

&arr[1]
|...|...|...|...|...
    Xxxx

指向 (arr + 4) 的字符指针

(char*)(&arr[1])
|...|...|...|...|...
    X

指向 (arr + 4 + 8) 的字符指针

((char*)(&arr[1])) + 8)
|...|...|...|...|...
            X

指向 (arr + 4 + 8) 的短指针

(short*)((char*)(&arr[1])) + 8)
|...|...|...|...|...
            Xx

short at (arr + 4 + 8 + (3 * 2))(这是一个数组索引)

((short*)((char*)(&arr[1])) + 8))[3]
|...|...|...|...|...
                  Xx

此处修改的确切字节取决于系统的字节序。在我的 little endian x86 上,我得到以下输出:

Before: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
After:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 64 00

祝你的课程好运。

【讨论】:

  • 您对表达式的分析是正确的,但是当您说他不对时,您似乎误解了OP的主张。他的(迄今为止的一个)编辑没有区别,因为添加到 short * 的演员表不是他正在询问的子表达式的一部分。
  • @JohnBollinger 谢谢,我已经更正了该声明!还要添加指针位置和内存使用情况。
【解决方案3】:

表达式会将arr开始后18个字节的两个字节设置为值100。

#include <stdio.h>

int main() {

    int arr[5];

    char* start=(char*)&arr;
    char* end=(char*)&((short*)(((char*) (&arr[1])) + 8))[3];

    printf("sizeof(int)=%zu\n",sizeof(int));
    printf("sizeof(short)=%zu\n",sizeof(short));
    printf("offset=%td <- THIS IS THE ANSWER\n",(end-start));
    printf("100=%04x (hex)\n",100);

    for(size_t i=0;i<5;++i){

       printf("arr[%zu]=%d (%08x hex)\n",i,arr[i],arr[i]);

    }

}

可能的输出:

sizeof(int)=4
sizeof(short)=2
offset=18 <- THIS IS THE ANSWER
100=0064 (hex)
arr[0]=0 (00000000 hex)
arr[1]=0 (00000000 hex)
arr[2]=0 (00000000 hex)
arr[3]=0 (00000000 hex)
arr[4]=6553600 (00640000 hex)

在你所有的教授恶作剧中,他将你转移了 1 个整数、8 个字符/字节和 3 个 4+8+6=18 字节的短裤。宾果游戏。

注意这个输出显示我运行它的机器有 4 字节整数,2 字节短(普通)和小端,因为数组的最后两个字节分别设置为 0x64 和 0x00。

我发现您的图表非常混乱,因为您的意思不是很清楚“|”是否是地址。

|....|....|....|....|
012345678901234567890
    ^     1 ^     ^ 2
A   X       C     S B

包括竖线 ('|') A 是 Arr 的开头,B 是“一个过去的结尾”(C 中的一个法律概念)。

X 是表达式 &Arr[1] 引用的地址。 C 通过表达式 (((char*) (&arr[1])) + 8)。 S由整个表达式。 S 和后面的字节被分配给它,这意味着什么取决于你平台的字节序。

我把它作为一个练习来确定在类似但大端的平台上输出什么。任何人? 我从 cmets 注意到你是大端的,而我是小端的(停止窃笑)。 您只需要更改输出的一行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-05
    • 2023-04-04
    相关资源
    最近更新 更多