C语言深入理解指针5

news2024/9/19 9:35:42

1.sizeof和strlen 对比

1.1sizeof

sizeof用来计算变量所占内存空间大小,单位是字节,操作数是类型的话,计算的是使用类型创建的变量所占空间的大小
sizeof只关注占用内存空间大小,不在乎内存中存放什么数据

int main()
{
	int a = 0;
	printf("z%d\n", sizeof(a));
	printf("%zd\n", sizeof(int));
	printf("%d\n", sizeof a);

	return 0;
}
  • 注:当类似是变量a的形式,就可以省略括号

1.2strlen

strlen是c语言库函数,功能是求字符串长度
函数原型

size_t strlen ( const char * str );

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

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

解释:由于arr1字符数组中没有存放\0,所以strlen会一直向后查找,直到找到\0为止,arr2字符串数组中,是默认结尾存放\0的,所以可以正常打印,因为arr1是三个字符,占用3个字节,sizeof(arr1)结果是3,arr2包括abc和\0所以是4个字符,占4个字节,所以结果是4

1.3sizeof和strlen对比

sizeof

  • 1.sizeof是操作符
  • 2.sizeof计算操作数所占内存大小,单位是字节
  • 3.不关注内存中存放什么数据
    strlen
  • 1.strlen是库函数,使用需要包含头文件string. h
  • 2.strlen是求字符串长度的,统计的是\0之前字符的个数
  • 3.关注内存中是否有\0,没有就会一直找,直到找到为止

2.数组和指针练习题

2.1一维数组

//数组名是数组首元素地址
//&arr,数组名表示的是整个数组,取出的是整个数组的地址
// sizeof(arr),数组名表示整个数组,计算的是整个数组的大小,
//单位是字节
int main()
{
	int a[] = { 1,2,3,4 };
	printf("%zd\n", sizeof(a));//结果是16,sizeof(数组名)表示整个数组,a数组中有4个int类型的元素,
	printf("%zd\n", sizeof(a + 0));//结果是4/8,a表示数组首元素地址,a+0,还是数组首元素地址
	//a+0====&a[0]
	printf("%zd\n", sizeof(*a));//结果是4,a是数组首元素地址,解引用表示第一个元素
	//*a是第一个元素,a[0]
	printf("%zd\n", sizeof(a + 1)); //结果是4/8,a表示数组首元素地址,+1表示第二个元素地址
	//a+1====&a[1]
	printf("%zd\n", sizeof(a[1]));//结果是4,a[1]表示第二个元素
	printf("%zd\n", sizeof(&a));//结果是4/8,&a表示数组的地址
	printf("%zd\n", sizeof(*&a));//结果是16, *&a,两者抵消,也就是sizeof(a)
	//抵消,16,&a-int(*p)[4]=&a
	//*p访问一个数组的大小,p+1跳过一个数组
	printf("%zd\n", sizeof(&a + 1));//结果是4/8,&a表示数组地址,&a+1表示跳过整个数组的地址
	//4/8
	printf("%zd\n", sizeof(&a[0]));//结果是4/8,a[0]表示第一个元素,&a[0]表示第一个元素的地址
	printf("%zd\n", sizeof(&a[0] + 1));//结果是4/8,&a[0]表示首元素地址,+1表示第二个元素地址
	return 0;
}

2.2字符数组

练习1

int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(arr));//结果是6,sizeof(arr)表示整个数组
    printf("%d\n", sizeof(arr + 0));//结果是4/8,首元素地址
	printf("%d\n", sizeof(*arr));//结果是1,首元素地址解引用,首元素
    printf("%d\n", sizeof(arr[1]));//结果是1,第二个元素
    printf("%d\n", sizeof(&arr));//结果是4/8,数组的地址
    printf("%d\n", sizeof(&arr + 1));//结果是4/8,跳过一个数组的地址
	printf("%d\n", sizeof(&arr[0] + 1));//结果是4/8,第二个元素地址

	return 0;
}

练习2

#include<string.h>
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%zd\n", strlen(arr));//随机值,字符数组中结尾没有\0
	printf("%zd\n", strlen(arr + 0));//随机值
	printf("%zd\n", strlen(*arr));//非法访问,只能传地址,传的是首字符,也就是访问97的地址,err
	printf("%zd\n", strlen(arr[1]));//非法访问,传的是第二个元素,也就是访问98的地址,err
	printf("%zd\n", strlen(&arr));//随机值,整个数组地址,起始位置地址
	printf("%zd\n", strlen(&arr + 1));//随机值,跳过一个数组的地址,
	printf("%zd\n", strlen(&arr[0] + 1));//随机值,第二个元素的地址,
	return 0;
}

练习3

#include<string.h>
int main()
{
	char arr[] = "abcdef";
	printf("%llu\n", sizeof(arr));//结果是7,数组大小
	printf("%llu\n", sizeof(arr + 0));//结果是4/8,首元素地址
	printf("%llu\n", sizeof(*arr));//结果是1,第一个元素
	printf("%llu\n", sizeof(arr[1]));//结果是1,第二个元素
	printf("%llu\n", sizeof(&arr));//结果是4/8,数组的地址
	printf("%llu\n", sizeof(&arr + 1));//结果是4/8,跳过一个数组的地址
	printf("%llu\n", sizeof(&arr[0] + 1));//结果4/8,第二个元素地址
	return 0;
}

练习4

int main()
{
	char arr[] = "abcdef";
	printf("%lld\n", strlen(arr));//结果是6,数组首元素地址,也就是起始位置从a开始
	printf("%lld\n", strlen(arr + 0));//结果是6,首元素地址开始,也就是起始位置从a开始
	printf("%lld\n", strlen(*arr));//结果是非法访问,首元素a,也就是访问97的地址,err
	printf("%lld\n", strlen(arr[1]));//结果是非法访问,第二个元素b,也就是访问98的地址,err
	printf("%lld\n", strlen(&arr));//结果是6,数组地址,也就是起始位置从a开始
	printf("%lld\n", strlen(&arr + 1));//结果是随机值,跳过一个数组的地址
	printf("%lld\n", strlen(&arr[0] + 1));//结果是5,第二个元素地址,也就是起始位置从b开始
	return 0;
}

练习5

int main()
{
	char* p = "abcdef";
	printf("%lld\n", sizeof(p));//结果是4/8,指针变量
	printf("%lld\n", sizeof(p + 1));//结果是4/8,b的地址
	printf("%lld\n", sizeof(*p));//结果是1,首元素a
	printf("%lld\n", sizeof(p[0]));//结果是1,首元素a,p[0]===*(p+0)
	printf("%lld\n", sizeof(&p));//结果是4/8,指针变量p的地址
	printf("%lld\n", sizeof(&p + 1));//结果是4/8,跳过p变量的地址
	printf("%lld\n", sizeof(&p[0] + 1));//结果是4/8,第二个元素地址
	return 0;
}

练习6

int main()
{
	char* p = "abcdef";
	printf("%lld\n", strlen(p));//结果是6,首元素地址起始
	printf("%lld\n", strlen(p + 1));//结果是5,第二个元素b的地址开始
	printf("%lld\n", strlen(*p));//结果是非法访问,首元素a,也就是访问97的地址,err
	printf("%lld\n", strlen(p[0]));//结果是非法访问,首元素a,也就是访问97的地址,err,*(p+0)==p[0]
	printf("%lld\n", strlen(&p));//结果是随机值,p的地址,从p所占空间起始位置查找
	printf("%lld\n", strlen(&p + 1));//结果是随机值,p+1的地址,跳过p的地址位置查找
	printf("%lld\n", strlen(&p[0] + 1));//结果是5,第二个元素b的地址开始
	return 0;
}

2.3二维数组

int main()
{
	int a[3][4] = { 0 };
	printf("%d\n", sizeof(a));//结果是48,整个数组大小
	printf("%d\n", sizeof(a[0][0]));//结果是4,第一行第一个元素
	printf("%d\n", sizeof(a[0]));//结果是16,第一行数组名,即第一行元素的大小
	//第一行数组名单独放在sizeof内部,计算的是第一行大小
	printf("%d\n", sizeof(a[0] + 1));//结果是4/8,第一行第二个元素地址
	//a[0]是第一行数组的数组名,但是数组名并非单独放在sizeof内部,
	// 所以数组名是数组首元素地址,+1就是第一行第二个元素地址
	printf("%d\n", sizeof(*(a[0] + 1)));//结果是4,第一行第二个元素
	printf("%d\n", sizeof(a + 1));//结果是4/8,第二行地址
	//没有单独放在sizeof内部,没有&,就是首元素地址,第一行地址
	printf("%d\n", sizeof(*(a + 1)));//结果是16,第二行大小==a[1]
	printf("%d\n", sizeof(&a[0] + 1));//结果是4/8,第二行地址
	printf("%d\n", sizeof(*(&a[0] + 1)));//结果是16,第二行大小
	printf("%d\n", sizeof(*a));//结果是16,首元素
	//首元素地址解引用
	// *(a+0)==a[0]
	printf("%d\n", sizeof(a[3]));//结果是16,第四行大小,不会越界
	return 0;
}

注意:

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

3.指针运算练习题

练习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

解释:&a+1是跳过整个数组的地址,强制转换为int*复制给ptr, (a+1)表示第二个元素,(ptr-1)表示最后一个元素
练习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);//p+1    //跳过一个结构体,结构体是20个字节,0x100014
	printf("%p\n", (unsigned long)p + 0x1); //0x100001
	// 无符号整型
	printf("%p\n", (unsigned int*)p + 0x1);//0x100004
	//整形指针+1(整型)
	return 0;
}

练习3

int main()
{
	int a[4] = { 1, 2, 3, 4 };//a数组
	int* ptr1 = (int*)(&a + 1);//ptr1存放的是跳过数组的地址
	int* ptr2 = (int*)((int)a + 1);
	printf("%x,%x", ptr1[-1], *ptr2);
	//ptr[-1],*(ptr-1), 向前移动一个整形,指向4,解引用得到4,十六进制打印,和十进制4一样
	//小端
    //a指向01000000,强换成int, +1就是1,在转换成int*, 指向01后面的,00,给ptr2,取四个整型, 00000002
    //取出来还原成0x02000000,即*ptr2是20000000
	return 0;
}

练习4

int main()
{
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };//逗号表达式,从左向右,最后一个表达式是结果,1,3,5
	//数组元素为,1,3,5,0,0,0
	int* p;
	p = a[0];//&a[0][0]
	printf("%d", p[0]);//*(p+0) 1
	return 0;
}

练习5

int main()
{
	int a[5][5];//a数组5行5列
	int(*p)[4];//p是一个存放有4个整型元素数组的数组指针
	p = a;//p指向a
	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
	//&p[4][2]-&a[4][2]表示(p+4*4+2)-(a+4*5+2)=p-a-4,p和a的起始地址相同,差值为-4
	//FFFFFFFC,-4
	//跳过一个整型,指针-指针=元素个数
	//%p是输出地址,输出-4的补码0xFFFFFFFC, %d输出整数,即为-4
	return 0;
}

练习6

int main()
{
	int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//aa是一个2行5列的数组
	int* ptr1 = (int*)(&aa + 1);//ptr1指向跳过整个数组的地址
	int* ptr2 = (int*)(*(aa + 1));//第二行地址
	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));//10   5
	//-一个整型,10,-一个整型,5
	return 0;
}

练习7

int main()
{
	char* a[] = { "work","at","alibaba" };//字符指针数组a,分别存放指向3个字符串的首字符地址
	char** pa = a;//首元素地址
	pa++;//第二个元素地址
	printf("%s\n", *pa);//at
	return 0;
}

练习8

int main()
{
	char* c[] = { "ENTER","NEW","POINT","FIRST" };
	//字符指针数组.分别是指向"ENTER","NEW","POINT","FIRST"的首字符地址 
	char** cp[] = { c + 3,c + 2,c + 1,c };
	//cp存放的是{c+3, c+2, c+1, c},分别是存放,指向FIRST的地址, 指向POINT地址, 指向NEW地址,指向,ENTER的地址, 
	char*** cpp = cp;
	//cpp指向cp,即首元素地址,c+3的地址
	printf("%s\n", **++cpp);
	//++cpp, 跳过第一个元素指向cp的第二个元素c+2,
	//*++cpp表示c+2,指向POINT的首字母地址,**++cpp拿出c+2里面的内容,,从P开始打印,表示POINT,
	printf("%s\n", *-- * ++cpp + 3);
    //++cpp指向,指向cp第三个元素,指向c+1, ,*++cpp得到c+1,
   //--*++cpp,c+1, --c,得到c,指向变为ENTER
   //*--*++cpp, 拿到E的地址,
   // *--*++cpp+3指向ENTER第四个元素
   //从E开始得到ER
	printf("%s\n", *cpp[-2] + 3);
	//cpp[-2],是 *(cpp-2)
    //指向c+3, 解引用得到c+3, 
    //*cpp[-2],**(cpp-2)+3
    //解引用得到FIRST,
    //从首字母地址开始+3
    //*cpp[-2]+3, 从第四个位置开始,即ST
	printf("%s\n", cpp[-1][-1] + 1);
	//指向还是c
    //cpp[-1][-1] + 1,*(*(cpp-1)-1)+1
    //cpp-1, 指向c+2, 解引用得到c+2, 指向POINT的地址
    //*(*(cpp-1)-1),指向NEW得地址,首字母地址+1, 指向E, 从E开始EW
	return 0;
}

在这里插入图片描述

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

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

相关文章

验证码识别之点选验证码识别——绪论

基于深度学习与传统算法的点选验证码识别 绪论 随着互联网的飞速发展&#xff0c;网络安全问题日益凸显&#xff0c;验证码作为一种有效的安全防护手段&#xff0c;广泛应用于登录验证、注册验证、防止自动化攻击等多个场景。传统的验证码形式如文本验证码、图形验证码等&…

使用 Parallel 类进行多线程编码(上)

用 C# 进行多线程编程有很多方式&#xff0c;比如使用 Thread 对象开启一个新线程&#xff0c;但这已经是一种落后的写法了&#xff0c;现在推荐的写法是使用 Parallel 类&#xff0c;它可以让我们像写传统代码一样编写多线程的程序&#xff0c;Parallel 类有三个常用的方法如下…

erlang学习: Mnesia Erlang数据库

创建Mnesia数据库 mnesia:create_schema([node()]).在shell里输入该行代码即可创建一个mnesia数据库于当前文件夹下 编译器文件路径下同样也有 数据库表定义创建 之后是数据库表定义&#xff0c;打开数据库创建完成后&#xff0c;启动数据库&#xff0c;添加一些表定义&…

ccpc网络热身赛: Iris’Food

题目 做法 第一位选除0外最小的数&#xff0c;其他位按从小到大选。 #include<bits/stdc.h> #define int unsigned long long using namespace std; int t,a[20],m; const int mod1e97; int ksm(int a,int b){int ans1;while(b){if(b%2) ansans*a%mod;b/2;aa*a%mod;}r…

哪里打印便宜一点?什么地方打印便宜?

在这个快节奏的时代&#xff0c;无论是学生、上班族还是创业者&#xff0c;都有可能面临需要紧急打印文件的情况。然而&#xff0c;面对市面上琳琅满目的打印服务提供商&#xff0c;如何选择性价比高的打印服务成了许多人关心的问题。今天&#xff0c;我们就来探讨一下“哪里打…

SQL注入基础入门完整教学

SQL注入-概述 什么是sql注入漏洞&#xff1f; 攻击者利用Web应用程序对用户输入验证上的疏忽&#xff0c;在输入的数据中包含对某些数据 库系统有特殊意义的符号或命令&#xff0c;让攻击者有机会直接对后台数据库系统下达指令&#xff0c;进而 实现对后台数据库乃至整个应用…

如何进行不同数据库的集群操作?--从部署谈起,今天来看MySQL和NoSql数据库Redis的集群

篇幅较长&#xff0c;主要分为mysql和Redis两部分。找想要的部分可见目录食用。。 目录 什么是集群&#xff1f;为什么要集群&#xff1f; 1.1 数据库主要分为两大类&#xff1a;关系型数据库与 NoSQL 数据库 1.2 为什么还要用 NoSQL 数据库呢&#xff1f; ----------------…

python怎么输入中文

解决中文输入的两种应用&#xff1a; 在脚本中加语言编码声明 “-*- coding: uft-8 -*-” 应用一&#xff1a;print中出现中文 方法一&#xff1a;用unicode( , encoding utf-8 ) 或者 unicode(" ", encoding "utf-8" )。 方法二&#xff1a;用u 或者…

springboot 的共享session方案?

问&#xff1a;springboot 的共享session方案&#xff1f; 参考&#xff1a; https://juejin.cn/post/7195227930077691963分布式之session共享问题 4种解决方案及spring session的使用_分布式session共享方案-CSDN博客 什么是 Session &#xff1f; 答&#xff1a;因为Http协…

新能源动力组中预充电路及电阻选型分析

新能源动力组中预充电路及电阻选型分析 1.概述2.预充电路与预充电阻3.预充电阻参数选择4.实例分析 1.概述 最近几年&#xff0c;新能源行业在中国得到迅猛发展。由于其高效、节能、低噪声、无污染等特点&#xff0c;它已成为国内工业发展的新趋势包括汽车和飞机。虽然应用在新…

微波无源器件2 用于双极化波束形成网络的增强型双极化定向耦合器

摘要&#xff1a; 定向耦合器和混合相移器是用于实现波束形成网络的关键器件。通常一个波束形成网络用线极化和正交极化两个极化给天线馈电。双极化器件被用于降低波束形成网络的复杂性和尺寸。双极化定向耦合器由相同的作者提出。一种增强型的双极化耦合器在本文中提出。此器件…

JumpServer关闭admin mfa验证

背景 因为上一次启动了mfa验证&#xff0c;但是没有验证就关机重启&#xff0c;导致再开机输入密码后需要mfa绑定&#xff0c;但是怎么也无法绑定成功&#xff0c;导致无法登录。 故希望通过后台取消mfa的验证 from users.models import Useru User.objects.get(usernameadmin…

ThreadLocal 释放的方式有哪些

ThreadLocal基础概念&#xff1a;IT-BLOG-CN ThreadLocal是Java中用于在同一个线程中存储和隔离变量的一种机制。通常情况下&#xff0c;我们使用ThreadLocal来存储线程独有的变量&#xff0c;并在任务完成后通过remove方法清理这些变量&#xff0c;以防止内存泄漏。然而&…

前端开发的单例设计模式

一、什么是单例模式 单例模式&#xff08;Singleton Pattern&#xff09;是一种常见的设计模式&#xff0c;它确保在整个应用程序的生命周期中&#xff0c;一个类只能创建一个实例。无论你在代码的任何地方尝试创建该类的新实例&#xff0c;它都会返回已经存在的唯一实例。这在…

鸿蒙开发(API 12 Beta6版)【NFC标签读写】 网络篇

简介 近场通信(Near Field Communication&#xff0c;NFC)是一种短距高频的无线电技术&#xff0c;在13.56MHz频率运行&#xff0c;通信距离一般在10厘米距离内。电子设备可以通过NFC通信技术和NFC标签通信&#xff0c;从标签中读取数据&#xff0c;或写入数据到标签。 NFC标…

XInput手柄输入封装

功能全面地封装了XInput的输入, 1. 普通按钮按下, 按住, 弹起状态检查, 2. 摇杆4个方向的按下, 按住, 弹起检查 3. 按键状态变化检测并且记录按下触发时间, 按住保持时间, 方便用来完全自定义的输入功能 4. 多手柄输入合并 CXinputHelper.h #pragma once #include <win…

微信支付开发避坑指南

1 微信支付的坑 1.1 不能用前端传递过来的金额 订单的商品金额要从数据库获取&#xff0c;前端只传商品 id。 1.2 交易类型trade type字段不要传错 v2版API&#xff0c;不同交易类型&#xff0c;要调用的支付方式也不同。 1.3 二次签名 下单时&#xff0c;在拿到预支付交…

哈希表-数据结构

一、哈希表基本概念 哈希表&#xff08;也称为散列表&#xff09;是根据键而直接访问在内存存储位置的数据结构&#xff0c;也就是说实际上是经过哈希函数进行映射&#xff0c;映射道表中一个位置来访问记录&#xff0c;这个存放记录的数组称为散列表。 哈希函数&#xff1a;就…

计组基础知识

操作系统的特征 并发 共享 虚拟 异步 操作系统的功能 1、资源分配&#xff0c;资源回收 硬件资源 CPU、内存、硬盘、I/O设备。 2、为应⽤程序提供服务 操作系统将硬件资源的操作封装起来&#xff0c;提供相对统⼀的接⼝&#xff08;系统调⽤&#xff09;供开发者调⽤。 3、管…

Redis 集群会有写操作丢失吗?为什么?

大家好&#xff0c;我是锋哥。今天分享关于 【Redis 集群会有写操作丢失吗&#xff1f;为什么&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; Redis 集群会有写操作丢失吗&#xff1f;为什么&#xff1f; Redis 并不能保证数据的强一致性&#xff0c;这意味这在…