C语言指针

1.指针是一个变量,用来存放地址,一个地址标记唯一一块内存空间;
2.指针的大小是固定的,4/8个字节(32/64位机);
3.指针是有类型的,指针的类型决定了指针±整数的步长(跨几个字节),以及指针解引用时可访问的内存空间(几个字节);

字符指针

字符指针的两种用法:
1.指向某个字符变量;
2.指向某个字符串首字符的地址。
面试题:

int main()
{
	char str1[] = "hello bit.";
	char str2[] = "hello bit.";
	char *str3 = "hello bit.";
	char *str4 = "hello bit.";
	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 str4 are same\n");
	else
		printf("str3 and str4 are not same\n");
	return 0;
} 

运行结果

str3与str4指向同一个常量字符串,C/C++会把常量字符串存到单独一块内存空间,当几个指针指向同一个字符串时,他们实际会指向同一个内存空间,因此str3 == str4,但使用相同字符串去初始化不同的数组时,会开辟不同的内存空间。

指针数组

指针数组是存放一组指针的数组;

int* arr1[2];//一级指针数组
int** arr2[2];//二级指针数组

数组指针

数组指针是能够指向数组的指针

int (*p) [2];

p先和 * 结合,说明p是一个指针([]的优先级要比*高,所以要加括号),然后指针p指向一个具有两个元素的数组,数组元素类型为整形

int arr[10];

&arrarr区别:
arr是数组名,数组名表示数组首元素的地址;

int main()
{
	int arr[10] = { 0 };
	printf("%p\n", arr);
	printf("%p\n", &arr);
	return 0;
}

在这里插入图片描述

说明数组名和&数组名指向的地址是一样的。

int main()
{
	int arr[10] = { 0 };
	printf("arr = %p\n", arr);
	printf("arr + 1 = %p\n", arr+1);

	printf("&arr = %p\n", &arr);
	printf("&arr + 1 = %p\n", &arr+1);
	return 0;
}

在这里插入图片描述

arr+1加了一个数组中元素所占的字节数
&arr+1加了一个数组所占的字节数(以上结果跳过了40)

实际上,&arr表示的是数组的地址,而不是数组首元素的地址。

数组指针的使用
数组指针中存放数组的地址

void print2(int (*p) [5], int x, int y)
{
	int i = 0;
	int j = 0;
	for (i = 0; i<x; i++)
	{
		for (j = 0; j<y; 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 }; 
//	print1(arr, 3, 5);
	print2(arr, 3, 5);
	return 0;
}

数组名arr是数组首元素的地址,arr是二维数组,所以arr表示二维数组第一行的地址,是一维数组的地址,可用数组指针来接收。
p+i 是二维数组第i行的地址,*(p+i)*表示第 i 行(一维数组), ( *(p+i)+j)表示第 i 行第 j 个元素的地址,再解引用得到该元素。

在这里插入图片描述

数组参数 指针参数

一维数组传参

void test(int arr[])//ok?
{}
void test(int arr[10])//ok?
{}
void test(int *arr)//ok?
{}
void test2(int *arr[20])//ok?
{}
void test2(int **arr)//ok?
{}
int main()
{
	int arr[10] = {0};
	int *arr2[20] = {0};
	//test(arr);
	test2(arr2);
}

以上(int* arr)表示指针arr指向一个元素为int类型的数组
(int* *arr)表示指针arr指向一个整型指针

二维数组传参

void test(int *arr)//ok?
{}
void test(int* arr[5])//ok?
{}
void test(int(*arr)[5])//ok?
{}
void test(int **arr)//ok?
{}
int main()
{
	int arr[3][5] = { 0 };
	test(arr);
}

(int* arr)无法确定arr是几行几列的二维数组
(int* arr[5])中arr是指针数组
(int(*arr)[5])中arr为数组指针,指向具有五个元素的一维数组
(int **arr)arr是二级指针

一级指针传参

#include <stdio.h>
void print_arr(int* p, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}
	printf("\n");
}
int main()
{
	int arr[] = { 1, 2, 3, 4, 5 };
	int* p = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	print_arr(p, sz);
	return 0;
}

当函数参数部分为一级指针时,可以传递哪些参数?

void test(int* p)

p可以指向一个整型变量,也可以指向一个一维整形数组的首元素;

void test(char* p)

p可以指向一个字符型变量,也可以指向一个字符数组的首元素;

二级指针传参

void print(int** ptr)
{
	printf("%d\n", **ptr);
}
int main()
{
	int x = 10;
	int* p = &x;
	int** pp = &p;
	print(pp);
	print(&p);
	return 0;
}

当函数参数为二级指针时,可以接收哪些参数?

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;
}

二级指针可以指向一级指针,也可以指向一维指针数组;

函数指针

看以下代码:

void test()
{
	printf("test\n");
}
int main()
{
	printf("%p\n", test);
	printf("%p\n", &test);
	return 0;
}

运行结果
输出的地址为test函数的地址;
函数指针可以保存函数地址,函数指针怎么写呢?

void (*pfun) ();

pfun先和 * 结合,则pfun是指针,指向一个函数,该函数没有参数,返回类型为void

看一下这两个
代码1:

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

void(*)()是一个函数指针类型,将0强制类型转换为这个函数指针类型,再对地址0处的这个函数指针解引用得到这个函数,后面加上括号表示调用这个函数。

代码2:

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

signal一个函数名,signal函数的第一个参数为int型,第二个参数为一个函数指针,现在就缺少函数的返回类型,void ( * signal(int, void(*)(int)) )(int),剩余部分为函数的返回类型,返回类型为函数指针类型,所以这是一个函数声明。

注意:C语言语法不支持以下写法:

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

函数名必须和 * 结合。
代码2难以理解。可以简化为:

typedef void(*pfun)(int);
//typedef void(*)(int) pfun;(语法不支持)
pfun signal(int, pfun);

利用typedef重命名 void (*) (int)函数指针类型为pfun;
则signal函数的函数返回类型和第二个参数都为pfun类型。

函数指针数组

把函数的地址存到一个数组中,那么这个数组就叫函数指针数组。
函数指针数组定义:

int (*parr[10])();

parr先和[]结合,说明parr是一个数组,int (* parr[10] )(),数组元素类型为int (*)();

函数指针数组的用途:转移表
实现计算器

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 x = 0;
	int y = 0;
	int input = 1;
	int ret = 0;
	int(*p[5])(int, int) = { 0, add, sub, mul, div }; //转移表
	while (input)
	{
		printf("*************************\n");
		printf(" 1:add 2:sub \n");
		printf(" 3:mul 4:div \n");
		printf("*************************\n");
		printf("请选择:");
		scanf("%d", &input);
		if (input <= 4 && input >= 1)
		{
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = (*p[input])(x, y);
		}
		else
		{
			printf("输入有误\n");
		}
		printf("ret = %d\n", ret);
	}
	return 0;
}

指向函数指针数组的指针

一个指针指向一个数组,该数组的元素类型为指针数组;
如何定义:

void test(const char* str)
{
	printf("%s\n", str);
}
int main()
{
	void (*pfun)(const char*) = test;//函数指针pfun
	void(*pfunarr[5])(const char*); //函数指针数组pfunarr
	pfunarr[0] = test;
	void(*(*p_pfunarr)[5])(const char*);//指向函数指针数组pfunarr的指针p_pfunarr
	return 0;
}

p_pfunarr先和 * 结合说明p_pfunarr是一个指针,指向一个具有5个元素的函数指针数组

回调函数

回调函数就是通过函数指针调用的函数。如果把一个函数指针作为参数传递给另一个函数,通过该函数指针调用其指向的函数,就叫做回调函数。回调函数不是由函数实现方直接调用,而是在特定的条件或事件发生时由另一方调用的,用于对事件或条件作出回应。

库函数qsort的声明:

void qsort(void *base, size_t num, size_t width, int(__cdecl *compare)(const void *elem1, const void *elem2))

qsort的使用演示:

int arr_cmp_by_int(const void* p1, const void* p2)
{
	return *(int*)p1 - *(int*)p2;
}
int main()
{
	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]), arr_cmp_by_int); 
	print_arr(arr, sz);
	return 0;
}

arr_cmp_by_int作为函数指针,在qsort函数内部被回调

模拟实现bubble_sort

void Swap(char* buf1, char* buf2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char ret = *buf1;
		*buf1 = *buf2;
		*buf2 = ret;
		buf1++;
		buf2++;
		//因为类型未知,所以逐个字节交换
	}
}

void bubble_sort(void *base, int num, int width, int(*cmp) (const void *e1, const void *e2))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < num-1; i++)
	{
		for (j = 0; j < num - i - 1; j++)
		{
			//类型未知,已知width,先强制类型转换为char* ,+width跳到下一值
			if (cmp((char*)base + width*j, (char*)base + width*(j + 1))>0)
			{
				Swap((char*)base + width*j, (char*)base + width*(j + 1), width);
			}
		}
	}
}

末尾有指针相关练习:
https://github.com/QIYU1024/C-C-_Work/blob/master/test_11_8/test_11_8/test.c
qsort的使用,以及模拟实现bubble_sort:
https://github.com/QIYU1024/C-C-_Work/blob/master/test_11_5/test_11_5/test.c

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值