C语言从入门到实战————数组和指针的深入理解

前言

  • 在C语言中,数组和指针有的密切得联系,因为数组名本身就相当于一个指针常量。
  • 指针是一个变量,专门用来存储另一个变量的内存地址,通过这个地址可以访问和操作该变量的值,同时也包括数组。
  • 数组是一组连续存储的同类型数据的集合,它允许通过索引快速访问各个元素。同时数组名也是数组首元素的地址。

1.sizeof和strlen的对比

 1.1 sizeof

sizeof是C语言中的单目操作符,用于返回对象或类型所占的内存字节数。如果操作数是类型的化,则计算该变量类型的内存大小。

sizeof 只关注占⽤内存空间的大小,不在乎内存中存放什么数据。

比如:

int main()
{int a = 10;printf("%d\n", sizeof(a)); //sizeof根据类型计算大小,a为整型,大小为4printf("%d\n", sizeof a); //少了括号,结果还是4,目的是想说明sizeof不是函数,仅仅只是一个操作符printf("%d\n", sizeof(int)); //给的是整型,结果大小为4return 0;
}

1.2 strlen 

strlen 是C语⾔库函数,功能是求字符串⻓度。函数原型如下:

size_t strlen ( const char * str );

 统计的是从 strlen 函数的参数 str 中这个地址开始向后, \0 之前字符串中字符的个数。 strlen 函数会⼀直向后找 \0 字符,直到找到为⽌,所以可能存在越界查找。

比如:

int main()
{char arr1[3] = { 'a', 'b', 'c' };char arr2[] = "abc";printf("%d\n", strlen(arr1));//数组名不在sizeof后面,所以理解为首元素的地址,往后找'\0',所以为随机值printf("%d\n", strlen(arr2));//3printf("%d\n", sizeof(arr1));//arr1为数组名,所以在sizeof后面,计算的是所有元素的大小,大小3printf("%d\n", sizeof(arr2));//4return 0;
}


sizeofstrlen
1. sizeof是操作符1. strlen是库函数,使⽤需要包含头⽂件 string.h
2.sizeof计算操作数所占内存的 ⼤⼩,单位是字节2. srtlen是求字符串⻓度的,统计的是 \0 之前字符的隔个数
3.不关注内存中存放什么数据3. 关注内存中是否有 \0 ,如果没有 \0 ,就会持续往后找,可能会越界。

2.数组和指针笔试题解析 

 2.1 一维数组

int main()
{int a[] = { 1,2,3,4 };printf("%d\n", sizeof(a)); //a为数组名,在sizeof后面求的是整个数组的大小,16printf("%d\n", sizeof(a + 0));//a为数组名,但是不是单独出现在sizeof后面,所以是首元素的地址,sizeof求地址的大小,值为4/8printf("%d\n", sizeof(*a)); //a为首元素的地址,*a为1,1的类型为int,所以大小为4printf("%d\n", sizeof(a + 1));//a为首元素的地址,+1,就是往后位移一个int类型的大小,所以值为4printf("%d\n", sizeof(a[1]));//a[1] = *(a+1) = 2 , 所以大小为4printf("%d\n", sizeof(&a));//a为数组名,&a取出的是整个数组的地址,而sizeof求地址的大小,所以值为4/8printf("%d\n", sizeof(*&a));//*和&相互抵消,a为数组名,单独出现在sizeof后面,计算的是整个数组的大小,所以值为16printf("%d\n", sizeof(&a + 1));//&a取出是整个数组的地址,+1后跳过整个数组,本质上还是指针,所以值为4/8printf("%d\n", sizeof(&a[0]));//a[0] = *(a+0) = a = &a ,得到的是a的地址,大小为4/8printf("%d\n", sizeof(&a[0] + 1));//&a,指向a的地址,也就是首元素的地址,+1后得到第二个元素的地址,大小为4/8return 0;
}

 

2.2 字符数组 

代码1 

int main()
{char arr[] = { 'a','b','c','d','e','f' };printf("%d\n", sizeof(arr)); //arr为数组名,单独出现在sizeof后面,则计算整个数组元素的大小,值为6printf("%d\n", sizeof(arr + 0)); //arr为数组名,但并没有单独出现在sizeof后面,所以表示首元素的地址,所以值为4/8printf("%d\n", sizeof(*arr)); //arr为数组名,*arr = 'a',求大小,所以值为1printf("%d\n", sizeof(arr[1])); //arr[1] = *(arr + 1) = 'b' , 求大小,所以值为1printf("%d\n", sizeof(&arr)); //&arr取出的是整个数组的地址,本质上还是地址,sizeof求地址大小,所以值为4/8printf("%d\n", sizeof(&arr + 1));//&arr取出整个数组的地址,+1后跳出整个数组,本质上还是指针,求大小,值为4/8printf("%d\n", sizeof(&arr[0] + 1)); //arr[0] = *(arr + 1) = b = &b + 1 = c的地址,本质还是地址,求大小,值为4/8return 0;
}

代码2:

int main()
{char arr[] = { 'a','b','c','d','e','f' };printf("%d\n", strlen(arr)); //arr为首元素的地址,数据类型为字符数组,其中并没有'\0',所以为随机值printf("%d\n", strlen(arr + 0)); //arr为首元素的地址,+0,还是一样,值为随机值//printf("%d\n", strlen(*arr)); //arr为首元素的地址,*arr = 'a',a的askii码值为97,程序报错//printf("%d\n", strlen(arr[1])); //arr[1] = *(arr + 1) = 'b',程序报错printf("%d\n", strlen(&arr)); //&arr取出的是整个数组的地址,和第一个没区别,还是会往后找'\0',值为随机值printf("%d\n", strlen(&arr + 1));//&arr取出的是整个数组的地址,+1后跳出数组,值为随机值printf("%d\n", strlen(&arr[0] + 1)); //arr[0] = *(arr + 0) = &'a' + 1 的到'b'的地址,结果也为随机值,比第一个小1return 0;
}

代码3: 

int main()
{char arr[] = "abcdef";printf("%d\n", sizeof(arr)); //arr单独出现在sizeof后面,表示求的是整个数组的大小,包括'\0',7printf("%d\n", sizeof(arr + 0)); //arr并没有单独出现在sizeof后面,所以arr为首元素的地址,sizeof求地址大小,值为4/8printf("%d\n", sizeof(*arr)); //arr为首元素的地址,*arr = a ,a为char类型,所以值为1printf("%d\n", sizeof(arr[1])); //arr[1] = *(arr+1) = b , b为char类型,所以值为1printf("%d\n", sizeof(&arr)); //&arr,取出的是整个数组的地址,本质上还是地址,值为4/8printf("%d\n", sizeof(&arr + 1)); //&arr+1,跳出整个数组,本质上还是地址,值为4/8printf("%d\n", sizeof(&arr[0] + 1)); //arr[0] = *(arr+0) = &a+1 = b的地址,本质上还是地址,值为4/8 return 0;
}

代码4:

int main()
{char arr[] = "abcdef";printf("%d\n", strlen(arr)); //arr为首元素的地址,strlen就是往后找'\0',值为6printf("%d\n", strlen(arr + 0)); //arr为首元素地址,值为6//printf("%d\n", strlen(*arr)); //*arr = a ,a的ascii码值为97,程序崩溃//printf("%d\n", strlen(arr[1])); //arr[1] = *(arr + 1) = b , 程序崩溃printf("%d\n", strlen(&arr)); //&arr取出的是整个数组的地址,和第一个没区别,值为6printf("%d\n", strlen(&arr + 1)); //&arr+1,跳出数组范围,为随机值printf("%d\n", strlen(&arr[0] + 1)); //arr[0] = *(arr+0) = &a+1=b的地址,值为5return 0;
}

代码5:

int main()
{char* p = "abcdef";char arr[] = "abcdef";printf("%d\n", sizeof(p)); //为指针,4/8printf("%d\n", sizeof(p + 1)); //p并没有单独出现在sizeof后面,p为首元素的地址,+1后得到,b的地址,值为4/8printf("%d\n", sizeof(*p));  //p为首元素的地址,*p的到a,a为char类型,值为1printf("%d\n", sizeof(p[0]));  //p[0] = *(p+0) = a ,值为1printf("%d\n", sizeof(&p));  //&p取出的是指针的地址,本质上还是地址,值为4/8printf("%d\n", sizeof(&p + 1)); //&p+1,本质上还是地址,值为4/8printf("%d\n", sizeof(&p[0] + 1)); //p[0] = *(p+0) = &a+1 = b的地址,本质还是地址,值为4/8return 0;
}

 

代码6:

int main()
{char* p = "abcdef";printf("%d\n", strlen(p)); //p为指针,首元素的地址,值为6printf("%d\n", strlen(p + 1)); //p + 1指向b,则值为5//printf("%d\n", strlen(*p));  //*p = a ,ascii码值为97,程序崩溃//printf("%d\n", strlen(p[0])); //p[0] = *(p+0) = a,程序崩溃printf("%d\n", strlen(&p)); //p为指针,&p为指针的地址,所以为随机值printf("%d\n", strlen(&p + 1)); //本质还是地址,所以为随机值printf("%d\n", strlen(&p[0] + 1)); //p[0] = *(p+0) = &a + 1 = b的地址,所以值为5return 0;
}

2.3 二维数组 

int main()
{int a[3][4] = { 0 };printf("%d\n", sizeof(a));//a为数组名,单独出现在sizeof后面,表示求整个二维数组的大小,所以值为48printf("%d\n", sizeof(a[0][0])); //a[0][0] = 0 ,类型为int,所以值为4printf("%d\n", sizeof(a[0])); //a[0]为二维数组一行的数组名,单独出现在sizeof后面,求的是第一行数组的大小,16printf("%d\n", sizeof(a[0] + 1)); //a[0]为二维数组第一行的数组名,没有单独出现在sizeof后面,则表示首元素的地址,+1得到第二个元素的地址,4/8printf("%d\n", sizeof(*(a[0] + 1)));//4printf("%d\n", sizeof(a + 1)); //a为数组名,没有单独出现在sizeof后面,所以a为首元素的地址,+1后跳过整个二维数组,本质上还是地址,值为4/8printf("%d\n", sizeof(*(a + 1)));//a为数组名,但没有单独出现在sizeof之后,所以为首行的地址,+1后为第一行的地址,解引用得到第一行元素为16printf("%d\n", sizeof(&a[0] + 1)); //a[0] = *(a+0) = &0 + 1 = 第零行第一列的地址,值为4/8printf("%d\n", sizeof(*(&a[0] + 1)));//对第一行进行解引用,值为16printf("%d\n", sizeof(*a)); //a为数组名,并没有单独出现在sizeof后面,所以为首行的地址,解引用后得到第零行的元素,值为16printf("%d\n", sizeof(a[3]));//a[3] = *(a + 3) ,首行元素的地址,加三,得到第三行元素的地址,再解引用,得到第四行元素,值为16return 0;
}

 

数组名的意义:

1. sizeof(数组名),这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩
2.&数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址。
3.除此之外所有的数组名都表⽰⾸元素的地址。

3.指针运算笔试题解析

3.1 题目1 

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

 3.2 题目2

//在X86环境下
//假设结构体的⼤⼩是20个字节
//程序输出的结果是啥?
struct Test
{int Num;char* pcName;short sDate;char cha[2];short sBa[4];
}*p = (struct Test*)0x100000;
int main()
{printf("%p\n", p + 0x1); //0x100000 + 20 = 00100014 //此处环境为32位,每4位可以用一个十六进制表示,所以,总共是有8个十六进制,所以结果在前面补了两个0printf("%p\n", (unsigned long)p + 0x1); //0x100000 + 1 = 00100001printf("%p\n", (unsigned int*)p + 0x1); //0x100000 + 4 = 00100004return 0;
}

 3.3 题目3

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

 

3.4 题目4

int main()
{int a[5][5];int(*p)[4];p = a;printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);return 0;
}

 

 3.5 题目5

 

int main()
{int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };int* ptr1 = (int*)(&aa + 1);int* ptr2 = (int*)(*(aa + 1));printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));// 10 5return 0;
}

 

3.6 题目6

int main()
{char* a[] = { "work","at","alibaba" };char** pa = a;pa++;printf("%s\n", *pa);return 0;
}

 

3.7 题目7 

int main()
{char* c[] = { "ENTER","NEW","POINT","FIRST" };char** cp[] = { c + 3,c + 2,c + 1,c };char*** cpp = cp;printf("%s\n", **++cpp);printf("%s\n", *-- * ++cpp + 3);printf("%s\n", *cpp[-2] + 3);printf("%s\n", cpp[-1][-1] + 1);return 0;
}

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/746395.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

什么是云?公共云,私有云,混合云分别是什么?云概念的提出 白话总结

一、什么是云 “云” 通常是指云计算环境,非本地化的计算环境。即我们没有环境或者机器,那么可以将我们的提供的服务部署到云计算环境,而这个环境是由其他运营商来提供和维护。 也可以理解为我们将我们的产品或者计算服务部署在远端网络。 …

【CKA模拟题】查询消耗CPU最多的Pod

题干 For this question, please set this context (In exam, diff cluster name) 对于此问题,请设置此上下文(在考试中,diff 集群名称) kubectl config use-context kubernetes-adminkubernetesFind the pod that consumes the …

QT信号与槽实现方式

1、第一种实现方式 在QT开发工具UI界面先拖入按钮,然后鼠标右键拖入按钮,点击选中槽,在页面选着需要的信号,然后OK,随即将会跳转到类的.cpp文件,(这种UI代码结合的方式,会自动去绑定…

kruakal算法(P3603,3366)

[图论与代数结构 301] 最短树问题_1 - 洛谷 分三步,输入,排序,并查集连接n次 using ll long long; constexpr int N 2e5 5; constexpr int M 5e5 5; ll ans, sum, n, m;struct DSU {std::vector<int> f, siz;DSU() {}DSU(int n) {init(n);}void init(int n) {f.re…

ArrayList 是线程安全的么?

1、典型回答 ArrayList 是非线程安全的数据结构 多线程环境下&#xff0c;如果多个线程同时对同一个 ArrayList 进行添加、删除或修改操作&#xff0c;可能会导致数据不一致或发生异常。这是因为&#xff0c;ArrayList 在内部实现时&#xff0c;并没有添加任何线程同步的机制…

服务器做raid几好

服务器应该做RAID几?RAID是一种将多块独立的硬盘存储数据跟其它硬盘进行存储管理的方法。 RAID是一种存储性能、数据安全和校验等技术,可用于提高数据存储的安全性和可靠性。由于去中心化的方式,它可以保证数据 在所有的硬盘中只有一个硬盘,因为存储性能不需要像硬盘那样根据需…

如何在项目中应用“API签名认证”

❤ 作者主页&#xff1a;李奕赫揍小邰的博客 ❀ 个人介绍&#xff1a;大家好&#xff0c;我是李奕赫&#xff01;(&#xffe3;▽&#xffe3;)~* &#x1f34a; 记得点赞、收藏、评论⭐️⭐️⭐️ &#x1f4e3; 认真学习!!!&#x1f389;&#x1f389; 文章目录 为什么需要AP…

windows脚本启动程序的方式

1. 获取管理员权限并运行程序 powershell -command "Start-Process -Verb RunAs -FilePath xxx.exe -ArgumentList parameters......" 2. 无须管理员权限 powershell -command "Start-Process -FilePath xxx.exe -ArgumentList parameters......"

ORBSLAM3与GPS进行松组合

文章目录 预备知识orbslam3vins-fusion中的GPS融合策略数据准备环境准备更改配置globalOptNode.cpp代码运行融合结果预备知识 orbslam3 ORBSLAM3 是一个最新的视觉SLAM(Simultaneous Localization and Mapping)系统,它建立在成功的 ORBSLAM2 系统的基础上,引入了多种新的…

在SwiftUI中使用Buider模式创建复杂组件

在SwiftUI中使用Buider模式创建复杂组件 我们在前面的博客闲聊SwiftUI中的自定义组件中聊到了如何在SwiftU中创建自定义组件。 在那里&#xff0c;我们创建了一个非常简单的组件RedBox&#xff0c;它将展示内容增加一个红色的边框。 RedBox非常简单&#xff0c;我们用普通的方…

Java面试题总结12MySQL之MVCC和主从复制原理

什么是MVCC 即多版本并发控制&#xff0c;读取数据时通过一种类似快照的方式将数据保存下来&#xff0c;这样读写与写锁就不会 冲突&#xff0c;不同的事务session只会看到自己特定版本的数据&#xff0c;版本链 MVCC只会在READ COMMITTED(已提交读)和REPEATABLE READ(可重复…

腾讯春招后端一面(算法篇)

前言&#xff1a; 哈喽大家好&#xff0c;前段时间在小红书和牛客上发了面试的经验贴&#xff0c;很多同学留言问算法的具体解法&#xff0c;今天就详细写个帖子回复大家。 因为csdn是写的比较详细&#xff0c;所以更新比较慢&#xff0c;大家见谅~~ 就题目而言&#xff0c;…

深度学习_GoogLeNet_4

目标 知道GoogLeNet网络结构的特点能够利用GoogLeNet完成图像分类 一、开发背景 GoogLeNet在2014年由Google团队提出&#xff0c; 斩获当年ImageNet(ILSVRC14)竞赛中Classification Task (分类任务) 第一名&#xff0c;VGG获得了第二名&#xff0c;为了向“LeNet”致敬&#x…

Java应用卡死 生产故障深度分析与排查实战

一、引言 在实际的生产环境中&#xff0c;我们经常会遇到Java应用出现线程阻塞进而导致服务卡死的问题。这种问题不仅影响用户体验&#xff0c;严重时甚至会导致整个系统崩溃。本文将通过一次真实的生产故障案例&#xff0c;详解从发现异常到定位原因的详细步骤&#xff0c;并…

Linux学习之网络

目录 认识协议 网络协议初始 协议分层 OSI七层模型 TCP/IP的四层模型 数据包封装和分用 以太网通信 ip地址与MAC地址 网络编程套接字 端口号&#xff08;port&#xff09; 认识协议 网络字节序 socket接口 网络的产生是计算机历史的必然性&#xff0c;是计算机发展…

SwiftUI组件-DatePicker

SwiftUI组件-DatePicker 本文记录一下SwiftUI组件-DatePicker import SwiftUIstruct DatePickerBootCamp: View {State var selectedDate: Date Date()var dateFormatter: DateFormatter {let formatter DateFormatter()formatter.dateStyle .shortformatter.timeStyle .…

AI-逻辑回归模型

&#x1f606;&#x1f606;&#x1f606;感谢大家的支持~&#x1f606;&#x1f606;&#x1f606; 逻辑回归的应用场景 逻辑回归&#xff08;Logistic Regression&#xff09;是机器学习中的 一种分类模型 &#xff0c;逻辑回归是一种分类算法&#xff0c;虽然名字中带有回…

09-设计模式 企业场景 面试题

目录 1.简单工厂模式 ​编辑 2.工厂方法模式 3.抽象工厂模式 4.策略模式 5.登录案例(工厂模式+策略模式) 6.责任链设计模式 7.单点登录怎么是实现的? 8.权限认证是如何实现的 9.上传数据的安全性你们怎么控制? 10.你负责项目的时候遇到了哪些比较棘手的问题?怎…

《天软行业全景画像》报告第7期

内容概要&#xff1a; 传媒、环保、美容护理行业的拥挤度较高&#xff0c;实际投资交易应注意&#xff1b;煤炭行业动量较强&#xff0c;医药生物、综合、环保行业动量较弱&#xff0c;业绩整体表现较差&#xff1b;食品饮料、公用事业行业景气度较高&#xff0c;财务基本面状况…

MySQL的索引下推

一、什么事索引下推 MySQL的索引下推&#xff08;Index Condition Pushdown, ICP&#xff09;是一种优化查询的方式&#xff0c;它可以改善查询性能&#xff0c;特别是对于包含多个条件的查询。在没有索引下推的情况下&#xff0c;MySQL会先从索引中找到满足某一条件的行的指针…