C语言提高_03

本文深入讲解了一维数组和二维数组的初始化方式、数组名的概念、数组数据类型的定义、指针数组与数组指针的区别及应用、二维数组名的本质以及数组作为函数参数的使用技巧。

1. 数组  一维数组的初始化有以下几种

	int a[] = { 1,3 };
	int b[30] = {1,4};
	int c[120] = {0};
	memset(c,0,sizeof(c));

2.数组名的技术盲点

2.1 数组首元素地址和数组的地址是两个不同的概念;b是首元素地址,&b才是整个数组的地址。b+1  步长为4个字节;&b+1 步长为30*4个字节;

2.2数组名是首元素的地址,是一个常量,不能修改;数组一定义就分配好内存了,不能改变的。

2.3 数组首元素的地址和数组的地址值是相等的;

3.如何定义一个数组数据类型

#include<stdint.h>
#include<stdio.h>

void main()
{
	typedef int(Arr)[5];//定义了一个数组的数据类型为Arr;
	Arr a = {1,2,3,4,5};//相当于这么写:int a[5] = {1,2,3,4,5};

	for (int i = 0; i < 5; i++)
	{
		printf("%d \n", a[i]);
	}
	system("pause");
}
a为数组首元素的地址,a+1位数组的第二个元素的地址;&a为整个数组的地址,&a+1 的步长 为20个字节;


4.指针数组 和 数组指针

指针数组之前学习过了:指针组成的结合;例如:char* arr[3] = {"1111","2222","3333"};

数组指针:就是一个指针指向一个数组;有三种方法

第一种:定义数组类型,取数组类型为指针变量;

void main()
{
	typedef int(Arr)[5];//定义了一个数组的数据类型为Arr;
	Arr a = {1,2,3,4,5};//相当于这么写:int a[5] = {1,2,3,4,5};

	Arr* b= &a;  //这就是一个数组指针,也是一个二级指针,a本来就是一个一级指针,取地址a赋值给b就成了二级指针了,指向了一个数组a;
	for (int i = 0; i < 5; i++)
	{
		printf("%d \n", (*b)[i]);
	}
	system("pause");
}

第二种:直接定义一个数组指针类型

void main()
{
	typedef int(*Arr)[5];//定义了一个数组指针数据类型;
	int a[] = {1,2,3,4,5};//相当于这么写:int a[5] = {1,2,3,4,5};
	Arr b= &a;  //这就是一个数组指针,也是一个二级指针,a本来就是一个一级指针,取地址a赋值给b就成了二级指针了,指向了一个数组a;
	for (int i = 0; i < 5; i++)
	{
		printf("%d \n", (*b)[i]);
	}
	system("pause");
}


第三种就是直接定义了,也是第二种把typedef去掉后,其他部分不用的变。

int(*Arr)[5]; //定义了一个数组指针,5个字节,32位平台下,64位下是8个字节;

int a[5]={1,2,3};

Arr a=&a;

5.二维数组名的本质

int a [3][5];定义一个二维数组a,其实a是个二级指针,数组指针;a+1跳到下一行的首地址,*(a)+1为第一行的第二个元素地址;*(a+1)+1为第二行第二个元素的地址。也就是二级指针指向的是行,一级指针指向的是列。

int (*Arr)[5];//定义一个数组指针;

Arr=a;  //不用取地址,都是二级指针。


6.数组做函数参数

void init(int a[10])
{
	printf("%d \n",a);//这里打印a和实参a的地址是相同的,说明传递过来的是地址。其实实参a是指针,形参a也是指针接受。
						//这种传递不是赋值,a是不能被赋值修改的。
}
void main()
{
	int a[5] = { 1,2,3,4,5 };//相当于这么写:int a[5] = {1,2,3,4,5};
	printf("%d \n", a);
	init(a);
	system("pause");
}
但是这样的写法不好,不高大上;看看下面的写法吧
//void init(int a[10]) --> void init(int a[]) --> void init(int *a)
//void init(int a[3][5]) --> void init(int a[][5]) --> void init(int (*b)[3])


做函数参数的等价关系:

char a[30]     ==>     char* a

char* a[30] ==>char** a

char a[3][5] ==>char(*a)[30]


7.指针数组的结束位

别人写了一个指针数组,给你用,但是没有给你数组的长度,当你在循环输出的时候需要对非NULL判断,一下三种对数组的约束控制都是可以的。

	char* str[]{
//		"1111","2222","3333","\0"
//		"1111","2222","3333",0
		"1111","2222","3333",NULL
	}
	 
	for(int i=0;str[i]!=NULL,i++)
	{
		printf("%s",str[i]);
	}



### 关于C语言实现GB28181客户端的相关资料 为了使非标准设备能够接入GB28181平台,通常需要开发一套基于C语言的客户端程序来模拟GB28181协议的行为。以下是一个简单的示例代码框架,用于展示如何通过C语言实现GB28181客户端的核心功能。 #### 1. 初始化SIP栈并注册到GB28181服务器 GB28181协议基于SIP(会话发起协议),因此初始化SIP栈是第一步。以下是创建SIP消息并向GB28181服务器发送REGISTER请求的一个简单例子: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define SIP_SERVER "sip:server.example.com" #define USER_AGENT "CustomGB28181Client" void send_register_request(const char *from, const char *to) { printf("Sending REGISTER request to %s\n", SIP_SERVER); // 构造REGISTER消息体 char register_message[512]; snprintf(register_message, sizeof(register_message), "REGISTER %s SIP/2.0\r\n" "Via: SIP/2.0/UDP client.example.com;branch=z9hG4bK776sgklf\r\n" "Max-Forwards: 70\r\n" "From: <%s>;tag=12345abcde\r\n" "To: <%s>\r\n" "Call-ID: abcdefg@example.com\r\n" "CSeq: 1 REGISTER\r\n" "User-Agent: %s\r\n" "Contact: <sip:%s@client.example.com>\r\n" "Expires: 3600\r\n" "Content-Length: 0\r\n\r\n", SIP_SERVER, from, to, USER_AGENT, from); // 假设这里有一个函数send_sip_message负责实际发送数据包 // send_sip_message(register_message); } int main() { const char *device_id = "sip:deviceid@example.com"; send_register_request(device_id, device_id); return 0; } ``` 上述代码展示了如何构建一个基本的`REGISTER`请求,并将其发送至指定的GB28181服务器[^1]。 --- #### 2. 处理实时音视频流传输 (RTP) 除了SIP信令外,GB28181还涉及媒体流的传输,这通常是通过RTP(实时传输协议)完成的。下面是一段伪代码,说明如何设置RTP会话并发送音视频帧: ```c #include <stdint.h> typedef struct RtpPacket { uint8_t version : 2; /* RTP版本 */ uint8_t padding : 1; /* 填充标志 */ uint8_t extension : 1; /* 扩展头部标志 */ uint8_t csrc_count : 4; /* CSRC计数器 */ uint8_t marker : 1; /* 标记位 */ uint8_t payload_type : 7; /* 负载类型 */ uint16_t sequence_number; /* 序列号 */ uint32_t timestamp; /* 时间戳 */ uint32_t ssrc; /* 同步源ID */ } RtpHeader; void send_rtp_packet(RtpHeader *header, void *payload, size_t payload_size) { printf("Sending RTP packet with seq=%u and ts=%u\n", header->sequence_number, header->timestamp); // 这里假设存在一个网络层接口用于发送RTP数据包 // send_network_data((uint8_t *)header, sizeof(*header), payload, payload_size); } int main() { RtpHeader rtp_header = { .version = 2, .padding = 0, .extension = 0, .csrc_count = 0, .marker = 0, .payload_type = 96, .sequence_number = 1, .timestamp = 0, .ssrc = rand() }; // 创建虚拟负载数据 uint8_t test_payload[] = { 0x01, 0x02, 0x03 }; send_rtp_packet(&rtp_header, test_payload, sizeof(test_payload)); return 0; } ``` 此部分实现了基础的RTP头结构定义以及发送逻辑。 --- #### 3. 使用第三方库简化开发流程 如果不想完全手动实现SIP和RTP协议细节,可以选择一些成熟的开源库作为辅助工具。例如,PJSIP 是一种广泛使用的多媒体通信库,它提供了对SIP的支持;libsrtp 则专注于安全化的RTP处理。这些库可以帮助开发者快速搭建起GB28181兼容的应用环境[^2]。 --- ### 总结 以上分别介绍了利用纯C语言手写方式达成GB28181客户终端的功能模块设计思路及其对应的部分核心算法片段。对于更复杂的场景,则建议引入专门针对此类需求优化过的外部依赖项以减少重复劳动量的同时提高稳定性与性能表现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值