指针是什么?
指针也就是内存地址,指针变量是用来存放内存地址的变量。(存放在指针中的值都被当做地址处理)
对于32位的机器,假设有32根地址线,那么假设每根地址线在寻找地址的时候会产生高电平(高电压)和低电平(低电压)就是(1或者0)
那么32根地址线产生的地址就会是
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
00000000 00000000 00000000 00000010
…
11111111 11111111 11111111 11111111
这里就有2的32次方个地址
每个地址标识一个字节,那我们就可以给4G的空间进行编辑地址
2^32Byte == 2^32/1024KB ==2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB
在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以
一个指针变量的大小就应该是4个字节
那么64根地址线产生的地址就会是
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000010
...
11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
这里就有2的64次方个地址
每个地址标识一个字节,那我们就可以给8G的空间进行编辑地址
2^64Byte == 2^64/1024KB ==2^64/1024/1024MB==2^64/1024/1024/1024GB == 8GB
在32位的机器上,地址是64个0或者1组成二进制序列,那地址就得用8个字节的空间来存储,所以
一个指针变量的大小就应该是8个字节
指针变量是用来存放地址的,地址是唯一标识一个内存单元的
指针的大小在32位平台是4个字节,在64位平台是8个字节
如何使用指针?
使用指针时会频繁进行以下几个操作:定义一个指针变量、把变量地址赋值给指针、访问指针变量中可用地址的值。这些是通过使用一元运算符 ***** 来返回位于操作数所指定地址的变量的值。
#include <stdio.h>
int main ()
{
int var = 20; /* 实际变量的声明 */
int *ip; /* 指针变量的声明 */
ip = &var; /* 在指针变量中存储 var 的地址 */
printf("var 变量的地址: %p\n", &var );
/* 在指针变量中存储的地址 */
printf("ip 变量存储的地址: %p\n", ip );
/* 使用指针访问值 */
printf("*ip 变量的值: %d\n", *ip );
return 0;
}
编译结果:
var 变量的地址: 0x7ffeeef168d8
ip 变量存储的地址: 0x7ffeeef168d8
*ip 变量的值: 20
C 中的 NULL 指针
在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为空指针。
NULL 指针是一个定义在标准库中的值为零的常量。
内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。
#include <stdio.h>
int main ()
{
int *ptr = NULL;
printf("ptr 的地址是 %p\n", ptr );
return 0;
}
编译结果:
ptr 的地址是 0x0
指针和指针类型
char *pc = NULL; //char* 类型的指针是为了存放 char 类型变量的地址。
int *pi = NULL; //int* 类型的指针是为了存放 int 类型变量的地址。
short *ps = NULL; //short* 类型的指针是为了存放 short 类型变量的地址。
long *pl = NULL; //long* 类型的指针是为了存放 long 类型变量的地址。
float *pf = NULL; //float* 类型的指针是为了存放 float 类型变量的地址。
double *pd = NULL; //double* 类型的指针是为了存放 double 类型变量的地址。
指针±整数
#include <stdio.h>
//演示实例
int main()
{
int n = 10;
char *pc = (char*)&n;//因为n和pc类型不同,直接将pc指向n编译器会显示类型不兼容,如果一定要进行操作,需要对n进行强制类型转换
int *pi = &n;
printf("%p\n", &n);
printf("%p\n", pc);
printf("%p\n", pc+1);//因为char型占1个字节,所以向后移动1位
printf("%p\n", pi);
printf("%p\n", pi+1);//因为int型占4个字节,所以向后移动4位
return 0;
}
编译结果:
004FFB20
004FFB20
004FFB21
004FFB20
004FFB24
指针的类型决定了指针向前或者向后走一步是多大
指针的解引用
//演示实例
#include <stdio.h>
int main()
{
int n = 0x11223344;
char *pc = (char *)&n;
int *pi = &n;
*pc = 0; //重点在调试的过程中观察内存的变化。
*pi = 0; //重点在调试的过程中观察内存的变化。
return 0;
}
指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。
比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节
什么是野指针?
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
野指针产生的原因
- 指针未初始化
- 指针越界访问
- 指针指向的空间被释放
如何避免野指针的产生
- 指针初始化
- 小心指针越界
- 指针指向空间释放,及时置NULL
- 避免返回局部变量的地址
- 指针使用之前检查有效性
指针运算
指针±整数
#define N_VALUES 5
float values[N_VALUES];
float *vp;
//指针+-整数;指针的关系运算
for (vp = &values[0]; vp < &values[N_VALUES];)
{
*vp++ = 0;
}
指针-指针
int my_strlen(char *s)
{
char *p = s;
while(*p != '\0' )
p++;
return p-s;
}
指针的关系运算
for(vp = &values[N_VALUES]; vp > &values[0];)
{
*--vp = 0;
}
C语言标准规定
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与
指向第一个元素之前的那个内存位置的指针进行比较。
指针和数组
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,0};
printf("%p\n", arr);
printf("%p\n", &arr[0]);
return 0;
}
运行结果:
004FFDFC
004FFDFC
数组名表示的是数组首元素的地址
二级指针
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dtqh0XwL-1674044271602)(C:\Users\LENOVO\AppData\Roaming\Typora\typora-user-images\image-20230117222645294.png)]
a的地址存放在pa中,pa的地址存放在ppa中
pa是一级指针,ppa是二级指针
指针数组
int* arr[5];
arr是一个数组,有5个元素,每个元素是一个整形指针
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EHYYyA1K-1674044271604)(C:\Users\LENOVO\Desktop\arr.png)]
数组指针
数组指针的定义
数组指针是能够指向数组的指针,数组指针中存放的是数组的地址
int (*p)[10];//p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10的整型的数组,所有p是一个指针,指向一个数组,叫数组指针
&数组名VS数组名的区别
&arr表示的是数组的地址,&arr+1表示跳过整个数组的大小
arr表示的是数组首元素的地址,arr+1表示跳过一个元素的大小
函数指针
函数指针是指向函数的指针变量。
通常我们说的指针变量是指向一个整型、字符型或数组等变量,而函数指针是指向函数。
函数指针可以像一般函数一样,用于调用函数、传递参数。
typedef int (*fun_ptr)(int,int); // 声明一个指向同样参数、返回值的函数指针类型
函数指针数组
数组是存放相同类型数据的存储空间,那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组
指向函数指针数组的指针
指向函数指针数组的指针是一个指针
指针指向一个数组,数组的元素都是函数指针
定义形式
void (*(ppfunArr)[5])(const char*)=&pfunArr;//指向函数指针数组ppfunArr
回调函数
回调函数就是一个通过函数指针调用的函数,如果你把函数的指针(地址)作为参数传递给另一个函数当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方法直接调用,而是在特定的事件或条件发生时由另外一方调用的,用于对该事件或条件进行响应
字符指针
方式1:
int main()
{
char ch = 'w';
char *pc = &ch;
printf("%c",*pc);
return 0;
}
方式2:
int main()
{
const char* pstr = "hello bit.";//本质是把字符串"hello bit"首字符的地址放到了pstr中
printf("%s\n", pstr);
return 0;
}
数当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方法直接调用,而是在特定的事件或条件发生时由另外一方调用的,用于对该事件或条件进行响应
字符指针
方式1:
int main()
{
char ch = 'w';
char *pc = &ch;
printf("%c",*pc);
return 0;
}
方式2:
int main()
{
const char* pstr = "hello bit.";//本质是把字符串"hello bit"首字符的地址放到了pstr中
printf("%s\n", pstr);
return 0;
}