C语言指针进阶(个人笔记)

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;
}
void test(int arr[3][5]){}//ok?     YES
void test(int arr[][]){}//ok?       NO
void test(int arr[][5]){}//ok?      YES
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
void test(int *arr){}//ok?          NO
void test(int* arr[5]){}//ok?       NO
void test(int (*arr)[5]){}//ok?     YES
void test(int **arr){}//ok?         NO
int main()
{
 int arr[3][5] = {0};
 test(arr);
}
int Add(int x, int y)
{
	return x + y;
}

//&函数名得到就是函数的地址

int main()
{
	//printf("%p\n", &Add);
	//printf("%p\n", Add);
	//pf就是函数指针
	int (* pf)(int, int) = Add;//函数的地址要存起来,就得放在【函数指针变量】中
	
	int ret = (*pf)(3, 5);
	//int ret = Add(3, 5);
	//int ret = pf(3, 5);
	printf("%d\n", ret);
	return 0;
}
int main()
{
    //代码1
	//1. 将0强制类型转换为void (*)() 类型的函数指针
	//2. 这就意味着0地址处放着一个函数,函数没参数,返回类型是void
	//3. 调用0地址处的这个函数
	//下面代码是一次函数调用
	//(*( void (*)() ) 0)();

	//代码2
	void (* signal(int, void(*)(int) ) )(int);
	//
	pf_t signal(int, pf_t);
	//上述的代码是一个函数的声明
	//函数的名字是signal
	//signal函数的参数第一个是int类型,第二个是void(*)(int)类型的函数指针
	//该函数指针指向的函数参数是int,返回类型是void
	//
	//signal函数的返回类型也是一个函数指针	//该函数指针指向的函数参数是int,返回类型是void
	//
	//void (* signal(int, void(*)(int)))(int)
	return 0;
}

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

模拟实现类似qsort这个功能的冒泡排序bubble_sort()

#include <stdlib.h>
#include <string.h>

//qsort函数的使用者提供这个函数
int cmp_int(const void* p1, const void* p2)
{
	return *(int*)p1 - *(int*)p2;
}

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

test1()
{
	int arr[] = { 3,1,5,2,4,9,8,6,5,7 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	//使用qsort来排序整型数组,这里就要提供一个比较函数,这个比较函数能够比较2个整数的大小
	//qsort 默认是排成升序的
	qsort(arr, sz, sizeof(arr[0]), cmp_int);
	print_arr(arr, sz);
}

//测试qsort 排序结构体数据
struct Stu
{
	char name[20];
	int age;
};

//按照年龄来比较
int cmp_stu_by_age(const void* p1, const void* p2)
{
	return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}

int cmp_stu_by_name(const void* p1, const void* p2)
{
	return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}

void test2()
{
	struct Stu s[] = { {"zhangsan", 30}, {"lisi", 25}, {"wangwu", 50}};
	int sz = sizeof(s) / sizeof(s[0]);
	//测试按照年龄来排序
	//qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
	//测试按照名字来排序
	qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}

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

//假设排序为升序
//希望这个bubble_sort函数可以排序任意类型的数据
void bubble_sort(void* base, size_t num, size_t width, 
	int (*cmp)(const void* p1, const void* p2))
{
	//要确定趟数
	size_t i = 0;
	for (i = 0; i < num - 1; i++)
	{
		int flag = 1;//假设已经有序了
		//一趟冒泡排序的过程
		size_t j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
			//两个相邻的元素比较
			//arr[j] arr[j+1]
			if (cmp((char*)base+j*width, (char*)base+(j + 1)*width)>0)
			{
				//交换
				flag = 0;
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
		if (flag == 1)
		{
			break;
		}
	}
}

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

void test4()
{
	struct Stu s[] = { {"zhangsan", 30}, {"lisi", 25}, {"wangwu", 50} };
	int sz = sizeof(s) / sizeof(s[0]);
	//测试按照年龄来排序
	//bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_age);
	//测试按照名字来排序
	bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}

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

彻底搞懂strlen和sizeof的一些试题(附加答案)

数组和指针
数组 - 能够存放一组相同类型的元素,数组的大小取决于数组的元素个数和元素类型
指针 - 地址/指针变量 ,大小是4/8个字节
数组是数组,指针是指针,二者不等价
数组名是数组首元素的地址,这个地址就可以存放在指针变量中
我们就可以使用指针来遍历数组

数组名
大部分情况下数组名是数组首元素的地址
但是有2个例外:
sizeof(数组名) - 数组名表示整个数组,计算的是整个数组的大小
&数组名 - 数组名表示整个数组,取出的是数组的地址

//一维数组
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));//16,sizeof(a)就是数组名单独放在sizeof内部,计算的数组总大小,单位是字节
printf("%d\n",sizeof(a+0));//4/8,a+0 其实是数组首元素的地址
printf("%d\n",sizeof(*a));//4,a是数组首元素的地址 - &a[0],*a -> *&a[0] -> a[0]
printf("%d\n",sizeof(a+1));//4/8,a是数组首元素的地址 -- int*,a+1 跳过1个整型, 是第二个元素的地址
printf("%d\n",sizeof(a[1]));//4
printf("%d\n",sizeof(&a));//4/8,&a - 取出的是数组的地址,但是数组的地址也是地址呀,是地址大小就是4/8字节
printf("%d\n",sizeof(*&a));//16
printf("%d\n",sizeof(&a+1));//4/8,&a -->  int (*)[4],&a+1 跳过一个数组
printf("%d\n",sizeof(&a[0]));//4/8,取出首元素的地址
printf("%d\n",sizeof(&a[0]+1));//4/8,第二个元素的地址

//字符数组
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));//6
printf("%d\n", sizeof(arr+0));//4/8,arr+0是数组首元素的地址
printf("%d\n", sizeof(*arr));//1,*arr是首元素的,计算的是首元素的大小
printf("%d\n", sizeof(arr[1]));//1
printf("%d\n", sizeof(&arr));//4/8,&arr是数组的地址
printf("%d\n", sizeof(&arr+1));//4/8,&arr + 1跳过一个数组后的地址
printf("%d\n", sizeof(&arr[0]+1));//4/8,第二个元素的地址
printf("%d\n", strlen(arr));//随机值,因为不知道\0的位置
printf("%d\n", strlen(arr+0));//随机值
printf("%d\n", strlen(*arr));//非法访问
printf("%d\n", strlen(arr[1]));//非法访问
printf("%d\n", strlen(&arr));//随机值
printf("%d\n", strlen(&arr+1));//随机值-6
printf("%d\n", strlen(&arr[0]+1));随机值-1

char arr[] = "abcdef";
printf("%d\n", sizeof(arr));//7
printf("%d\n", sizeof(arr+0));//4/8
printf("%d\n", sizeof(*arr));//1,*arr -是数组首元素
printf("%d\n", sizeof(arr[1]));//1
printf("%d\n", sizeof(&arr));//4/8,数组的地址
printf("%d\n", sizeof(&arr+1));//4/8
printf("%d\n", sizeof(&arr[0]+1));//4/8
printf("%d\n", strlen(arr));//6
printf("%d\n", strlen(arr+0));//6
printf("%d\n", strlen(*arr));//非法访问
printf("%d\n", strlen(arr[1]));//非法访问
printf("%d\n", strlen(&arr));//6
printf("%d\n", strlen(&arr+1));//随机值
printf("%d\n", strlen(&arr[0]+1));//5

char *p = "abcdef";
printf("%d\n", sizeof(p));//4/8
printf("%d\n", sizeof(p+1));4/8
printf("%d\n", sizeof(*p));1
printf("%d\n", sizeof(p[0]));//1
printf("%d\n", sizeof(&p));//4/8
printf("%d\n", sizeof(&p+1));//4/8
printf("%d\n", sizeof(&p[0]+1));//6
printf("%d\n", strlen(p));//6
printf("%d\n", strlen(p+1));//5
printf("%d\n", strlen(*p));//非法访问
printf("%d\n", strlen(p[0]));//非法访问
printf("%d\n", strlen(&p));//随机值
printf("%d\n", strlen(&p+1));//随机值
printf("%d\n", strlen(&p[0]+1));//5
//二维数组
int a[3][4] = {0};
printf("%d\n",sizeof(a));//48,a这个二维数组的数组名单独放在sizeof内部,计算整个数组的大小
printf("%d\n",sizeof(a[0][0]));//4,第一行第一个元素
printf("%d\n",sizeof(a[0]));//16,a[0] 第一行的数组名,这时数组名单独放在sizeof内部了
printf("%d\n",sizeof(a[0]+1));//4,a[0]不是单独放在sizeof内部,a[0]表示的首元素的地址,即第一行第一个元素的地址 - &a[0][0],a[0] + 1 是第一行第2个元素的地址 &a[0][1]
printf("%d\n",sizeof(*(a[0]+1)));//4,a[0][1]
printf("%d\n",sizeof(a+1));//a作为二维数组的数组名并非单独放在sizeof内部,所以表示首元素的地址,二维数组的首元素是第一行,这里的a就是第一行的地址---  int (*)[4],a+1是跳过第一行,指向了第二行
printf("%d\n",sizeof(*(a+1)));//16,*(a+1)-->a[1]
printf("%d\n",sizeof(&a[0]+1));//4/8,&a[0]是第一行的地址,&a[0]+1是第二行的地址
printf("%d\n",sizeof(*(&a[0]+1)));//16,a[1]
printf("%d\n",sizeof(*a));//16,*a - 就是第一行,*a -- *(a+0) -- a[0]
printf("%d\n",sizeof(a[3]));//16
//sizeof()里的值并不会进行计算

笔试题
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

索隆43

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值