原创 函数与指针

2014-8-23 12:23 573 4 4 分类: MCU/ 嵌入式

-------------------------------------------------------函数---------------------------------------------------------

将输入的数据按照指定算法进行处理  -------->  处理的过程需要各种控制流的参与  ------>  需要申请不定大小的空间 --> 函数栈


·为什么使用函数?

1)  函数的使用可以省去重复代码的编写

2)  函数的使用更具模块化,从而有利于程序的阅读修改和完善

 

· 复合语句体格式

  返回值类型  函数名 ( 形 参 )

  {

    变量;

    表达式;

    控制流;

    调用外部函数;          

  }

E.g

  int main(int argc, char **argv)    ----> 当前函数执行完毕返回一个int型的数值

----> 当前函数执行时,需要传入两个值,一个 int ,一个 char **

  {                     

    .....                  ----> 函数的执行内容

    .....

 }

Linux的终端中,输入

 nm  test

返回:

 08048494 T main   ---->  08048494 是当前main函数代码的地址

可见当test运行时,系统会将08048494地址上的数据及代码拷贝到函数栈(函数体的运行内存空间)中;

 

-----------------------------------------------------------------------------------------------------------

 ·在标准C 中,main()C语言程序的唯一主入口。

 编译成功的可执行程序中,除了main入口 还有汇编入口。

 

 test.c  ---->  实现了main的定义  以及 实现了相关的自定义函数。

 经过编译后的test.c ,加载了编译器提供的各种库,还有汇编入口文件。

 

 /lib/libxxx.so         ----->

 

 //汇编入口文件

 /usr/lib/i386-linux-gnu/crt1.o

 /usr/lib/i386-linux-gnu/crti.o  

 /usr/lib/i386-linux-gnu/crtn.o

 

 内容分析:

 

 nm  point

 

0804846c T _fini

08048488 R _fp_hw

08048294 T _init

08048300 T _start  ----> 汇编程序入口

 

080483b4 T main    ----> main函数的代码段地址(存放在point程序中)

 

/usr/lib/i386-linux-gnu/crt1.o

00000000 R _fp_hw

00000000 T _start

 

/usr/lib/i386-linux-gnu/crti.o

00000000 T _fini

00000000 T _init

 

结论:

 

    标准C 程序编译后,通过编译器提供的汇编代码,从汇编的入口跳转到main执行。

 

----------------------------------------------------------------------------------------------------------------------

 

  当从 080483b4  执行main函数时,系统将main函数对应的代码,开辟一个新的内存空间进行运行。

 

   通过调试器跟踪函数运行内存:

   gdb

   使用GDB ,在编译程序时,加上 -g 选项:

如:  gcc -g  point.c -o point

 

  使用gdb

  gdb point

  gdb常用命令:

  l  ----> list  陈列当前程序代码

  l  行号  ---> 成列所在行号附近的代码

  p  ---->  print  打印数据,查看数据的值  --->  p   A   ----> 打印A的值

     p  main  ---->  在程序运行前,可以查看main函数存在内存的初始位置

           ---->  在程序运行后,可以通过info frame X 查看main函数存在内存的栈位置

r  ---> run  运行调试器,执行代码

     ---> 一般在run前设置断点,方便调试;

c  ---> 从当前断点,跳转到下一个断点

b  ---> break;

  b  行号  -----> 在当前行设置断点,按 r 运行到当前行头即停止(还未执行本行代码)

           -----> 断点一般设置在赋值或者执行语句前;

info b  ---> 查看当前断点信息

断点号  -->删除断点

q  ---> 退出debug

bt  ----> 查看当前函数栈

n   ---> next  执行下一步(不进入函数体)

s   --->  step 单步执行,进入函数体

 

 (gdb) bt

 #0  main (argc=1, argv=0xbffff3e4) at point.c:8  ---> 当前有一个函数栈,编号为0

 

 //查看栈信息

 

(gdb) info frame 0

Stack frame at 0xbffff350:     -----> main函数运行的栈地址

 eip = 0x80483b7 in main (point.c:8); saved eip 0xb7e384d3

 source language c.

 Arglist at 0xbffff348, args: argc=1, argv=0xbffff3e4

 Locals at 0xbffff348, Previous frame's sp is 0xbffff350

 Saved registers:

  ebp at 0xbffff348, eip at 0xbffff34c

-------------------------------------------------------------------------------------------------------

 

   ·或许会有一个疑问,如果虚拟地址都是随机分配的话,那么程序编译后的程序执行地址是否冲突

很显然,答案是否定的,因有MMU存储管理单元的存在,它会将每个程序执行的虚拟地址关联到具体的物理地址。

 

  linux C 程序 生成的镜像,执行地址均为虚拟地址。   ------>  为了实现多任务共存

-------> 解决内存使用问题  ----> 需要内存规划

   ------> 每个程序均拥有一个虚拟地址表 ----> 32bit ----> 4G

   -----> 具体的虚拟地址的物理地址空间 ----> MMU完成转化

 

  -------------------------------------------------------------------------------------------------------------------

 

  局部函数的存储位置,在对应的函数栈中,随着函数的运行而分配,随着函数的退出而消亡。

 

  &A   --->  取变量A的地址;

 

  装载地址的变量   ----> 指针变量   ----->  规范指针变量的使用  ----> 指针变量也有类型

 

  char A  ----->   &A    -----> 变量A 的地址  -----> 装载变量A地址的变量  ----->  char *pA;

  int B

 

  char A   ====>  char (*)pA   ---->   变量pA 是一个(*) 指针,指向char 型的变量的地址

  pA  =  &A

 

  指针定义:

 

       int *pA;

 

  使用指针:

   

        int A;

        pA = &A;

 

  获取A的值:

 

       *pA    ----> 去除对应地址上的数据

 

  存在指针的必要性?

------------------------------------------demo--------------------------------------------

 

void swap(int x,int y)

{

       int temp;

       temp = x;

       x = y;

       y = temp;

}

 

int main(int argc,char **argv)

{

      int a = 5;

      int b = 6;

 

      add(a,b);    

      swap(a,b);

     

      printf("%d  %d,a,b\n");

       return 0;

}

 

结论:

     swap() 只实现了传递的数据在函数体内值交换。(函数的调用是值传递)所以a=5 b=6

 

·如果想通过swap()实现 a b 的内部值交换,需要操作ab的存储空间 ------> 操作存储地址  -----> 操作地址变量  ----> 指针

 

改进方法:

void Swap(int *x,int *y)

{

       int temp;

       temp = *x;

       *x = *y;

       *y = temp;

}

 

int main(int argc,char **argv)

{

      int a = 5;

      int b = 6;

 

      add(a,b);    

      Swap(&a,&b);

     

      printf("a = %d   b = %d \n",a,b);

      printf("%d  %d,a,b\n");

     

       return 0;

 

输出: a=6 b=5                                        

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
我要评论
0
4
关闭 站长推荐上一条 /3 下一条