char型指针
char型指针实质上跟别的类型的指针并无本质区别,但由于C语言中的字符串以字符数组的方式存储,而数组在大多数场合又会表现为指针,因此字符串在绝大多数场合就表现为char型指针。
- 定义
char *p = "abcd"; // 指针p指向了常量区中的数据"abcd" 不允许该该数据的任何内容
多级指针
- 如果一个指针变量 p1 存储的地址,是另一个普通变量 a 的地址,那么称 p1 为一级指针
- 如果一个指针变量 p2 存储的地址,是指针变量 p1 的地址,那么称 p2 为二级指针
- 如果一个指针变量 p3 存储的地址,是指针变量 p2 的地址,那么称 p3 为三级指针
- 以此类推,p2、p3等指针被称为多级指针
- 示例:
int a = 123 ; int * p1 = &a ; // p1 中存放了变量 a 的地址 一级指针 int **p2 = &p1; // p2 中存放了 指针变量 p1 的地址 二级指针 int ***p3 = &p2; // p3 中存放了 一个二级指针p2的地址 三级指针 printf("&a:%p \ta:%d\n" , &a , a ); printf("&p1:%p \tp1:%p\n" , &p1 , p1 ); printf("&p2:%p \tp2:%p\n" , &p2 , p2 ); printf("&p3:%p \tp3:%p\n" , &p3 , p3 ); 如何访问数据: printf("a:%d\n" , a ); printf("*p1:%d\n" , *p1 ); // p1 = &a --> *p1 = *&a --> *p1 = a &取地址 *解引用 printf("**p2:%d\n" , **p2 ); // p2 = &p1 -> *p2 = *&p1 -> *p2 = p1 -> *p1 = **p2 printf("***p3:%d\n" , ***p3 ); // p3 = &p2 -> *p3 = *&p2 -> *p3 = p2
指针万能拆解法
- 任意的指针,不管有多复杂,其定义都由两部分组成。
- 第1部分:指针所指向的数据类型,可以是任意的类型
- 第2部分:指针的名字以及明确他是一个指针*
- 示例:
char (*p1); // 第2部分:*p1; 第1部分:char; char *(*p2); // 第2部分:*p2; 第1部分:char *; char **(*p3); // 第2部分:*p3; 第1部分:char **; char (*p4)[3]; // 第2部分:*p4; 第1部分:char [3]; char (*p5)(int, float); // 第2部分:*p5; 第1部分:char (int, float);
- 注解:
- 上述示例中,p1、p2、p3、p4、p5本质上并无区别,它们均是指针
- 上述示例中,p1、p2、p3、p4、p5唯一的不同,是它们所指向的数据类型不同
- 第1部分的声明语句,如果由多个单词组成,C语言规定需要将其拆散写到第2部分的两边
void型指针
- 概念:无法明确指针所指向的数据类型时,可以将指针定义为 void 型指针
- 要点:
-
- void 型指针无法直接索引目标,必须将其转换为一种具体类型的指针方可索引目标
- void 型指针无法进行加减法运算 (有点奇怪实际能进行一个字节的加减运算)
参考:
建议不要随便直接对void 指针进行加减运算(对编译器及其版本有所限制)。
- void关键字的三个作用:
-
- 修饰指针,表示指针指向一个类型未知类型的数据。
- 修饰函数参数列表,表示函数不接收任何参数。
- 修饰函数返回类型,表示函数不返回任何数据。
- 示例:
// 指针 p 指向一块 4 字节的内存,且这4字节数据类型未确定 void *p = malloc(4); // 1,将这 4 字节内存用来存储 int 型数据 *(int *)p = 100; printf("%d\n", *(int *)p); // 2,将这 4 字节内存用来存储 float 型数据 *(float *)p = 3.14; printf("%f\n", *(float *)p);
const关键字
作用:修饰变量,使一个变量不可修改(常量)
- 修饰普通变量,使之不可修改
- 修饰指针变量,使之不可修改或者使其指向的目标不可修改
const 型变量
语法: const 变量类型 变量名 = 初始值 ;
int a = 123 ;
// 修饰普通变量,使得该变量的值不可修改
const int b = 456 ; // 被const 修饰的变量除了初始化时能对他赋值意外
// 其他的情况下无法对它进行赋值
printf("a:%d \t b:%d\n" , a , b);
a = 543 ; // [正确]
// b = 321 ; // [错误] error: assignment of read-only variable ‘b’
const 型指针
常(量)指针:
修饰的是指针本身, 该指针为一个常量。
语法: 数据类型 * const 指针名 = 指向的地址 ;
操作示例:
int a = 123 ;
printf("LINE:%d a:%d \n" , __LINE__ , a );
int * p = &a ;
int * const p1 = &a ;
*p = 456 ; // 通过指针 p 来修改变量 a 中的数据
printf("LINE:%d a:%d \n" , __LINE__ , a );
*p1 = 789 ; // 通过指针 p1 来修改变量 a 中的数据
printf("LINE:%d a:%d \n" , __LINE__ , a );
int b = 999 ;
p = &b ; // 修改指针p的指向 (把b的地址存入指针变量 p 中)
printf("LINE:%d *p:%d \n" , __LINE__ , *p );
// p1 定义的时候被修饰成了一个常量指针 (该指针是一个常量)
// 因此 无法修改该常量指针的值 (无法改变指向)
p1 = &b ; // [错误] 修改指针p1的指向 (把b的地址存入指针变量 p1 中)
常(量)目标指针:
修饰的是指针所指向目标类型, 指针所指向的目标是一个常量。
语法:
- const 类型 * 指针名 = 指向的地址 ;
- 类型 const * 指针名 = 指向的地址 ;
使用示例:
// argv[0][1] = 'k'; // [错误] 数组argv中的类型为 char const 类型,不允许被修改
// argv 数组中存放的 是常目标指针
int a = 123 ;
printf("LINE:%d:%d\n" ,__LINE__ , a );
int * p1 = &a ;
const int * p2 = &a ;
int const * p3 = &a ;
*p1 = 345 ; // 把345的二进制编码存入到 p1 所指向的内存中 (修改内存中的值)
printf("LINE:%d:%d\n" ,__LINE__ , a );
// error: assignment of read-only location ‘*p2’
// *p2 = 567 ; // [错误] p2 所指向的是一个常量的数据 不允许被修改
a = 888 ; // [正确] 变量自身是可以被改变的只是不允许通过 p2 来改变 a 的值
int b = 999 ;
p1 = &b ; // 修改p1中的值(修改p1 指向)
p2 = &b; // [正确] 修改p2中的值(修改p2 指向)
printf("*p2:%d\n" , *p2);
-
- 常指针在实际应用中不常见。
- 常目标指针在实际应用中广泛可见,用来限制指针的读写权限(保护内存不会被误操作修改)
数组指针:
概念: 这是一个指针,用来指向一个数组的指针。
语法: 类型(*p)[元素个数] ;
操作示例:
int arr [5] = {1,2,3,4,5};
// arr 与 &arr 得到的值是同一个值, 都是代表某一个地址
// 区别在于他的意义不一样 , arr 表示的是首元素的地址 &arr 表示的是整个数组的地址
int * p1 = arr ; // arr 代表首元素 1 的地址, p1 就指向了数据 1 的地址
// int * p0 = &arr ; // [有警告] 数据的类型不匹配
int (*p2) [5] = &arr ; // &arr 表示的是整个数组的地址, p2 指向了整个数组
// int (*p3) [5] = arr ;
// 数组指针加减运算
int * p4 = p2+1 ; // p2是一个指针,它指向的类型 是 int [5] ,因此 +1 则+ int * 5 = 20 字节
printf("arr:%p\n" , arr );
printf("p4:%p\n" , p4);
// 如何通过数组指针来访问数组中的没有个数据
printf( "**p2:%d\n" , **p2 );
// p2 = &arr -> *p2 = *&arr -> *p2 = arr -> **p2 = *arr ->*arr = arr[0] / *(arr+0)
printf( "*(*p2+1):%d\n" , *(*p2+1) ); // *p2 = arr -> *(arr+1) -> arr[1]
printf( "(*p2)[2] :%d\n" , (*p2)[2] ); // (*p2) = arr
对代码的理解: