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.rhkb.cn/news/276480.html

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

相关文章

社交革命的引领者:探索Facebook如何改变我们的生活方式

1.数字社交的兴起 随着互联网的普及,社交媒体成为我们日常生活的重要组成部分。Facebook作为其中的先驱,从最初的社交网络演变成了一个拥有数十亿用户的全球化平台。它不仅改变了我们与世界互动的方式,还深刻影响了我们的社交习惯、人际关系以…

nut-ui组件库icon中使用阿里图标

1.需求 基本每个移动端组件库都有组件 icon组件 图标组件、 但是很多组件库中并找不到我们需要的图标 这时候 大家有可能会找图标库 最大众的就是iconfont的图标了 2.使用 有很多方式去使用这个东西 比如将再限链接中的css引入 在使用 直接下载图标 symbol 方式 等....…

解锁未知:探索 Web3 的创新与前景

在数字化时代的潮流下,Web3作为下一代互联网的关键构建,正引领着数字经济的崭新篇章。本文将深入探讨Web3的创新特性及其对未来发展的影响。 1. Web3 的崭新定义 Web3不仅是技术的革新,更是一种理念的演进。其核心特征包括去中心化、可编程性…

Linux编译器gcc/g++的功能与使用

一、程序的生成 首先,我们知道程序的编译分为四步: 1、预处理 2、编译 3、汇编 4、链接 1.1预处理 预处理功能主要包括头文件展开、宏定义、文件包含、条件编译、去注释等。 所谓的头文件展开就是在预处理时候,将头文件内容拷贝至源文…

探索TikTok云手机在社交媒体营销的作用

近年来,TikTok作为全球短视频平台之一,其用户基数呈现持续增长的趋势。伴随社交媒体的蓬勃发展,企业和个人纷纷涌入TikTok平台,追求更广泛的曝光和用户互动。为满足这一需求,TikTok云手机应运而生。本文将深度剖析TikT…

ETH共识升级之路

简介 根据我们之前的介绍,了解到ETH网络的共识方式,已经从 PoW 切换到了 PoS,今天我们就回顾下升级之路,以及升级带来的影响 最早的共识机制 PoW 以太坊创建之初采用了类似比特币的工作量证明机制,即矿工通过计算哈希函…

HandyControl PropertyGrid及自定义编辑器

前提条件 项目引入对应HandyControl对应版本包。 使用案例 UI部分 <Window xmlns:hc"https://handyorg.github.io/handycontrol"><hc:TabControl><hc:TabItem Header"默认样式"><hc:PropertyGrid Width"380" SelectedO…

Rust 深度学习库 Burn

一、概述 Burn 它是一个新的综合动态深度学习框架&#xff0c;使用 Rust 构建的&#xff0c;以极高的灵活性、计算效率和可移植性作为其主要目标。 Rust Burn 是一个以灵活性、高性能和易用性为核心设计原则工具&#xff0c;主打就是灵活性 、高性能 及易用性。 二、Rust B…

[蓝桥杯]-最大的通过数-CPP-二分查找、前缀和

目录 一、题目描述&#xff1a; 二、整体思路&#xff1a; 三、代码&#xff1a; 一、题目描述&#xff1a; 二、整体思路&#xff1a; 首先要知道不是他们同时选择序号一样的关卡通关&#xff0c;而是两人同时进行两个入口闯关。就是说两条通道存在相同关卡编号的的关卡被通…

Linux--基本知识入门

一.几个基本知识 终端: CtrlAltT 或者桌面/文件夹右键,打开终端切换为管理员: sudo su 退出:exit查看内核版本号: uname -a内核版本号含义: 5 代表主版本号;13代表次版本号;0代表修订版本号;30代表修订版本的第几次微调;数字越大表示内核越新. 二.目录…

Halcon OCR文字识别

1、OCR文字识别 FontFile : Universal_0-9_NoRej dev_update_window (off) read_image (bottle, bottle2) get_image_size (bottle, Width, Height) dev_open_window (0, 0, Width, Height, black, WindowHandle) set_display_font (WindowHandle, 16, mono, true, false) dev…

关于Ubuntu虚拟机突然上不了网的问题

今天刚重新把Ubuntu虚拟机下回来准备大干一场&#xff0c;结果去吃饭回来虚拟机就上不去网了&#xff0c;具体体现为右上角没有网络的图标&#xff0c;下图是有网络的情况&#xff0c;废话不多说&#xff0c;直接给出解决方案&#xff1a;博客在此 我就是运行了这三行代码就成功…

抖音开放平台第三方开发,实现代小程序备案申请

大家好&#xff0c;我是小悟 抖音小程序备案整体流程总共分为五个环节&#xff1a;备案信息填写、平台初审、工信部短信核验、通管局审核和备案成功。 服务商可以代小程序发起备案申请。在申请小程序备案之前&#xff0c;需要确保小程序基本信息已填写完成、小程序至少存在一个…

项目性能优化—性能优化的指标、目标

项目性能优化—性能优化的指标、目标 性能优化的终极目标是什么 性能优化的目标实际上是为了更好的用户体验&#xff1a; 一般我们认为用户体验是下面的公式&#xff1a; 用户体验 产品设计&#xff08;非技术&#xff09; 系统性能 ≈ 系统性能 快 那什么样的体验叫快呢…

通付盾Web3专题 | SharkTeam:2023年加密货币犯罪分析报告

2023年&#xff0c;Web3行业共经历了940多起大大小小的安全事件&#xff0c;同比2022年增长了超过50%&#xff0c;损失金额达到17.9亿美元。其中&#xff0c;第三季度发生的安全事件最多&#xff08;360起&#xff09;&#xff0c;损失最大&#xff08;7.4亿美元&#xff09;&a…

19.ADC模数转换器知识点+AD单通道AD多通道应用程序示例

0. 江协科技/江科大-STM32标准库开发-各章节详细笔记-查阅传送门_江协科技stm32笔记-CSDN博客文章浏览阅读2.9k次&#xff0c;点赞44次&#xff0c;收藏128次。江协科技/江科大-STM32标准库开发-各章节详细笔记-传送门至各个章节笔记。基本上课程讲的每句都详细记录&#xff0c…

整型变量的原子操作

什么是原子操作 原子操作&#xff08;Atomic Operation&#xff09;是指不可中断的操作&#xff0c;即在多线程环境下&#xff0c;当一个线程在执行原子操作时&#xff0c;不会被其他线程的调度和中断所影响。这种操作在多线程编程中尤为重要&#xff0c;因为它能保证操作的原…

H5 流光分割个人主页源码

源码名称&#xff1a;流光分割个人主页源码 源码介绍&#xff1a;一款流光分割特效个人主页源码&#xff0c;源码带大量跳转个人联系方式按钮和朋友按钮。同时带有个人介绍。 需求环境&#xff1a;H5 下载地址&#xff1a; https://www.changyouzuhao.cn/10241.html

稀碎从零算法笔记Day15-LeetCode:判断子序列

跑样例的时候LC炸了&#xff0c;以为今天回断更 题型&#xff1a;字符串、双指针 链接&#xff1a;392. 判断子序列 - 力扣&#xff08;LeetCode&#xff09; 来源&#xff1a;LeetCode 题目描述&#xff08;此题建议结合样例理解&#xff09; 给定字符串 s 和 t &#xf…