C语言.指针(5)

指针(5)

  • 1.sizeof和strlen的对比
    • 1.1sizeof
    • 1.2strlen
    • 1.3sizeof和strlen的对比
  • 2.数组和指针笔试题解析
    • 2.1一维数组
    • 2.2字符数组
    • 2.3二维数组
  • 3.指针运算笔试题解析
    • 3.1 题目1
    • 3.2 题目2
    • 3.3 题目3
    • 3.4 题目4
    • 3.5 题目5
    • 3.6 题目6
    • 3.7 题目7

1.sizeof和strlen的对比

1.1sizeof

sizeof 计算变量所占内存内存空间大小的,单位是字节,如果操作数是类型的话,计算的是使用类型创建的变量所占内存空间的大小。

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

比如:

#include<stdio.h>

int main()
{
	int a = 0;

	printf("%zd\n", sizeof(a));//4
	printf("%zd\n", sizeof a);//4
	printf("%zd\n", sizeof(int));//4

	return 0;
}

1.2strlen

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

size_t strlen(const char* str);

统计的是从 strlen 函数的参数 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));//35:随机值
	printf("%zd\n", strlen(arr2));//3

	printf("%zd\n", sizeof(arr1));//3
	printf("%zd\n", sizeof(arr2));//4

	return 0;
}

1.3sizeof和strlen的对比

在这里插入图片描述

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

2.1一维数组

#include<stdio.h>
#include<string.h>

int main()
{
	int a[] = { 1,2,3,4,5,6,7,8,9,10 };

	printf("%zd\n", sizeof(a));
	//a表示的是整个数组的地址,所以是sizeof(int)*10 == 40个字节
	printf("%zd\n", sizeof(a + 0));
	//a表示的是数组首元素的地址,计算的是首元素的地址,是地址就是4/8个字节
	printf("%zd\n", sizeof(*a));
	//a表示的是数组首元素的地址,*a表示的是 首元素的大小,是int类型,为4个字节
	printf("%zd\n", sizeof(a + 1));
	//a表示的是数组首元素的地址,a + 1,表示的是a[1]的地址,是地址就是4/8个字节
	printf("%zd\n", sizeof(a[0]));
	//a表示的是数组首元素的地址,a[0]表示的是数组首元素,是int类型,为4个字节
	printf("%zd\n", sizeof(a[1]));
	//a表示的是数组首元素的地址,a[1]表示的是第二个元素,是int类型,为4个字节
	printf("%zd\n", sizeof(&a));
	//&a,表示的是取出的是整个数组的地址,是地址就是4/8个字节
	printf("%zd\n", sizeof(&a + 1));
	//&a,表示的是取出的是整个数组的地址,&a + 1就跳出整个整型数组,是地址就是4/8个字节
	printf("%zd\n", sizeof(&a[0]));
	//&a[0]表示的是首元素的地址,是地址就是4/8个字节
	printf("%zd\n", sizeof(&a[0] + 1));
	//&a[0]表示的是首元素的地址,&a[0] + 1表示的是下标为1的元素的地址,是地址就是4/8个字节
	
	return 0;
}

2.2字符数组

代码1:

#include <stdio.h>

int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	//             a b c d e f 
	printf("%zd\n", sizeof(arr));
	//arr表示的是整个数组的地址,所以是sizeof(char)*6 == 6个字节
	printf("%zd\n", sizeof(arr + 0));
	//arr + 0表示的是数组首元素的地址,是地址就是4/8个字节
	printf("%zd\n", sizeof(*arr));
	//arr表示的是整个数组的地址,*arr得到的是首元素,arr的类型是char,是1个字节
	printf("%zd\n", sizeof(arr[1]));
	//arr[1],表示的是下标为1的数组元素,类型是char,是1个字节
	printf("%zd\n", sizeof(&arr));
	//&arr表示取出的是整个数组的地址,是地址就是4/8个字节
	printf("%zd\n", sizeof(&arr + 1));
	//&arr,表示的是取出的是整个数组的地址,&a + 1就跳出整个字符数组,是地址就是4/8个字节
	printf("%zd\n", sizeof(&arr[0] + 1));
	//&arr[0]表示的是首元素的地址,&arr[0] + 1表示的是下标为1的数组元素的地址,是地址就是4/8个字节

	return 0;
}

代码2:

#include <stdio.h>
#include<string.h>
//size_t strlen ( const char * str );
//strlen()需要的是首元素的地址
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	//              a b c d e f
	printf("%zd\n", strlen(arr));
	//arr表示的是首元素的地址,数组中没有\0,就会导致越界访问,结果就会是随机值
	printf("%zd\n", strlen(arr + 0));
	//arr表示的是首元素的地址,数组中没有\0,就会导致越界访问,结果就会是随机值
	printf("%zd\n", strlen(*arr));
	//arr是首元素的地址,*arr是首元素,就是'a','a'的ASCII码值是97,相当于把97当作地址传给strlen(),strlen得到的就是野指针, 代码是有问题的,结果错误//err
	printf("%zd\n", strlen(arr[1]));
	//arr[1]表示的是下标为1的数组元素,就是'b','b'的ASCII码值是98,相当于把98当作地址传给strlen(),strlen得到的就是野指针, 代码是有问题的,结果错误//err
	printf("%zd\n", strlen(&arr));
	//&arr表示取出的是整个数组的地址,数组中没有\0,就会导致越界访问,起始位置指向数组的第一个元素的位置,结果就会是随机值 x
	printf("%zd\n", strlen(&arr + 1));
	//&arr表示取出的是整个数组的地址,&arr + 1跳过整个数组的地址,数组中没有\0,就会导致越界访问,起始位置指向数组的最后一个元素的下一个位置,结果就会是随机值 x - 6
	printf("%zd\n", strlen(&arr[0] + 1));
	//从第2个元素开始向后统计的,得到的也是随机值 x - 1
	return 0;
}

代码3:

#include <stdio.h>
#include <string.h>

int main()
{
	char arr[] = { "abcdef" };
	//              a b c d e f \0
	printf("%zd\n", sizeof(arr));
	//arr表示的是整个数组的地址,计算的是整个数组的大小,sizeof(char)*7 == 7个字节
	printf("%zd\n", sizeof(arr + 0));
	//arr + 0表示的是数组首元素的地址,是地址就是4/8个字节
	printf("%zd\n", sizeof(*arr));
	//*arr得到的是数组的首元素,类型是char,1个字节
	printf("%zd\n", sizeof(arr[1]));
	//arr[1]是数组下标为1的元素,类型是char,1个字节
	printf("%zd\n", sizeof(&arr));
	//&arr取出的是整个数组的地址,是地址就是4/8字节
	printf("%zd\n", sizeof(&arr + 1));
	//&arr取出的是整个数组的地址,&arr + 1跳过整个数组,还是地址,是地址就是4/8个字节
	printf("%zd\n", sizeof(&arr[0] + 1));
	//&arr[0]表示的是数组首元素的地址,&arr[0] + 1就是下标为1的数组元素的地址,是地址就是4/8个字节
	return 0;
}

代码4:

#include <stdio.h>
#include<string.h>

int main()
{
	char arr[] = "abcdef";
	//            a b c d e f \0
	printf("%zd\n", strlen(arr));
	//arr首元素的地址,向后在\0之前有6个字符
	printf("%zd\n", strlen(arr + 0));
	//arr首元素的地址,arr+0还是首元素的地址,向后在\0之前有6个字符
	printf("%zd\n", strlen(*arr));
	//arr是首元素的地址,*arr是首元素,就是'a','a'的ASCII码值是97,相当于把97当作地址传给strlen(),strlen得到的就是野指针, 代码是有问题的,结果错误
	printf("%zd\n", strlen(arr[1]));
	//arr[1]是下标为1数组的元素,就是'b','b'的ASCII码值是98,相当于把98当作地址传给strlen(),strlen得到的就是野指针, 代码是有问题的,结果错误
	printf("%zd\n", strlen(&arr));
	//&arr表示取出的是整个数组的地址,起始位置指向数组的第一个元素的位置,向后在\0之前有6个字符
	printf("%zd\n", strlen(&arr + 1));
	//&arr表示取出的是整个数组的地址,&arr + 1跳过整个数组的地址,起始位置指向数组的最后一个元素的下一个位置,数组中没有\0,就会导致越界访问,结果就会是随机值
	printf("%zd\n", strlen(&arr[0] + 1));
	//&arr[0]表示的是数组首元素的地址,&arr[0] + 1就是下标为1的数组元素的地址,向后在\0之前有5个字符
	return 0;
}

代码5:

#include <stdio.h>

int main()
{
	const char* p = "abcdef";
	//               a b c d e f \0
	printf("%zd\n", sizeof(p));
	//p是指针变量,计算的是指针变量的大小,4/8个字节
	printf("%zd\n", sizeof(p + 1));
	//p + 1是b的地址,是地址大小就是4/8个字节
	printf("%zd\n", sizeof(*p));
	//p的类型是const char*,*p就是char类型,大小是1个字节
	printf("%zd\n", sizeof(p[0]));
	//1.p[0] -> *(p + 0) -> *p -> 得到的就是'a',大小是1个字节
	//2.把常量字符串想象成数组,p可以理解为数组名,p[0], 就是首元素
	printf("%zd\n", sizeof(&p));
	//取出的是p的地址,地址的大小是4/8个字节
	printf("%zd\n", sizeof(&p + 1));
	//&p + 1是跳过p指针变量后的地址,是地址就是4/8个字节
	printf("%zd\n", sizeof(&p[0] + 1));
	//&p[0]表示的是取出字符串首字符的地址,&p[0] + 1是第二个字符的地址,大小是4/8个字节
	return 0;
}

代码6:

#include <stdio.h>
#include <string.h>

int main()
{
	const char* p = "abcdef";
	//               a b c d e f \0
	printf("%zd\n", strlen(p));
	//p是指针变量,向后在\0之前有6个字符
	printf("%zd\n", strlen(p + 1));
	//p是指针变量,p + 1向后在\0之前有5个字符
	//printf("%zd\n", strlen(*p));
	//p的类型是const char*,*p就是char类型,*p就是'a'-97,err
	//printf("%zd\n", strlen(p[0]));
	//p[0] -> *(p + 0) -> *p -> 得到的就是'a'-97.err
	printf("%zd\n", strlen(&p));
	//&p是指针变量p的地址,和字符串"abcdef"关系不大,从p这个指针变量的起始位置开始向后数的,p变量存放的地址是什么,不知道,所以是随机值
	printf("%zd\n", strlen(&p + 1));
	//和上面的情况一样了,随机值
	printf("%zd\n", strlen(&p[0] + 1));
	//p[0] -> *(p + 0) -> *p,&p[0] == &*p == p:首元素的地址
	//&p[0]-取出字符串首字符的地址,+1是第二个字符的地址, 5
	return 0;
}

2.3二维数组

#include<stdio.h>

int main()
{
	int a[3][4] = { 0 };

	printf("%zd\n", sizeof(a));
	//a是二维数组的数组名,单独放在sizeof()中,计算的是整个数组的大小,3*4*sizeof(int) == 48个字节
	printf("%zd\n", sizeof(a[0][0]));
	//a[0][0] 是第一行第一个元素,大小是4个字节 
	printf("%zd\n", sizeof(a[0]));
	//a[0]表示的是第一行的数组名,数组名单独放在sizeof内部了,计算的是数组的总大小 16 个字节
	printf("%zd\n", sizeof(a[0] + 1));
	//a[0] + 1表示的是第二行的数组名,数组名单独放在sizeof内部了,计算的是数组的总大小 16 个字节
	printf("%zd\n", sizeof(*(a[0] + 1)));
	//*(a[0] + 1)表示的是第一行第二个元素,大小就是4
	//*(*(p + 0) + 1) == p[0][1]
	printf("%zd\n", sizeof(a + 1));
	//a表示数组首元素的地址,是二维数组首元素的地址,也就是第一行的地址,a+1,跳过一行,指向了第二行,a+1是第二行的地址,a+1是数组指针,是地址大小就是4/8个字节
	printf("%zd\n", sizeof(*(a + 1)));
	//1.a表示数组首元素的地址,是二维数组首元素的地址,也就是第一行的地址,a+1,跳过一行,指向了第二行,*(a+1)表示的是第二行,a+1是数组指针,计算的是第二行的大小 - 16字节
	//2.*(a + 1) == a[1] -> 表示的是第二行的数组名,单独放在sizeof中,4*sizeof(int) == 16字节
	printf("%zd\n", sizeof(&a[0] + 1));
	//&a[0]取出的是第一行的数组名,&a[0] + 1指向的是第二行的地址,是地址大小就是4/8个字节
	printf("%zd\n", sizeof(*(&a[0] + 1)));
	//&a[0] + 1指向的是第二行的地址,*(&a[0] + 1)是对第二行的地址解引用,访问的就是第二行,大小是16字节
	printf("%zd\n", sizeof(*a));
	//1.a表示的是二维数组首元素的地址,计算的就是第一行的大小,16字节
	//2.*a == *(a + 0) == a[0] ->第一行的数组名
	printf("%zd\n", sizeof(a[3]));
	//a[3]无需真实存在,仅仅通过类型的推断就能算出长度a[3]是第四行的数组名,单独放在sizeof内部,计算的是第四行的大小,16个字节
	return 0;
}

数组名的意义:

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

3.指针运算笔试题解析

3.1 题目1

#include <stdio.h>

int main()
{
	int a[] = { 1,2,3,4,5 };
	int* ptr = (int*)(&a + 1);

	printf("%d %d\n", *(a + 1), *(ptr - 1));
	//                  2         5
	return 0;
}
//输出的结果是什么?

3.2 题目2

//在X86的环境下
//假设结构体的大小是20个字节
//输出的结果是什么?
#include <stdio.h>

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);
	printf("%p\n", (unsigned long)p + 0x1);
	printf("%p\n", (unsigned int*)p + 0x1);

	return 0;
}

3.3 题目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]);//1

	return 0;
}

3.4 题目4

//假设环境是x86环境,程序输出的结果是啥?
#include<stdio.h>

int main()
{
	int a[5][5];
	int(*p)[4];//p是一个数组指针,p指向的数组是4个整型元素

	p = a;

	printf("%p, %d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
	//                 FFFFFFFC,            -4
	return 0;
}

3.5 题目5

#include<stdio.h>

int main()
{
	int a[2][5] = { 1,2,3,4,5,6,7,8,9,10 };
	
	int* ptr1 = (int*)(&a + 1);
	int* ptr2 = (int*)(*(a + 1));

	printf("%d, %d\n", *(ptr1 - 1), *(ptr2 - 1));
	//                   10,          5
	return 0;
}

3.6 题目6

#include<stdio.h>

int main()
{
	char* a[] = {"work", "at", "alibaba"};
	char** pa = a;
	pa++;

	printf("%s\n", *pa);//at

	return 0;
}

3.7 题目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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Qt栅格布局的示例

QGridLayout * layoutnew QGridLayout;for(int i0;i<10;i){for(int j0;j<6;j){QLabel *labelnew QLabel(this);label->setText(QString("%1行%2列").arg(i).arg(j));layout->addWidget(label,i,j);}}ui->widget->setLayout(layout); 这样写程序会崩…

NC65 查询默认密码(sql)

NC65 使用sql查询设置的默认密码&#xff08;如果系统设置有&#xff09;&#xff1a; select * from sm_user_defaultpwd

电商技术揭秘十三:云计算在电商中的应用场景

相关系列文章 电商技术揭秘一&#xff1a;电商架构设计与核心技术 电商技术揭秘二&#xff1a;电商平台推荐系统的实现与优化 电商技术揭秘三&#xff1a;电商平台的支付与结算系统 电商技术揭秘四&#xff1a;电商平台的物流管理系统 电商技术揭秘五&#xff1a;电商平台…

C++11 设计模式2. 简单工厂模式

简单工厂&#xff08;Simple Factory&#xff09;模式 我们从实际例子出发&#xff0c;来看在什么情况下&#xff0c;应用简单工厂模式。 还是以一个游戏举例 //策划&#xff1a;亡灵类怪物&#xff0c;元素类怪物&#xff0c;机械类怪物&#xff1a;都有生命值&#xff0…

「Java开发指南」如何利用MyEclipse启用Spring DSL?(一)

本教程将引导您通过启用Spring DSL和使用Service Spring DSL抽象来引导Spring和Spring代码生成项目&#xff0c;本教程中学习的技能也可以很容易地应用于其他抽象。在本教程中&#xff0c;您将学习如何&#xff1a; 为Spring DSL初始化一个项目创建一个模型包创建一个服务和操…

实体抽取全解析:技术与实战

目录 一、前言二、实体抽取技术概览基于规则的实体抽取基于统计的实体抽取基于深度学习的实体抽取 三、实体抽取的发展历程早期的实体抽取方法基于规则和词典的方法基于特征的机器学习方法 深度学习时代的实体抽取从传统模型到神经网络序列标注模型的兴起预训练语言模型的革命 …

如何修改 MySQL 8.0 的密码

在 MySQL 8.0 中修改用户密码是数据库管理的一项基本任务。本教程将引导您完成这一过程&#xff0c;确保即使是初学者也能理解并成功执行。 介绍 MySQL 是最流行的关系数据库管理系统之一。作为数据库管理员或开发人员&#xff0c;您可能需要更改用户的密码来保证账户安全。本…

QT 信号与槽的简单使用

文章目录 1.通过Singloat and Slots Editor 添加信号与槽2. 通过拖动动态添加3.通过转到槽方式添加&#xff08;自动关联&#xff09;4. 自定义信号与槽&#xff08;connect)4.1 connect方式4.2 自定义信号 1.通过Singloat and Slots Editor 添加信号与槽 点添加&#xff0c;然…

突破像素限制,尽显照片细腻之美——Topaz Gigapixel AI for Mac/Win

在这个数字化的时代&#xff0c;我们都热爱用照片记录生活中的美好瞬间。然而&#xff0c;有时候我们会发现&#xff0c;由于各种原因&#xff0c;照片的像素可能无法满足我们的需求。这时候&#xff0c;Topaz Gigapixel AI for Mac/Win 这款强大的照片放大工具应运而生。 Top…

open c UF_MODL_create_simple_hole 识别放置平面 UF_MODL_ask_face_data

在BLOCK上创建一个简单孔 UF_FEATURE_SIGN sign UF_NULLSIGN;double block_orig[3] { -25.0,-25.0,0.0 };char* block_len[3] { "50","50","30" };tag_t blk_obj;UF_MODL_create_block1(sign, block_orig, block_len, &blk_obj);tag_t bo…

PSPICE、Multisim和Saber哪个更适合电路仿真?没想到是它

PSPICE、Multisim和Saber这三个软件都是非常流行的模拟电路仿真工具&#xff0c;它们各自有各自的优缺点&#xff0c;我简单讲一下&#xff1a; PSPICE&#xff1a; 优点&#xff1a; 精度高&#xff1a;PSPICE是专业的电路仿真软件&#xff0c;可以进行高精度的模拟电路仿真…

兮兮牧场养殖小游戏积分兑换互动商城引流模式

刚注册的新会员必须要进入牧场才能激活所有功能 一、获得动物的途径的方式 第一种是邀请好友注册获得&#xff0c;第二种是看广告获得 邀诘好友注册获得动物明细: 1、从兮兮牧场的邀请好友的链接去邀请好友才能获得&#xff0c;其他邀请码无效 2、注册赠送小鸡一只; 3、邀…

基于Springboot + vue + MySQL +Tomcat 社区医院管理服务系统 (含源码)

目录 &#x1f4da; 前言 &#x1f4d1;摘要 &#x1f4d1;系统架构 &#x1f4da; 数据库设计 &#x1f4ac; 用户注册实体属性图 &#x1f4ac; 医生实体属性图 &#x1f4da; 系统功能的具体实现 &#x1f4ac; 前台模块 用户注册 医生信息 &#x1f4ac; 后台功能模…

Docker部署SpringBoot+Vue前后端分离项目

文章目录 1. 安装Docker1. 1 卸载旧版Docker1.2 配置yum仓库1.3 安装Docker1.4 添加自启动配置1.5 配置阿里云镜像加速1.6 测试 2. 安装Nginx2.1 拉取镜像2.2 安装Nginx2.3 测试 3. 安装MySQL3.1 拉取镜像3.2 安装MySQL3.3 连接MySQL 4. 部署SpringBoot项目4.1 Maven打包4.2 编…

centos7安装 on-my-zsh

如下&#x1f447; yum install -y zsh chsh -s /bin/zsh yum install -y git sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)" 重启即可生效啦~

Windows搭建LightPicture图片管理网站结合内网穿透实现公网访问本地图片

文章目录 1.前言2. Lightpicture网站搭建2.1. Lightpicture下载和安装2.2. Lightpicture网页测试2.3.cpolar的安装和注册 3.本地网页发布3.1.Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1.前言 现在的手机越来越先进&#xff0c;功能也越来越多&#xff0c;而手机…

【LAMMPS学习】八、基础知识(2.2)类型标签

8. 基础知识 此部分描述了如何使用 LAMMPS 为用户和开发人员执行各种任务。术语表页面还列出了 MD 术语&#xff0c;以及相应 LAMMPS 手册页的链接。 LAMMPS 源代码分发的 examples 目录中包含的示例输入脚本以及示例脚本页面上突出显示的示例输入脚本还展示了如何设置和运行各…

车载数据终端丨车载平板丨车载平板电脑为何在叉车上使用

车载终端平板是一种专门设计用于车辆和运输行业的电子设备。它们通常被安装在叉车、卡车、拖车和其他机动车辆上&#xff0c;以便驾驶员可以更方便地管理和控制运输任务。 为了了解车载终端平板为何能够在叉车上使用&#xff0c;我们需要考虑以下几个方面&#xff1a; 物理结构…

HarmonyOS4 页面路由

Index.ets: import router from ohos.routerclass RouterInfo {// 页面路径url: string// 页面标题title: stringconstructor(url: string, title: string) {this.url urlthis.title title} }Entry // 入口組件 Component struct Index {State message: string 页面列表// …

StylizedGS: Controllable Stylization for 3D Gaussian Splatting

StylizedGS: Controllable Stylization for 3D Gaussian Splatting StylizedGS&#xff1a;3D高斯溅射的可控样式化 Dingxi Zhang, Zhuoxun Chen, Yu-Jie Yuan, Fang-Lue Zhang, Zhenliang He, Shiguang Shan, and Lin Gao1 张定西&#xff0c;陈卓勋&#xff0c;袁玉洁&#x…