一.指针基本概念:
- 指针是内存中一个最小单元的编号,也就是地址
- 平时口语中说的指针,通常指的是指针变量,是用来存放地址的变量
#include<stdio.h> int main() {int a = 0;//a是整型变量,占用四个字节的内存空间,在内存中开辟一块空间int* pa = &a;printf("%p", pa);//pa是一个指针变量,用来存放地址,这里是将a的四个字节的第一个字节的地址存放在ap变量中return 0; }
- 指针变量:我们可以通过&(取地址操作符)取出变量的内存真实地址,把地址可以存放到一个变量中这个变量就是指针变量。(存放在指针中的值都被当成地址处理)
- 指针变量中存放的是地址,通过这个地址,就可以找到一个内存单元
- 指针的大小在32位平台是四个字节,在64位平台是八个字节
-
x86 - 32位
x64 - 64位
二.指针和指针类型:
- 我们都知道普通变量都有不同的类型,由整型,字符型,浮点型等等,而指针变量也有不同的类型
int a = 5;假设有以上a变量,那么我们可以将a的地址储存进以下指针变量中char* pc = &a;short* ps = &a;int* pi = &a;long* pl = &a;float* pf = &a;double* pd = &a;
- 我们需要注意的是:例如:int*p——指针类型是int*,指针所指向的类型是int。(不要记混)
- 既然一个整型类型的数据可以被别的不同类型数据的指针储存,那么指针类型的意义又是什么呢?
(1)指针+-整数:
总结:指针的类型决定了指针向前或向后走一步有多大(距离)
(2)指针的解引用:
#include<stdio.h>
int main()
{int n = 0x11223344;char* pc = (char*)&n;int* pi = &n;*pc = 0;//重点在调试的过程中观察内存的变化*pi = 0;//重点在调试的过程中观察内存的变化return 0;
}
总结:指针类型决定了,对指针解引用的时候有多大权限(能操作几个字节)。
比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针解引用就能访问四个字节
三.野指针:
概念:野指针就是指针指向位置是不可知的(随机的,不正确的,没有明确限制的)
(1)野指针成因:
- 指针未初始化:
#include<stdio.h> int main() {int* p;//p没有初始化,就意味着没有明确的指向//一个局部变量不初始化的话,放的是随机值*p = 10;//非法访问内存了return 0; }
- 指针越界访问:
#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; }
- 指针指向的空间释放:
#include<stdio.h> int* test() {int a = 10;//a为局部变量,函数开始时创建,函数结束时销毁return &a; } int main() {int* p = test();*p = 20;//此时p指向的空间已销毁(被释放),不属于当前程序,此时指针p就为野指针return 0; }
(2)如何规避野指针:
- 指针初始化:
- 注意指针是否越界
- 指针指向被释放空间时置为NULL(空指针)
- 指针使用之前应检查其有效性
这里展示几个代码:
比较以下代码,并注意注释的内容:
函数栈帧与销毁:
四.指针运算:
(1)指针+-整数:
总结:
- 从0位到5位,地址是由低到高的
- *p++=0;相当于:*p=0 p++
- 指针变量的自增自减运算,指针加一或减一运算,表示指针向前或向后移动一个单位(不同类型的指针,单元长度不同 )。这个在数组中非常常用。
#include<stdio.h> int main() {int arr[10] = { 0 };int i = 0;int n = sizeof(arr) / sizeof(arr[0]);//计算出数组中元素的个数/*for (i = 0; i < n; i++){arr[i] = 1;}*/int* p = &arr[0];for (i = 0; i < n; i++){*p++ = 1;//*(p+i)=1}return 0; }
(2)指针-指针:
观察下图:
可以很直观的得出:数组中两个指针相减得到的是指针和指针之间元素的个数
注意:
- 不是所有指针都能相减
- 指向同一块空间的两个指针才能相减!
例题:编写函数(不允许创建临时变量),求字符串的长度(三种方法)
- 循环
- 递归
- 指针-指针
//循环
#include<stdio.h>
int my_strlen(char* n)
{int count = 0;while (*n != '\0'){count++;n++;}return count;
}
int main()
{int len = my_strlen("lover");printf("%d", len);return 0;
}
//递归
#include<stdio.h>从
int my_strlen(char* n)
{if (*n != '\0'){return 1 + my_strlen(n + 1);}else{return 0;}
}
int main()
{int len = my_strlen("lover");printf("%d", len);return 0;
}
//指针-指针
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int my_strlen(char* n)
{char* np = n;while (*n != '\0'){n++;}return n - np;
}
int main()
{char arr[] = "study";int len = my_strlen(arr);printf("%d", len);return 0;
}
(3)指针的关系运算:
#include<stdio.h>
#define A 5
int main()
{int arr[10] = { 0 };int* p = &arr[A];for (p = &arr[A]; p > &arr[0];){*--p = 0;}return 0;
}
标准规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
五.指针和数组:
- 数组名表示的是数组首元素地址(两种情况除外,数组章节讲了——sizeof,&)
(不必多说,数组那节都已经讲了)
六.二级指针:
二级指针变量用来存放一级指针变量的地址
观察下面代码:
- pa是一个指针变量,一级指针
- ppa是一个二级指针变量
- int*是说明ppa是指针,ppa指向的对象是int*类型
七.指针数组:
指针数组是数组,是存放指针的数组(数组我们已经了解了整型数组,字符数组)
观察下列代码:
#include<stdio.h>
int main()
{int a = 10;int b = 20;int c = 30;int arr[10];int* ap = &a;int* bp = &b;int* cp = &c;//parr是存放指针的数组,就是指针数组int* parr[10] = { &a,&b,&c };int i = 0;for (i = 0; i < 3; i++){printf("%d\n", *(parr[i]));}return 0;
}
可以用其打印二维数组:
#include<stdio.h>
int main()
{int arr1[4] = { 1,2,3,4 };int arr2[4] = { 2,3,4,5 };int arr3[4] = { 3,4,5,6 };int* parr[3] = { &arr1,&arr2,&arr3 };int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 4; j++){printf("%d", parr[i][j]);//相当于:arri[j]//parr[1]相当于arr1,也可以改为 *( *(parr+i)+j)//arr[i] <==> *(arr+i)printf("\n");}return 0;
}
知识点:arr[i] <==> *(arr+i)