IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 【C语言初阶】初始指针 -> 正文阅读

[C++知识库]【C语言初阶】初始指针

指针是什么

在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。
从根本上看,指针(Pointer)是一个值为内存地址的变量(或数据对象)。正如char类型变量的值是字符,int类型变量的值是整数,指针变量的值是地址。

注:与指针相关的运算符
取地址操作符:&
间接访问(或解引用)操作符:*

例:

 #include <stdio.h>
 int main({
	int a = 10;  //在内存中开辟一块空间
	int* p = &a; //这里我们对变量a,取出它的地址,使用&操作符。
				 //将a的地址存放在p变量中,p就是一个之指针变量。
	return 0;
}

总结∶指针就是一个变量,用来存放地址的变量。(存放在指针中的值都被当成地址处理)。

经过上方的初步了解,我们可能会产生一下问题:
1.一个内存单元有多大?(1byte)
2.如何编址?

我们内存单位可以分为:bit(比特)、byte(字节)、kb、mb、gb、tb、pb…
1byte = 8bit
1kb = 1024byte
1mb = 1024kb
1gb = 1024mb以此类推

假设我们一个内存单元是1bit,那么一个char类型的变量所占空间是1byte,所以内存就需要为char类型的变量分配8个内存空间,若是int类型的变量,那就需要分配32个内存空间,这样就会造成内存空间不够用的情况。

若一个内存单元是1byte,那一个char类型的变量所占空间是1byte,所以内存就只需要为char类型的变量分配1个内存空间,若是int类型的变量,那只需需要分配4个内存空间,这样则刚刚好。

若一个内存单元是1kb,那一个char类型的变量所占空间是1byte,所以内存就只需要为char类型的变量分配1个内存空间,若是int类型的变量,那也只需要分配1个内存空间,但这样是非常不合适的,1kb有1024个byte,会造成空间的浪费

综合考虑,一个内存单元为1byte最为合适。

我们计算机可以分为32位机器和64位机器

对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的是产生一个电信号正电 / 负电(1或者0)
那么32根地址线产生的地址就会是∶
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001

10000000 00000000 00000000 00000000
10000000 00000000 00000000 00000001
…….
11111111 11111111 11111111 11111111
这里就有2的32次方个地址。 每个地址标识一个字节,那我们就可以给(2 ^ 32Byte == 2 ^ 32 / 1024KB == 2 ^
32 / 1024 / 1024MB == 2 ^ 32 / 1024 / 1024 / 1024GB == 4GB)4G的空闲进行编址。
同样的方法,64位机器,如果给64根地址线,我们也可以计算出能编址的空间大小

这里我们就明白 :

  • 在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节。
  • 那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。

总结:
指针是用来存放地址的,地址是唯一标示一块地址空间的。
指针的大小在32位平台是4个字节,在64位平台是8个字节。

声明指针:
指针声明示例:

int * pi;//pi是指向int类型变量的指针
char * pc;//pc是指向char类型变量的指针
float * pf;//pf是指向float类型变量的指针

类型说明符表明了指针所指向对象的类型,星号( * )表明声明的变量是一个指针。int* pi;声明的意思是pi是一个指针变量。

指针和指针类型

我们都知道,数据的类型有char、short、int、float、double、long等类型,那指针是不是也有类型呢?答案是有的。

我们平时使用指针变量的时候,一般都会这样声明指针变量:

int main()
{
	int* pa;
	char* pb;
	short* pc;
	float* pf;
	double* pd;
	
	return 0;
}

从上方我们也知道,指针的大小是固定的,在32位平台是4个字节,在64位平台是8个字节。那为什么还要将指针进行分类的?这样做有什么意义呢?请看下方代码:

#include <stdio.h>
int main()
{
	int a = 0x11223344;
	int* pa = &a;
	*pa = 0;

	return 0;
}

我们将a的地址放入指针变量pa中,再加上 pa = 0; 即调用指针来更改指针指向地址中的内容。通过F10调试,打开窗口-- - 内存监视器(类似变量监视窗口),可以看到a的内容确实改变了:
在这里插入图片描述
当我们将上方代码中a的地址放入 char
的指针变量中,接下来我们看看a的内容会发生怎样的变化:

#include <stdio.h>
int main()
{
	int a = 0x11223344;
	//int* pa = &a;
	//*pa = 0;
	char* pc = &a;
	*pc = 0;

	return 0;
}

在这里插入图片描述
我们可以发现,这回与上方放入int* 类型的指针变量有所不同,它只将一个字节的内容改为0,而上方int* 类型指针变量是将四个字节的内容全部改为0。
通过这个我们可以发现指针的类型还是有意义的,其意义在于指针解引用操作时,一次访问几个字节(访问内存的大小),比如int * 类型的指针,可以改动4个字节的内容,而char * 类型的指针只能更改一个字节的内容。

指针类型的意义1:指针的解引用

总结:指针类型决定了指针进行解引用操作的时候,能够访问内存的大小

举例:
char * p; *p能够访问1个字节
int * p; *p能够访问4个字节
double * p; *p能够访问8个字节

指针类型的意义2:指针 + - 整数
请看下面例子:

#include <stdio.h>
int main()
{
	int a = 10; 
	int* pa = &a;
	char* pc = &a;

	printf("%p\n", pa);
	printf("%p\n", pa + 1);

	printf("\n");

	printf("%p\n", pc);
	printf("%p\n", pc+1);

	return 0;
}

在这里插入图片描述
从运行结果可以看出:pa与pa+1之间相差为4,而pc与pc+1之间相差为1。

总结:指针类型决定了,指针 + - 整数的时候的步长(指针向前或者向后跳过几个字节)

举例:
char* 指针 + 1 跳过1个字节
int* 指针 + 1 跳过4个字节
double* 指针 + 1 跳过8个字节

指针类型意义的运用(价值):
在这里插入图片描述
如果我们将int * p = arr; 改成char* pc = arr;会发生什么呢?
是不是只会将10个字节改成1呢? 而我们数组int arr[10]有40个字节 ,10个字节的大小相当于2个半int类型的大小,我们可以打开内存窗口调试,看一下是不是这样的情况。

在这里插入图片描述从这我们可以直观的看出指针类型的价值所在了。

野指针

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

野指针成因:

1. 指针未初始化:

#include <stdio.h>
int main()
{ 
 int *p;//局部变量指针未初始化,默认为随机值
 *p = 20;//当对指针解引用时,由于指针随意指向一块空间,这个空间不属于我们当前的程序,就会造成非法访问内存,程序就会报错
 //所以p就是野指针
 return 0; 
}

在这里插入图片描述
2. 指针越界访问:

#include <stdio.h>
int main()
{
	//指针越界造成野指针的问题
	int arr[10] = 0;
	int i = 0;
	int* p = arr;

	for (i = 0; i <= 10; i++)
	{
		*p = i;
		p++;//超出数组的范围,会将数组的最后一个元素后面一个空间里的内容赋值为10,造成非法访问。
	}

	return 0;
}

在这里插入图片描述
3. 指针指向的空间释放:

#include <stdio.h>
int* test()
{
	int a = 10;
	return &a;//int*
	//当返回a的地址后,a的生命周期结束,a的空间就会返回给操作系统
	//之后a的空间就不属于我们的程序了
}

int main()
{
	int* p = test();//存储返回来的a的空间地址

	printf("%d\n", *p);//由于a的空间不在属于我们此程序,这是再进行访问,就造成非法访问

	return 0;
}

如何避免野指针:
1.指针初始化
2.小心指针越界
3.指针指向空间释放即使置NULL
4.指针使用之前检查有效性
5.避免返回局部变量的地址

#include <stdio.h>
int main()
{
	int a = 10;
	int* p = &a;//明确地初始化,确定指向

	int* p2 = NULL;//不知道一个指针当前应该指向哪里时,可以初始化为NULL
	//*p2 = 100;//err,对空指针进行解引用操作,空指针是0,0也是一个地址,0指向的地址空间是不允许使用的
	//对空指针是不能直接解引用的
	if (p2 != NULL)//这样就可以使用了
	{
		*p2 = 100;
	}
	return 0;
}

指针运算

指针运算包括一下三种:
1、指针+ - 整数
2、指针 - 指针
3、指针的关系运算

下面我们对这三种运算进行详细讲解

指针 + - 整数

#include<stdio.h>
int main()
{
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int* p = arr;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d\t%p\n", *p, p);
		p++;//p = p + 1; //指针 + 整数 
	}
	return 0;
}

在这里插入图片描述
我们可以这样将数组里的内容一一打印出来。
这里我们使用的是指针 + 1,那么指针 + 2或指针 + 3是什么样子的呢?请看下面的举例:
在这里插入图片描述
指针除了可以 + 整数,也可以 - 整数,例如:

#include<stdio.h>
int main()
{
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int* p = &arr[9];

	printf("%p\n", p);
	printf("%p\n", p-2);

	return 0;
}

在这里插入图片描述
可以看出p与p-2之间相差8个字节,说明向前跳过了2个整型。

指针 - 指针
我们现在已经可以知道,指针变量是用来存放地址的,那么 指针-指针 就是 地址-地址,那么两指针相减所得结果是什么呢?请看下方举例:

#include<stdio.h>
int main()
{
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

	printf("%d\n", &arr[9] - &arr[0]);
	printf("%d\n", &arr[0] - &arr[9]);

	return 0;
}

在这里插入图片描述
从运行结果可以得出,打印&arr[9] - &arr[0]和&arr[0] - &arr[9]的结果分别为9和-9,其实这代表从arr[0]到arr[9], 其中间有9个元素。

建议:指针 - 指针,用大地址 - 小地址。否则就会像本例所示,出现负数。

总结:指针 - 指针 得到的数字的绝对值是指针和指针之间元素的个数。

警告:指针 - 指针的前提是两个指针指向同一块区域(比如同一个数组)
错误示例:
在这里插入图片描述
指针 - 指针实际应用举例:
求字符串长度:

#include<stdio.h>
//求字符串长度:
int my_strlen(char* s)
{
	int count = 0;
	char* str = s;//将首元素地址赋给指针变量str
	while (*s != '\0')
	{
		s++;
	}
	return s-str;//指针 - 指针就是中间元素的个数
}
int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);

	printf("%d\n", len);

	return 0;
}

在这里插入图片描述
指针的关系运算
指针的关系运算就是比较指针的大小
请看下方举例:
#include<stdio.h>
float values[5];
float* vp;
//指针+ -整数;指针的关系运算
for (vp = &values[5]; vp > &values[0]; )
{
*- -vp = 0;
}
在这里插入图片描述
我们将代码简化, 代码修改如下:
在这里插入图片描述
实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证它可行。
标准规定:

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

图形理解:
在这里插入图片描述

指针和数组

数组是什么?
数组 – 是一块连续的空间,放的是相同类型的元素。
数组大小和元素类型,元素个数有关系
指针是什么?
指针(变量)-- 是一个变量,存放地址
指针变量的大小 是 4(32bit)/8(64bit)个byte

简单复习一下数组的知识点:
在之前学习数组的时候,我们已经知道了数组名就是首元素的地址,请看下面这个例子:
在这里插入图片描述
可以看到arr 和& arr[0]结果是一样,说明数组名就是首元素的地址。
我们学习过数组就会知道,数组名是首元素地址还有两个例外:

数组名在绝大多数情况下是首元素地址,但有两个例外:
1、& arr-- - 取地址+数组名 – 这时数组名不是首元素的地址,表示整个数组
&数组名 取出的是整个数组的地址。
而整个数组的地址和首元素地址有什么区别呢?这个问题我们在数组中已经详细讲解过
2、sizeof(arr)----sizeof(数组名)-- - 数组名表示的整个数组–sizeof(数组名)计算的是整个数组的大小。

既然数组名是首元素地址,那么就可以把数组名当成地址存放到一个指针中,这样我们就可以使用指针来访问一个数组。看下面例子:

#include<stdio.h>
int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int* p = arr;
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i < sz; i++)
	{
		printf("&arr[%d] = %p  <===> p+%d = %p\n", i, &arr[i], i, p + i);
	}
	return 0;
}

在这里插入图片描述
所以p + i其实计算的是数组arr下标为i的地址。那么我们就可以直接通过指针来访问数组,例如:

#include <stdio.h>
int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
	int *p = arr; //指针存放数组首元素的地址
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i<sz; i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}

在这里插入图片描述

二级指针

指针变量是用来存放地址的,但它也是一个变量,是变量就有存储空间,就有地址,那么指针变量的地址存放在哪里呢?

这就出现了二级指针的概念:

二级指针:存放指针变量的地址

请看下面例子:

#include <stdio.h>
int main()
{
	int a = 20;

	int* p = &a;//p是一级指针

	int** pp = &p;//pp是二级指针

	return 0;
}

这里我们进行画图理解:
在这里插入图片描述
接下来我们要如何使用二级指针进行访问呢?
我们可以对pp进行解引用操作 * pp就能找到 p的内容也就是a的地址,再次进行解引用操作** pp,就能找到a了!
如图:
在这里插入图片描述

指针数组

指针数组是指针还是数组呢?
我们可以进行这样的类推,如:

整型数组:存放整型的数组就是整型数组
字符数组:存放字符的数组就是字符数组
在这里插入图片描述

那我们就可以认为:
指针数组:存放指针的数组就是指针数组
在这里插入图片描述

总结:指针数组 - - - 是数组,是存放指针的数组。

指针数组的声明:
在这里插入图片描述
int* parr[5]代表整型指针数组,每个元素都是一个整型的地址
char* pc[6]代表字符指针数组,每个元素都是一个字符的地址

那指针数组是如何使用的呢?请看下面例子:

int main()
{
	int a = 10;
	int b = 20;
	int c = 30;

	//当变量过多时,比较繁琐
	//int* pa = &a;
	//int* pb = &b;
	//int* pc = &c;

	int* arr[3] = { &a, &b, &c };//创建指针数组,比较简洁
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		printf("%d\n", *(arr[i]));
	}
	return 0;
}

在这里插入图片描述
以上就是初阶指针的全部内容。
希望大佬们帮忙指点一下呀 😃

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-27 11:41:17  更:2021-08-27 11:41:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 16:44:58-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码