Chinaunix首页 | 论坛 | 博客
  • 博客访问: 652335
  • 博文数量: 128
  • 博客积分: 4385
  • 博客等级: 上校
  • 技术积分: 1546
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-22 14:05
文章分类

全部博文(128)

文章存档

2012年(2)

2011年(51)

2010年(75)

分类: C/C++

2010-08-16 13:48:16

 

《嵌入式linux上的c语言编程实践》(亚嵌教材)学习笔记--------by Andy

第九章内存的堆和栈

 

c使用的内存分为:静态区和动态区,静态区

静态区:只读数据区,初始化数据区,未初始化数据区

动态区:堆区,栈区

 

栈:

1.使用依赖硬件机制,有两种增长方向。有空栈和满栈。空栈:栈指针指向未使用的数据区。满栈:栈指针指向没使用的数据区。

2.由编译器管理。函数退出内容释放,栈上的内存不能被别的函数使用(不能返回站内变量的地址)。

3.使用线性存储方式

4.主要特性:FILO,先进后出

5.有一个指针,通过指针+偏移量来访问

6.

 

堆:

1.堆的增长方向和栈相反。(大体上如此)

2.堆由程序员管理,调用和释放由程序的库函数完成。每次分配返回一个指针,可多次分配,得到多个指针,可用每个指针来访问。

3.用链表实现

4.四个分配和释放的函数:mallocfreecallocreallooc

5.可以再一个函数中开辟,另一个函数中释放。

 

堆内存管理容易出现的问题:

1.       内存泄露

2.       野指针的使用和释放

3.       非法释放指针。

正确的使用方法:在内存释放后,将内存指针置为NULL,在使用时判断指针释放为NULL

正确使用堆

/* how to use malloc correctly */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
        char *pa;
        pa = (char *)malloc(sizeof(char)*20);
        if(NULL != pa)
        {
                strcpy(pa, "memory leak");
                printf("pa = %x \n", (unsigned int)pa);
                printf("pa = %s \n", pa);
        }
        free(pa);
        pa = NULL;
        if(NULL != pa)
        {
                printf("pa = %s \n", (unsigned int)pa);
        }
        return;
}

堆,栈的使用比较:

1.       利用返回值传递信息

1)返回值可以是任何内存的地址,但不能返回内部栈区的地址。

当希望返回栈上较多的内容时,不能用指针,可以用结构体(结构体在函数外定义),但开销比较大。

2)返回结构体是,要从被掉函数的栈空间中将结构体复制的调用函数的栈空间上,pushpop开销大。

2.利用参数传递信息

1)参数是变量的情况,swap函数,不能交换

2)参数是指针的情况,swap可以交换

3)参数是结构体的情况和变量类似。开销大,可以定义结构体指针,来传递参数。

4)参数是数组的情况,当做指针来处理。实际入栈的是数组地址的指针。

第十章 函数指针的使用

       函数指针是指向函数代码地址的指针。C语言中,函数名表示函数代码在内存中的地址。

例如:函数foo()&foofoo的含义相同。和数组名类似。

1.函数指针的声明:int (*pf)()   因为()的结合优先级高于*,所以用括号,

否则为*g(),意为返回值为指针的函数。

简单函数指针的定义:

       Void (*pf) (void);    

然后复制:    pf=foo;

2.       函数指针的类型转换

类型转换只需要将声明中的变量名和声明末尾的分号去掉,然后用括号括起来即可。

      int (*fp) ();      表示fp是个指向返回值为整形的函数的指针。因此

  ( int  (*) () ) 表示一个“指向返回值为整形的的函数的指针”的类型转换符。

注:函数的声明和调用不同。

 

 

第十四章 嵌入式c语言常用语法

1.使用指针操作内存

       向地址0x0040处写一个字节的数据0xf0

Unsigned char *p = (unsigned char *)0x0040;

*p = 0x40;

或:

*(unsigned char *)0x40;              //注意unsigned的使用,思考读?16位的读?32位的读?

 

3.       结构体成员的对齐问题

typedef struct _s1

{

       char m1;

       int           m2;

       char m3;

       shaort     m4;

}S1;

sizeof(S1)不等于8个字节,等于12个字节,故在定义结构体时,注意结构体成员的排列顺序,使结构体最小。

 

4.       变量的初始化技巧

//数组的初始化

char a[10] = "abcde"

//编译器做了很多事情才完成,先在栈上开辟10个字节空间,然后从内存中复制。

static const rodata[6] = "abcde";

char b[10];

strcpy(b,rodata);

//使用函数完成赋值,在函数运行时完成

//两者依赖的库不通,效率和规模类似。

 

结构体的初始化

使用每个赋值的方式比使用列表赋值的方式开销小。

struct S

{

       int a;

       int b;

       int c;

}s1;

//列表方式,开销大

fun()

{

       s1 = {1,2,3};

}

//成员分别赋值开销小

fun()

{

       s1.a=1;

       s1.b=2;

       s1.c=3;

}

阅读(2197) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~