别踩static的地雷

只要做过项目的朋友对关键字static应该都有一些了解,但未见了解很全面的。在C语言中,关键字static有以下明显的作用:

1.static变量分配到静态内存中,这一点和全局非静态变量相同。

2.在函数体,static变量只要不进行修改操作,在被调用过程中其值将保持不变。

3.在模块内,全局static变量可以被模块内所有函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量,具有私有特点。

4.在模块内,一个static函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用,也具有私有特点。

如果我们能够很好地利用static的这些特性,书写高内聚低耦合,更具模块化的代码就不会显得象句口号;如果不能很好地理解和使用它,一切都只是空谈。下面来举个例子来说明static在实际应用可能会遇到问题。

如果要求实现下面的接口:为每个大小约1600字节的音视频裸数据(audio/video)添加包头(packet header),然后将打过包头的整个数据包发送到网络。打过包头的数据包格式如下:

<------packet----->

| header | data |

我想有人可能会这么做:

#define MAX_PACKET_SIZE 1600

typedef struct _header

{

bool type;  /*数据包类型*/

bool length; /*数据包长度*/

 ...

}header;

bool send(bool type,uint8* data,uint32 length)

{

uint8 packet[MAX_PACKET_SIZE+sizeof(header)]={0};  /*定义数组*/

header* pheader=(header*)packet;

if(NULL==data || length<1)

{

return FALSE;

}

 /*设置包头*/

pheader->type=type;

pheader->length=length;

pheader++;

memcpy((uint8*)pheader,data,length);  /*拷贝数据到包头之后*/

netSend(packet);  /*将包发送到网络*/

return TRUE;

}

  

对于上面的代码,有人可能会有这样的疑虑,发送数据包到网络是一个非常频繁的操作,所以在send函数中,频繁地为packet分配栈内存是一种低效的做法。他可能会将上的代码修改为:

bool send(bool type,uint8* data,uint32 length)

{

uint8* packet=NULL;

header* pheader=NULL;

if(NULL==data || length<1)

{

return FALSE;

}

packet= (uint8*)malloc(length+sizeof(header));

if(NULL==packet)

{

return FALSE;

}

pheader=(header*)packet;

pheader->type=type;

pheader->length=length;

pheader++;

memcpy((uint8*)pheader,data,length);

net_send(packet);/*将包发送到网络*/

free(packet);

packet=NULL;

return TRUE;

}

使用动态内存好象可以解决上面的问题,但它没有考虑到频繁地使用malloc-free会产生大量的内存碎片。在嵌入式系统环境中,一般内存大小有限,所以这种做法最终会导致分配失败。对于处理大流量数据问题,一种比较常用的高效方法就是在函数内部使用静态数组(全局静态数组在这个应用中不建议使用,因为全局变量会增加函数间的耦合度)。嘿嘿,听到这样的建议,估计有人会马上这么改:

bool send(bool type,uint8* data,uint32 length)

{

static uint8 packet[MAX_PACKET_SIZE+sizeof(header)]={0};

header* pheader=(header*)packet;

if(NULL==data || length<1)

{

return FALSE;

}

pheader->type=type;

pheader->length=length;

pheader++;

memcpy((uint8*)pheader,data,length);

net_send(packet);/*将包发送到网络*/

memset(packet,0,sizeof(packet));/*清除本次内存操作*/

return TRUE;

}

  朋友且慢,小心地雷!

  你是否忘了考虑代码可重入(reentrance)问题呢?这里使用packet静态数组的确不用频繁地分配动态或栈内存,但同时引入了代码不可重入的问题。因为函数内的static变量分配在静态内存区,供所有对象共用。在多任务系统中,如果有一个以上的任务同时访问该内存,很可能会出现问题。所以我们必须要用其它手段来消除这个不可重入问题。使用信号量semaphore是一个很好解决不可重入问题的方法。在上面代码中加入信号量:

bool send(bool type,uint8* data,uint32 length)

{

static uint8 packet[MAX_PACKET_SIZE+sizeof(header)]={0};

header* pheader=(header*)packet;

if(NULL==data || length<1)

{

return FALSE;

}

semTake(semaphore,WAIT_FOREVER);/*等待信号量*/

pheader->type=type;

pheader->length=length;

pheader++;

memcpy((uint8*)pheader,data,length);

net_send(packet);/*将包发送到网络*/

memset(packet,0,sizeof(packet));/*清除本次内存操作*/

semGive(semaphore);/*释放信号量*/

return TRUE;

}

好,现在终于解决这个问题了。在我们的视频会议系统中,曾经就在将数据发送到网络上

踩过这个地雷,修改这个bug也化了很大功夫。

别踩static的地雷相关推荐

  1. 2021考研常见的“地雷”,千万不要踩!

    对于各位2021考研er来说,由于缺乏备考经验,导致备考过程中一不小心就踩到一些"地雷",所以为大家总结了以下经常遇到的雷区.我们一起来看看: 一.比速度 很多小伙伴在备考的时候, ...

  2. C#入门学习——飞行棋

    用之前学习的内容做一个飞行棋游戏 当我们打开程序,显示出飞行棋游戏1.0这一部分内容,然后是要求用户输入玩家姓名. 当玩家姓名输入无误之后,显示如上界面.这一部分为玩家棋子和地图. 可以看到输入的玩家 ...

  3. C# 实现飞行棋小游戏

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  4. 飞行棋游戏代码(C#)

    220224飞行器v1.0 using System;namespace AeroplaneChess {class Program{//地图static int[] Maps = new int[1 ...

  5. C#基础(10)——飞行棋游戏

    1.打印游戏头 using System; using System.Collections.Generic; using System.Linq; using System.Text; using ...

  6. C#基础代码笔记(五)

    文章目录 前言: 1.模拟静态全局变量代码: 2.主函数运行代码: 3.游戏头运行代码: 4.初始化游戏地图的关卡代码: 5.画简易地图运行代码: 6.地图中关卡对应标识符赋给地图的运行代码: 7.玩 ...

  7. C#学习第六天 基础语法练习游戏--飞行棋

    前几天学习的实践:游戏---飞行棋 步骤: 1.画游戏头 2.初始化地图(加载地图所需要的资源) 将整个数组中的数字变成控制台中显示的特殊字符串的过程,就是初始化地图 int[100]代表100个符号 ...

  8. c#飞行棋游戏(控制台)

    需求分析 1.制作游戏头部:游戏头部介绍 2.绘制地图 使用一维数组装整个地图的路线 如果这个位置是0,绘制普通格子□ 如果这个位置是1,绘制幸运轮盘◎ 如果这个位置是2,绘制地雷★ 如果这个位置是3 ...

  9. C#基础知识---飞行棋小游戏

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

最新文章

  1. 深度学习工程师必看:更简单的超分辨重构方法拿走不谢
  2. jquery and jquery validation 常见问题解决
  3. lombok 中的@Data注解
  4. 使用report找出系统里维护了available status reason的document
  5. IOS15仿微信我的页面
  6. linux进程网络流量使用查询,linux centos 查看进程网络流量状态、网络流量(使用nethogs、nload)...
  7. HTML中利用纯Microsoft Ajax Library做出可调用WebSerives的AutoComplete
  8. linux全自动备份网站到百度云盘,Linux定时备份数据到百度云盘(示例代码)
  9. WPF监控云台控制组件实现简单方案
  10. 无障碍,root,adb如何隐藏,去除检测
  11. Android-手撸抖音“潜艇大挑战”,非科班面试之旅
  12. 支付宝查询自己UID
  13. Floyd是咋求图的最短路径?
  14. html写钢琴键盘按键错乱,键盘按键错乱怎么修复
  15. 苹果消息推送服务教程:第一部分(共2部分)
  16. 树莓派mysql重置密码_树莓派忘记密码了?四步重设密码 | 树莓派实验室
  17. iPhone12与mate40,你pick哪一款?
  18. 够快联想坚果云这三个产品哪个更好
  19. 电机坐标变换simulink仿真(笔记)
  20. 2014年最新电脑公司系统下载

热门文章

  1. 解读《医疗器械监督管理条例》的重大改变
  2. JavaScript做轮播图片(2)
  3. web课程设计网页规划与设计:中国风茶文化网站设计(6个页面) HTML+CSS+JavaScript
  4. 盘点:为了准确统计门店客流,业内都用过哪些方法
  5. 将Spring Security OAuth2授权服务JWK与Consul 配置中心结合使用
  6. tomcat经常挂掉MySQL_tomcat 挂掉,假死无响应原因总结和解决方案
  7. Java学习之路01_软件江湖_旺旺老师
  8. 华帝破壁机怎么打豆浆_破壁机打豆浆步骤具体是什么?
  9. 爬虫返回状态码“521” 解决方案 | 设置Cookie解决
  10. docker run --rm