0
点赞
收藏
分享

微信扫一扫

七、指针详解

Villagers 2022-04-07 阅读 75
c语言

【1】指针

1.概念

​ 地址:内存中每个自己都有唯一的编号,这编号就是地址。(门牌号)
​ 指针:就是地址。
​ 指针变量:存储指针(地址)的变量就是指针变量。

2.定义格式:

​ [存储类型] [数据类型] *指针变量名;
​ (auto) int *p;

  * :在定义指针变量时,*仅起标识作用。
  数据类型:指针的数据类型要和指向的数据类型统一。

####     3.指针的应用:

#####   1)指针赋值运算
    
​    **a.指针指向普通变量**  

​	  

```
   * 将普通变量的地址赋值给了指针变量,指针变量存储普通变量地址即为指向该变量。
       int  a = 10;
     	  int  *p1 = &a;   //&取地址符:取出变量的地址
     	  int  *p2 = NULL; //指针指空,防止出现野指针,没指向对象的指针就叫野指针。
     	  p2 = &a;  //正确
     	  *p2 = a;  //错误,因为指针指空,这相当于给空地址赋值,导致段错误。
     	  

​	  int  *p3; //野指针,指针变量里是随机值,指针随机指向一个地址,造成非法访问,导致段错误。
​	
​	  int b;
​	  int *p4 = NULL;
​	  p4 = &b;
​	  *p4 = 10;
​	  //正确写法 ,相当于给变量赋值。
```

​	b.**指针指向数组**

​	

```
  将数组的首个元素地址(首地址)赋值给指针变量。
	  int num[10] = {0};
	  int *p1 = num;
	  int *p2 = &num[0];
	c.多个指针指向同一个地址(指针之间互相赋值)
	  将一个指针变量里存储的地址赋值给另一个指针。
      int  a = 10;
	  int  *p1 = &a; 		   
	  int  *p2 = p1;
	  int  *p3 = NULL;
	  p3 = p1;                      	  
	  //p1、p2、p3都指向变量a.
		
	  //*:取值符    *指针变量 = 取指针变量存储的地址上的值
```

   **2)指针的算术运算**

​     

```
 ++  --
	  p++ : 指针向高地址方向移动一个数据
      p-- : 指针向低地址方向移动一个数据
	 (具体移动几个字节根据指向的数据类型来判断) 
```

3)指针的关系运算

      ==  >  <  >=  <=  !=
      指针的关系运算本质上是比较的地址的高低
	  指向高地址的指针大于指向低地址的指针
	  注:指向不同数据类型或不同区域的指针之间没有关系运算的意义。
	  int a[5] = {1,2,3,4,5};
	  int *p1 = &a[0];
	  int *p2 = &a[2];
      //p2 > p1

4)& 取址符 : 取变量的地址

 取值符 : 取地址上的值
 &和*互为逆运算,可以抵消。
  int a[5] = {1,2,3,4,5};
  int *p = NULL;
  p = a;
  *p  =  *a =  *(&a[0]) = a[0] = 1

​      访问a[1]的值:
​	    a[1]    *(a+1)
​    	p[1]    *(p+1)
​      访问a[1]的地址
​	    &a[1]    a+1
​    	p[1]    *(p+1)		
​		
​    约定地址的等级比值的等级高
​	a[1]  -> 升级成地址  &a[1]     升级 &  
​	a[1]  -> 升级成地址  a+1       升级 去掉[]
​	
​	&a[0] -> 降级成元素 *(&a[0]) ->  a[0]      降级 * 
​	a 数组首地址 ->  a+0 -> 降级成元素 a[0]    降级 加上[]

4.指针的大小

   指针的大小与指针的数据类型无关,与指针的寻址空间有关。
	32位操作系统中,指针恒为4字节。
	32位的操作系统,有效寻址空间是0~4G,地址都是一个8位的十六进制数,占用4字节。
	8位16进制 = 32位2进制 = 4字节
	8位2进制 = 1字节
	
    int a[10];
    int *p1 = a;    //sizeof(p1) = 4
    char b;
    char *p2 = &b;  //sizeof(p2) = 4
    double f;
    double *p3 = &f;  //sizeof(p3) = 4

练习:从终端输入一串字符,将其中的大写字母转换成小写字母,小写字母转换成大写字母,
其他字符全部转换成*,输出该字符串。(用指针实现)

练习:用指针实现字符串倒置。

5.指针的修饰

1)const常量化

功能:只读修饰,变量经过只读修饰后无法被更改。
const int a = 10;
a = 20; //error: assignment of read-only variable ‘a’
//经过const修饰的变量无法被更改。

const修饰指针
情形一:

  int a = 10;
  int b = 20;
  const int *p = &a;  //对于const来说修饰的是*p
  int const *p = &a;  //同上
  *p = 20;   //报错,*p被const修饰无法更改
  p = &b;    //正确,p的指向可以改
  总结:可以通过更改p的指向间接更改*p的值,但无法通过*p直接更改。

情形二:

  int a = 10;
  int b = 20;
  int * const p = &a;  
  *p = 20; //正确,*p可以被更改。    
  p = &b;  //错误,p被const修饰,p的指向无法更改。
  总结:可以更改*p,但无法更改p的指向。

##### 
2)void空类型
    void  *p = NULL;  //空类型指针可以指向任意一种类型的变量。
    示例:
	void *p = NULL;
    int a = 10;
    p = &a;
    printf("*p = %d\n",*(int *)p);
    
    char b = 'A';
    p = &b;
    printf("*p = %c\n",*(char *)p);
    
    double c = 3.1415926;
    p = &c;
    printf("*p = %.2lf\n",*(double *)p);

【2】二级指针

1.概念:

指向一级指针的指针称为二级指针
示例:

int a = 10;
		 int *p = &a;    //一级指针
		 int **q = &p;   //二级指针 
		 int ***s = &q;  //三级指针
		 int x = &s; //四级指针

​ 访问a的值:
​ a *p (**q)
​ 访问a的地址
​ &a p *q

​ //二级指针q里面保存的是一级指针p的地址,
​ *q指的是p里面的值,即a的地址,
​ q指的是a的值。

2.指针自加运算

 p++ :++在后,先取p的值(指p变量里保存的地址)进行打印,再让指针向后移动。
 ++p :++在前,先让指针向后移动一个数据,再取p的值进行打印。		

*p++ : 先取出p的值参与取值运算(*),再让指针向后移动一个数据。

   *(p++): 先取出p的值参与取值运算(*),再让指针向后移动一个数据。		

(*p)++:先取出p的值参与取值运算(*),让指针指向的值自加1.

 ++*p:取出p指向的地址上的值进行自加1
++(*p):取出p指向的地址上的值进行自加1

*++p :先让指针向后移动一个数据,再取出p指向的地址上的值。
*(++p) :先让指针向后移动一个数据,再取出p指向的地址上的值。	

【3】数组指针

1.概念:

本质上是个指针,指向数组(二维数组)的指针称为数组指针。

2.定义格式:

    [存储类型]    [数据类型]    (*数组指针名)[列数];
	  (auto)       int          (*p)[i];
示例:
    数组指针指向二维数组(指向了二维数组的行地址)
	//数组指针是一个二级指针
	p    行地址   
	*p   列地址  元素地址
	**p  元素值
	

    int  a[2][3] = {{1,2,3},{4,5,6}};
    int (*p)[3] = a;        
    访问元素a[i][j]的值:
    直接访问:  a[i][j]  *(a[i]+j)   *(*(a+i)+j)  
    间接访问:  p[i][j]  *(p[i]+j)   *(*(p+i)+j)  
    访问元素a[i][j]的地址:
    直接访问:  &a[i][j]   a[i]+j     *(a+i)+j  
    间接访问:  &p[i][j]   p[i]+j     *(p+i)+j 

【4】指针数组

1.概念:

本质上是个数组,存放指针(一级)的数组称为指针数组。
	定义格式: 
	    [存储类型]  [数据类型]   *指针数组名[大小];
          (auto)      int      *p[i];       
    大小:元素个数
	应用示例:
        1.存放普通变量的地址
          int a = 10,b = 20,c = 30;
          int *p[3] = {&a,&b,&c};		  
          通过指针数组名p访问b的值:
		    *p[1]      *(*(p+1))
     		   

​      通过指针数组名p访问b的地址:
​	     p[1]      *(p+1)

​       注:*(*(p+1)) : p是指针数组名即数组中第一个元素的地址,p+1是数组中p[1]这个元素的地址,
​          *(p+1)是p[1]这个元素的值即&b,*(*(p+1))则为b的值。	

2.存放二维数组每行首个元素的地址

	  int  a[2][3] = {1,2,3,4,5,6};
      int  *p[2] = {&a[0][0],&a[1][0]};		//指针数组名相当于二级指针
	  int  *p[2] = {a[0],a[1]};	  
	  
	  通过指针数组p访问元素a[i][j]的值:
      p[i][j]	  *(p[i]+j)      *(*(p+i)+j)  
	  通过指针数组p访问元素a[i][j]的地址:			
	 &p[i][j]	    p[i]+j         *(p+i)+j 
	 
	  注:p是指针数组,存放的是二维数组每行首个元素的地址,p[1]是a[1]即a[1][0]这个元素的地址,
          p[1]+1即为a[1][1]的地址,*(p[1]+1)即为a[1][1]的值。		  
		  
	  指针数组中存放的是一级地址,p[1]即为数组中下标为1的元素值,即为&a[1][0]
		                          p[1]+1 即为 &a[1][0] + 1 =>  &a[1][1]
		                        *(p[1]+1)  => a[1][1]

3. #### 存放字符串

```
 	3. ​	   char  *p[3] = {"farsight","jinan","yyds!"};

​	   打印"yyds!"字符串:
​	   printf("%s\n",p[2]);
​	   printf("%s\n",*(p+2));  
​	
​       打印‘!’字符:
​	   printf("%c\n",*(p[2]+4));
​	   printf("%c\n",*(*(p+2)+4));	
​	   
​	//注:p[2]是“yyds!”的首地址,即'y'这个字符的地址,	
​          p[2]+4就是'!'这个字符的地址,	
​         *(p[2]+4)就是'!'这个字符。			  
​		 *(*(p+2)+4)
```

​	

4.命令行传参

	  int main(int argc, const char *argv[])
      const char *argv[] : argv是一个指针数组,用于存放命令行传递的字符串。
	  int argc :argc是一个整型变量,用于记录命令行传递参数的个数。
      示例:
	  #include<stdio.h>
      int main(int argc, const char *argv[])
      {
          puts(argv[0]);
          puts(argv[1]);
          printf("argc = %d\n",argc);
          return 0;
      }                                          
      执行:  ./a.out hello                                 
      打印结果:
	          ./a.out
              hello
              argc = 2

​ 练习:仿写strncpy的功能
​ 例如:s1 : hello
​ s2 : abc
​ n : 2
​ 输出:abllo

举报

相关推荐

0 条评论