一、指针是什么
想要知道指针是什么?
我们首先要理解一个概念:内存 。
1、内存
可以将内存想象成一条长走廊。走廊上有很多房间,每个房间的大小是“一个字节”(8个比特位),每个房间都有自己的编号,从0开始递增(通常用16进制表示),这个编号就是内存地址。
内存还有一个重要特点:访问内存上任意地址的数据,开销都很小。
2、内存的作用
存储数据,和CPU进行交互。
3、内存VS外存
打开此电脑,右键点击属性,我们可以看到电脑内存的大小。
而平时我们我们看到的1T机械,256G固态……这些都是属于外存。
1.内存存储空间小,外存存储空间大。
2.内存访问速度快,外存慢。
3.内存价格高,外存价格低。
4.内存掉电后没法保存,外存掉电后还可以保存。
4、指针
了解内存之后,我们就可以来理解指针了。
在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为"指针"。意思是通过它能找到以它为地址的内存单元。
可以这样理解:
我们在长走廊上开了个房间,这时我们会拿到一张房卡,房卡上保存的就是这个房间的地址,通过房卡我们可以找到这个房间,这张房卡也就是指针,其中存放的地址就是指针的内容。
5、总结
指针就是一个变量(变量是分配给你使用的一块内存空间),其中存放的是内存单元的地址。
二、指针和指针类型
1、指针定义方式和大小
#include<stdio.h>
int main()
{
char* pc = NULL;
int* pi = NULL;
short* ps = NULL;
long* pl = NULL;
float* pf = NULL;
double* pd = NULL;
}
指针定义的方式是type*+名字,char*,int*,short*……都是不同的类型,统称为指针。
NULL表示指针的起始地址。
无论是什么类型的指针变量,,它自身所占的空间都是一样的:
在32位系统是4个字节,在64位系统是8个字节。
2、指针类型的意义
(1)指针类型决定了对指针解引用能操作几个字节
char a = 'a';
char* pa = &a;
*pa操作:
先根据pa中存放的0x100找到内存中对应位置的空间,接下来从这个空间中读取1个字节(取决于指针类型char
)的数据。
int n = 10;
int* pn = 0x200;
*pn操作:
先根据pn中存放的0x200找到内存中对应位置的空间,接下来从0x200开始读取4个字节(取决于指针类型int
)的数据。
(2)指针类型决定了指针走一步有多大距离
#include<stdio.h>
#include<stdlib.h>
int main()
{
int n = 10;
char* pc = (char*)&n;
int* pi = &n;
printf("%p\n", &n);
printf("%p\n", pc);
printf("%p\n", pc+1);
printf("%p\n", pi);
printf("%p\n", pi+1);
system("pause");
return 0;
}
结果:可以看出char* 类型向前走一步走了1个字节,int* 类型向前走一步走了4和字节。
三、野指针
野指针:保存了非法地址的指针,指针指向的位置是不可知的。(空指针也是一种野指针)
1、成因
(1)指针未初始化
#include<stdio.h>
int main()
{
int* p;
*p = 20;
return 0;
}
结果:
(2)指针越界访问
#include <stdio.h>
int main()
{
int arr[5] = {
9, 5, 2, 7, 10 };
for (int i = 0; i <8 ; i++){
printf("%d ", *(arr + i));
}
system("pause");
return 0;
}
结果:
当指针指向的范围超过数组arr的的范围时,指针就是野指针。
注意:合法地址才能解引用,野指针解引用是未定义行为。
四、指针运算
1、指针±整数
指针±整数:跳过整数个元素。
#include <stdio.h>
int main()
{
int arr[5] = {
9, 5, 2, 7, 10 };
int* pi = &arr[4];
printf("%d ", *(arr + 0));
printf("%d ", *(arr + 1));
printf("%d ", *(pi - 0));
printf("%d ", *(pi - 1));
printf("%d ", *(pi - 2));
system("pause");
return 0;
}
结果:
2、指针-指针
实际上指针相减,大部分情况下没有意义。
除非两个指针指向同一个连续的有效内存空间才有意义。
(1)没有意义的写法
//代码1
#include <stdio.h>
int main()
{
int arr1[4] = {
1, 2, 3, 4 };
int arr3[1] = {
200 };
int arr2[4] = {
1, 2, 3, 4 };
int* p1 = &arr1[0];
int* p2 = &arr2[2];
printf("%d\n", p2- p1);
system("pause");
return 0;
}
//代码2:
#include <stdio.h>
int main()
{
int arr3[1] = {
200 };
int arr1[4] = {
1, 2, 3, 4 };
int arr2[4] = {
1, 2, 3, 4 };
int* p1 = &arr1[0];
int* p2 = &arr2[2];
printf("%d\n", p2- p1);
system("pause");
return 0;
}
结果:
代码1:代码2:
此时指针相减的结果是不确定的,取决于变量定义的前后顺序。
(2)有意义的写法
两个指针指向同一个连续的有效内存空间才有意义。
#include <stdio.h>
int main()
{
int arr[4] = {
1, 2, 3, 4 };
int* p1 = &arr[0];
int* p2 = &arr[2];
printf("%d\n", p2- p1);
system("pause");
return 0;
}
(3)结论
指针相减得到的结果是两个地址之间隔了几个 “元素” 。
//野指针不能解引用. 以下这俩野指针, 是用来演示运算的
#include <stdio.h>
int main()
{
int* p1 = (int*)0x100;
int* p2 = (int*)0x110;
printf("%d\n", p2- p1);
system("pause");
return 0;
}
结果:
3、指针的关系运算
指针的关系运算有<、>、<=、>=、= =、!=,最常用的是= = 、!=。
指针的比较操作(<、>、<=、>=)要求两个指针指向同一连续的有效内存空间 ,这样才是有意义的。
(1)if (p != NULL)等价于if ( p )
//代码1
#include <stdio.h>
int main()
{
int num = 10;
int* p = #
if (p) {
printf("不是空指针\n");
} else {
printf("是空指针!\n");
}
system("pause");
return 0;
}
//代码2
#include <stdio.h>
int main()
{
int num = 10;
int* p = #
if (p != NULL) {
printf("不是空指针\n");
} else {
printf("是空指针!\n");
}
system("pause");
return 0;
}
结果:
代码1:代码2:
(2)if (p == NULL)等价于if ( !p )
//代码1
#include <stdio.h>
int main()
{
int num = 10;
int* p = #
if (!p) {
printf("是空指针\n");
} else {
printf("不是空指针!\n");
}
system("pause");
return 0;
}
//代码2
#include <stdio.h>
int main()
{
int num = 10;
int* p = #
if (p == NULL) {
printf("是空指针\n");
} else {
printf("不是空指针!\n");
}
system("pause");
return 0;
}
结果:
代码1:代码2:
五、指针和数组
指针和数组的区别是什么?
当我们看到这个问题的时候,不要上当了, 指针和数组完全是两个概念,要说区别那是不合理的 。
只不过在C语言中,
数组有时候会隐式转换成指针,指针可以用[ ]取下标。
1、数组会隐式转成指针
(1)参与运算
#include <stdio.h>
int main()
{
int arr[4] = {
0 };
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr + 0));
system("pause");
return 0;
}
结果:
(2)函数调用传参
#include <stdio.h>
void change(int arr){
printf("%d\n", sizeof(arr));
}
int main()
{
int arr[4] = {
0 };
printf("%d\n", sizeof(arr));
change(arr);
system("pause");
return 0;
}
结果:
2、指针进行 [ ] 操作
#include <stdio.h>
int main()
{
int arr[4] = {
1, 2, 3, 4 };
int* p = arr;
for (int i = 0; i < 4; i++) {
printf("%d\n", p[i]);
//p[i]等价于*(p + i)
}
system("pause");
return 0;
}
结果:
(1)指针使用[ ]的下标问题
指针下标可以是负值。
整体的原则就是: 保证解引用操作必须针对有效内存进行.。
#include <stdio.h>
int main()
{
int arr[4] = {
1, 2, 3, 4 };
int* p = arr + 1;
//这个操作是允许的,数组下标一定是 [0, size-1] 范围
// 但是指针的下标不一定. 取决于指针初始情况下指向谁.
printf("%d\n", p[-1]);
printf("%d\n", *(p - 1));
system("pause");
return 0;
}
结果:
六、二级指针
其实二级指针和套娃类似。
指针也是一个变量,也要占据内存,也有地址。
int num =10;
int*p = #
int** pp =&p;
套娃套多了,就会不好理解,所以可以使用一些方法来简化高级指针,比如typedef。
#include <stdio.h>
int main()
{
int num = 10;
int*p = #
typedef int* IntPtr;
IntPtr* pp = &p;
system("pause");
return 0;
}
七、指针数组
1、区分指针数组和数组指针
(1)指针数组
数组,每个元素是一个指针类型的变量。
int* arr[4] = {
0 };
(2)数组指针
指针,,指向了一个数组。
int(*p)[4] = &arr;
八、const 和指针之间的关系
1、限制 p 中保存的内存地址对应的变量不能被修改
#include <stdio.h>
int main()
{
int num = 0;
int num2 = 10;
const int* p = #
*p = 100;
p = &num2;
system("pause");
return 0;
}
2、限制 p 变量本身保存的地址不能改变
#include <stdio.h>
int main()
{
int num = 0;
int num2 = 10;
int* const p = #
*p = 100;
p = &num2;
system("pause");
return 0;
}
来源:oschina
链接:https://my.oschina.net/u/4359914/blog/4710706