指针(进阶)

目录

1.字符指针(char*)

2.指针数组

 3.数组指针

3.1数组指针的定义

3.2 &数组名和数组名

4.数组参数、指针参数

4.1一维数组传参

4.2二维数组传参

4.3一级指针传参

5.函数指针

6.函数指针数组

7.指向函数指针数组的指针

8.回调函数


1.字符指针(char*)

上面的代码char* ps前加上const修饰更加严谨

将字符串"abcdef"的首地址放到ps中,"abcdef"是常量字符串不能被修改

char arr[ ]=" abcdef ";

char* p=arr;//p指向arr的起始位置

*p=‘w’;//对p进行解引用,其实就是数组的第一个元素

p指向的是数组的首元素,arr数组是可以被修改

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

str1和str2是两个独立的数组

 str3和str4中的hello CSDN.是常量字符串,常量字符串不能被修改,所以这个常量字符串没必要存放两份,只存一份就够(内存优化)

如果要比较字符串的内容,使用:strcmp

2.指针数组

指针数组的本质是数组,里面存的是指针

int * arr1[ 10 ]; / /整形指针数组

char* arr2[ 4 ]; / /一级字符指针数组

char** arr3[ 5 ]; / /二级字符指针数组

用指针数组模拟实现二维数组

 3.数组指针

3.1数组指针的定义

数组指针是指针

 去掉名字就是它的类型

 这里的(*p)就相当于arr

一般情况下,数组指针应用于二维数组

数组指针的应用

 p+i指向第i行的地址

二维数组的数组名,也表示数组首元素的地址,二维数组的首元素是第一行,首元素的地址就是第一行的地址,是一维数组的地址

*(p+0)-->p[0]-->&p[0][0]

3.2 &数组名和数组名

数组名是数组首元素的地址

有两个例外:

  • 1.sizeof(数组名)
  • 2.&数组名

int   arr [ 5 ] ;  / /整型数组

int   *parr1 [ 10 ] ;  / /指针数组

int   ( *parr2 )[10 ] ;  / /数组指针

int   ( *parr3 [10 ])[ 5 ] ; parr3 是存放数组指针的数组

/ /将名字parr3 [ 10 ] 去掉

/ /int (*) [ 5 ] - ->数组的元素类型

4.数组参数、指针参数

4.1一维数组传参

#include<stdio.h>

void test ( int  arr [ ])

{}

void test ( int arr[ 10 ])

{}

void test ( int *arr )

{}

void test2 ( int  * arr[ 20 ])

{}
void test2 ( int  **arr)

{}

int main()

{

        int arr[ 10 ]={ 0 };

        int *arr2[20]={ 0 };

        test2(arr2);

}

/ /以上写法均可以

4.2二维数组传参

void  test (  int  arr [ 3 ] [ 5 ]  ) / / ok

{ }

void  test ( int  arr [ ] [ ] )

{ }

void  test ( int  arr [ ] [ 5 ] ) / / ok

{ }

//总结:二维数组传参,函数形参的设计只能省略第一个[ ]的数字

//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少个元素

//这样才方便运算

void  test ( int* arr )

{ } / /二维数组名是数组第一行的地址,而这个函数是整型指针,所以不行

void  test ( int* arr [ 5 ] )

{ } / /二维数组名是数组首元素地址,而这个函数的参数是数组,所以不行

void  test(int(*arr)[5])/ / ok

{ }

void  test ( int ** arr )

{ } / /传过来是一维数组的地址(一行的地址),二级指针是用来接收一级指针变量的地址

int  main( )

{

        int arr [ 3 ] [ 5 ] = { 0 } ;

        test ( arr ) ;

}

/ / 二维数组传参,函数形参的设计只能省略第一个[ ]的数字。 

/ / 因为对一个二维数组,可以不知道有多少行

/ / 但是必须知道一行多少元素,这样才方便运算

二维数组传参

参数可以是指针,也可以是数组

如果是数组,行可以省略,但是列不可以省略

如果是指针,传过去的是第一行的地址,形参就应该是数组指针

4.3一级指针传参

一级指针p

当一个函数的参数部分位一级指针的时候 ,函数能接收什么参数?

int a;

print(&a,10);

int*p1=&a;

print(p1,10);

int arr[10];

print(arr,10);

当函数的参数为二级指针的时候,可以接受什么参数?

void test(char **p)
{

}
int main()
{
    char c='b';
    char*pc=&c;
    char**ppc=&pc;
    char*arr[10];
    test(&pc);
    test(ppc);
    test(arr);
    return 0;
} 

5.函数指针

整型指针-指向整型的指针  int *

字符指针-指向字符的指针  char*

数组指针-指向数组的指针  int arr[10];  int (*p)[10]=&arr;【数组指针:存放数组地址的指针】

函数指针-指向函数的指针【函数指针:存放函数地址的指针】

函数名和&函数名都是取函数的地址

 函数名就是函数的地址,无论有没有&都是函数的地址,没有区别

此时pf就是函数指针变量 

 / /代码1

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

将0强制类型转换为void(*)( )--->把0当作一个函数的地址

这就意味着0地址处放着一个函数,函数没有参数,返回类型是void

然后解引用0的地址,找0地址处的函数--->调用该地址处的函数

上面的代码是一次函数调用

把0直接转换一个void(*)()的函数指针,然后去调用0地址处的函数

/ /代码2

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

这里是函数声明,没有传参数,不是函数调用,函数的定义需要有大括号,故不是函数的定义,但是又有函数的参数类型,所以这里是函数的声明

signal是函数名,signal的参数一个是int类型,另一个是void(*)(int)函数指针类型,然后这个signal函数的返回类型是void (*)(int)函数指针类型

简而言之:

上述代码是一次函数声明

声明的函数:signal

signal函数的第一个参数是int类型的

signal函数的第二个参数是一个函数指针类型,该函数指针指向的函数参数是int,返回类型是void

signal函数的返回类型也是一个函数指针类型,该函数指针指向的函数参数是int,返回类型是void

注意:这个写法是固定的,不能这样写void(*) (int)signal (int , void(*)int))

这个代码看起来很繁琐,可用typedef重新命名一个名字,使这行代变得简洁

typedef void ( *pf_t )( int ) / /重新定义的类型名必须放在它的内部这是语法要求 

/ /那么上面的代码可以写为

pf_t  signal( int , pf_t );

typedef void(*pf_t2)(int);//pf_t2是类型名

void(*pf)(int);//pf是函数指针变量的名字

int mystrlen(const char* str)
{
    //...
}
int main()
{
    //指针数组
    char* arr[10];
    //数组指针
    int arr2[5];
    int(*p)[5]=&arr2;
    //函数指针
    int(*pf)(const char*)=my_strlen;//pf是一个指向函数的函数指针变量
    (*pf)("abcdef");
    pf("abcdef");
    my_strleln("abcdef");
    //上面三种方式调用都是可以的
    
}

6.函数指针数组

函数指针数组可以存放多个参数相同、返回类型相同的函数的地址

函数指针数组:数组的每个元素是一个函数指针

函数指针  int (*pf ) ( int  , int )

函数指针数组  int  ( *pfArr[ 4 ] ) ( int  , int )

用函数指针数组写一个计算器

#include<stdio.h>
void menu()
{
	printf("*******************************\n");
	printf("*******   1.add  2.sub   ******\n");
	printf("*******   3.mul  4.div   ******\n");
	printf("*******   0.exit         ******\n");
	printf("*******************************\n");

}
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()

{

	int input = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		int x = 0;
		int y = 0;
		if (input == 0)
		{
			printf("退出计算器\n");
            break;
		}
		else if (input >= 1 && input <= 4)
		{
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			int(*pfArr[5])(int, int) = { 0,Add,Sub,Mul,Div };
			int ret = (*pfArr[input])(x,y);
			printf("%d\n", ret);
		}
		else
		{
			printf("输入错误,请重新输入\n");
		}


	} while (input);
	return 0;
}

7.指向函数指针数组的指针

int Add(int x, int y)
{
    return x + y;
}
int Sub(int x , int y)
{
    return x - y;
}
int main()
{
    int (*pf)(int , int)=Add;
    //函数指针数组
    int (*pfArr[4])(int ,int )={Add,Sub};
    int (*(*ppfArr[4]))(int ,int )=&pfArr;//ppfArr是一个指向函数指针数组的指针变量
    return 0;
}

8.回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现直接调用,而是再特定的事件或条件发生时由另外的以方调用的,用于该事件或条件进行响应

此时的calc就是回调函数

 

冒泡排序的缺点: 只能排序整型的数据

而qsort函数能够排任意类型的数据

qsort函数的使用:

 void qsort(void* base,size_t num,size_t witdh,int(*cmp)(const void* e1,const void* e2))

void*base--->待排序的数据首元素的地址,也就是指向起始位置

size_t num--->待排序数据的元素个数

size_t witdh--->待排序数据中每个元素的大小 (单位是字节)

int (*cmp)(const void*e1,const void *e2)--->比较两个元素的大小的函数指针

两个整型使用关系运算符比较大小

两个字符串用库函数strcmp比较大小

两个结构体需要指定方式进行比较(比如:一个人是按照名字进行比较还是用年龄进行比较)

由于比较方式的不同,比较方法单独分装一个函数

void*:

void*可以接受任意类型的指针,但是void*类型的指针不能++,因为不知道能跳过几个字节,不过,想要void*++,需要将void*类型的指针强制类型转换为其他类型的指针,并且解引用*,便可让它++

 将冒泡排序改为可以排任意元素的排序(根据qsort函数)---->用冒泡排序模拟实现qsort函数

冒泡排序中的比较部分分装一个函数指针(因为qsort函数就是用函数指针)来比较大小

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值