指针的概念
1. 怎么理解指针
int b = 1 ;
int *a = &b ;
int *c = (int *)b ;
printf("指针a保存的地址是 : %d \n" ,a );
printf("指针a保存的地址中保存的值是: %d \n", *a);
printf("指针a保存的地址是 : %d \n" ,c );
printf("指针a保存的地址中保存的值是: %d \n", *c); // 无输出
-
我的理解是一个记录内存地址的小卡片
输出:
指针a保存的地址是:0061ff10
指针a保存的地址中保存的值是:1
指针c保存的地址是:00000001
指针c保存的地址中保存的值是: 这里会报错
举个例子 , p指针这个小纸条上记录的内存地址是7338864 , 我们去内存中找到这个对应的地址 , 这个地址中保存的值 是 1
指针的类型
-
指针的类型 有什么意义 , 不同类型指针之间有什么区别
首先 要知道 指针本身就是一种数据类型 ,示例
( sizeof(char *) == sizeof(int *) )?printf("true"):printf("false"); // printf : true
输出 :
true
那么在使用数据类型来修饰指针是用来干嘛的 ,是用来修饰 指针指向 变量 的数据类型的吗 ??? ,
我们上面做了比喻 , 说指针就是一个小纸条 ,上面记录了变量在内存的地此 ,那么 , 是不是可以把 int 类型的指针赋值 char 类型的指针 ,
int b = 1 ;
int *a = &b ;
char *c = (int *)&b ;
printf("指针 a 储存的地址: %p \n" ,a );
printf("指针 c 储存的地址: %p \n", c);
printf("指针 a 指向内存中的数据: %d \n" ,*a );
printf("指针 c 指向内存中的数据: %d \n", *c);
输出 :
指针 a 储存的地址: 0x0061ff14
指针 c 储存的地址: 0x0061ff14
指针 a 指向内存中的数据: 1
指针 c 指向内存中的数据: 1
没错是可以的 从结果上来看视乎没有区别
那么我们接下来看这个:
char str[] = "abcdefgh"; //
char *pchar = str ;
int *pint = (int *)str; //将 char 类型的指针转换成 int 类型的
printf(" char 类型的指针 +1 的地址:%p \n" , (pchar + 1)); // char 类型的指针 +1 指针跳跃了 1个字节
printf(" char 类型的指针 +1 的地址内保存的字符:%c \n" , *(pchar + 1));
printf(" 字符数组 str[1] 的地址:%p \n" , &str[1]);
printf(" 字符数组 str[1] 保存的字符:%c \n" , str[1]);
printf(" int 类型的指针 +1 的地址:%p \n" , (pint + 1)); // int 类型的指针 +1 指针跳跃了 4个字节
printf(" int 类型的指针 +1 的地址内保存的字符:%c \n" , *(pint + 1));
printf(" 字符数组 str[4] 的地址:%p \n" , &str[4]);
printf(" 字符数组 str[4] 保存的字符:%c \n" , str[4]);
输出 :
char 类型的指针 +1 的地址:0061ff0c
char 类型的指针 +1 的地址内保存的字符:b
字符数组 str[1] 的地址:0061ff0c
字符数组 str[1] 保存的字符:b
int 类型的指针 +1 的地址:0061ff0f
int 类型的指针 +1 的地址内保存的字符:e
字符数组 str[4] 的地址:0061ff0f
字符数组 str[4] 保存的字符:e
很显然 区别这就出来了
总结:指针的类型 代表着指针+1的跳跃的字节 , 数据类型是几个字节 ,就跳跃几个字节
char + 1 跳跃 sizeof(char) == 1 个字节
int + 1 跳跃 sizeof(int) == 4 个字节
float + 1 跳跃 sizeof(float) == 4 个字节
double + 1 跳跃 sizeof(double) == 8 个字节
特殊的指针
1. 常量指针
常量指针 概念是 : 你不能通过常量指针去修改 这个地址中的数据 如:
int a = 6 ;
const int *b = &a ;
*b = 3 ; // 错误 不能通过常量指针去更新地址内保存的数据
a = 4 ; // 正确 不影响 a 变量正常赋值 ,
2. 无类型指针
无类型指针怎么理解呢 , 通过上面的测试 我们明白了 指针的类型 , 所以 无类型指针 本质上就是 没有跳跃力 的的指针
无类型指针有以下特点:
- 任何指针(包括函数指针)都可以赋值给void指针
- void指针赋值给其他类型的指针时都要进行转换
- void指针在强制转换成具体类型前,不能解引用;(即不能读取改地址里面的数值)
- void指针不能参与指针运算,除非进行转换;(转换前不能加减)
int a = 1 ;
char b = 'a';
int *p_int = &a ;
char *p_char = &b ;
void *p_void ;
// 任何指针(包括函数指针)都可以赋值给void指针
p_void = p_int ; // 可正常编译执行
p_void = p_char ; // 可正常编译执行
// void指针赋值给其他类型的指针时都要进行转换 // 没转换所以报错
// p_char = p_void ; //错误代码: error: invalid conversion from 'void*' to 'char*
p_char = (char *)p_void ; // 可正常编译执行
// void指针在强制转换成具体类型前,不能解引用
// printf(" %c \n" , *p_void); //错误代码: error: 'void*' is not a pointer-to-object type
printf(" %p \n" , p_void); //可正常编译执行
// void指针不能参与指针运算,除非进行转换;(转换前不能加减) //
p_void = p_void + 1 ; //警告代码: warning: pointer of type 'void *' used in arithmetic
printf(" %p \n" , p_void ); // gcc 编译器下 可以打印出地址
输出 :
0061ff0b
0061ff0c
3. 函数指针
既是 这个指针指向 的是 一个函数的地址
注意:
- 不符合定义的函数地址 不能保存在函数指针内
- 函数指针 不能参与指针运算
- 函数指针和指针函数 是有区别的 , 函数指针是指针 , 指针函数是函数
#include<stdio.h>
typedef int (*fp)(int,int); //这样只是定义一个函数指针类型
int test(int a , int b){ return a + b; } // 定义 一个符合的函数
int test2(int a , int b , int c){ return a + b + c; } // // 定义 不一个符合的函数
int main ()
{
// 定义 一个函数指针
fp fun ;
// fun = test2 ; //error: invalid conversion from 'int (*)(int, int, int)' to 'int (*)(int, int)'
fun = test ; // 正常编译执行
printf(" %d \n" , fun(1 , 2) ) ; // 调用
printf("pointer fun address:%p \n" , fun ) ;
//warning: pointer to a function used in arithmetic
printf("pointer fun + 1 address:%p \n" , fun + 1 ) ;
return 0;
}
输出 :
3
pointer fun address:00401460
pointer fun address:00401461
从结果上来看 函数指针 也是没有跳跃力的 对其做运算 结果 和无类型指针是 差不多一样
数组指针
1. 在了解 数组指针 之前我们必须先明白 数组和指针的区别
注意:
- 数组和指针本质上是不一样的 , 是两种数据类型 ,
- 数组和指针 在大多数情况下是可以互相转换的 ,这也是很多人被误导的地方
int arr[4] = { 1,2,3 ,4 } ; // 定义一个int数组
int *p_int ; // 定义一个int 指针
//在多数情况下 指针 和 数组 会隐式转换
p_int = arr ; // 这是把数组当指针用
p_int = &arr[0] ; // 正常对指针赋值
printf(" p_int[0] == %d \n" , p_int[0] ); // 这是把指针当数组用 ,这是可以的
printf(" *p_int == %d \n" , *p_int ); // 正常使用指针
// 对数组和指针 使用 sizeof 得到的结果不同
printf(" sizeof(arr) == %d \n" , sizeof(arr) ); // sizeof(arr) == 16
printf(" sizeof(p_int) == %d \n" , sizeof(p_int) ); // sizeof(p_int) == 4
// 对数组和指针 使用 & 得到的结果不同
printf(" &arr == %p \n" , &arr ); // 输出数组arr 首个元素的地址
printf(" &p_int == %p \n" , &p_int ); // 输出指针 p_int 的地址
输出 :
p_int[0] == 1
*p_int == 1
sizeof(arr) == 16
sizeof(p_int) == 4
&arr == 0061febc
&p_int == 0061feb8
**总结: 指针还是原来的那个小纸条 , 数组则是 变量的集合体 **
- 通过 sizeof 可以看出来 , 这两种数据类型 有着本质的区别 , 不要被他们之间的隐式转换 误导了
2. 数组指针 怎么理解呢 , 我们对 指针类型 的是时候做了测试 , 定义指针的类型 ,其实上就是定义指针的 跳跃力,
抛开指针的 跳跃力 ,指针只不过是一个记录内存地址的变量而已
注意:
- 数组指针和指针数组 是有区别的 , 数组指针是指针 , 指针数组是数组
- 我们测试指针和数组的区别时 , 测试过在大多数情况下指针 和数组 是可以 隐式转换的 ,
既是 : 数组 ≈ 指针
那么 : 数组指针 ≈ 数组 数组 = 二维数组
所以 : 数组指针 ≈ 二维数组- 数组指针其实是复合类型的指针 , 我们验证了指针的类型代表指针的跳跃力 , 那么 数组指针的跳跃力就是类型和数组的组合
int a[5][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}};
int b[3][4] ;
// 1. 数组指针和指针数组 是有区别的 , 数组指针是指针 , 指针数组是数组
// 这样 定义是 指针数组 , 这个本质上是数组
// sizeof(void*[4]) == 16 , 这是定义了 4 个指针 , 然后把他们组成一个数组
int *pi[4] ;
// 定义是 数组指针 , 这个本质值上是指针
// sizeof(void *) == 4 , 指针 int (*p)[4] 必须指向一个 int[n][4] 的 二维数组 ,所以数组指针 也被称为 行指针
int (*p)[4] ;
// 2. 我们测试指针和数组的区别时 , 测试过在大多数情况下指针 和数组 是可以 隐式转换的 ,
// 既是 : 数组 ≈ 指针
// 那么 : 数组指针 ≈ 数组 数组 = 二维数组
// 所以 : 数组指针 ≈ 二维数组
int n = 3 , a1[n] , *p1 ;
p1 = a1 ; // 数组 ≈ 指针 int a1[n] == int (*p1)
p = a ; //数组指针 ≈ 二维数组 int a[n][4] == int (*p1)[4] , 切记 二维长度 要相等
printf("a[1][1] = %d \n" , p[1][1]); // 也可以将 数组指针 当做二维数组来用
printf("&a[1][1] = %d \n" , *( (*(p + 1)) + 1) ); // 指针的方式使用, 注意:符号优先级 () > [] > *
// 3.数组指针的跳跃力 就是 类型 * 数组 二维长度 的组合
printf("int *p1 = %p \n" , p1);
printf("int *p1 + 1 = %p \n" , p1 + 1); // int *p + 1 = sizeof(int)
printf("int (*p)[4] = %p \n" , p);
printf("int (*p)[4] + 1 = %p \n" , p + 1);// int (*p)[4] + 1 = sizeof(int) * 4
输出 :
a[1][1] = 6
&a[1][1] = 6
int *p1 = 0061fe38
int *p1 + 1 = 0061fe3c
int (*p)[4] = 0061fe9c
int (*p)[4] + 1 = 0061feac