[C语言] &a+1在keil C51下的异常运行

[复制链接]
1589|16
 楼主| luoengineer 发表于 2020-8-13 09:53 | 显示全部楼层 |阅读模式

&a + 1 :取数组a的首地址,改地址的值加上sizeof(a)的值,即:&a + 5*sizeof(long),也就是下一个数组的首地址。所以下面的代码temp1=5,temp2=1,
        long  a[5] = {1,2,3,4,5};
        long  b[5] = {11,12,13,14,15};
        long  *ptr1 = ( long *)(&a + 1);
        long  *ptr2 = ( long *)(a + 1);
        long  temp1 = ptr1[-1];
        long  temp2 = ptr2[-1];


但是上面代码在keil 4 C51环境下编译运行,*ptr1和*ptr2的表现是一样的,指针都是移动指向a[1],最后temp1=1,temp2=1。

keil的这种编译方式不符合C语言的理解。
大家有何高见?



ayb_ice 发表于 2020-8-13 10:36 | 显示全部楼层
应该是你自己理解有问题吧,其它编译也应该是这样的

&a+1只能加一,不能指向下一单元,
你可以这样
pData = &a; pData ++;这时是指向下一单元
ayb_ice 发表于 2020-8-13 10:36 | 显示全部楼层
long  *ptr1 = ( long *)(&a【1】);
 楼主| luoengineer 发表于 2020-8-13 11:06 | 显示全部楼层
ayb_ice 发表于 2020-8-13 10:36
long  *ptr1 = ( long *)(&a【1】);

在cvi这样类似vc的开发环境下,&a+1就是指向下一个单元,见下图:

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
ayb_ice 发表于 2020-8-13 11:37 | 显示全部楼层
luoengineer 发表于 2020-8-13 11:06
在cvi这样类似vc的开发环境下,&a+1就是指向下一个单元,见下图:

keil不也是这样的吗,
那就是我的理解错了,不建议写这样的程序,按规范的方法写
 楼主| luoengineer 发表于 2020-8-13 17:13 | 显示全部楼层
2个不同的编译器造成了不同的结果,keil的工程师来解释一下就好了。
diweo 发表于 2020-8-14 10:43 | 显示全部楼层
C语言虽然有标准,但也还是有很多东西是未定义的。也就是说编译器厂商可以有一些自由发挥的余地。
这个没什么解释不解释的,你觉得合理就用,你觉得不合理就别用。
ayb_ice 发表于 2020-8-14 11:09 | 显示全部楼层
不应该一样吗
对于数组a和&a都是指地址

评论

我一直用MDK,没用过其他环境,感觉这样很正常呀。倒是指针下标ptr[-1]这个做法,确实没用过。一直都是ptr--;或者ptr-=1;这样使用的。  发表于 2020-8-15 23:25
oufuqiang 发表于 2020-8-14 13:56 | 显示全部楼层
好奇怪的写法,楼主打算出题搞倒学生吗?还是准备拿这种题面试别人
William1994 发表于 2020-8-15 17:20 | 显示全部楼层
luoengineer 发表于 2020-8-13 11:06
在cvi这样类似vc的开发环境下,&a+1就是指向下一个单元,见下图:

a就是&a,所有的编译器都是这样的。
cvi不应该有这种低级错误啊。
William1994 发表于 2020-8-15 17:33 | 显示全部楼层
c++ builder 认为 long a[4]={9,2,3,4}; 后,a的类型是长度为4*4=16字节的一个类型,这个类型加1就是指针加16个字节。




本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
john_lee 发表于 2020-8-15 22:51 | 显示全部楼层
keil的编译器bug。
xyz549040622 发表于 2020-8-15 23:28 | 显示全部楼层
john_lee 发表于 2020-8-15 22:51
keil的编译器bug。

李老师的意思是,mdk这么做反而是不符合c语言规范的吗?
xyz549040622 发表于 2020-8-15 23:30 | 显示全部楼层


&a + 1 :取数组a的首地址,改地址的值加上sizeof(a)的值,即:&a + 5*sizeof(long),也就是下一个数组的首地址。所以下面的代码temp1=5,temp2=1,
        long  a[5] = {1,2,3,4,5};
        long  b[5] = {11,12,13,14,15};
        long  *ptr1 = ( long *)(&a + 1);
        long  *ptr2 = ( long *)(a + 1);
        long  temp1 = ptr1[-1];
        long  temp2 = ptr2[-1];
楼主这里的        long  *ptr1 = ( long *)(&a + 1);
改为 long  *ptr1 = ( long *)(&a + 5*sizeof(long));应该结果就是5了吧。
john_lee 发表于 2020-8-16 01:29 | 显示全部楼层
C的变量,不论是“标量类型(scalar type)”还是“聚合类型(aggregate type)”,对其取地址得到的指针,都是该变量本身类型的指针,那么对该指针做算数加减运算,单位就是sizeof(变量类型)。

例如:
  1. // 1 ------
  2. char a;                 // char变量,标量类型
  3. char* p = &a + 2;       // 指针值为a的地址+偏移量sizeof(char) * 2
  4. // 2 ------
  5. struct a_t {
  6.   char x[2];
  7. };
  8. struct a_t a;           // struct变量,聚合类型
  9. struct a_t* p = &a + 1; // 指针值为a的地址+偏移量sizeof(struct a_t) * 2
  10. // 3 ------
  11. char a[4];    // 数组变量,聚合类型
  12. char (*p)[4] = &a + 2;  // 指针值为a的地址+偏移量sizeof(char[4]) * 2

上面所说的聚合类型不包括“未指定边界的数组(array with unspecified bounds)”,未指定边界的数组也可以取地址,由于数字大小未知,所以其指针不允许做加减运算,只能用作访问数组元素。
例如上面的例子3就是一个已指定数组边界的数组,如果紧接着写:
  1. p++;                    // 合法,指针值为a的地址+偏移量sizeof(char[4]) * 3

而:
  1. char a[4];
  2. char (*p)[] = &a + 2;   // 未指定数组边界的数组指针,指针值为a的地址+偏移量sizeof(char[4]) * 2
  3. (*p)[1] = 1;            // 合法,访问数组元素,赋值地址为a的地址+偏移量(sizeof(char[4]) * 2) + sizeof(char),注意:访问的地址已越界
  4. p++;                    // 非法
john_lee 发表于 2020-8-16 11:14 | 显示全部楼层
本帖最后由 john_lee 于 2020-8-16 11:18 编辑

C的变量,不论是“标量类型(scalar type)”还是“聚合类型(aggregate type)”,对其取地址得到的指针,都是该变量本身类型的指针,那么对该指针做算数加减运算,单位就是sizeof(变量类型)。

例如:
  1. // 1 ------
  2. char a;                 // char变量,标量类型
  3. char* p = &a + 2;       // 指针类型为char,值为a的地址+偏移量sizeof(char) * 2
  4. // 2 ------
  5. struct a_t {
  6.   char x[2];
  7. };
  8. struct a_t a;           // struct变量,聚合类型
  9. struct a_t* p = &a + 1; // 指针类型为a_t,值为a的地址+偏移量sizeof(struct a_t) * 2
  10. // 3 ------
  11. char a[4];              // 数组变量,聚合类型
  12. char (*p)[4] = &a + 2;  // 指针类型为char[4],值为a的地址+偏移量sizeof(char[4]) * 2
上面所说的聚合类型不包括“未指定边界的数组(array with unspecified bounds)”,未指定边界的数组也可以取地址,由于数字大小未知,所以其指针不允许做加减运算,只能用作访问数组元素。
例如上面的例子3就是一个已指定数组边界的数组,如果紧接着写:
  1. p++;                    // 合法,指针值为a的地址+偏移量sizeof(char[4]) * 3
而:
  1. char a[4];
  2. char (*p)[] = &a + 2;   // 指针类型为char[],是未指定数组边界的数组指针,值为a的地址+偏移量sizeof(char[4]) * 2
  3. (*p)[1] = 1;            // 合法,访问数组元素,赋值地址为a的地址+偏移量(sizeof(char[4]) * 2) + sizeof(char),注意:访问的地址已越界
  4. p++;                    // 非法
您需要登录后才可以回帖 登录 | 注册

本版积分规则

19

主题

80

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部