06--指针(2)

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 型指针
  • 要点:
    1. void 型指针无法直接索引目标,必须将其转换为一种具体类型的指针方可索引目标
    2. void 型指针无法进行加减法运算 (有点奇怪实际能进行一个字节的加减运算)

参考:

建议不要随便直接对void 指针进行加减运算(对编译器及其版本有所限制)。 

  • void关键字的三个作用:
    1. 修饰指针,表示指针指向一个类型未知类型的数据。
    2. 修饰函数参数列表,表示函数不接收任何参数。
    3. 修饰函数返回类型,表示函数不返回任何数据。
  • 示例:
    // 指针 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 

对代码的理解:

  • 16
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值