赌你会懵的C语言指针进阶数组场景解析

目录
  • 正片开始
  • 一维数组
  • 字符数组
  • 二维数组
  • 整点硬菜

正片开始

细化指针这一部分内容,现在着重把一些指针的运用情景搬出来康康,如果对指针盘的不是非常熟练,或者指针还出于入门阶段的铁子请绕道(晕头警告)

直接给大家盘个套餐:

一维数组

int a[] = {1,2,3,4,5};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a+0));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(a[1]));
printf("%d\n",sizeof(&a));
printf("%d\n",sizeof(*&a));
printf("%d\n",sizeof(&a+1));
printf("%d\n",sizeof(&a[0]));
printf("%d\n",sizeof(&a[0]+1));

问题很简单,这组 printf 的值是多少?小朋友你是否有些许害怕,但是没有关系我们逐个击破,这是后续内容的基础

记住你的答案,来看看编译器是怎么解析的:

首先我们应该知道数组名代表的是数组首元素的地址,但是要知道我们有两种例外:

1.sizeof (),()内是数组名时,代表的就是整个数组,计算的就是数组的大小,单位是字节;

2. & 数组名时,表示的也是整个数组,取出的就是整个数组地址;

除了以上两种情况,其余的所有数组名都表示首元素地址!

所以

1.sizeof(a)就是直接将数组名放进去,算出的就应该是 sizeof(int)*5 = 20

2.sizeof(a+0)此时不只有数组名,因此 a 代表首元素地址,a+0 等价于 a,是地址大小,在32位/64位平台下对应 4/8 字节大小

3.*a ,不只有数组名 a 代表首元素地址,解引用得到首元素,sizeof(a)= sizeof(int)= 4

4.a+1 老规矩还是首元素地址+1,就是第二个元素地址,是地址大小为 4/8 字节

5.a[ 1 ],大小为4

6.&a 为整个数组地址,但是地址终归是 4/8 字节

7. * &a&a 是类型为 int()[4] 的数组指针,解引用数组地址为整个数组,大小 20

8. &a +1取出整个数组地址,一个数组指针的单位就是整个数组,+1 就会跳过整个数组,大小还是为第二个数组地址,大小 4/8

9. &a[0],首元素地址,4/8

10. &a[0] + 1,第二个元素地址,4/8

字符数组

char a[] = {'a','b','c','d','e','f'};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a+0));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a[1]));
printf("%d\n",sizeof(&a));
printf("%d\n",sizeof(&a+1));
printf("%d\n",sizeof(&a[0]+1));

和上面同理,再来看看编译器怎么解答的:

1.a 是数组名,首元素地址计算整个数组大小为 6

2.a + 0,首元素地址 + 0 还是首元素地址,4/8

3.*a,首元素大小 ,1

4.1

5.&数组名为整个数组地址,4/8

6.跳过一个数组,下一个数组地址,4/8

7.第二个元素地址,4/8

这么简单?nonono,这才刚刚开始

char a[] = {'a','b','c','d','e','f'};
printf("%d\n",strlen(a));
printf("%d\n",strlen(a+0));
printf("%d\n",strlen(*a));
printf("%d\n",strlen(a[1]));
printf("%d\n",strlen(&a));
printf("%d\n",strlen(&a+1));
printf("%d\n",strlen(&a[0]+1));

我还是先把运行结果啪出来吧:

诶?怪诶,打印7下出来2个?nnd给我玩阴的是吧。这里首先强调一下,sizeof 是操作符,海纳百川来啥算啥,‘ \0 ’也会照收,而 strlen 是傲娇的库函数,傲娇在于strlen 针对的是 \0 之前的字符串长度(个数),而且不包含 ‘ \0 ’。

1.a 数组名,没有在 sizeof 内部,为首元素地址,我们知道 strlen 在遇到 \0 之前是不会停下来的,字符数组我们没有给到 \0就是没有,因此什么时候遇到他我们不知道,起码会比当前数组大,不知道嘛时候停就是个随机值

2.同理,随机值

3.首元素地址解引用为首元素 ‘a’,strlen 需要的是一个地址,此时会从把‘a’ 默认为一个地址,a的ASCII码值为 97,就会从内存中地址为 97 的地方开始往后数字符个数,但是注意 97 不属于我原本分配的内容,属于非法访问内存,直接报错崩溃垮掉

4.同理,报错

5.数组的地址,虽然和 strlen 参数类型有所差异,但还是从第一个位置向后数,那就是随机值啦

6.跳过一个数组的地址,依然是随机值

7.第二个元素地址,依然依然是随机值

那么就又双可以联想到字符串了,引入指针变量进行讨论:

char* p = "abcdef";
printf("%d\n",sizeof(p));
printf("%d\n",sizeof(p+1));
printf("%d\n",sizeof(*p));
printf("%d\n",sizeof(p[0]));
printf("%d\n",sizeof(&p));
printf("%d\n",sizeof(&p+1));
printf("%d\n",sizeof(&p[0]+1));

printf("%d\n",strlen(p));
printf("%d\n",strlen(p+1));
printf("%d\n",strlen(*p));
printf("%d\n",strlen(p[0]));
printf("%d\n",strlen(&p));
printf("%d\n",strlen(&p+1));
printf("%d\n",strlen(&p[0]+1));

直接看结果吧,这下就感觉出有点意义不明了,结果对应哪个数据都不知道了,所以这时候自主分析的价值就出来了

  • p是一个指针,sizeof 算指针的大小,4/8字节
  • p+1,就是 p 的值加1,字符指针p是一个地址,数值上+1是 b 的地址,所以大小 4/8字节
  • p 为char* 的指针,解引用访问首元素,大小为 1
  • 小问号你是否有很多朋友,指针为啥还可以 [0] ?其实 p[0] 等价于 *(p+0),p是首元素地址,p+0亦是,解引用出来就是首元素,1
  • p是地址,&p是对地址取地址,也就是二级指针,是地址那就是 4/8
  • 跳过一个char*的地址即跳过了一个p的地址,本质上还是地址,4/8
  • p[0]为首元素,取地址+1 为第二个元素的地址,4/8

1.在 “abcdef”中是隐含了一个‘ \0 ’的,所以传入p算出大小为 6

2.同理,从第二个元素开始,5

3.解引用为a,97,报错

4,等价于*p,就是首元素,报错

5,取数组地址的地址往后数字符串,为随机值

6.+1跳过一个地址的地址往后数,依然是随机值

7.第二个元素的地址往后数,随机值,等价于 p+1

二维数组

int a[3][4] = {0};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a[0][0]));
printf("%d\n",sizeof(a[0]));
printf("%d\n",sizeof(a[0]+1));
printf("%d\n",sizeof(*a[0]+1));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(*(a+1)));
printf("%d\n",sizeof(&a[0]+1));
printf("%d\n",sizeof(*(&a[0]+1)));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a[3]));

看一下运行结果:

1.a数组名,为整个数组,124 = 48

2.数组首元素,14 = 4

3.数组第一行元素,44 = 16

4.a[0]此时为首元素地址,即a[0]|0] 地址,+1为 a[0]|1] 地址,4/8

5.等价于a[0] [1],,14 = 4

6.数组名不是单独存放为第一行地址,+1表示第二行地址,4/8

7.解引用为第二行元素,44 = 16

8.a[0]为第一行地址,&a[0]+1为第二行地址,4/8

9.解引用为第二行元素,44 = 16

10.解引用首元素地址为第一行,44 = 16

11.淦!好怪!数组只有3行却搞出个 a[3],这不越界了吗?其实编译器会很聪明的自行推算,按照已给数组排列就是 44 = 16

整点硬菜

热身完了,来整点题目,让你混乱的大脑脉动回来(雪上加霜)

1.

int main()
{
int a[6] = {1,2,3,4,5,6};
int *ptr = (*int)(&a+1);
printf("%d %d",*(a + 1),*(ptr - 1));
return 0;
}

ptr 是int类型指针,&a是数组地址,+1跳过一个数组,指向数组末位的地址,强转为 int* 类型,*(a+1)首元素地址+1再解引用得到第二个元素 ,ptr - 1指向 5 的地址,所以答案为 2,5。

2.

//假设stu 大小为 20 字节
struct stu
{
char* name;
int age;
float score;
} *p;
int main()
{
(struct  stu*)p =0x100000;
printf("%p\n",p + 0x1);
printf("%p\n",(unsigned long)p + 0x1);
printf("%p\n",(unsigned int*)p + 0x1);
return 0;
}

p是一个指针类型指向结构体,代表结构体的地址, p + 0x1 就是地址数值上进行 + 0x1 操作,p = 0x100000 ,我们知道 int* +1 跳过4个字节,char* + 1跳过1 个字节,我们这里是结构体类型,就跳过 20 个字节,20 化成16进制相加就是 0x100014

p 强转为 unsigned long 其实就是整型,p 就是一个纯纯的数字了,那0x100000 既然已经是“数字”了,那就有了可以直接加减的属性,就是 0x100001

p强转成无符号整型指针类型, 一个指针类型是 4 个字节,结果就是 0x100004

到这里是不是有内味儿了,那咱继续

3.

int main()
{
int a[4] = {1,2,3,4};
int* ptr = (int*)(&a+1);
int* ptr2 = (int*)((int)a+1);
printf("%x %x",ptr[-1],*ptr2);
return 0;
}

此题请仔细思考,大坑警告!

ptr 为整型指针,&a为整个数组地址,+1跳过整个数组来到末位的地址,再强转成 int * 类型(这里大可不必,因为它本来就是一个 int * 指针)ptr[-1] 等价于 * (ptr - 1),再代入 ptr 就代表最末地址 -1 来到 4 的地址,ptr[-1 ]就是 4。

ptr2中 a为首元地址,这里注意优先级问题,先强转成 int 就是个纯纯的数字可以直接进行 +1 操作,即地址进行了数值+1,格局高不高就要看下一步,接下来需要考虑 大小端问题,因为我们是小端存储,按照低位字节在低地址处,所以 1,2,3,4 在内存中是这样的:

…… | 01 | 00 | 00 | 00 | 02 | 00 | 00 | 00 | 03 | 00 | ……

假设 01 地址是 0x01,int 强转后 a +1 偏移一个字节,指向的就是 01 后面一个 00 的位置,所以 ptr2 就指向这个位置,我们解引用拿到他的值就是整型大小的值就是 00 00 00 02,再以小端的形式拿出来就是 20 00 00 00。

4.

int main()
{
int a[3][2] = {(0,1),(2,3),(4,5)};
int* p;
p = a[0];
printf("%d",p[0]);
return 0;
}

p[0] 等价于*(p+0),p = a[0], 就是*a[0] ,a[0] 是首元素地址,那么问题来了:是 0 的地址吗?

请仔细看看我们二维数组的逗号表达式,其实整个数组就只有 3 个元素:1,3 5;他们在数组中排列出来是:

1 | 3
5 | 0
0 | 0

所以解引用出来就是 1。

今天就到这儿吧,摸了家人们

以上就是赌你会懵的C语言指针进阶的详细内容,更多关于C语言指针进阶的资料请关注我们其它相关文章!

(0)

相关推荐

  • 深入浅出理解C语言指针的综合应用

    目录 指针是什么? 指针变量 使用指针变量的例子 通过指针引用数组 &数组名vs数组名 野指针 野指针成因 1.指针未初始化 2.指针越界访问 如何避免野指针 指针运算 指针是什么? 指针是c语言中的一个重要概念,也是C语言的一个重要的特色,正确而灵活地运用它,可以使程序简洁,紧凑,高效,每一个学习和使用c语言的人,都应当深入了解地学习和掌握指针,可以说,不掌握指针就是没有掌握C的精华也可以说 指针是C语言的灵魂(doge) 由于通过地址能找到所需的变量单元,可以说,地址指向变量单元,打个比方,

  • C语言双指针多方法旋转数组解题LeetCode

    目录 暴力思路 外加数组 格局抬高 环形替代 LeetCode题目如下: 首先这个中等难度我是没搞懂,后面才发现原来中等中在要求多方法上,那就来看看怎么搞定他吧. 暴力思路 首先我说一下我本人的思路,就是函数进行倒序操作,分三步: 1.整体倒序 :1234567-------7654321 2.前半部分倒序:7654321------- 5674321 3.后半部分倒序:5674321-------5671234 由于题目已经给出了我们 k 的值,我们直接暴力思路(注意是暴力思路非暴力求解),双

  • C语言修炼之路初识指针阴阳窍 地址还归大道真上篇

    目录 (壹) 敢问指针为何物 1.1 指针的概念 1.2 指针的背后 (贰) 指针和指针类 2.1 指针的类型 2.2 指针类型的意义 2.3 指针的解引用 (壹)  敢问指针为何物 1.1  指针的概念 在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值.由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元.因此,将地址形象化的称为“指针”.意思是通过它能找到以它为地址的内存单元. 那我们就可以这样

  • 深入了解C语言中的const和指针

    目录 前言 指针的赋值 问题 ANSI C 有关简单赋值的标准 问题解决 const修饰 const修饰变量 const修饰指针 前言 文章内容由阅读<C专家编程>整理而来.希望可以帮助大家解决在指针赋值和const方面的问题,也希望大家多多指正文章中的错误,共同进步. 指针的赋值 问题 将一个类型为 char** 的值赋值给一个 const char** 类型的对象是否合法呢? 先说结果,在vs的环境下,编译器不会报错也不会有任何警告. 但在linux环境下用gcc编译就会出现下面的警告:

  • C语言中const和指针的秘密你知道吗

    目录 指针的赋值 问题 ANSI C 有关简单赋值的标准 问题解决 char* 和 const char* char** 和 const char** const修饰 const修饰变量 const修饰指针 const int* p int* const p const int* const p 总结 指针的赋值 问题 将一个类型为 char** 的值赋值给一个 const char** 类型的对象是否合法呢? 先说结果,在vs的环境下,编译器不会报错也不会有任何警告. 但在linux环境下用g

  • C语言指针教程示例详解

    目录 指针 内存 指针类型 指针运算 二级指针 指针数组 指针 指针提供了对地址操作的一种方法,因此,使用指针可使得 C 语言能够更高效地实现对计算机底层硬件的操作.另外,通过指针可以更便捷地操作数组.在一定意义上可以说,指针是 C 语言的精髓. 概念解释就不去搬原定义了,又臭又长不好理解,精炼两点就是: 1.指针是内存中的一个最小单元的编号,也就是地址: 2.平时我们说的指针,通常是指指针变量,用来存储内存地址的变量 也就是说:指针就是地址,口语中指针通常是指针变量 内存 要搞明白指针首先要搞

  • C语言双指针算法朋友过情人节我过算法

    目录 双指针 对撞指针 快慢指针 真题实战 双指针 首先咱得知道何为双指针,听起来很上流,其实有手就行. 双指针,指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个相同方向(快慢指针)或者相反方向(对撞指针)的指针进行扫描,从而达到相应的目的. 换言之,双指针法充分使用了数组有序这一特征,当遇到有序数组时,应该优先想到双指针来解决问题,因两个指针的同时遍历会减少空间复杂度和时间复杂度从而在某些情况下能够简化运算 对撞指针 类似于相遇问题,对撞指针是指在有序数组中,将指向最左侧

  • 老生常谈C语言中指针的使用

    目录 前提 一.指针基础 1.1变量指针 1.2数据指针 1.3指针的本质 1.4指针数组 1.5指针的移动 1.5Scanf函数的解释 二.指针的进阶玩法 2.1二维指针 2.2结构体指针 结语 前提 指针,是C语言中的一个重要概念及其特点,也是掌握C语言比较困难的部分.指针也就是内存地址,指针变量是用来存放内存地址的变量,在同一CPU构架下,不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同.有了指针以后,不仅可以对数据本身,也可以对

  • C语言修炼之路初识指针阴阳窍 地址还归大道真下篇

    目录 (壹) 行经旅途遇猛虎--“野指针” 1.1野指针成因 1.指针未初始化 2.指针越界访问 3. 指针指向的空间释放 1.2 巧法规避野指针 (贰) 指针之运算 2.1 指针+-整数 2.2 指针-指针 课堂小补充 (利用指针-指针实现strlen) (叁) 指针和数组 3.1 数组名 3.2 二级指针 3.3 指针数组 (壹)  行经旅途遇猛虎 -- “野指针” 概念:野指针就是指针指向的位置是不可知的(随机的.不正确的.没有明确限制的)指针变量在定义时如果未初始化,其值是随机的,指针变

  • 赌你会懵的C语言指针进阶数组场景解析

    目录 正片开始 一维数组 字符数组 二维数组 整点硬菜 正片开始 细化指针这一部分内容,现在着重把一些指针的运用情景搬出来康康,如果对指针盘的不是非常熟练,或者指针还出于入门阶段的铁子请绕道(晕头警告) 直接给大家盘个套餐: 一维数组 int a[] = {1,2,3,4,5}; printf("%d\n",sizeof(a)); printf("%d\n",sizeof(a+0)); printf("%d\n",sizeof(*a)); pri

  • C语言 指针与数组的详解及区别

    C语言 指针与数组的详解及对比 通俗理解数组指针和指针数组 数组指针: eg:int( *arr)[10]; 数组指针通俗理解就是这个数组作为指针,指向某一个变量. 指针数组: eg:int*arr[10]; 指针数组简言之就是存放指针的数组: --数组并非指针&&指针并非数组 (1)定义一个外部变量: eg:int value=10; int *p=&value; 举例:当需要在一个函数中用这个变量时:externa int*p;而非extern int p[]; 分析:当用:e

  • C语言指针引用数组案例讲解

    前言:C语言中指针玩的是什么,是内存,要想学好指针的小伙伴们要先对数据在内存中是怎么玩的做一番了解~       当在程序中定义一个变量时,系统会根据其数据类型为其开辟内存空间,例如Visual C++为整型变量分配四个字节的空间,为单精度浮点型变量分配四个字节,为字符型变量分配一个字节,内存中每个字节都有自己独立且唯一的一个编号,这就是地址 ,如下图,系统为变量i分配了2000~2004的存储单元. _访问变量的方式_有如下图两种: 第一种直接访问方式,直接通过变量名访问,变量名与地址有一一对

  • C语言指针和数组深入探究使用方法

    目录 1.数组参数和指针参数 1.1 一维数组传参 1.2 一级指针传参 1.3 二维数组参数和二级指针参数 1.4 野指针的问题 2.函数指针 3.函数指针数组 4.指向函数数组的指针 5.回调函数 6.一道笔试题 1.数组参数和指针参数 1.1 一维数组传参 这里在前几期我们已经初略的见识过了,但是这里我们要提一个概念,数组给函数传参是会发生降维的,降维成什么呢?我们看代码: 这里通过打印形参的大小,发现是 4,其实也不奇怪,目前我们是 32 位操作环境,所以一个指针也就是 4 个字节,所以

  • 零基础详解C语言指针进阶

    目录 前言 1.字符指针 例题 1 2.指针数组 例题 2 3.数组指针 3.1数组指针的定义 3.2 &数组名与数组名 3.3 数组指针的使用 4.数组与指针在函数里的传参 4.1 一维数组的传参 4.2 二维数组的传参 4.3 一级指针的传参 4.4 二级指针的传参 5.函数指针 6. 函数指针数组 用函数指针数组实现一个计算器 7.回调函数 前言 这是指针的进阶,如果想入门指针的朋友可以关注我的另外一篇文章—c语言 指针零基础讲解 坚持看完,一定会有很大的收获~~ 那接下来—起航 1.字符

  • 一篇文章带你了解C语言指针进阶

    目录 1.字符指针 2.指针数组 3.数组指针 4.函数指针 5.数组传参 总结 1.字符指针 我们已经知道了数组名在大部分时候表示数组的地址,指针本质上也表示一个地址,那么我们能否用指针来创建一个字符串呢? int main() { char arr1[] = "abcdef"; char arr2[] = "abcdef"; const char* p1 = "abcdef"; const char* p2 = "abcdef&qu

  • C语言中指针和数组试题详解分析

    目录 数组题: 程序一(一维数组): 字符数组 程序二(字符数组): 程序三(字符数组): 程序四(字符数组): 程序五(字符数组): 二维数组 程序六( 二维数组): 指针题 程序七( 指针): 程序八( 指针): 程序九( 指针): 程序十( 指针): 程序十( 图): 程序十一( 指针): 程序十二( 指针): 程序十三( 指针): 指针 和 数组 试题解析 小编,在这里想说一下,c语言的最后一节 C预处理,可能还需要一些时间,因为小编,昨天才下载了虚拟机 和 linux 系统,还没开始安

  • 深入理解C语言中使用频率较高的指针与数组

    目录 定义 指针与二维数组 指针数组与数组指针 数组指针的应用 操作 总结 定义 指针:C语言中某种数据类型的数据存储的内存地址,例如:指向各种整型的指针或者指向某个结构体的指针. 数组:若干个相同C语言数据类型的元素在连续内存中储存的一种形态. 数组在编译时就已经被确定下来,而指针直到运行时才能被真正的确定到底指向何方.所以数组的这些身份(内存)一旦确定下来就不能轻易的改变了,它们(内存)会伴随数组一生. 而指针则有很多的选择,在其一生他可以选择不同的生活方式,比如一个字符指针可以指向单个字符

  • C语言零基础讲解指针和数组

    目录 一.指针和数组分析-上 1.数组的本质 2.指针的运算 3.指针的比较 4.小结 二.指针与数组分析-下 1.数组的访问方式 2.下标形式 VS 指针形式 3.a 和 &a 的区别 4.数组参数 5.小结 一.指针和数组分析-上 1.数组的本质 数组是一段连续的内存空间 数组的空间大小为 sizeof(array_type) * array_size 数组名可看做指向数组第一个元素的常量指针 下面看一段代码: #include <stdio.h> int main() { int

  • C语言sizeof和strlen的指针和数组面试题详解

    目录 一.概念 sizeof: strlen: 二.例题及解析 2.1 一维数组 2.2 字符数组 2.3 二维数组 三.总结 一.概念 sizeof: sizeof操作符的结果类型为size_t,(它在头文件用typedfe定义为unsigned int类型),计算的是分配空间的实际字节数.sizeof是运算符,可以以类型.函数.做参数 . strlen: strlen结果类型也为size_t(size_t strlen( const char *string )),但strlen是计算的空间

随机推荐