一、初始指针
内存是电脑上特别重要的东西,计算机中所有的程序的运行都是在内存中进行的。
所以为了有效的使用内存,就把内存划分为一个个小的内存单元,每个内存单元的大小是1字节。
为了能够有效访问到内存的每个单元,就给内存单元进行了编号。这些编号被称为该内存单元的地址。
指针是什么?
在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,该地址指向该变量单元。因此,将地址形象化的称为:指针。意思是,通过它能找到以它为地址的内存单元。
1、 编号如何产生?
(1) 电脑上有地址线,地址线一旦通电,就能产生电信号,把电信号转为数字信号,数字信号产生的二进制序列(1或0),把数字信号作为二进制的编号,可作为内存单元的一个编号,即内存单元的地址。十六进制显得方便。
(2) 对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的是产生一个电信号正电/负电(1或0)。
(3) 那么32根地址线产生的地址就会是:
00000000 0000000 00000000 00000000
这里就有二的三十二次方个地址。
每个地址标识一个字节,那我们就可以给(2^32byte==2^32/1024kb==2^32/1024/1024mb==2^32/1024/1024/1024gb==4gb)4G的空闲进行编址。
同样的方法,64位机器,如果给64根地址线,能编址多大空间,自己计算。
这里我们明白:
1) 在32位机器上,地址是32个0或1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节。
2) 在64位机器上,如果有64个地址线,那一个指针变量的大小是8字节,才能存放一个地址。
2、 一个内存单元是多大?
一个字节比较合适。
#include <stdio.h> int main() { int a =10; int *p = &a;//指针是用来存放地址的,p为指针变量 return 0; }
P里面存的是地址,地址也叫指针。p变量是用来存放地址的,所以叫它指针变量,类型是int*。
存放地址的,就称为指针变量,而这个指针变量存放的又是地址。
指针就是地址,地址就是指针。
指针其实就是个变量,变量里面存放的是内存单元的地址,也就可以说:指针就是地址。(存放在指针中的值都被当成地址处理)
#include <stdio.h> int main() { int a =10;//在内存中开辟一个空间 int *p = &a;//我们对变量a,取出它的地址,可以使用&操作符 // 将a的地址存放在p的变量中,指针是用来存放地址的,p为指针变量 return 0; }
总结:
指针是用来存放地址的,地址是唯一标示一块地址空间的。
指针的大小在32位平台是4个字节,在64位平台是8个字节。
二、指针和指针的类型
指针的类型和意义
我们来看一下,各种指针类型的大小:
#include<stdio.h> #define _CRT_SECURE_NO_WARNINGS int main() { printf("%d\n",sizeof(char*)); printf("%d\n",sizeof(short*)); printf("%d\n",sizeof(int*)); printf("%d\n",sizeof(double*)); return 0; }
运行结果:8 8 8 8
当前默认是64位平台,所以结果都是8个字节。
1)指针的解引用
①问题抛出
❓ 问:它们指针大小都是4个字节,那为什么要区分?
那指针类型究竟有什么样的意义?
先定义一个整型变量a。
int a = 0x11223344;
将十六进制数0x11223344存入a中。
可以放进去吗?
可以的哦,两个十六进制位占一个字节(1个十六进制是4个二进制位,2个十六进制位就是8个二进制位,即1个字节)。
这样的话,11占一个字节,22占一个字节,33占一个字节,44占一个字节。总共4个字节。
a是整型变量,4个字节的空间。所以是可以存放的。
再将a的地址取出来(&a)。
放在变量pa里面,pa的是指针类型的变量,即
int*
类型。如下:
int* pa=&a;
既然在探讨指针类型,那么我们将a的地址放入
char*
类型的变量pc里面行吗?如下:
char* pc=&a;
pa和pc都能存放好a的地址吗?
来输出看一下:
int* pa=&a;//用int类型指针来存放a的地址 printf("%p\n",pa); char* pc = &a;//用char类型指针来存放a的地址 printf("%p\n",pc);
运行结果: 000000000022FE44 000000000022FE44(分开运行)
由上图可见,两次输出地址一样。
可见,无论是什么类型的,都能存好a的地址。
为啥都能存放?因为pa和pc都是指针变量,都是4字节(32平台)/8字节(64平台)大小。
类型不匹配,只会报警告,类型不兼容。但还是可以存放进去的。
既然无论什么类型都能存放,那指针类型还有什么意义了呢?
一起往下继续探讨吧!
②探讨
接下来,我们分别看一下不同类型存储a地址的内存。
<1>当我们用
Int
类型来存储时现在我们将a变量的地址存入一个整型指针
pa
中。当用来存储的变量的类型发生变化,我们解引用操作的时候,结果不一样。
以上结果我们看到,用Int类型来存储时,当进行数据改变,原来的都改变了;而用char类型来存储时,只改变了前两个数(1个字节)。
🍰 指针在存储数据的时候,类型无差,都能够存放。但当我们对它进行解引用操作的时候,整型指针,可以改4个字节;而字符指针,确实可以从指针访问相应空间,但
*pc
去访问空间的时候,只能改1个字节。③总结
指针类型决定了指针进行解引用操作的时候,能够访问空间的大小。
不同类型的指针能够访问的空间大小不同:
int* p; p能够访问4个字节 char* p; p能够访问1个字节 double* p; p能够访问8个字节
那么学这个类型有什么意义?
指针类型既然决定了指针变量解引用能够一次访问几个字节,那当我们给指针赋值的时候,应该赋一个合理的指针。
比如我们希望从这个位置向后访问一个字节,那我们应该把它交给一个char指针;我们希望一次访问两个字节,那我们应该把它交给一个short类型的指针。
2)指针+整数
上面我们讨论了第一个意义,下面来讨论第二个意义。
如下代码:
int a=0x11223344; int* pa=&a; char* pc=&a; printf("%p\n",pa); printf("%p\n",pa+1); printf("%p\n",pc); printf("%p\n",pc+1);
看一下最终结果:
000000000022FE44和000000000022FE48
000000000022FE47和000000000022FE48
我们可以看到,当指针类型是
int
类型时,pa+1向后访问了4个字节;而指针类型是
char
类型时,pc+1向后访问了1个字节。即指针类型决定了指针加一向后跳几个字节,指针走一步走多远(步长)。
int* p; p+1 --> 4 char* p; p+1 --> 1 double* p; p+1 --> 8
❓ 指针向前向后一次走多大?
向后走的是指针所指向对象的类型的大小,整型指针向后加的是4,因为向后加一是跳过一个整型(4个字节);字符指针向后加一加的是1个字节,因为向后跳的是1个字符。
3)总结
🍰 总结
指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。比如:char的指针解引用就只能访问一个字节,而Int的指针的解引用就能访问四个字节。
指针的类型决
4)举例
那我们知道指针类型的意义有什么用?
我们来举个例子:定了指针向前或者向后走一步走多大(距离),单位是字节。
将arr数组的地址拿出来,赋值给指针变量p:
int* p=arr; //arr为数组名,即首元素地址
🚗 需求:把arr数组里面的元素全部改成1。
<1> 用int类型的指针
改变数组第一个元素:*(p+0)=1
p指针指向数组第一个元素地址,解引用之后就是第一个元素,再将1赋值给它即可。
改变数组第二个元素:*(p+1)=1
p指针向后移动一位,指向数组第二个元素地址,解引用之后就是第二个元素,再将1赋值给它。
后面的规律就找到了。
改变数组里面元素:*(p+i)=1。(i=0,1,…)
#include <stdio.h> int main() { int arr[10] = {0}; int* p = arr; //char* p = arr; int i = 0; for(i=0;i<10;i++) { *(p + i) = 1; } for (i=0; i<10;i++) { printf("%d\n",arr[i]); } }
运行结果:1 1 1 1 1 1 1 1 1
<2> 用char类型的指针
刚才我们使用了int类型的指针,那么使用char类型的话,会怎么样呢?
int arr[10] = { 0 }; char* p=arr;//arr为数组名,即首元素地址 int i = 0; for (i=0;i<10;i++) { *(p + i) = 1; }
对于
*(p+1)
,指针向后偏移了一个字节。循环10次,就访问了10个字节。
可数组里面有40个字节!
一个整型4个字节,按照逻辑,只会初始化两个半整型的空间。
4)介绍reinterpret_cast(强制类型转换符)的使用
初学指针我们可能会遇到如下代码:
#include<stdio.h>
int main()
{
int a = 0x11223344;
int* pa = &a;
char* pc = &a;
printf("%p\n",pa);
printf("%p\n",pc);
return 0;
}
但是有的vs版本输入这段代码会报错,运行不了,报错的原因是:无法从int*转化到char*。所以这时候就需要我们将int类型强制转化为char类型,那我们就可以用reinterpret_cast(强制类型转换符)。
所以将以上代码修改为如下代码段:
#include<stdio.h>
int main()
{
int a = 0x11223344;
int* pa = &a;
char* pc = reinterpret_cast<char*>(&a);
printf("%p\n",pa);
printf("%p\n",pc);
return 0;
}
reinterpret_cast的用法:
reinterpret_cast<type-name>(expression)
reinterpret_cast后面的<>中的type-name类型必须是一个指针、引用、算术类型、函数指针或者指针成员,它可以把一个指针类型转换成一个整数,也可以把一个整数转换成一个指针。