第四周 指针进阶

目录

1.字符指针

字符串数组和字符串指针的区别

2.指针数组

3.数组指针

3-1.一维数组指针

3-1-1.如何取出整个数组的地址

3-1-2.如何用指针遍历一维数组

3-2.二维数组指针

3-2-1. 如何用指针遍历二维数组

3-2-2.遍历时的注意事项

4.指针传参

4-1. 一级指针传参

4-2. 二级指针传参

5.函数指针

5-2. 理解两个有趣的代码(来自c陷阱和缺陷)

5-2-1.

5-2-2

6.函数指针数组

6-1.应用part

6-1-1.计算机(加减乘除) 原始代码呈现

6-1-2.利用函数指针数组简化

6-2-2.利用回调函数来简化代码

7.回调函数


1.字符指针

#include<stdio.h>
int main (){
    char *ps="hello world"; 
	printf("%c",*ps);
    return 0;
    //输出:h
    //本质上是把"hello world"这个字符串的首字符
	//的地址存储在了ps中 
}

#include<stdio.h>
int main (){
    char *ps="hello world"; 
	printf("%s",ps);
    return 0;
    //输出:hello world 
    //给出字符串首个元素的地址,就可以输出这个字符串 
}


字符串数组和字符串指针的区别

int main ()
{ 
	char str1 [] = "hello bit.";
	char str2 [] = "hello bit.";
	char* str3= "hello bit.";
	char* str4="hello bit.";
	if (str1==str2)
	printf ("strl and str2 are same\n");
	else{printf ("strl and str2 are not same\n");} 
	if (str3==str4)
	printf ("str3 and str4 are same\n");
	else{printf ("str3 and str4 are not same\n");} 
	return 0;
	/*
	  strl and str2 are not same
      str3 and str4 are same
      
	*/
} 

int main ()
{ 
	char str1 [] = "hello bit.";
	char str2 [] = "hello bit.";
	char* str3= "hello bit.";
	char* str4="hello bit.";
	*str3='w';
	printf("%s",str3) ;
    /*
	没有输出,因为''hello world''是一个常量字符串!	
	*/
}

2.指针数组

3.数组指针

3-1.一维数组指针

3-1-1.如何取出整个数组的地址

#include<stdio.h>
int main()
{
    int arr[10] ={1,2,3,4,5};
    int(*parr)[10]=&arr;
	//取出的是数组的地址
	//parr 就是一个数组指针-其中存放的是数组的地址
    arr;
	//arr-数组名是首元素的地址-arr[0]的地址
	return 0;
}

3-1-2.如何用指针遍历一维数组

#include<stdio.h>
int main()
{   //遍历arr数组 
    int arr[10] ={1,2,3,4,5,6,7,8,9,10};
    int (*p)[10]=&arr;
    int i;
    for(i=0;i<10;i++){
    	printf("%d\n",*(*p+i));  
	}
	return 0;
}

注意:为什么不写成printf("%d",*(p+1)呢

           原因在于指针p指向的是数组arr 所以p+1就是p加上整个arr数组的长度

        (*p)才是数组首元素的地址

注意:数组名是数组首元素的地址,但是有2个例外:

           1.sizeof(数组名)-数组名表示整个组,计算的是整个数组大小,单位是字节

           2.&数组名-数组名表示整个数组,取出的是整个数组的地址

3-2.二维数组指针

3-2-1. 如何用指针遍历二维数组

#include<stdio.h>
void bianli(int (*p)[4],int n,int m);
int main(){
	int arr[3][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11}};
	bianli(arr,3,4); 
	return 0;	
}
void bianli(int (*p)[4],int n,int m){
	int i,j;
	for(i=0;i<n;i++){
		for(j=0;j<m;j++){
			printf("%2d ",*(*(p+i)+j));
		}
		printf("\n");
	}
}
//输出  0  1  2  3
//      4  5  6  7
//      8  9 10 11

3-2-2.遍历时的注意事项

遍历二维数组应该写成*(*(p+i)+j)

*(p+1)表示取地址上的数据,也就是整个第i行的数据。是多个数据。

*(p+1)单独使用时表示的是第 i 行数据,放在表达式中会被转换为第 i 行数据的首地址,也就是第 i  行第 0 个元素的地址,

因为使用整行数据没有实际的含义,编译器遇到这种情况都会转换为指向该行第 0 个元素的指针;就像一维数组的名字,在定义时或者和 sizeof、& 一起使用时才表示整个数组,出现在表达式中就会被转换为指向数组第 0 个元素的指针。

4.指针传参

4-1. 一级指针传参

4-2. 二级指针传参

 注:&a只是一个地址的值 只有把这个值放到一个变量里才会有地址!

5.函数指针

注意:Add与&Add是等价的

           Add可以直接赋给pf

           Add也等价于pf

#include<stdio.h>
int Add(int x,int y);
int main()
{   //  pf就是一个函数指针变量
        int (*pf)(int,int)=&Add;
    //  int (*pf)(int,int)=Add; Add与&Add是等价的!!! 
    //  Add === pf  等价的 
		int ret= (*pf) (3,5);//输出8 
        int ret=pf (3, 5) ;//输出8 
		int ret= Add (3, 5) ;//输出8 
}
int Add(int x,int y){
	return x+y;
}

5-2. 理解两个有趣的代码(来自c陷阱和缺陷)

5-2-1.

(*(void)(*)()0)(); 

调用地址为0处的函数
该函数无参 返回类型是void
1.void(*)()是函数指针类型 
2.(void(*)())0对0进行强制类型转化,被解释为一个函数地址 
3.*(void(*)())0对0地址进行了解引用操作 
4.(*(void(*)())0)() 调用函数 

5-2-2

void(*signal(int,void(*)(int)))(int);

1.signal先和()结合,说明signal是一个函数 
2.signal的第一个参数类型是int,第二个参数类型是函数指针 
这个函数指针指向一个参数类型为int,返回类型为void的函数 
3.signal函数的返回类型也是一个函数指针 
为了更好理解,可以把这个代码解释为
typedef  对类型进行重定义

typedef void(*pfun_t)(int);//把void(*)()改写成为pfun_t 
pfun_t signal(int,pfun_t) 

6.函数指针数组

6-1.应用part

6-1-1.计算机(加减乘除) 原始代码呈现

#include<stdio.h>
int add(int x,int y){
	return x+y;
}
int sub(int x,int y){
	return x-y;
}
int mul(int x,int y){
	return x*y;
}
int div(int x,int y){
	return x/y;
}
int main(){
	printf("1:add\n");
	printf("2:sub\n");
	printf("3:mul\n");
	printf("4:div\n");
	printf("请输入你要进行的计算:");
	int input,x,y;
	scanf("%d",&input);
	switch(input){
		int ret;
		case 1:
			printf("选择两个操作数:");
			scanf("%d %d",&x,&y);
			ret=add(x,y);
			printf("%d",ret); 
		case 2:
			printf("选择两个操作数:");
			scanf("%d %d",&x,&y);
			ret=sub(x,y);
			printf("%d",ret); 
		case 3:
			printf("选择两个操作数:");
			scanf("%d %d",&x,&y);
			ret=mul(x,y);
			printf("%d",ret); 
		case 4:
			printf("选择两个操作数:");
			scanf("%d %d",&x,&y);
			ret=div(x,y);
			printf("%d",ret); 
	}
	return 0;
}

一个直球的代码 重复部分多 比较冗长

6-1-2.利用函数指针数组简化

#include<stdio.h>
int add(int x,int y){
	return x+y;
}
int sub(int x,int y){
	return x-y;
}
int mul(int x,int y){
	return x*y;
}
int div(int x,int y){
	return x/y;
}
int main(){
	printf("1:add\n");
	printf("2:sub\n");
	printf("3:mul\n");
	printf("4:div\n");
	printf("请输入你要进行的计算:");
	int input,x,y,ret;
	scanf("%d",&input);
	printf("选择两个操作数:");
	scanf("%d %d",&x,&y);
	int (*arr[5])(int,int)={0,add,sub,mul,div};//重要步骤!!
	ret=arr[input](x,y);//调用!!
	printf("%d",ret);
	return 0;
}

函数指针数组相当于一个跳板,使我们可以调用出所需的函数(将函数的地址放在数组中)

6-2-2.利用回调函数来简化代码

#include<stdio.h>
int add(int x,int y){
	return x+y;
}
int sub(int x,int y){
	return x-y;
}
int mul(int x,int y){
	return x*y;
}
int div(int x,int y){
	return x/y;
}
int calc(int (*p)(int,int)){
	int x,y,ret;
	printf("选择两个操作数:");
	scanf("%d %d",&x,&y);
	ret=p(x,y);
	return ret;
}
	
int main(){
	printf("1:add\n");
	printf("2:sub\n");
	printf("3:mul\n");
	printf("4:div\n");
	printf("请输入你要进行的计算:");
	int input,x,y;
	scanf("%d",&input);
	switch(input){
		int ret;
		case 1:
		    ret=calc(add);
			printf("%d",ret); 
		case 2:
			ret=calc(sub);
			printf("%d",ret); 
		case 3:
			ret=calc(mul);
			printf("%d",ret); 
		case 4:
			ret=calc(div);
			printf("%d",ret); 
	}
	return 0;
}

7.回调函数

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值