C语言之指针进阶


前言

1.指针就是变量,用来存放地址。地址唯一标识一块内存空间。
2.指针的大小是固定 4/8 个字节(32位操作系统/64位操作系统)。
3.指针是有类型的,指针的类型决定了±整数的步长,指针解引用时候的操作内存大小的权限。
4.指针的运算。


提示:以下是本篇文章正文内容,下面案例可供参考

一、字符指针

在指针类型中有字符类型指针 char*
示例:

int main()
{
	char a = 'a';
	char* p = &a;
	return 0;
}
int main()
{
	char arr[] = "abcdef";
	char* p = arr;
	printf("%s\n", arr);	//输出abcdef
	printf("%s\n", p);		//输出abcdef
	return 0;
}

//还有一种写法
void test()
{
	//abcdef是个常量
	char* p = "abcdef";
	//p里面存的是首元素的地址
	printf("%c\n", *p);	//输出a
	printf("%c\n", p);	//输出abcdef
}

相关面试题

int main()
{
	char arr1 = "abcdef";
	char arr2 = "abcdef";

	char* p1 = "abcdef";
	char* p2 = "abcdef";

	if (arr1 == arr2)
	{
		printf("arr1 = arr2");
	} 
	else
	{
		printf("arr1 != arr2");
	}

	if (p1 == p2)
	{
		printf("p1 = p2");
	} 
	else
	{
		printf("p1 != p2");
	}
	return 0;

	//1.输出 arr1 = arr2
	//2.输出 p1 = p2
	//因为char* p1 = "abcdef";
	//char* p2 = "abcdef";
	//abcdef是常量,所以 p1 p2指向的首元素位置是同一个地方
}

二、数组指针

数组指针是数组呢?还是指针?答案是:指针。

int main()
{
	int* p = NULL; //整形指针 指向整形的指针 可以存放整形的地址
	char* cp = NULL; //字符指针 指向字符的指针	可以存放字符的地址
	
	int arr[10] = {0}; //数组指针 指向数组的指针 可以存放数组的地址
	//arr和 arr[0] 是首元素地址  
	//&arr是数组的地址
	return 0;
}

数组指针:

int main()
{
	int arr[10] = {1,2,3,4,5,6,7,8,9,10};
	int (*p)[10] = &arr;
	return 0;
}

数组指针的使用练习:

void print(int (*p)[5], int row, int col)
{
    for( int i = 0; i < row; i++ )
    {
        //printf("%p \n", *(p + i)); //每一行的地址
        for( int j = 0; j < col; j++ )
        {
            printf("%d ", *(*(p + i) + j)); //每个行每个元素
            //printf("%d ", (*(p + i))[j]); //每个行每个元素
            //printf("%d ", p[i][j]); //每个行每个元素
        }
        printf("\n");
    }
}

int main()
{
    int arr[3][5] = {{1,2,3,4,5}, {2,3,4,5,6}, {3,4,5,6,7}};
    print(arr, 3, 5);
    return 0;
}

三、指针数组

指针数组是用来存放指针的数组

int main()
{
	int arr[10] = { 0 }; //整形数组
	char ch[10] = { 0 }; //字符数组

	int* pArr[10];	//整形指针数组
	char* cArr[10];	//字符指针数组
	
	return 0;
}

指针数组的使用

int main()
{
	int arr1[] = {1, 2, 3, 4, 5};
	int arr2[] = {6, 7 ,8 ,9 , 0};
	int arr3[] = {12, 23, 34, 45, 56};
	
	//指针数组
	int* pArr[] = {arr1, arr2, arr3};
	
	//获取三个数组的每一个元素
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 5; j++)
		{
			printf("%d ", *(pArr[i] + j));
		}
		printf("\n");
	}
	return 0;
}

四、数组传参和指针传参

一维数组

void test1(int arr[]){}			//ok? ok
void test1(int* arr[]){}		//ok? ok
void test1(int arr[10]){}		//ok? ok
void test2(int *arr[20]){}		//ok? ok
void test2(int **arr){}			//ok? ok

int main()
{
	int arr[10] = {0};
	int* arr2[20] = {0};
	test1(arr);
	test2(arr2);
	return 0;
}

二维数组

void test(int *arr){} //ok? err! 
void test(int *arr[5]){} //ok? err!
void test(int (*arr)[5]){} //ok? ok!
void test(int **arr){} //ok? err!

int main()
{
	int arr[3][5] = {0};
	test(arr);
	return 0;
}

二级指针传参

void test(int** pp)
{}

int main()
{
	int a = 10;
	int* p = &a;
	int** pp = &p;
	test(pp);
	return 0;
}

五、函数指针

指向函数的指针
&函数名和函数名都是函数的地址

int Add(int x, int y)
{
	return x + y;
}

void Print(char* str)
{
    printf("%s ", str);
}

int main()
{
	int a = 10;
	int b = 20;
	
	int (*p)(int, int) = Add;
	int ret = (*p)(a, b);
    printf("%d\n", ret);

    void (*p2)(char*) = Print;
    (*p2)("hello world");
	return 0;
}

六、函数指针数组

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 (*parr[4])(int, int) = {Add, Sub, Mul, Div};

	for (int i = 0; i < 4; i++)
	{
		int result = parr[i](10, 20);
		printf("%d\n", result);
	}
	
	return 0;
}

练习:
char* my_strcpy(char* dest, const char* src)
写一个函数指针和函数指针数组,数组长度4

#include <stdio.h>

char* my_strcpy(char* dest, const char* src)
{

}

int main()
{
    char* (*pf)(char*, const char*) = my_strcpy;
    char* (*pf[4])(char*, const char*);
}

函数指针数组案例,实现一个计算器:
先写一个不用函数指针数组的情况下:

#include <stdio.h>
void menu()
{
	printf("***************************\n");
	printf("*****1. add 	2. sub ****\n");
	printf("*****3. mul		4. div ****\n");
	printf("***** 		0. ext 	   ****\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;
	int x = 0;
	int y = 0;
	do
	{
		menu();
		printf("请选择:>");
		
		scanf("%d", &input);

		
		
		switch(input)
		{
			case 1:
                printf("请输入两个操作数:>");
                scanf("%d%d", &x,&y);
				printf("%d\n", Add(x, y));
                break;
			case 2:
                printf("请输入两个操作数:>");
                scanf("%d%d", &x,&y);
				printf("%d\n", Sub(x, y));
				break;
			case 3:
                printf("请输入两个操作数:>");
                scanf("%d%d", &x,&y);
				printf("%d\n", Mul(x, y));
				break;
			case 4:
                printf("请输入两个操作数:>");
                scanf("%d%d", &x,&y);
				printf("%d\n", Div(x, y));
				break;
			case 0:
				printf("退出\n");
                break;
			default:
				printf("输入有误,请重新输入");
				break;
		}
	}
	while(input);
	return 0;
}

用函数指针数组后:

#include <stdio.h>
//menu, add, sub, mul, div函数是跟上面都一样的,所以就省略掉了,主要改变在main函数的
void menu()
{
	printf("***************************\n");
	printf("*****1. add 	2. sub ****\n");
	printf("*****3. mul		4. div ****\n");
	printf("*****5. xor			   ****\n");
	printf("***** 		0. ext 	   ****\n");
	printf("***************************\n");
}

//此时如果我们想再加个位运算方法
//就不需要改变很多代码,比较易维护
int Xor(int x, int y)
{
	return x ^ y;
}

int main()
{
	int input = 0;
	int x = 0;
	int y = 0;

    int (*pf[])(int, int) = {0, Add, Sub, Mul, Div, Xor};

	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);

        if (input >= 1 && input <= 5)
        {
            printf("请输入两个操作数:>");
            scanf("%d%d", &x, &y);
            int result = pf[input](x, y);
            printf("%d\n", result);
        }
        else if (input == 0)
        {
            printf("退出\n");
        }
        else
        {
            printf("输入错误,请重新输入");
        }
		
	}
	while(input);
	return 0;
}

七、指针函数指针数组的指针

int main()
{
	int arr[10] = {0};
	int (*p)[10] = &arr; //指针数组
	
	int (*pfArr[4])(int, int); //pfArr是数组,函数指针数组
	int (*(*ppfArr)[4])(int, int) = &pfArr;//指向函数指针数组的指针
	//ppfArr是一个数组指针,指针指向的数组有4个元素 
	//指向的数组的每个元素的类型是一个函数指针
	//那个函数指针的类型是 int(*)(int, int)
	return 0;
}

八、回调函数

回调函数就是一个通过函数指针调用的函数。
案例:
比如上面的函数指针数组中的 Switch case 实现的案例有很多冗余代码,在这里用回调函数的方式优化一下。

#include <stdio.h>
void menu()
{
	printf("***************************\n");
	printf("*****1. add 	2. sub ****\n");
	printf("*****3. mul		4. div ****\n");
	printf("***** 		0. ext 	   ****\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;
}

void Calc(int (*fp)(int, int))
{
    int x = 0;
	int y = 0;
    printf("请输入两个操作数:>");
    scanf("%d%d", &x,&y);
    printf("%d \n", fp(x, y));
}

int main()
{
	int input = 0;
	
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch(input)
		{
			case 1:
                Calc(Add);
                break;
			case 2:
                Calc(Sub);
				break;
			case 3:
                Calc(Mul);
				break;
			case 4:
                Calc(Div);
				break;
			case 0:
				printf("退出\n");
                break;
			default:
				printf("输入有误,请重新输入");
				break;
		}
	}
	while(input);
	return 0;
}

案例2:

//qsort函数的使用
void qsort(void *base,
		   size_t num,
		   size_t width,
		   int(*cmp)(const void *e1, const void *e2)
		   );

还是先写一个简单的冒泡排序吧,,这样能知道接下来到底要干什么

void bubble_sort(int arr[], int sz)
{

	for(int i = 0; i < sz - 1; i++)
	{
		for (int j = 0; j < sz - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
	}
}

int main()
{
	int arr[] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
	int sz = sizeof(arr)/sizeof(arr[0]);
	bubble_sort(arr, sz);

	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

但是发现我们写冒泡排序只能排序整形数组,如果要排序的数组是个float类型的数组或者结构体数组或者其他任何类型的数组就无法实现排序了。
C库函数qsort刚好解决了此问题,我们来看一下他是如何使用的。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*test1,2,3,4函数分别用不同数据类型冒泡排序*/
int cmp_int(const void* e1, const void* e2)
{
	//比较两个整形值
	return *(int*)e1 - *(int*)e2;
}

int cmp_float(const void* e1, const void* e2)
{
	//比较两个整形值
	return ((int)(*(float*)e1 - *(float*)e2));
}

void test1()
{
    int arr[] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
	int sz = sizeof(arr)/sizeof(arr[0]);

	qsort(arr, sz, sizeof(arr[0]), cmp_int);

	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
    printf("\n");
}

void test2()
{
    float arr[] = {9.0, 8.0, 7.0, 6.0, 5.0};
	int sz = sizeof(arr)/sizeof(arr[0]);

	qsort(arr, sz, sizeof(arr[0]), cmp_float);

	for (int i = 0; i < sz; i++)
	{
		printf("%f ", arr[i]);
	}
    printf("\n");
}

struct Stu
{
    char name[20];
    int age;
};

int cmp_stu_by_age(const void*e1, const void*e2)
{
    return ((struct Stu*)e1) -> age - ((struct Stu*)e2) -> age;
}

int cmp_stu_by_name(const void*e1, const void*e2)
{
    //使用 string库函数的 strcmp 函数比较字符串
    return strcmp(((struct Stu*)e1) -> name, ((struct Stu*)e2) -> name);
}

void test3()
{
    struct Stu stu[3] = { {"张三", 33 }, {"李四", 22 }, { "王五", 11 } };
    int sz = sizeof(stu)/sizeof(stu[0]);
    qsort(stu, sz, sizeof(stu[0]), cmp_stu_by_age);
    for (int i = 0; i < sz; i++)
	{
		printf("%s ", stu[i].name);
	}
}

void test4()
{
    struct Stu stu[3] = { {"张三", 33 }, {"李四", 22 }, { "王五", 11 } };
    int sz = sizeof(stu)/sizeof(stu[0]);
    qsort(stu, sz, sizeof(stu[0]), cmp_stu_by_name);
    for (int i = 0; i < sz; i++)
	{
		printf("%d ", stu[i].age);
	}
}

int main()
{
	test1();
    test2();
    test3();
    test4();
	return 0;
}

手写一个qsort函数

#include <stdio.h>

struct Stu
{
	char name[20];
	int age;
};

void Swap(char* buf1, char* buf2, int width)
{
	for (int i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

void my_qsort(void* base, int sz, int width, int (*cmp)(void* e1, void* e2))
{
	for (int i = 0; i < sz - 1; i++)
	{
		for (int j = 0; j < sz - 1 - i; j++)
		{
			if (cmp((char*)base + j*width, (char*)base + (j + 1)*width) > 0)
			{
				//交换两个元素
				Swap((char*)base + j*width, (char*)base + (j + 1)*width, width);
			}
		}
	}
}

int cmp_stu_by_age(const char* el1, const char* el2)
{
	return ((struct Stu*)el1) -> age - ((struct Stu*)el2) -> age;
}

int main()
{
	struct Stu stu[] = { { "张三", 20 }, { "李四", 18 } };
	
	int sz = sizeof(stu) / sizeof(stu[0]);

	my_qsort(stu, sz, sizeof(stu[0]), cmp_stu_by_age);

	for (int i = 0; i < sz; i++)
	{
		printf("%s\n", stu[i].name);
	}
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值