推广 热搜: csgo  vue  angelababy  2023  gps  新车  htc  落地  app  p2p 

C语言数组与指针

   2023-07-07 网络整理佚名2080
核心提示:指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身决定。它是“储存指针的数组”的简称。数组指针:首先它是一个指针,它指向一个数组。它是“指向数组的指针”的简称。下面到底哪个是数组指针,哪个是指针数组呢:类型数据的指针,即指针数组。类型数据的数组,即数组指针。p2-----也许应该这么定义数组指针是指针变量。都是数组指针,指向的是整个数组。

1、指针数组和数组指针的内存布局

初学者并不总是能够区分指针数组和指针数组之间的区别。 其实很容易理解:

指针数组:首先它是一个数组,数组的元素都是指针。 数组占用多少字节由数组本身决定。 它是“指针数组”的缩写。

数组指针:首先它是一个指针,指向一个数组。 在32位系统下,始终会占用4个字节。 至于它指向的数组占用多少字节,我不知道。 它是“指向数组的指针”的缩写。

下面哪个是数组指针,哪个是指针数组:

A)

int *p1[10];

二)

int (*p2)[10];

每次我在课堂上问这个问题时,我总是感到困惑。 这里需要了解符号之间的优先级问题。

“[]”的优先级高于“*”。 p1首先与“[]”组合形成数组定义,数组名为p1,int *修改数组的内容,即数组的每个元素。 现在我们清楚了,这是一个数组,里面包含了10个int类型数据的指针,也就是一个指针数组。 至于p2,就比较容易理解了,这里“()”的优先级比“[]”高,“*”和p2构成了一个指针定义,指针变量名为p2,int修改的是p2的内容大批。 即数组的每个元素。 这里的数组没有名字,它是一个匿名数组。 现在我们知道p2是一个指针,它指向一个包含10个int类型数据的数组,即数组指针。 我们可以借助下图来加深理解:

二、int (*)[10] p2-----也许数组指针应该这样定义

这里有一个有趣的话题值得讨论:通常我们定义指针的时候,不是总是在数据类型后面加上指针变量名吗? 为什么指针p2的定义不按照这个语法定义呢? 也许我们应该这样定义 p2:

int (*)[10] p2;

int(*)[10]是指针类型,p2是指针变量。 这看起来确实不错,但就是看起来有点尴尬。 事实上,数组指针的原型确实是这样的,只是为了方便和美观,将指针变量p2前移了一些。 这个你可以私下理解。 虽然编译器不这么认为。 ^_^

3.再讨论一下a和&a的区别

在这种情况下,问题就出现了。 前面我们讲了a和&a的区别,现在我们看下面的代码:

int main()

{

char a[5]={'A','B','C','D'};

char (*p3)[5] = &a;

char (*p4)[5] = a;

0;

}

上述 p3 和 p4 的用法哪一个是正确的? p3+1 的值是多少? p4+1 的值是多少? 毫无疑问,p3和p4都是数组指针,指向整个数组。 &a是整个数组的首地址,a是数组第一个元素的首地址,它们的值相同但含义不同。 在C语言中,赋值符号“=”两边的数据类型必须相同,如果不同,则需要进行显式或隐式类型转换。 p3的定义中“=”号两边的数据类型完全一致,但是p4的定义中“=”号两边的数据类型不一致。 左边的类型是指向整个数组的指针,右边的数据类型是指向单个字符的指针。 在 C++6.0 上给出以下警告:

C4047:“”:“char *”中的“char (*)[5]”。

幸运的是,这里虽然给出了警告,但由于 &a 与 a 的值相同,而且当变量为右值时,编译器只是取变量的值,所以运行起来没有问题。 我仍然警告你不要使用这个。

既然已经明确了p3和p4都指向整个数组,那么p3+1和p4+1的值就很容易理解了。

但如果修改代码,会发生什么呢? p3+1和p4+1的值是多少?

int main()

{

char a[5]={'A','B','C','D'};

char (*p3)[3] = &a;

char (*p4)[3] = a;

0;

}

您甚至可以再次修改代码:

int main()

{

char a[5]={'A','B','C','D'};

字符 (*p3)[10] = &a;

字符 (*p4)[10] = a;

0;

}

这个时候会出现什么样的问题呢? p3+1和p4+1的值是多少?

希望读者认真思考上述问题。

4、地址强制转换

考虑以下示例:

测试

{

整数数;

字符*;

短日期;

查查[2];

短 sBa[4];

}*p;

假设 p 的值为 。 下表中的表达式的值是多少?

p + 0x1 = 0x___ ?

(长)p + 0x1 = 0x___?

(int*)p + 0x1 = 0x___?

相信很多人一开始会不明白这个问题意味着什么。 其实仔细一看,这个知识点似曾相识。 如何分析指针变量和整数的加减?

还记得我们前面的表达式“a+1”和“&a+1”之间的区别吗? 其实这里也是一样的。 指针变量与整数的加减并不是直接将整数与指针变量中的地址相加或减。 这个整数的单位不是字节而是元素的数量。 所以:p + 0x1 的值为+sizof(Test)*0x1。 至于这个结构体的大小,在前面的章节中已经详细解释过。 所以 p +0x1 的值为: 。

(long)p + 0x1 的值怎么样? 这就涉及到强制转换,将指针变量p保存的值强制转换为无符号长整型。 一旦任何值被强制,它的类型就会改变。 所以这个表达式实际上是一个 long 加另一个整数。 所以它的值为: 。

(int*)p + 0x1 的值怎么样? 这里 p 被转换为指向无符号整数的指针。 所以它的值为:+sizof(int)*0x1,等于。

上面的问题看起来没有什么技术含量,那么这里就来一个技术性的问题:在x86系统下,它的价值是什么?

()

{

int a[4]={1,2,3,4};

int *ptr1=(int *)(&a+1);

int *ptr2=(int *)((int)a+1);

("%x,%x",ptr1[-1],*ptr2);

0;

}

这是我听课时一位学生问我的问题。 他在网上看到的,据说难倒了n个人。 看完问题我告诉他,这些人肯定不懂汇编。 对于一个懂汇编的人来说,这种题实在是小case。 我们来分析分析这个问题:

根据上面的解释,&a+1和a+1的区别已经很清楚了。

ptr1:将&a+1的值强制转换为int*类型,赋值给int*类型的变量ptr,ptr1必须指向数组a中的下一个int类型数据。 ptr1[-1]被解析为*(ptr1-1),即ptr1回退4个字节。 所以它的值为0x4。

ptr2:根据上面的解释,(int)a+1的值就是元素a[0]的第二个字节的地址。 然后将这个地址强制转换为int*类型的值并赋值给ptr2,也就是说*ptr2的值应该是从元素a[0]的第二个字节开始的连续4个字节的内容。

其内存布局如下:

那么问题来了,这4个连续的字节到底存储了什么? 也就是说元素a[0]和a[1]中的值是如何存储的。 这就涉及到系统的大小端模式。 如果你了解汇编,这根本不是问题。 由于你不知道当前系统处于什么模式,所以你必须找到一种方法来测试它。 大、小端模式和测试方法在第一章解释union关键字时已经详细讨论过,请参阅那里,这里不再赘述。 我们可以使用下面的函数来测试当前的系统模式。

整型 ( )

{

工会支票

{

整数我;

字符ch;

} C;

ci = 1;

(c.ch ==1);

}

如果当前系统处于big-模式,该函数返回0; 如果是-模式,该函数返回1。也就是说,如果该函数的返回值为1,则*ptr2的值为。 如果该函数的返回值为0,则*ptr2的值为0x100。

 
反对 0举报 0 收藏 0 打赏 0评论 0
 
更多>同类资讯
推荐图文
推荐资讯
点击排行
网站首页  |  关于我们  |  联系方式  |  使用协议  |  版权隐私  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报
Powered By DESTOON