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

news2024/9/22 23:24:12

前言

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

1.sizeof和strlen的对比

 1.1 sizeof

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

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

比如:

int main()
{
	int a = 10;
	printf("%d\n", sizeof(a)); //sizeof根据类型计算大小,a为整型,大小为4
	printf("%d\n", sizeof a); //少了括号,结果还是4,目的是想说明sizeof不是函数,仅仅只是一个操作符
	printf("%d\n", sizeof(int)); //给的是整型,结果大小为4
	return 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));//3
	printf("%d\n", sizeof(arr1));//arr1为数组名,所以在sizeof后面,计算的是所有元素的大小,大小3
	printf("%d\n", sizeof(arr2));//4
	return 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后面求的是整个数组的大小,16
	printf("%d\n", sizeof(a + 0));//a为数组名,但是不是单独出现在sizeof后面,所以是首元素的地址,sizeof求地址的大小,值为4/8
	printf("%d\n", sizeof(*a)); //a为首元素的地址,*a为1,1的类型为int,所以大小为4
	printf("%d\n", sizeof(a + 1));//a为首元素的地址,+1,就是往后位移一个int类型的大小,所以值为4
	printf("%d\n", sizeof(a[1]));//a[1] = *(a+1) = 2 , 所以大小为4
	printf("%d\n", sizeof(&a));//a为数组名,&a取出的是整个数组的地址,而sizeof求地址的大小,所以值为4/8
	printf("%d\n", sizeof(*&a));//*和&相互抵消,a为数组名,单独出现在sizeof后面,计算的是整个数组的大小,所以值为16
	printf("%d\n", sizeof(&a + 1));//&a取出是整个数组的地址,+1后跳过整个数组,本质上还是指针,所以值为4/8
	printf("%d\n", sizeof(&a[0]));//a[0] = *(a+0) = a = &a ,得到的是a的地址,大小为4/8
	printf("%d\n", sizeof(&a[0] + 1));//&a,指向a的地址,也就是首元素的地址,+1后得到第二个元素的地址,大小为4/8
	return 0;
}

 

2.2 字符数组 

代码1 

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

代码3: 

int main()
{
	char arr[] = "abcdef";
	printf("%d\n", sizeof(arr)); //arr单独出现在sizeof后面,表示求的是整个数组的大小,包括'\0',7
	printf("%d\n", sizeof(arr + 0)); //arr并没有单独出现在sizeof后面,所以arr为首元素的地址,sizeof求地址大小,值为4/8
	printf("%d\n", sizeof(*arr)); //arr为首元素的地址,*arr = a ,a为char类型,所以值为1
	printf("%d\n", sizeof(arr[1])); //arr[1] = *(arr+1) = b , b为char类型,所以值为1
	printf("%d\n", sizeof(&arr)); //&arr,取出的是整个数组的地址,本质上还是地址,值为4/8
	printf("%d\n", sizeof(&arr + 1)); //&arr+1,跳出整个数组,本质上还是地址,值为4/8
	printf("%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',值为6
	printf("%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取出的是整个数组的地址,和第一个没区别,值为6
	printf("%d\n", strlen(&arr + 1)); //&arr+1,跳出数组范围,为随机值
	printf("%d\n", strlen(&arr[0] + 1)); //arr[0] = *(arr+0) = &a+1=b的地址,值为5
	return 0;
}

代码5:

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

 

代码6:

int main()
{
	char* p = "abcdef";
	printf("%d\n", strlen(p)); //p为指针,首元素的地址,值为6
	printf("%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的地址,所以值为5
	return 0;
}

2.3 二维数组 

int main()
{
	int a[3][4] = { 0 };
	printf("%d\n", sizeof(a));//a为数组名,单独出现在sizeof后面,表示求整个二维数组的大小,所以值为48
	printf("%d\n", sizeof(a[0][0])); //a[0][0] = 0 ,类型为int,所以值为4
	printf("%d\n", sizeof(a[0])); //a[0]为二维数组一行的数组名,单独出现在sizeof后面,求的是第一行数组的大小,16
	printf("%d\n", sizeof(a[0] + 1)); //a[0]为二维数组第一行的数组名,没有单独出现在sizeof后面,则表示首元素的地址,+1得到第二个元素的地址,4/8
	printf("%d\n", sizeof(*(a[0] + 1)));//4
	printf("%d\n", sizeof(a + 1)); //a为数组名,没有单独出现在sizeof后面,所以a为首元素的地址,+1后跳过整个二维数组,本质上还是地址,值为4/8
	printf("%d\n", sizeof(*(a + 1)));//a为数组名,但没有单独出现在sizeof之后,所以为首行的地址,+1后为第一行的地址,解引用得到第一行元素为16
	printf("%d\n", sizeof(&a[0] + 1)); //a[0] = *(a+0) = &0 + 1 = 第零行第一列的地址,值为4/8
	printf("%d\n", sizeof(*(&a[0] + 1)));//对第一行进行解引用,值为16
	printf("%d\n", sizeof(*a)); //a为数组名,并没有单独出现在sizeof后面,所以为首行的地址,解引用后得到第零行的元素,值为16
	printf("%d\n", sizeof(a[3]));//a[3] = *(a + 3) ,首行元素的地址,加三,得到第三行元素的地址,再解引用,得到第四行元素,值为16
	return 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个十六进制,所以结果在前面补了两个0
	printf("%p\n", (unsigned long)p + 0x1); //0x100000 + 1 = 00100001
	printf("%p\n", (unsigned int*)p + 0x1); //0x100000 + 4 = 00100004
	return 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]);//1
	return 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 5
	return 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.coloradmin.cn/o/1518529.html

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

相关文章

three.js 入门四:THREE.BoxGeometry 解析

环境: three.js 0.159.0 问题:如何理解BoxGeometry? BoxGeometry在创建的时候, 内部划分成了6个小组, 每个小组就是一个面有4个点, 假设长宽深的segments都是1的话(默认是1), 那么每个小组拥有2个三角面, 如下面创建的代码: var geometry new THRE…

AI智能分析网关V4烟火识别技术在沿街商铺消防安全管理中的应用

一、背景需求 2024年3月13日,廊坊三河市燕郊镇一炸鸡店发生疑似燃气泄漏引发的爆燃事故,应急、消防、卫健等有关部门第一时间赶赴现场全力救援处置。目前现场搜救工作已经结束,事故善后处置、原因调查等工作正在进行中。本次事故也引发了社会…

AI-逻辑回归模型

😆😆😆感谢大家的支持~😆😆😆 逻辑回归的应用场景 逻辑回归(Logistic Regression)是机器学习中的 一种分类模型 ,逻辑回归是一种分类算法,虽然名字中带有回…

家具销售电商平台|基于springboot框架+ Mysql+Java+Tomcat的家具销售电商平台设计与实现(可运行源码+数据库+设计文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 目录 前台功能效果图 用户功能模块 管理员功能登录前台功能效果图 系统功能设计 数据库E-R图设计 lunwen…

CXL-Enabled Enhanced Memory Functions——论文阅读

IEEE Micro 2023 Paper CXL论文阅读笔记整理 问题 计算快速链路(CXL)协议是系统社区的一个重要里程碑。CXL提供了标准化的缓存一致性内存协议,可用于将设备和内存连接到系统,同时保持与主机处理器的内存一致性。CXL使加速器&…

瑞_23种设计模式_策略模式

文章目录 1 策略模式(Strategy Pattern)★1.1 介绍1.2 概述1.3 策略模式的结构1.4 策略模式的优缺点1.5 策略模式的使用场景 2 案例一2.1 需求2.2 代码实现 3 案例二3.1 需求3.2 代码实现 4 JDK源码解析(Comparator) &#x1f64a…

Flutter学习笔记---flutter环境搭建以及dart语法的学习

Flutter笔记 Flutter环境搭建 获取 Dart SDK | Dart dart-pub | 镜像站使用帮助 | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror Flutter、Dart SDK镜像资源 - 掘金 (juejin.cn) Index of /flutter/dart-archive/channels/stable/release/3.2.6/sdk/ | 清华大学…

优思学院|质量管理方面的证书有哪些?

在这个迅速变化的时代,每个行业都在寻找方法来提高效率、质量和生产力。 对于那些刚踏入或有兴趣深入探索质量管理领域的朋友们来说,一个常见的疑问浮现:为了提升自己的专业技能和职业前景,应该考取哪些证书? 在这方…

OS---python中OS模块的相关学习总结

介绍 在Python中,os模块是一个与操作系统交互的标准库,提供了许多用于文件和目录操作、进程管理、环境变量等功能。下面是os模块中一些常用的功能和方法: 用法 1.查看os模块内容(import os print(dir(os)) ) 结果如下: [Dir…

Windows客户端漏洞挖掘(红队角度)

0x01 前言 周五的时候看了key佬的演讲受益良多呀,来水水,写下目前针对Windows客户端类程序的部分挖掘入口吧,然后分享一下随手挖的很简单很简单的案例。 传统客户端 监听类的: 这里说的监听类的,指的就是安装客户端后启动的端口…

爬虫练习:获取某招聘网站Python岗位信息

一、相关网站 二、相关代码 import requests from lxml import etree import csv with open(拉钩Python岗位数据.csv, w, newline, encodingutf-8) as csvfile:fieldnames [公司, 规模,岗位,地区,薪资,经验要求]writer csv.DictWriter(csvfile, fieldnamesfieldnames)writer…

Halcon绘制图形

1、ROI是Halcon中的一个很重要的概念,为了减少计算量,只关注待检测物体或该物体周围的一片区域即可(类似于图片裁剪) * ROI是Halcon中的一个很重要的概念,为了减少计算量,只关注待检测物体或该物体周围的一片区域即可&#xff0c…

嵌入式方向还有希望吗?

我刚开始学习,也不知道我定位的是单片机工程师,嵌入式工程师职位的。 我只知道电子工程师,这个职位其实偏硬件,很多岗位需求是硬件设计,PCB设计,还要懂焊接、各种仪器仪表使用,还有些需要懂单片…

[Redis]——主从同步原理(全量同步、增量同步)

目录 Redis集群: 主从同步原理: replid和offset: 全量同步和增量同步: repl_baklog文件: 主从集群的优化: Redis集群: 部署多台Redis我们称之为Redis集群,他有一个主节点(负责写操作)&…

List(CS61B学习记录)

问题引入 上图中,赋给b海象的weight会改变a海象的weight,但x的赋值又不会改变y的赋值 Bits 要解释上图的问题,我们应该从Java的底层入手 相同的二进制编码,却因为数据类型不同,输出不同的值 变量的声明 基本类型…

【数据结构和算法初阶(C语言)】栈的概念和实现(后进先出---后来者居上的神奇线性结构带来的惊喜体验)

目录 1.栈 1.1栈的概念及结构 2.栈的实现 3.栈结构对数据的处理方式 3.1对栈进行初始化 3.2 从栈顶添加元素 3.3 打印栈元素 3.4移除栈顶元素 3.5获取栈顶元素 3.6获取栈中的有效个数 3.7 判断链表是否为空 3.9 销毁栈空间 4.结语及整个源码 1.栈 1.1栈的概念及结构 栈&am…

电力运维物联网平台

我们构建软硬件结合的生态系统,打造电力设备9物联平台,实现软件、硬件、平台、云数据一体化。 在硬件领域通过自主研发推出了一系列电力领域的硬件设备,包括:智能电力测控终端、智能电力采集终端等产品。在软件领域,我们搭…

韩国大带宽服务器的数据中心位置

很多用户会选择韩国大宽带服务器,那么韩国大带宽服务器的数据中心位置在哪,rak小编为您整理发布韩国大带宽服务器的数据中心位置。 韩国大带宽服务器的数据中心通常位于**首尔及其周边地区**。 韩国因其地理位置的优势,拥有丰富的网络带宽资源…

【智能算法】白鲨算法(AVOA)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.代码实现4.参考文献 1.背景 2022年,Braik 等人受到白鲨捕食行为启发,提出了非洲秃鹫优化算法(White Shark Optimizer, WSO)。 2.算法原理 2.1算法思想 海洋中白鲨拥有敏锐的感知、听觉和嗅觉&#xf…

【竞技宝】LOL:sheer对位压制369 JDG鏖战三局力克TES

北京时间2024年3月15日,英雄联盟LPL2024春季常规赛继续进行,昨日共进行三场比赛,第三场比赛由TES对阵JDG。本场比赛前两局双方战至1-1平,决胜局JDG前期就打出完美节奏,中期两次团灭TES后轻松取胜,最终JDG鏖战三局击败TES。以下是本场比赛的详细战报。 第一局: TES:鳄鱼、盲僧、…