C语言 - 指针进阶

1. 数组和指针

1.1 区分数组和指针

数组是一组相同类型元素的集合,而指针实际上是一个存储地址的变量

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	// 数组arr
	int arr[] = { 1,2,3,4,5 };
	int a = 0;
	// 指针变量 pa
	int* pa = &a;

}

如例,数组arr是一组整数的集合,指针pa是一个存储整形变量a地址的变量

 数组和指针是两个不同的概念,它们之间有什么联系?

1.2 数组和指针的关联 

数组和指针之间唯一的联系是,数组名 = 数组首元素的地址,这个地址可以用指针进行存储

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	// 数组arr
	int arr[] = { 1,2,3,4,5 };
	int* pa = arr;
	printf("%p %p %p\n",&arr[0], arr, pa);
	
}

如图,&arr[0] = arr ,数组首元素地址 = 数组名

在这个例子中,数组首元素的类型是int,所以可以用整形指针pa存储数组名所表示的数组首元素地址

2.1 指针数组

2.1 指针数组的概念和写法

指针数组是存储一组指针的数组,写法如下

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	int a, b, c;
    // 指针数组arr
	int* arr[] = { &a,&b,&c };
	return 0;
	
}

arr[]表示数组,int*表示数组每一个元素的类型是整形指针

2.2 指针数组的使用

使用指针数组模拟一个二维数组

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };

	int* arr[] = { &arr1,&arr2,&arr3 };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ",arr[i][j]);
		}
		printf("\n");
	}
}

3. 数组指针

3.1 数组名 VS &数组名

数组名等于数组首元素的地址,&数组名等于什么?

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	int arr[10];
	printf("%p %p\n", arr, arr + 1);
	printf("%p %p\n", &arr, &arr + 1);

}

如图,arr +1,跳过4个字节,而 &arr + 1,跳过了40个字节

因为指针步长由指针类型决定,所以 &arr 取出的是整个数组的地址

3.2 数组指针的概念和写法

&arr 取出的数组地址可以由变量存储,这个变量叫做数组指针

数组指针,写法如下

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	int arr[10];
    // 数组指针
	int(*pa)[10] = &arr;

}

(*pa) 表示pa是一个指针

[10] 表示pa存储的是一个数组的地址,且数组存放10个数据

int 表示数组每一个数据的类型是整型

3.3 二维数组数组名等于什么

已知,一维数组数组名 = 数组首元素的地址,&一维数组数组名 = 数组的地址,二维数组数组名等于什么?

二维数组数组名 = 二维数组 第一行 一维数组的地址

如图,二维数组arr + 1,直接从第一行跳到了第二行

3.4 数组指针的使用

数组指针通常用于二维数组传参,如下例

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void print(int(*pa)[5], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			printf("%d ", pa[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);
}

二维数组数组名 = 二维数组 第一行 一维数组的地址,所以形参用 数组指针 int(*pa)[5] 来接收

4. 数组传参,指针传参

总结数组,指针传参,函数参数如何设计

4.1 一维数组传参

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void test(int arr[])//ok? 对 - 数组传参 形参可以写成数组形式 且因为arr本质上传递的是首元素地址,所以可以不指定数组大小
{}
void test(int arr[10])//ok? 对 
{}
void test(int* arr)//ok? 对 - arr传递数组arr首元素地址 所以形参可以写成整形指针
{}
void test2(int* arr[20])//ok? 对 - 数组传参 形参可以写成数组形式
{}
void test2(int** arr)//ok? 对 - 数组arr2每个元素类型是int*整形指针,所以数组名arr2 = &整形指针int* = 二级指针,形参用int**二级指针接收 
{}
int main()
{
    int arr[10] = { 0 };
    int* arr2[20] = { 0 };
    test(arr); 
    test2(arr2);
}

4.2 二维数组传参

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void test(int arr[3][5])//ok? 对 
{}
void test(int arr[][])//ok?错 - 二维数组的行可以省略,列不能省略
{}
void test(int arr[][5])//ok? 对
{}
void test(int* arr)//ok?错 - 二维数组数组名 = 二维数组 第一行 一维数组的地址 是一个数组指针 而这里形参arr是整形指针int*
{}
void test(int* arr[5])//ok?错 - 这里的形参arr是个指针数组
{}
void test(int(*arr)[5])//ok?对 - arr是数组指针 指针指向存放5个元素的数组 数组每个元素类型是整形int 
{}
void test(int** arr)//ok?错 - 形参arr 是个二级指针
{}
int main()
{
    int arr[3][5] = { 0 };
    test(arr);
}

4.3 一级指针传参

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

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
// 一级指针传参,用一级指针p接收
void print(int* p)
{
}
int main()
{
    int a = 0;
    int arr[10] = { 1,2,3,4,5,6,7,8,9 };
    int* p = arr;
    // 可以传整形变量地址
    print(&a);
    // 传一级指针
    print(p);
    // 传数组名
    print(arr);
    return 0;
}

4.4 二级指针传参

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

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void test(char** p)
{

}
int main()
{
    char c = 'b';
    char* pc = &c;
    char** ppc = &pc;
    char* arr[10];
    // 可以传 &一级字符指针pc
    test(&pc);
    // 可以传 二级字符指针
    test(ppc);
    // 可以传 每个元素类型为char*字符指针的arr数组名 
    test(arr);
    return 0;
}

5. 函数指针

5.1 函数指针的概念和写法

函数指针与数组指针类型,是一个存储函数地址的变量

写法如下

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int add(int x, int y)
{
	return x + y;
}
int main()
{
    // 函数指针pa,pb
	int (*pa)(int, int) = add;
	int (*pb)(int, int) = &add;
}

(*pa) 表示pa是一个指针
(int, int)表示pa存储的是一个函数的地址,函数有两个类型为整形的形参

(*pa)前面的int,表示函数的返回类型为int

需要注意的是,在函数指针中,&函数名 = 函数名 = 函数的地址

5.2 函数指针的使用

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int add(int x, int y)
{
	return x + y;
}
int main()
{
	int (*pa)(int, int) = add;
	int ret = (*pa)(1, 2);
	printf("%d\n", ret);
}

6. 函数指针数组

6.1 函数指针数组的概念和写法

函数指针数组是存放函数地址的数组

写法如下

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int add(int x, int y)
{
	return x + y;
}
int sub(int x, int y)
{
	return x - y;
}
int main()
{
	int(*ptr[2])(int,int) = { add,sub };
}

int(*ptr[2])(int,int) 如何理解?

ptr首先会与[2]结合,表示ptr是一个数组,数组有2个元素

int (* ptr[2] )(int,int) ---> int (*)(int,int)

(*) (int,int)表示ptr数组的每一个元素是函数地址,函数有两个整形类型形参

6.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;
}
void menu()
{
	printf("1.add	2.sub\n");
	printf("3.mul	4.div\n");
}
 
int main()
{
	int(*ptr[5])(int, int) = { 0,add,sub,mul,div }; //函数指针数组
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	do 
	{
		menu();
		printf("输入: ");
		scanf("%d", &input);
		if (input > 4 || input <= 0)
		{
			printf("退出程序\n");
		}
		else
		{
			printf("输入x: ");
			scanf("%d", &x);
			printf("输入y: ");
			scanf("%d", &y);
			ret = (ptr[input])(x, y); //通过input,锁定函数地址,最后传参得到结果
			printf("%d\n", ret);
		}
	} while (input);
}

7. 回调函数

7.1 什么是回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的地址作为参数传递给另一个
函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数

下面举例说明

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

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

void calc(int(*pc)(int, int))
{
	int x = 0;
	int y = 0;
	int ret = 0;
	printf("请输入2个操作数:");
	scanf("%d %d", &x, &y);
	ret = pc(x, y);
	printf("%d\n", ret);
}
int main()
{
	calc(Add);
}

将Add函数地址作为参数传递给函数calc,当函数指针pc(Add函数地址)在calc函数内部使用调用Add函数时,这时候的Add函数就可以称为回调函数

7.2 回调函数的应用

#include <stdio.h>
 
int cmp_int(const void* e1, const void* e2)
{
	return * (int*)e1 - * (int*)e2;
}
struct Stu
{
	char name[20];
	int age;
};
// 以名字排序
int cmp_struct_name(const void* e1, const void* e2)
{
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
// 以年龄排序
int cmp_struct_age(const void* e1, const void* e2)
{
	return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
 
void swap(char* s1, char* s2, size_t width)
{
	size_t i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *s1;
		*s1 = *s2;
		*s2 = tmp;
		s1++;
		s2++;
	}
}
 
void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))
{
	size_t i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		size_t j = 0;
		for (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);
			}
		}
	}
}
 
// 排序整形
void sort_int()
{
	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr,sz,sizeof(arr[0]), cmp_int);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
 
// 排序结构体
void sort_struct()
{
	struct Stu s[5] = { {"b",25},{"e",20},{"a",18},{"c",30},{"d",12} };
	int sz = sizeof(s) / sizeof(s[0]);
	//bubble_sort(s, sz, sizeof(s[0]), cmp_struct_name);
	bubble_sort(s, sz, sizeof(s[0]), cmp_struct_age);
 
}
 
int main()
{
 
	//sort_int();
	sort_struct();
 
}

以下是对提供的参考资料的总结,按照要求结构化多个要点分条输出: 4G/5G无线网络优化与网规案例分析: NSA站点下终端掉4G问题:部分用户反馈NSA终端频繁掉4G,主要因终端主动发起SCGfail导致。分析显示,在信号较好的环境下,终端可能因节能、过热保护等原因主动释放连接。解决方案建议终端侧进行分析处理,尝试关闭节电开关等。 RSSI算法识别天馈遮挡:通过计算RSSI平均值及差值识别天馈遮挡,差值大于3dB则认定有遮挡。不同设备分组规则不同,如64T和32T。此方法可有效帮助现场人员识别因环境变化引起的网络问题。 5G 160M组网小区CA不生效:某5G站点开启100M+60M CA功能后,测试发现UE无法正常使用CA功能。问题原因在于CA频点集标识配置错误,修正后测试正常。 5G网络优化与策略: CCE映射方式优化:针对诺基亚站点覆盖农村区域,通过优化CCE资源映射方式(交织、非交织),提升RRC连接建立成功率和无线接通率。非交织方式相比交织方式有显著提升。 5G AAU两扇区组网:与三扇区组网相比,AAU两扇区组网在RSRP、SINR、下载速率和上传速率上表现不同,需根据具体场景选择适合的组网方式。 5G语音解决方案:包括沿用4G语音解决方案、EPS Fallback方案和VoNR方案。不同方案适用于不同的5G组网策略,如NSA和SA,并影响语音连续性和网络覆盖。 4G网络优化与资源利用: 4G室分设备利旧:面对4G网络投资压减与资源需求矛盾,提出利旧多维度调优策略,包括资源整合、统筹调配既有资源,以满足新增需求和提质增效。 宏站RRU设备1托N射灯:针对5G深度覆盖需求,研究使用宏站AAU结合1托N射灯方案,快速便捷地开通5G站点,提升深度覆盖能力。 基站与流程管理: 爱立信LTE基站邻区添加流程:未提供具体内容,但通常涉及邻区规划、参数配置、测试验证等步骤,以确保基站间顺畅切换和覆盖连续性。 网络规划与策略: 新高铁跨海大桥覆盖方案试点:虽未提供详细内容,但可推测涉及高铁跨海大桥区域的4G/5G网络覆盖规划,需考虑信号穿透、移动性管理、网络容量等因素。 总结: 提供的参考资料涵盖了4G/5G无线网络优化、网规案例分析、网络优化策略、资源利用、基站管理等多个方面。 通过具体案例分析,展示了无线网络优化中的常见问题及解决方案,如NSA终端掉4G、RSSI识别天馈遮挡、CA不生效等。 强调了5G网络优化与策略的重要性,包括CCE映射方式优化、5G语音解决方案、AAU扇区组网选择等。 提出了4G网络优化与资源利用的策略,如室分设备利旧、宏站RRU设备1托N射灯等。 基站与流程管理方面,提到了爱立信LTE基站邻区添加流程,但未给出具体细节。 新高铁跨海大桥覆盖方案试点展示了特殊场景下的网络规划需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值