返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >赌你会懵的C语言指针进阶数组场景解析
  • 410
分享到

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

2024-04-02 19:04:59 410人浏览 独家记忆
摘要

目录正片开始一维数组字符数组二维数组整点硬菜正片开始 细化指针这一部分内容,现在着重把一些指针的运用情景搬出来康康,如果对指针盘的不是非常熟练,或者指针还出于入门阶段的铁子请绕道(晕

正片开始

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

直接给大家盘个套餐:

一维数组

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语言指针进阶的资料请关注编程网其它相关文章!

--结束END--

本文标题: 赌你会懵的C语言指针进阶数组场景解析

本文链接: https://lsjlt.com/news/139083.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 赌你会懵的C语言指针进阶数组场景解析
    目录正片开始一维数组字符数组二维数组整点硬菜正片开始 细化指针这一部分内容,现在着重把一些指针的运用情景搬出来康康,如果对指针盘的不是非常熟练,或者指针还出于入门阶段的铁子请绕道(晕...
    99+
    2024-04-02
  • C语言 指针数组进阶详解
    目录指针与数组中的sizeof与strlensizeofstrlen数组名1、一维数组整型数组字符数组指针数组2、二维数组指针笔试题 笔试题1笔试题2笔试题3笔试题4笔试题...
    99+
    2024-04-02
  • 【C进阶】指针和数组笔试题解析
    做题之前我们先来回顾一下 对于数组名的理解:除了以下两种情况,数组名表示的都是数组首元素的地址 (1)sizeof(数组名):这里的数组名表示整个数组 (2)&(数组名) :这里的数组名也表示整个数组 一、一维数组 int a[]...
    99+
    2023-09-26
    c语言 算法 开发语言
  • 一篇文章带你了解C语言指针进阶
    目录1.字符指针2.指针数组3.数组指针4.函数指针5.数组传参总结1.字符指针 我们已经知道了数组名在大部分时候表示数组的地址,指针本质上也表示一个地址,那么我们能否用指针来创建一...
    99+
    2024-04-02
  • C语言进阶教程之函数指针详解
    目录一、函数指针1.概念1.2函数指针的使用方法1.3练习巩固1.4小结一下二、阅读两段有趣的代码1.( *(void( *)( ))0 )( )2.void (* signal(i...
    99+
    2024-04-02
  • c语言的指针数组详解
    指针如何指向数组,并读取数组中的元素: #include <stdio.h> int main() { int arr[3] = {1,2,3}; int *p;...
    99+
    2024-04-02
  • C语言的数组指针与函数指针详解
    目录前言函数指针语法数组指针与指针数组总结前言 数组指针和函数指针都是C语言比较难的知识点,尤其是函数指针,并且函数指针在开发中有着巨大的作用。 函数指针语法 定义一个函数指针,并通...
    99+
    2024-04-02
  • C语言深入分析数组指针和指针数组的应用
    目录一、数组类型二、定义数据类型三、数组指针四、指针数组五、小结一、数组类型 C语言中的数组有自己特定的类型数组的类型由元素类型和数组大小共同决定 例:int array[5] 的类...
    99+
    2024-04-02
  • c语言的指针数组如何理解
    本篇文章给大家分享的是有关c语言的指针数组如何理解,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。指针如何指向数组,并读取数组中的元素:#include <std...
    99+
    2023-06-22
  • C语言中指针和数组试题详解分析
    目录数组题:程序一(一维数组):字符数组程序二(字符数组):程序三(字符数组):程序四(字符数组):程序五(字符数组):二维数组程序六( 二维数组):指针题程序七( 指针):程序八(...
    99+
    2024-04-02
  • C语言详细讲解指针数组的用法
    目录1. 指针数组定义方法2. 指针的指针(二级指针)3. 字符串和指针4. 数组指针定义方法数组指针的用法1. 指针数组定义方法 格式: 类型说明符 *数组名[ 元素个数 ] in...
    99+
    2024-04-02
  • C语言的数组与指针可以这样了解
    目录前言一、数组的定义二、数组空间的初始化1. char数组赋值2.char数组硬件开发规范二、数组与指针总结前言 自学笔记,没有历史知识铺垫(省略百度部分)C语言数组的概念及使用 ...
    99+
    2024-04-02
  • C语言sizeof和strlen的指针和数组面试题详解
    目录一、概念         sizeof:strlen:二、例题及解析2.1 一维数组2.2 字符数...
    99+
    2024-04-02
  • 深入理解C语言中使用频率较高的指针与数组
    目录定义指针与二维数组指针数组与数组指针数组指针的应用操作总结定义 指针:C语言中某种数据类型的数据存储的内存地址,例如:指向各种整型的指针或者指向某个结构体的指针。 数组:若干个相...
    99+
    2024-04-02
  • C语言全方位讲解指针与地址和数组函数堆空间的关系
    目录一、一种特殊的变量-指针二、深入理解指针与地址三、指针与数组(上)四、指针与数组(下)五、指针与函数六、指针与堆空间七、指针专题经典问题剖析一、一种特殊的变量-指针 指针是C语言...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作