C语言使用技巧及安全

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是一个数组类型的指针,指向一个数组(该数组不定长)且该数组中的每个一维数组有4int元素,指向实体后相当于p[][4],p[0] p[1]相当于&p[0][0] &p[1][0] 的地址,即p[1]比p[0]的地址大4*sizeof(int)=16.
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/3385f7947a124f16863bb893e69a992a.png)
[例程](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)

__attribute__说明

section:关键字可以将变量或函数定义到**指定的输入段**attribute((section(x))) 放入ram中可以加快程序的运行
使用说明连接

9.程序RAM中运行程序 用法
stm32–程序放ram链接

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

非洲WIP56

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

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

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

打赏作者

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

抵扣说明:

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

余额充值