C语言笔记
1.动态内存分配
malloc动态分配与数组静态分配一个最重要的区别:
假设数组char a[10]和charp=(char)malloc(10)都是全局的。那么数组a[10]在程序运行过程会一直存在,即一直占用10个字节空间。但动态申请的可以使用free()来释放掉.等到再使用的时候重新申请.
2. malloc函数以及free函数的注意事项
void Test( void )
{
char *str = (char *) malloc( 100 );
strcpy( str, "hello" );
free( str );
...
}
如果内存紧张,可能导致分配失败,此时会返回NULL,所以要对分配结果进行检查
对于free函数,需要注意的值域一点,那就是野指针,free(p)之后,需要让p=NULL。
下面的两种写法是等价的:
// calloc()分配内存空间并初始化
char *buf1 = (char *)calloc(10, 2);
// malloc()分配内存空间并用 memset()初始化
char *buf2 = (char *)malloc(10 * 2);
memset(buf2, 0, 20);
3. i++与++i效率问题
在内建数据类型的情况下,效率没有区别;
在自定义数据类型的情况下,++i效率更高!
分析1:
(在自定义数据类型的情况下)
++i返回对象的引用;
i++ 总是要创建一个临时对象,在退出函数时还要销毁它,而且返回临时对象的值时还会调用其拷贝构造函数。
分析2:
i++由于是在使用当前值之后再 +1, 所以需要一个临时变量来转储,而++i 则直接 +1,不存在临时变量的问题。
扩展:i=i+1, i+=1, i++, ++i 效率比较
++i 最快
i++ 次之,比++i多用一个临时变量
i += 1 第三,需要取地址
i = i + 1 最后,并多用一个临时变量
4. 内存泄漏
a. 什么是内存泄漏?
正规点的理解:动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元。直到程序结束。一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
b. 内存泄漏的危害
①长时间运行,程序变卡,性能严重下降
②程序莫名其妙挂掉
③Out Of Memory Error错误
④乱七八糟的错误,还不易排查
c. 内存泄漏原因
①大量使用静态变量(静态变量的生命周期与程序一致。因此常驻内存(static))
②连接资源未关闭
③线程ThreadLocal的错误使用
④乱七八糟的错误,还不易排查
4. 内存溢出
a. 什么是内存溢出?
是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出。
b. 内存溢出原因
①内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
②集合类中有对对象的引用,使用完后未清空,使得JVM不能回收
③代码中存在死循环或循环产生过多重复的对象实体
④使用的第三方软件中的BUG
⑤启动参数内存值设定的过小
c. 内存溢出解决方案
第一步,修改JVM启动参数,直接增加内存。(-Xms,-Xmx参数一定不要忘记加。)
第二步,检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误。
第三步,对代码进行走查和分析,找出可能发生内存溢出的位置。
5. C语言内存模型(内存组织方式)
内存中运行着很多程序,我们的程序只占用一部分空间,这部分空间又可以细分为以下的区域:
内存分区 | 说明 |
---|---|
程序代码区(code area) | 存放函数体的二进制代码 |
静态数据区(data area) | 也称全局数据区,包含的数据类型比较多,如全局变量、静态变量、一般常量、字符串常量。其中:全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。常量数据(一般常量、字符串常量)存放在另一个区域。 注意:静态数据区的内存在程序结束后由操作系统释放。 |
堆区(heap area) | 一般由程序员分配和释放,若程序员不释放,程序运行结束时由操作系统回收。malloc()、calloc()、free() 等函数操作的就是这块内存,这也是本章要讲解的重点。 注意:这里所说的堆区与数据结构中的堆不是一个概念,堆区的分配方式倒是类似于链表。 |
栈区(stack area) | 由系统自动分配释放,存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈。 |
命令行参数区 | 存放命令行参数和环境变量的值,如通过main()函数传递的值。 |
提示:关于局部的字符串常量是存放在全局的常量区还是栈区,不同的编译器有不同的实现,VC 将局部常量像局部变量一样对待,存储于栈(⑥区)中,TC则存储在静态数据区的常量区(②区)。
6. #define与const关键字区别
#define 是宏定义,它不能定义常量,但宏定义可以实现在字面意义上和其它定义常量相同的功能,本质的区别就在于 #define 不为宏名分配内存,而 const 也不为常量分配内存,怎么回事呢,其实 const 并不是去定义一个常量,而是去改变一个变量的存储类,把该变量所占的内存变为只读!
7. 一些实用代码
a.软件模拟PWM:
1. /* 控制 GPIO 输出高低电平 */
2.double freq = atof(argv[2]); //频率,整形
3.double duty = atof(argv[3]); //占空比, double形,0-1.0
4.double delay_time = atof(argv[4]); //方波持续时间, double形,0是死循环输出方波
5.
6.int times = (int)(delay_time*freq);
7.if(delay_time == 0)
8.{
9. while(1)
10. {
11. //open LED
12. gpio_config("value", "1");
13. usleep(1000000/freq*duty); //usleep以微秒为单位,所以周期=1000000us/freq
14. //close LED
15. gpio_config("value", "0");
16. usleep(1000000/freq*(1-duty));
17. }
18.}
19.else
20.{
21. while(times--)
22. {
23. //open LED
24. gpio_config("value", "1");
25. usleep(1000000/freq*duty);
26. //close LED
27. gpio_config("value", "0");
28. usleep(1000000/freq*(1-duty));
29.
30. }
31.}
b.流水控制io(低电平->高电平->低电平):
1.while(1)
2. {
3. for(i=1;i<cycle;i++) //cycle是周期
4. {
5. //open gpio
6. gpio_config("value", "1");
7. usleep(i*30);
8.
9. //close gpio
10. gpio_config("value", "0");
11. usleep((cycle-i)*30);
12.
13. }
14.
15. for(i=cycle-1;i>0;i--)
16. {
17. //open gpio
18. gpio_config("value", "1");
19. usleep(i*30);
20. //close gpio
21. gpio_config("value", "0");
22. usleep((cycle-i)*30);
23. }
24.
25. }
8.蜂鸣器音调频率表(Hz)
yin_1_l = 233.028 #低音1频率
yin_2_l = 261.626
yin_3_l = 293.665
yin_4_l = 329.629
yin_5_l = 369.994
yin_6_l = 415.305
yin_7_l = 466.164 #低音7频率
yin_1_m = 523.251 #中音1频率
yin_2_m = 587.33
yin_3_m = 659.255
yin_4_m = 739.989
yin_5_m = 830.609
yin_6_m = 932.328
yin_7_m = 1046.502
yin_1_h = 1174.659 #高音1频率
yin_2_h = 1318.52
yin_3_h = 1567.982
yin_4_h = 1760
yin_5_h = 2093.004
yin_6_h = 2489.016
yin_7_h = 2959.955
9.C语言.h文件为了防止头文件被重复包含,一般需要在文件的头部添加宏的判断:
#ifndef LED_H
#define LED_H
...头文件的具体内容...
#endif