当前位置: 首页 > news >正文

C语言指针5

文章目录

  • 1.sizeof和strlen对比
    • 1.1sizeof
    • 1.2strlen
    • 1.3sizeof和strlen的对比
  • 2.数组和指针的笔试题
    • 2.1一维数组
    • 2.2字符数组
    • 2.3二维数组
  • 3.指针运算笔试题

1.sizeof和strlen对比

1.1sizeof

在学习操作符的时候,我们学习了 sizeof。sizeof 用于计算变量所占内存空间的大小单位字节。如果操作数是类型,sizeof 计算的是使用该类型创建的变量所占内存空间的大小。sizeof 只关注占用内存空间的大小,不在乎内存中存放什么数据
#include<stdio.h>

int main()
{int a = 10;printf("%zd\n", sizeof(a));printf("%zd\n", sizeof(int));return 0;
}

在这里插入图片描述

1.2strlen

strlen是库函数是用来求 字符串长度。
size_t strlen ( const char * str );
该函数是从str这个地址开始查找,查找到\0结束,并记录从str\0之前元素个数。
因为strlen会一直向后查找\0,直到找到为止,所以可能存在越界行为。

#include <stdio.h>
#include<string.h>
int main()
{char arr1[3] = { 'a', 'b', 'c' };char arr2[] = "abc";printf("%zd\n", strlen(arr1));printf("%zd\n", strlen(arr2));printf("%zd\n", sizeof(arr1));printf("%zd\n", sizeof(arr2));return 0;
}

在这里插入图片描述
在这里插入图片描述
这里str1就是因为一开始没有写入\0所以产生了越界查找,找到了第36位才找到系统中的随机位置的\0,所以原本长度为3记录成了35。
在这里插入图片描述

1.3sizeof和strlen的对比

sizeofstrlen
1. sizeof是操作符1. strlen是库函数,使用时需要包含头文件srring.h
2.sizeof计算操作数占内存空间大小,单位是字节2. strlen计算的是字符串的长度,统计的是\0之前字符的长度
3.不关注内存中存放什么数据3. 关注内存中是否有\0

2.数组和指针的笔试题

2.1一维数组

  • 指针在32位编译器下内存大小为4,64位为8。
#include <stdio.h>
#include<string.h>
int main()
{int a[] = { 1,2,3,4 };printf("%zd\n", sizeof(a));//sizeof(数组名)计算整个数组内存空间大小printf("%zd\n", sizeof(a + 0));//计算首地址空间大小(这里是64位)printf("%zd\n", sizeof(*a));//计算首元素空间大小printf("%zd\n", sizeof(a + 1));//计算第二位数据地址空间大小printf("%zd\n", sizeof(a[1]));//计算第二个数据空间大小printf("%zd\n", sizeof(&a));//计算首地址空间大小printf("%zd\n", sizeof(*&a));//sizeof(数组名)计算整个数组内存空间大小printf("%zd\n", sizeof(&a + 1));//计算第二位数据地址空间大小printf("%zd\n", sizeof(&a[0]));//计算首地址空间大小printf("%zd\n", sizeof(&a[0] + 1));//计算第二位数据地址空间大小
}

在这里插入图片描述
补充:

这里的
&a+1一共跳过了多少字节?
答案:16   &a取出的是整个数组,+1相应的就跳过了整个数组,要和a+1做区分

2.2字符数组

例一

char arr[] = { 'a','b','c','d','e','f' };
printf("%zd\n", sizeof(arr));//sizeof(数组名)计算整个数组内存空间大小
printf("%zd\n", sizeof(arr + 0));//计算首地址空间大小(这里是64位)
printf("%zd\n", sizeof(*arr));//计算首元素空间大小
printf("%zd\n", sizeof(arr[1]));//计算首元素空间大小
printf("%zd\n", sizeof(&arr));//计算首地址空间大小
printf("%zd\n", sizeof(&arr + 1));//计算第二位数据地址空间大小
printf("%zd\n", sizeof(&arr[0] + 1));//计算第二位数据地址空间大小

在这里插入图片描述
例二

char arr[] = { 'a','b','c','d','e','f' };
printf("%zd\n", strlen(arr));//随机值;arr是首元素的地址,数组中没有\0,一直往后找,什么时候遇到\0不清楚
printf("%zd\n", strlen(arr + 0));//随机值;arr是首元素的地址,arr+0依然是首元素的地址
//printf("%zd\n", strlen(*arr));//*arr是首元素,是'a' - 97,传给strlen函数,97会被当做地址.
//以97作为地址,会形成非法访问,程序会崩溃
//printf("%zd\n", strlen(arr[1]));//arr[1]是第2个元素,就是'b' - 98,传给strlen函数,98会被当做地址.同上,程序崩溃
printf("%zd\n", strlen(&arr));//&arr是数组的地址,虽然是数组的地址,值和首元素的地址一样.strlen依然是从第一个
//字符的位置开始向后找\0,会得到随机值.
printf("%zd\n", strlen(&arr + 1));//&arr + 1是f后面的地址,什么时候遇到\0,依然不知道.随机值.
printf("%zd\n", strlen(&arr[0] + 1));//&arr[0]+1就是第二个元素的地址,得到的也是随机值

例三

char arr[] = "abcdef";
printf("%zd\n", sizeof(arr));//arr表示整个数,计算的是整个数组的大小单位字节,7*1 = 7(不用忘记\0)
printf("%zd\n", sizeof(arr + 0));//arr就是数组首元素的地址,arr+0还是数组首元素的地址 4 / 8(32位/64位)
printf("%zd\n", sizeof(*arr));//1,arr就是数组首元素的地址,*arr是首元素,大小就是1个字节
printf("%zd\n", sizeof(arr[1]));//arr[1]数组的第二个元素,计算的就是第二个元素的大小,单位是字节 - 1
printf("%zd\n", sizeof(&arr));//arr表示整个数组,&arr取出的是整个数组的地址,是地址大小就是4/8个字节
printf("%zd\n", sizeof(&arr + 1));//&arr + 1是跳过这个数组后的地址,是地址大小就是4/8个字节
printf("%zd\n", sizeof(&arr[0] + 1));//&arr[0]是数组首元素的地址,&arr[0]+1是数组第二个元素的地址:4 / 8

例四

char arr[] = "abcdef";
printf("%zd\n", strlen(arr));//6: arr是数组首元素的地址,从第一个元素开始,统计\0之前字符的个数
printf("%zd\n", strlen(arr + 0));//6:arr是数组首元素的地址,arr+0还是数组首元素的地址,结果同上
//printf("%zd\n", strlen(*arr));//arr是数组首元素的地址,*arr就是首元素了,*arr == 'a' == 97
//非法访问内存,导致程序崩溃
//printf("%zd\n", strlen(arr[1]));//arr[1]是第二个元素 == 'b' == 98, 道理同上
printf("%zd\n", strlen(&arr));//6:&arr取出的是数组的地址,数组的地址和首元素的地址是同一个值
//strlen也是从第一个字符开始向后统计\0之前的字符个数。
printf("%zd\n", strlen(&arr + 1));//随机值
printf("%zd\n", strlen(&arr[0] + 1)); //5:&arr[0] + 1是第二个元素的地址,\0之前有5个元素

例五

const char* p = "abcdef";
printf("%zd\n", sizeof(p));//4/8
//p是一个指针变量,大小就是4/8个字节
printf("%zd\n", sizeof(p + 1));//4/8
//p中存放的是'a'的地址,p+1是'b'的地址
//大小就是4/8个字节
printf("%zd\n", sizeof(*p));//1: *p=='a'
printf("%zd\n", sizeof(p[0]));//1:p[0]==*(p+0)==*p
//结果就同上
printf("%zd\n", sizeof(&p));//&p是指针变量p的地址
//是地址大小就是4/8个字节
printf("%zd\n", sizeof(&p + 1));//&p + 1还是地址大小是4/8个字节
//&p+1是指向p变量的后边
printf("%zd\n", sizeof(&p[0] + 1));//&p[0] + 1是'b'的地址
//大小是4/8个字节

例六

char* p = "abcdef";
printf("%zd\n", strlen(p));//6: p里边存放是'a'的地址
printf("%zd\n", strlen(p + 1));//5: p+1是'b'的地址
//printf("%zd\n", strlen(*p));//非法访问, *p是'a'
//printf("%zd\n", strlen(p[0]));//非法访问,效果同上
printf("%zd\n", strlen(&p));//随机值
//&p是p这个变量的地址,strlen就是从p这块空间的起始地址开始向后找\0的
//p中存放的地址是不确定的,所有有没有\0,什么时候会遇到\0都不确定
printf("%zd\n", strlen(&p + 1));//随机值
//&p+1是p变量后边的地址,从这个位置向后的内存数据不知道
//什么时候会遇到\0都不确定
printf("%zd\n", strlen(&p[0] + 1));//5

2.3二维数组

int a[3][4] = { 0 };
printf("%zd\n", sizeof(a));//48: a是数组名,单独放在sizeof内部,表示整个数组,计算的是整个数组的大小,单位是字节3*4*4 = 48
printf("%zd\n", sizeof(a[0][0]));//4: a[0][0]是第一行第一个元素
printf("%zd\n", sizeof(a[0]));//16: a[0]是第一行这个一维数组的数组名,数组名单独放在sizeof内部,计算的是第一行这个一维数组的大小
printf("%zd\n", sizeof(a[0] + 1));//4/8
//a[0]是数组名,这里表示数组首元素的地址,是第一行第一个元素的地址
//a[0] + 1就是第一行第二个元素的地址,是地址大小就是4/8
printf("%zd\n", sizeof(*(a[0] + 1)));//*(a[0] + 1))是第一行第二个元素-大小是4个字节
printf("%zd\n", sizeof(a + 1));//a+1就是第二行的地址,是地址就是4/8个字节
//a是二维数组的数组名,在这里表示首元素的地址,也就是第一行的地址
//a+1就是第二行的地址
printf("%zd\n", sizeof(*(a + 1)));//16
//a + 1是第二行的地址,*(a+1)得到的就是第二行
//int(*)[4]         对数组指针解引用,放一个数组,就是一行的一维数组
//*(a+1) == a[1], a[1]是第二行的数组名,sizeof(arr[1])计算的是第二行的大小
//
printf("%zd\n", sizeof(&a[0] + 1));//4/8:
//a[0]是第一行的数组名,&a[0]取出的是第一行这个一维数组的地址
//&a[0]+1就是第二行的地址
printf("%zd\n", sizeof(*(&a[0] + 1)));//*(&a[0] + 1)是第二行,计算的是第二行的大小,16个字节
printf("%zd\n", sizeof(*a));//16: a是二维数组的数组名,a是首元素的地址,就是第一行的地址,*a就是第一行
//计算的是第一行的大小,16个字节
//*a == *(a+0) == a[0]
printf("%zd\n", sizeof(a[3]));//16: 没有越界访问,sizeof内部的表达式是不计算的.
//sizeof(int) - 4
//sizeof(4+3);--4

数组名的意义:

  1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
  2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
  3. 除此之外,所有的数组名都表示首元素的地址。

3.指针运算笔试题

接下来的这些题,如果上面的知识掌握牢固是很简单的。我只会在略难的地方再次讲解。

题目1

#include <stdio.h>
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

//在X86环境下
//假设结构体的⼤⼩是20个字节
//程序输出的结果是啥?struct Test{int Num;char* pcName;short sDate;char cha[2];short sBa[4];}*p = (struct Test*)0x100000;printf("%zd\n", sizeof(unsigned long));printf("%p\n", p + 0x1);printf("%p\n", (unsigned long)p + 0x1);//这里加1加的是数字了printf("%p\n", (unsigned int*)p + 0x1);return 0;
  • 第一个输出:4
    解析:X86(32 位)环境中,unsigned long 类型的大小为 4 字节,因此 sizeof(unsigned long) 的结果是 4。
  • 第二个输出:0x100014
    解析:p 是指向 struct Test 类型的指针,该结构体大小为 20 字节(题目已说明)。指针运算中,p + 0x1 表示移动一个结构体的大小(20 字节)。初始地址是 0x100000,加上 20(十六进制为 0x14),结果为 0x100000 + 0x14 = 0x100014。
  • 第三个输出:0x100001
    解析:(unsigned long)p 将指针地址转换为无符号长整数,此时的 +0x1 是单纯的数值加 1。初始地址 0x100000 加 1,结果为 0x100001。
  • 第四个输出:0x100004
    解析:(unsigned int*)p 将指针转换为指向 unsigned int 类型的指针,X86 环境中 unsigned int 大小为 4 字节。指针运算中,+0x1 表示移动 4 字节。初始地址 0x100000 加 4,结果为 0x100004。

题目3

#include <stdio.h>
int main()
{int a[3][2] = { (0, 1), (2, 3), (4, 5) };int *p;p = a[0];printf( "%d", p[0]);return 0;
}

这段代码的输出结果是 1。
关键问题出在二维数组的初始化方式上:
这里使用了逗号表达式 (0, 1) 而不是大括号 {0, 1}。逗号表达式的结果是最后一个表达式的值,因此:
(0, 1) 的结果是 1
(2, 3) 的结果是 3
(4, 5) 的结果是 5

剩余未显式初始化的元素会被自动初始化为 0,因此数组实际存储为:
a[0][0] = 1,  a[0][1] = 3
a[1][0] = 5,  a[1][1] = 0
a[2][0] = 0,  a[2][1] = 0

指针 p 指向数组首行 a[0],因此 p[0] 等价于 a[0][0],其值为 1。

题目4

//假设环境是x86环境,程序输出的结果是啥?
#include <stdio.h>
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;
}

在这里插入图片描述
在 x86 环境下,程序的输出结果是:fffffffc,-4

  • a 是一个 5×5 的二维数组,数组名 a 代表首元素地址(即 a[0] 的地址)
  • p 是一个指向 “包含 4 个 int 元素的数组” 的指针
  • 指针运算分析:
    p = a 后,p 指向数组 a 的起始位置
    p[4] 等价于 *(p+4),由于 p 是指向 4 个 int 的数组指针,所以 p+4 实际移动 4×4×4 = 64 字节(x86 中 int 为 4 字节)
    p[4][2] 是上述位置向后再移动 2 个 int(8 字节)的位置
  • 地址差值计算:
    a[4][2] 实际地址:从起始位置移动 4×5×4 + 2×4 = 88 字节
    p[4][2] 实际地址:从起始位置移动 4×4×4 + 2×4 = 72 字节
    两者差值:72 - 88 = -16 字节,换算为 int 类型的个数(每个 int4 字节)就是 -4

题目5

#include <stdio.h>
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));return 0;
}

在这里插入图片描述
题目6

#include <stdio.h>
int main()
{char *a[] = {"work","at","alibaba"};char**pa = a;pa++;printf("%s\n", *pa);return 0;
}

在这里插入图片描述
题目7

#include <stdio.h>
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.dtcms.com/a/348069.html

相关文章:

  • 使用虚幻引擎5(UE5)开发类似《原神》的开放世界游戏:从技术架构到实践指南
  • LeetCode-542. 01 矩阵
  • (LeetCode 每日一题) 1493. 删掉一个元素以后全为 1 的最长子数组 (双指针)
  • 03-鸿蒙架构与编程模型
  • ChainVault:重塑亚洲黄金交易基建,引领RWA金融新浪潮
  • Java 22 新特性及具体应用
  • week4-[字符数组]英语作文
  • Games101 作业1 旋转与投影
  • 量子链(Qtum)分布式治理协议
  • Qt+windows+QtInstallerFramework打包可执行文件教程-美观且简单
  • Django的Serializers与 fastapi 的Pydantic
  • Excel 转化成JSON
  • Java:LinkedList的使用
  • Django的Settings 配置文件详解
  • 【ArcGIS Pro 全攻略】GIS 数据格式终极指南:从原理到实战,再也不纠结选哪种格式!
  • React useState 全面深入解析
  • Linux 824 shell:expect
  • 基于5G NR NTN与DVB-S2X/RCS2的机载卫星通信终端性能分析
  • 低功耗模式DMA数据搬运问题解析
  • 在测试接入抖音小游戏订阅消息推送时遇到的问题
  • bun + vite7 的结合,孕育的 Robot Admin 【靓仔出道】(十八)
  • K8s部署MySQL8.0数据库
  • Transformer实战(13)——从零开始训练GPT-2语言模型
  • 【go语言】字符串函数
  • imx6ull-驱动开发篇39——Linux INPUT 子系统实验
  • 05-ArkUI界面开发
  • Solidity学习笔记
  • ZKmall模块商城的推荐数据体系:从多维度采集到高效存储的实践
  • 用 Bright Data MCP Server 构建实时数据驱动的 AI 情报系统:从市场调研到技术追踪的自动化实战
  • 青少年软件编程(python五级)等级考试试卷-客观题(2024年6月)