指针初阶(1)

news2024/9/22 1:44:22

文章目录

  • 目录
    • 1. 指针是什么
    • 2. 指针变量的类型
      • 2.1 指针变量+-整数
      • 2.2 指针变量的解引用
    • 3. 野指针
      • 3.1 野指针成因
      • 3.2 如何规避野指针
    • 4. 指针运算
      • 4.1 指针+-整数
      • 4.2 指针-指针
      • 4.3 指针的关系运算
  • 附:

目录

  • 指针是什么
  • 指针变量的类型
  • 野指针
  • 指针运算
  • 指针和数组
  • 二级指针
  • 指针数组

1. 指针是什么

  1. 指针是内存中一个最小单元(1个字节)的编号,也就是地址
  2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量(存放在指针变量中的值都被当成地址处理)

总结: 指针就是地址,口语中说的指针通常指的是指针变量

#include <stdio.h>

int main()
{
	int a = 100;
	int * pa = &a;//pa是专门用来存放地址(指针)的,这里的pa就被称为指针变量
	
	//指针变量在32位平台下是4个字节
	//指针变量在64位平台下是8个字节
	
	//int arr[10];
	//printf("%p", &a);

	return 0;
}

注:

  • 经过仔细的计算和权衡,我们发现一个字节一个对应的地址是比较合适的。
  • 对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电频(高电压)和低电频(低电压),就是(1或者0),那么32根地址线会产生2的32次方个地址,每个地址标识一个字节,那我们就可以给 (2^32Byte == 2^32/1024KB == 2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB) 4G的空间进行编址,64位机器同理。
  • 32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节
  • 那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。
#include <stdio.h>

int main()
{
	printf("%d\n", sizeof(char*));
	printf("%d\n", sizeof(short*));
	printf("%d\n", sizeof(int*));
	printf("%d\n", sizeof(long*));
	printf("%d\n", sizeof(float*));
	printf("%d\n", sizeof(double*));

	return 0;
}

2. 指针变量的类型

char * pc = NULL;
short * ps = NULL;
int * pi = NULL;
long * pl = NULL;
float * pf = NULL;
double * pd = NULL;

这里可以看到,指针变量的定义方式是: type + *

其实:
char* 类型的指针变量是为了存放 char 类型变量的地址。
short* 类型的指针变量是为了存放 short 类型变量的地址。
int* 类型的指针变量是为了存放 int 类型变量的地址。

但是,指针变量的大小又和指针变量的类型无关,那么指针变量的类型的意义是什么呢?

2.1 指针变量±整数

#include <stdio.h>

int main()
{
	int a = 0x11223344;//0x开头的是16进制数字
	int * pa = &a;
	char * pc = &a;
	printf("%p\n", pa);//010FFE7C
	printf("%p\n", pc);//010FFE7C

	printf("%p\n", pa+1);//010FFE80
	printf("%p\n", pc+1);//010FFE7D

	return 0;
}

总结: 指针变量的类型决定了指针向前或者向后走一步有多大(距离)。

2.2 指针变量的解引用

#include <stdio.h>

int main()
{
	int a = 0x11223344;//0x开头的是16进制数字
	char * pa = &a;
	*pa = 0;

	return 0;
}

指针变量的解引用(1)

#include <stdio.h>

int main()
{
	int a = 0x11223344;
	int* pa = &a;
	*pa = 0;

	return 0;
}

指针变量的解引用(2)
总结: 指针变量的类型决定了对指针变量解引用的时候有多大的权限(能操作几个字节),比如: char* 的指针变量解引用就只能访问一个字节,而 int* 的指针变量解引用就能访问四个字节

3. 野指针

概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

3.1 野指针成因

  1. 指针未初始化
#include <stdio.h>

int main()
{
	int* p;//局部变量不初始化的时候,内容是随机值
	*p = 20;
	printf("%d\n", *p);

	return 0;
}
  1. 指针越界访问
#include <stdio.h>

int main()
{
    int arr[10] = { 0 };
    int * p = arr;
    int i = 0;
    
    for (i = 0; i <= 11; i++)
    {
        //当指针指向的范围超出数组arr的范围时,p就是野指针
        *(p++) = i;
    }
   
    return 0;
}
  1. 指针指向的空间释放

放在动态内存开辟的时候讲解,这里可以简单提示一下。

#include <stdio.h>

int * test()
{
	int a = 110;
	return &a;
}

int main()
{
	int* p = test();
	printf("%d\n", *p);

	return 0;
}

3.2 如何规避野指针

  1. 指针初始化
  2. 小心指针越界
  3. 指针指向空间释放,及时置NULL
  4. 避免返回局部变量的地址
  5. 指针使用之前检查有效性
#include <stdio.h>

int main()
{
	int a = 10;
	int* p = &a;

	int* ptr = NULL;//ptr是一个空指针,没有指向任何有效的空间,这个指针不能直接使用
	//int* ptr2;//野指针

	if (ptr != NULL)
	{
		//使用
	}

	return 0;
}

4. 指针运算

4.1 指针±整数

#include <stdio.h>

int main()
{
	int arr[10] = { 0 };
	//不使用下标访问数组
	int* p = &arr[0];
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);

	for (i = 0; i < sz; i++)
	{
		*p = i;
		p++;//p = p + 1
	}

	p = arr;

	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));//p + i
	}

	/*for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}*/

	return 0;
}
#define N_VALUES 5

int main()
{
	float values[N_VALUES];
	float *vp;
	//指针+-整数;指针的关系运算
	for (vp = &values[0]; vp < &values[N_VALUES]; )
	{
     	*vp++ = 0;
	}
	
	return 0;
}

注:

//int arr[10]
//int* p = arr;
//*(p+i) == arr[i]
//*(arr+i) == arr[i]

//arr[i] == *(arr+i) == *(i+arr) == i[arr]

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

	for (i = 0; i < 10; i++)
	{
		printf("%d ", i[arr]);//[] 操作符
	}

	//2+3 --> 3+2
	//arr[i] --> i[arr]

	return 0;
}

4.2 指针-指针

//地址-地址
//指针-指针

#include <stdio.h>

int main()
{
	int arr[10] = { 0 };
	printf("%d\n", &arr[9] - &arr[0]);//9
	printf("%d\n", &arr[0] - &arr[9]);//-9

	return 0;
}

//指针-指针得到的数值的绝对值是指针和指针之间的元素个数

之前我们学习过如何通过自己的函数来实现计算字符串长度:

#include <stdio.h>

int my_strlen(char* s)
{
	int count = 0;

	while (*s != '\0')
	{
		count++;
		s++;
	}

	return count;
}

int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);
	printf("%d\n", len);

	return 0;
}
#include <stdio.h>

int my_strlen(char* s)
{
	
	if ('\0' == *s)
	{
		return 0;
	}
	else
	{
		return 1 + my_strlen(s + 1);
	}

}

int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);
	printf("%d\n", len);

	return 0;
}

其实,这也能通过指针-指针来实现:

#include <stdio.h>

int my_strlen(char* s)
{
	char* start = s;

	while (*s != '\0')
	{
		s++;
	}

	return s - start;
}

int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);
	printf("%d\n", len);

	return 0;
}
#include <stdio.h>

int my_strlen(char* s)
{
	char* start = s;

	while (*s)//a b c d e f \0 -> 0
	{
		s++;
	}

	return s - start;
}

int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);
	printf("%d\n", len);

	return 0;
}
#include <stdio.h>

int my_strlen(char* s)
{
	char* start = s;

	while (*s++)
	{
		;
	}

	return s - start - 1;
}

int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);
	printf("%d\n", len);

	return 0;
}

注:

#include <stdio.h>

int main()
{
	int arr[10] = { 0 };
	char ch[5] = { 0 };
	//指针和指针相减的前提是:两个指针指向了同一块空间
	printf("%d\n", &ch[4] - &arr[0]);//err

	return 0;
}

4.3 指针的关系运算

#define N_VALUES 5

int main()
{
	float values[N_VALUES];
	float *vp;

	for(vp = &values[N_VALUES]; vp > &values[0];)
	{
    	*--vp = 0;
	}
	
	return 0;
}

这段代码也可以这样写:

#define N_VALUES 5

int main()
{
	float values[N_VALUES];
	float *vp;

	for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
	{
    	*vp = 0;
	}
	
	return 0;
}

实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证它可行。

标准规定:

允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。

附:

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

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

相关文章

redis集群设置

先下载redis数据库可以在一台机器上设置redis集群高可用 cd /etc/redis/ mkdir -p redis-cluster/redis600{1..6} for i in {1..6} do cp /opt/redis-5.0.7/redis.conf /etc/redis/redis-cluster/redis600$i cp /opt/redis-5.0.7/src/redis-cli /opt/redis-5.0.7/src/redis-s…

号外号外!首届开源 AI 游戏挑战赛圆满结束!

&#x1f917; 宝子们可以戳 阅读原文 查看文中所有的外部链接哟&#xff01; 北京时间 7 月 8 日到 7 月 10 日&#xff0c; 我们举办了首届开源 AI 游戏开发挑战赛。这是一场激动人心的赛事活动&#xff0c;游戏开发者在紧迫的 48 小时内使用 AI 创造、创新有创意的游戏。 本…

gazebo学习记录(杂乱)

一、完整系列教程 如何使用gazebo进行机器人仿真&#xff08;很重要&#xff09;&#xff1a;https://zhuanlan.zhihu.com/p/367796338 基础教程和关键概念讲解&#xff08;很重要&#xff09;&#xff1a;https://zhuanlan.zhihu.com/p/363385163 古月居&#xff1a;http://w…

Web自动化测试高级定位xpath

高级定位-xpath 目录 xpath 基本概念xpath 使用场景xpath 语法与实战 xpath基本概念 XPath 是一门在 XML 文档中查找信息的语言XPath 使用路径表达式在 XML 文档中进行导航XPath 的应用非常广泛XPath 可以应用在UI自动化测试 xpath 定位场景 web自动化测试app自动化测试 …

Selenium多浏览器处理

Python 版本 #导入依赖 import os from selenium import webdriverdef test_browser():#使用os模块的getenv方法来获取声明环境变量browserbrowser os.getenv("browser").lower()#判断browser的值if browser "headless":driver webdriver.PhantomJS()e…

每日一题——删除有序数组中的重复项

删除有序数组中的重复项 题目链接 注&#xff1a;本题所采用的方法是建立在移除元素的基础之上的&#xff0c;如果大家对双指针的方法不大了解&#xff0c;或者不会做《移除元素》这一题&#xff0c;建议先去看看&#x1f449;传送门 具体步骤 定义两个指针slow和fast&#…

计算并展示指定文件夹的大小

背景需求 有时候电脑里磁盘空间越来越小&#xff0c;不得不删除一些占空间大的文件。最笨的方法就是对某一个文件夹下一个一个查看大小&#xff0c;效率太慢。 效果图 Code import os import matplotlib.pyplot as plt plt.rcParams[font.family] sans-serif # 设置字体为…

Jenkins pipeline 脚本语言学习支持

1 引言 Groovy是用于Java虚拟机的一种敏捷的动态语言&#xff0c;它是一种成熟的面向对象编程语言&#xff0c;既可以用于面向对象编程&#xff0c;又可以用作纯粹的脚本语言。 使用该种语言不必编写过多的代码&#xff0c;同时又具有闭包和动态语言中的其他特性。 Groovy是一…

大模型开发(十四):使用OpenAI Chat模型 + Google API实现一个智能收发邮件的AI应用程序

全文共1.2w余字&#xff0c;预计阅读时间约24~40分钟 | 满满干货(附代码)&#xff0c;建议收藏&#xff01; 本文目标&#xff1a;将Gmail API接入Chat模型&#xff0c;编写一个智能收发邮件的AI应用程序 代码下载点这里 一、背景 大模型应用开发从谷歌云入手进行学习和AI…

Python爬虫的urlib的学习(学习于b站尚硅谷)

目录 一、页面结构的介绍  1.学习目标  2.为什么要了解页面&#xff08;html&#xff09;  3. html中的标签&#xff08;仅介绍了含表格、无序列表、有序列表、超链接&#xff09;  4.本节的演示 二、Urllib  1.什么是互联网爬虫&#xff1f;  2.爬虫核心  3.爬虫…

Sentinel 容灾中心的使用

Sentinel 容灾中心的使用 往期文章 Nacos环境搭建Nacos注册中心的使用Nacos配置中心的使用 熔断/限流结果 Jar 生产者 spring-cloud-alibaba&#xff1a;2021.0.4.0 spring-boot&#xff1a;2.6.8 spring-cloud-loadbalancer&#xff1a;3.1.3 sentinel&#xff1a;2021.0…

如何快速模拟一个后端 API

第一步&#xff1a;创建一个文件夹&#xff0c;用来存储你的数据 数据&#xff1a; {"todos": [{ "id": 1, "text": "学习html44", "done": false },{ "id": 2, "text": "学习css", "…

只会“点点点”,凭什么让开发看的起你?

众所周知&#xff0c;如今无论是大厂还是中小厂&#xff0c;自动化测试基本是标配了&#xff0c;毕竟像双 11、618 这种活动中庞大繁杂的系统&#xff0c;以及多端发布、多版本、机型发布等需求&#xff0c;但只会“写一些自动化脚本”很难胜任。这一点在招聘要求中就能看出来。…

Android 面试题 优化 (一)

&#x1f525; Android性能优化指标 &#x1f525; ​ &#x1f525; 包体积优化 &#x1f525; 安装包的大小会影响用户的安装率&#xff0c;如果一个包的太大&#xff0c;用户安装的意愿会大大降低。 经过包分析可以看到&#xff0c;安装包最大的部分是资源和第三方库&#…

奥迪A3:最新款奥迪A3内饰设计及智能科技应用

奥迪A3一直以来都是奥迪的入门级车型&#xff0c;但这并不意味着它在科技和内饰方面会有所退步。最新款奥迪A3的内饰设计和智能科技应用让人们再次惊叹奥迪的创新能力。 内饰设计 奥迪A3最新款的内饰设计引入了奥迪最新的设计元素&#xff0c;比如8.8英寸的中控显示屏&#xf…

山西电力市场日前价格预测【2023-07-30】

日前价格预测 预测明日&#xff08;2023-07-30&#xff09;山西电力市场全天平均日前电价为287.10元/MWh。其中&#xff0c;最高日前电价为309.20元/MWh&#xff0c;预计出现在09: 15。最低日前电价为252.82元/MWh&#xff0c;预计出现在24: 00。 价差方向预测 1&#xff1a;实…

Dendrogram | 今天是人见人爱、花见花开的环形Dendrogram!~(附完整代码)

1写在前面 好长时间没更新了&#xff0c;这周真的是天天都在手术室度过&#xff0c;常讲到的一句话就是苦的一比啊。&#x1fae0; 很久没有见过外面的世界了&#xff0c;世界那么大&#xff0c;我也想去看看&#xff01;~&#x1f602; 废话太多了&#xff0c;今天的教程是环形…

网络安全/信息安全(黑客技术)自学笔记

一、网络安全基础知识 1.计算机基础知识 了解了计算机的硬件、软件、操作系统和网络结构等基础知识&#xff0c;可以帮助您更好地理解网络安全的概念和技术。 2.网络基础知识 了解了网络的结构、协议、服务和安全问题&#xff0c;可以帮助您更好地解决网络安全的原理和技术…

从分片传输到并行传输之大文件传输加速技术

随着大文件的传输需求越来越多&#xff0c;传输过程中也会遇到很多困难&#xff0c;比如传输速度慢、文件安全性低等。为了克服这些困难&#xff0c;探讨各种大文件传输加速技术。其中&#xff0c;分片传输和并行传输是两种比较常见的技术&#xff0c;下面将对它们进行详细说明…

WEB:Web_python_template_injection

背景知识 python模板注入 ssit 题目 打开题目&#xff0c;发现页面提示&#xff0c;翻译为python模板注入 先测试是否存在注入 可以发现被执行了 先查看所有的子类 payload {{[].__class__.__base__.__subclasses__()}} 利用site.Printer的os模块执行命令 payload {{.__…