C语言安全
1. 字符串空判断
if(config->user && strcmp(config->user, "root")){
}
2.字符串数组一定要先初始化–防止越界
char strTemp[51];
memset(strTemp,'\0',sizeof(strTemp));
3.所有的数组使用前一定做越界的判断,全局变量的数组的长度最好用宏定义(直观易修改)
typedef struct _QUE_ETH_{
uint16_t dataLen; //数据长度
uint8_t buf[UART3_DMA_RXBUF_SIZE]; //2500数据
}QUE_ETH;
if(queData.dataLen>=UART3_DMA_RXBUF_SIZE){
}
4. ##符号:字符串连接符
#define DEF_INT(a,b) int a##b = 0
DEF_INT(a,b);
那么最终这个宏实现的意思就是定义一个int型的变量,变量的名字叫ab,将这个宏展开就是
int ab = 0;
c语言的使用
1.引用
1.指针的引用
int main(int argc, char const *argv[])
{
int val_1 = 10;
int * ptr_1 = &val_1; // a 指向 x
int * &ptr = ptr_1; //指针的引用 ptr 是 ptr_1 的引用(别名)
*ptr = 20; // 修改 val_1 的值(等同于 *ptr_1 = 20)
cout << *ptr << endl;
return 0;
}
2.引用与返回值 靠右原则
我们平时使用函数时,函数的返回值都是一个右值。
但是引用作为函数的返回是一个左值。
左值:既可以放在等号左边,也可以放在等号右边的值。
右值:只能放在等号右边的值。
引用作为函数的返回值,不能返回局部变量的引用,因为局部变量在函数调用结束时就被回收了。
#include <iostream>
#include <string>
using namespace std;
// 使用静态的方式
int & Func_1()
{
static int val = 10;
return val;
}
// 全局
int num;
int & Func_2()
{
num = 80;
return num;
}
// 函数传参
int & Func_3(int & val)
{
return val;
}
int main(int argc, char const *argv[])
{
// 引用作为返回值 传递给引用 可以修改返回的值
int & val = Func_2();
val = 90;
cout << num << endl; // 打印全局
// 引用作为返回值 是左值
Func_2() = 123;
cout << num << endl; // 打印全局
// 引用的返回值是 参数
int arg = 10;
Func_3(arg) = 50;
cout << arg << endl; // 打印传递的参数
return 0;
}
1.指定格式拼接字符串
sprintf(pub_topic, "/sys/%s/%s/thing/service/property/set", PRODUCTKEY, DEVICEID);
2.链表的增删改查
链表的增删改查的应用
3.函数指针的用法:最常见的是做接口使用
//函数指针在常规用法:回调或者接口函数
//1:接口
文件b.c
//串口0接受字节实体函数
uint32_t UART0_RecvWpr(uint8_t* RecvBuf, uint32_t BufLen,uint32_t TimeOut){
return UART0_Recv(RecvBuf, BufLen,TimeOut);
}
//串口1接受字节实体函数
uint32_t UART1_RecvWpr(uint8_t* RecvBuf, uint32_t BufLen,uint32_t TimeOut){
return UART1_Recv(RecvBuf, BufLen,TimeOut);
}
//定义初始化引用实体的函数指针
uint32_t (*UARTS_Recv_MAP[2])(uint8_t* RecvBuf, uint32_t BufLen,uint32_t TimeOut) = {
UART0_RecvWpr,
UART1_RecvWpr,
};
文件b.h
//头文件定义函数指针的引用
uint32_t (*UARTS_Recv_MAP[2])(uint8_t* RecvBuf, uint32_t BufLen,uint32_t TimeOut);
a.h
//宏定义函数指针
#define UARTS_Recv(UartNum, RecvBuf, BufLen,TimeOut) (*UARTS_Recv_MAP[UartNum])(RecvBuf, BufLen,TimeOut)
调用实体函数
文件a.c
//调用串口函数0 接受数据
UARTS_Recv(0, RecvBuf, BufLen,TimeOut)
3.宏定义的使用
//读取地址的值
#define read(addr,data){ data = (*((volatile unsigned int *) (addr)));}
uint32_t trim1_reg;
read(0xF0180000, trim1_reg);
4.内联函数 inline : 提高程序执行效率不用函数进栈压栈,经常用的函数可以加
//编译器编译时就先展开函数,程序运行时直接使用不调用函数,节省函数进栈压栈时间提高效率。
//编译器会在每处调用内联函数的地方将内联函数内容展开,这样既避免了函数调用的开销又没有宏机制的缺陷
inline void demo_01(char *s){
...
printf("%s", s);
}
4.结构体创建初始化
struct ABC_T{
uint32_t aaa;
uint32_t bbb[4];
}
//创建时赋值
struct ABC_T bcd_t=
{.aaa=10, .bbb={1,2,3,4} };
4.结构体相同可以直接复制,不需要使用copy函数,汇编会直接复制
#include <stdio.h>
struct Foo {
char a;
int b;
double c;
}foo1, foo2; //define two structs with three different fields
void struct_assign(void)
{
foo2 = foo1; //structure directly assignment
}
4.结构体指针的地址与内容
typedef struct{
uint32_t aaa;
}ABC_T
ABC_T *abc_t;
ABC_T bcd_t;
//读取地址与内容
abc_t=&bcd_t;
printf("%x %x %x\n",&acb_t,acb_t,*acb_t);//&acb_t:acb_t自己的地址,acb_t存储指向的地址 *acb_t存储指向的地址里的内容
4.不同地址类型的寻址
//读取地址的值
uint32_t data[20]={1,2,3,4,5,6,7,8,9,10,};
is_receive_send_ble_fun(&((uint8_t*)data)[length + 4]); //uint8_t 的第四个数据
//memset()结构体转化----结构体转成u8*
struct{
uint8_t a;
uint16_t c;
}data_var;
memset((uint8_t*)&data_var,0xff,sizeof(data_var));
4.固定多个地址的类型结构体注释
struct{
uint32_t a;
uint32_t b;
uint32_t c;
}GPIOA_Reg ;
#define GPIO_A_ADDR IO(0xc20000000)
#define GPIO_A ((GPIOA_Regs *) GPIO_A_ADDR)
//使用固定地址寄存器时直接: GPIO_A->b 即可 方便阅读
GPIO_A->b=0x32; //即可 方便阅读
5.二位数组的类型 int (*)[] 指向一维数组的指针
例1 int (*p)[4];
p是一个数组类型的指针,指向一个数组(该数组不定长)且该数组中的每个一维数组有4个int元素,指向实体后相当于p[][4],p[0] p[1]相当于&p[0][0] &p[1][0] 的地址,即p[1]比p[0]的地址大4*sizeof(int)=16.

[例程](https://blog.csdn.net/weixin_41416542/article/details/126374274)
int (*)[5] 可当参数使用 相当于[][5]
例2:做返回值使用:考察返回值时如何返回实体变量的地址与数组的意义还有长度。当值不知道咋返回数组地址以及数组的定义
typedef uint8_t ret_a[6]; //
uint8_t shiti_b[4][6] ;
ret_a *test_fun(void){ //返回值是指向一个数组类型长度为6(int 长度是4) 的指针,即ret_a[1]比ret_a[0] 的地址大6
ret_a *p_a=NULL;
....//对实体shiti_b各种赋值
p_a = (ret_a *)shiti_b;
return ret_a; //注意:调用者可使用的最大地址数是 4*6 如果返回的是一维的数组则可取范围时一维数组的长度大小。
}
//所有返回值指针本身就是个地址。为啥还有不同的返回地址类型(u8,u32,struct,[]等)。目的是应用层根据不能的类型方便取数据,返回时直接用指定类型获取即可,使用起来方便
5.int (*)[]与int ** 的区别
举例:int (*)[]与int ** 及 二维数组作函数返回值或参数
5.二位数组作为函数参数
举例链接

5.二位数组malloc的申请
[链接](https://www.yisu.com/ask/73079729.html)
int rows=5, cols=10, i, j;
// 创建二维数组
int** arr = (int**)malloc(rows * sizeof(int*));
for (i = 0; i < rows; i++) {
arr[i] = (int*)malloc(cols * sizeof(int));
}
6.字节对齐方式 #pragma pack 举例
#pragma pack(1) //内存对齐设置为1个字节
struct PROTOCOL
{
int time;
char head;
char cmd;
int length;
char tail;
}
struct STATUS_REG
{
//...
}
//...
#pragma pack() //恢复默认的内存对齐
```bash
7.attribute((aligned(n)))
编译器会将让n与默认的对齐字节数进行比较,取较大值为对齐字节数,与#pragma pack(n)恰好相反。
解释说明
8.attribute 用法
__attribute__是GUN C中极具特设的一大机制,可以用来设置
函数属性(Function Attribute)
变量属性(Variable Attribute)
类型属性(Type Attribute)
section:关键字可以将变量或函数定义到**指定的输入段**中 attribute((section(x))) 放入ram中可以加快程序的运行
使用说明连接
9.程序RAM中运行程序 用法
stm32–程序放ram链接

1万+

被折叠的 条评论
为什么被折叠?



