C语言程序内存分配

发布者:SparklingMoon最新更新时间:2015-09-18 来源: eefocus关键字:C语言  程序内存  分配 手机看文章 扫描二维码
随时随地手机看文章

(1) 内存分区状况

 

栈区 (stack) : 

-- 分配, 释放方式 : 由编译器自动分配 和 释放;

-- 存放内容 : 局部变量, 参数;

-- 特点 : 具有 后进先出 特性, 适合用于 保存 回复 现场;

 

堆区 (heap) : 

-- 分配, 释放方式 : 由程序员手动 分配(malloc) 和 释放(free), 如果程序员没有释放, 那么程序退出的时候, 会自动释放;

-- 存放内容 : 存放程序运行中 动态分配 内存的数据;

-- 特点 : 大小不固定, 可能会动态的 放大 或 缩小;

 

堆区内存申请 : 

-- 申请过程 : OS中有一个记录空闲内存地址的链表, 如果程序员申请内存, 就会找到空间大于申请内存大小的节点, 将该节点从空间内存链表中删除, 并分配该节点; 

-- 剩余内存处理 : 系统会将多余的部分重新放回 空闲内存链表中;

-- 首地址记录大小 : 分配内存的首地址存放该堆的大小, 这样释放内存的时候才能正确执行; 

全局区/静态区 (数据段 data segment / bss segment) : 

-- 分配, 释放方式 : 编译器分配内存, 程序退出时系统自动释放内存;

-- 存放内容 : 全局变量, 静态变量;

-- 特点 : 全局变量 和 静态变量存储在一个区域, 初始化的两种变量 和 未初始化的 存储在不同区域, 但是两个区域是相邻的;

 

常量区 : 

-- 分配, 释放方式 : 退出程序由系统自动释放;

-- 存放内容 : 常量;


代码区 (text segment) : 

-- 分配, 释放方式 : 编译器分配内存, 程序退出时系统自动释放内存;

-- 存放内容 : 存放 程序的二进制代码, 和一些特殊常量;

 

内存存放顺序 (由上到下) : 栈区 -> 堆区 -> 全局区 -> 常量区 -> 代码区;

 

(2) 内存分配方式

 

全局内存分配 : 

-- 生命周期 : 编译时分配内存, 程序退出后释放内存, 与 程序 的生命周期相同;

-- 存储内容 : 全局变量, 静态变量;

 

栈内存分配 :

-- 生命周期 : 函数执行时分配内存, 执行结束后释放内存;

-- 特点 : 该分配运算由处理器处理, 效率高, 但是栈内存控件有限;

 

堆内存分配 : 

-- 生命周期 : 调用 malloc()开始分配, 调用 free()释放内存, 完全由程序员控制;

-- 谨慎使用 : 如果分配了 没有释放, 会造成内存泄露, 如果频繁 分配 释放 会出现内存碎片; 

 

(3) register变量

 

使用场景 : 如果 一个变量使用频率特别高, 可以将这个变量放在 CPU 的寄存器中;

-- 修饰限制 : 只有 局部变量 和 参数 可以被声明为 register变量, 全局 和 静态的不可以;

-- 数量限制 : CPU 寄存器 很宝贵, 不能定义太多register变量;

 

(4) extern 变量

 

extern变量概念 : 声明外部变量, 外部变量就是在函数的外部定义的变量, 在本函数中使用;

-- 作用域 : 从外部变量定义的位置开始, 知道本源码结束都可以使用, 但是只能在定义extern后面使用, 前面的代码不能使用;

-- 存放位置 : 外部变量 存放在 全局区;

 

extern变量作用 : 使用extern修饰外部变量, ① 扩展外部变量在本文件中的作用域, ② 将外部变量作用域从一个文件中扩展到工程中的其它文件;

 

extern声明外部变量的情况 : 

-- 单个文件内声明 : 如果不定义在文件开头, 其作用范围只能是 定义位置开始, 文件结束位置结束;

-- 多个文件中声明 : 两个文件中用到一个外部变量, 只能定义一次, 编译 和 连接的时候, 如果没有这个外部变量, 系统会知道这个外部变量在别处定义, 将另一个文件中的外部变量扩展到本文件中;

 

extern编译原则 : 

-- 本文件中能找到 : 编译器遇到 extern 的时候, 现在本文件中找外部变量的定义的位置, 如果找到, 就将作用域扩展到 定义的位置 知道文件结束;

-- 本文件中找不到 : 如果本文件中找不到, 连接其它文件找外部变量定义, 如果找到, 将外部变量作用域扩展到本文件中;

-- 外部文件找不到 : 报错;

 

使用效果 : extern 使用的时候, 可以不带数据类型;

-- 本文件 : int A = 0; 在第10行, extern A 在第一行, 那么A的作用域就扩展为从第一行到文件末尾;

-- 多文件 : 在任意文件中定义了 int A = 0; 在本文件中声明 extern A, 那么从当前位置到文件末尾都可以使用该变量;

 

 

(5) static变量 与 全局变量区别

 

static 变量 与 全局变量 相同点 : 全局变量是静态存储的, 存储的方式 和 位置基本相同;

 

static 变量 与 全局变量不用点 : 全局变量的作用域是 整个项目工程 横跨过个文件, 静态变量的作用域是 当前文件, 其它文件中使用是无效的;

 

变量存储位置 : 全局变量 和 静态变量 存放在 全局区/静态去, 局部变量存放在 栈区(普通变量) 和 堆区(指针变量);

 

变量静态化 : 

-- 局部变量 : 局部变量 加上 static , 相当于将局部变量的生命周期扩大到了整个文件, 作用域不改变;

-- 全局变量 : 全局变量 加上 static , 相当于将全局变量的作用域缩小到了单个文件, 生命周期是整个程序的周期;

 

关于函数头文件的引申 : 

-- 内部函数 : 单个文件中使用的内部函数, 仅在那个特定文件中定义函数即可;

-- 全局函数 : 如果要在整个工程中使用一个全局函数, 需要将这个函数定义在一个头文件中;

 

static变量与普通变量区别 : 

-- static全局变量 与 全局变量区别 : static 全局变量 只初始化一次, 防止在其它文件中使用;

-- static局部变量 与 局部变量区别 : static 局部变量 只初始化一次, 下一次依据上一次结果;

 

static函数与普通函数区别 : static 函数在内存中只保留一份, 普通函数 每调用一次, 就创建一个副本;

.

 

(6) 堆 和 栈比较

 

 

堆(heap)和栈(stack)区别 : 

-- 申请方式 : stack 由系统自动分配, heap 由程序员进行分配;

-- 申请响应 : 如果 stack 没有足够的剩余空间, 就会溢出; 堆内存从链表中找空闲内存;

-- 内存限制 : stack 内存是连续的, 从高位向低位扩展, 而且很小, 只有几M, 是事先定好的, 在文件中配置; heap 是不连续的, 从低位向高位扩展, 系统是由链表控制空闲程序, 链表从低地址到高地址, 堆大小受虚拟内存限制, 一般32位机器有4G heap;

-- 申请效率 : stack 由系统分配, 效率高; heap 由程序员分配, 速度慢, 容易产生碎片;

 

(7) 各区分布情况

 

.

按照下图分布 : 由上到下顺序 : 栈区(stack) -> 堆区(heap) -> 全局区 -> 字符常量区 -> 代码区;

 

验证分区状况 : 

-- 示例程序 : 

 

 

[cpp] view plaincopy
 
  1. /************************************************************************* 
  2.     > File Name: memory.c 
  3.     > Author: octopus 
  4.     > Mail: octopus_work.163.com  
  5.     > Created Time: Mon 10 Mar 2014 08:34:12 PM CST 
  6.  ************************************************************************/  
  7.   
  8. #include  
  9. #include  
  10.   
  11. int global1 = 0, global2 = 0, global3 = 0;  
  12.   
  13. void function(void)  
  14. {  
  15.         int local4 = 0, local5 = 0, local6 = 0;  
  16.         static int static4 = 0, static5 = 0, static6 = 0;  
  17.         int *p2 = (int*)malloc(sizeof(int));  
  18.   
  19.         printf("子函数 局部变量 :  ");  
  20.         printf("local4 : %p  ", &local4);  
  21.         printf("local5 : %p  ", &local5);  
  22.         printf("local6 : %p  ", &local6);  
  23.   
  24.         printf("子函数 指针变量 :  ");  
  25.         printf("p2 : %p  ", p2);  
  26.   
  27.         printf("全局变量 :  ");  
  28.         printf("global1 : %p  ", &global1);  
  29.         printf("global2 : %p  ", &global2);  
  30.         printf("global3 : %p  ", &global3);  
  31.   
  32.         printf("子函数 静态变量 :  ");  
  33.         printf("static4 : %p  ", &static4);  
  34.         printf("static5 : %p  ", &static5);  
  35.         printf("static6 : %p  ", &static6);  
  36.   
  37.         printf("子函数地址 :  ");  
  38.         printf("function : %p  ", function);  
  39. }  
  40.   
  41. int main(int argc, char **argv)  
  42. {  
  43.         int local1 = 0, local2 = 0, local3 = 0;  
  44.         static int static1 = 0, static2 = 0, static3 = 0;  
  45.         int *p1 = (int*)malloc(sizeof(int));  
  46.         const int const1 = 0;  
  47.         char *char_p = "char";  
  48.   
  49.         printf("主函数 局部变量 :  ");  
  50.         printf("local1 : %p  ", &local1);  
  51.         printf("local2 : %p  ", &local2);  
  52.         printf("local3 : %p  ", &local3);  
  53.         printf("const1 : %p  ", &const1);  
  54.   
  55.         printf("主函数 指针变量 :  ");  
  56.         printf("p1 : %p  ", p1);  
  57.   
  58.         printf("全局变量 :  ");  
  59.         printf("global1 : %p  ", &global1);  
  60.         printf("global2 : %p  ", &global2);  
  61.         printf("global3 : %p  ", &global3);  
  62.   
  63.         printf("主函数 静态变量 :  ");  
  64.         printf("static1 : %p  ", &static1);  
  65.         printf("static2 : %p  ", &static2);  
  66.         printf("static3 : %p  ", &static3);  
  67.   
  68.         printf("字符串常量 :  ");  
  69.         printf("char_p : %p  ", char_p);  
  70.   
  71.         printf("主函数地址 :  ");  
  72.         printf("main : %p  ", main);  
  73.   
  74.   
  75.         printf("= = = = = = = = = = = = = = =  ");  
  76.   
  77.         function();  
  78.   
  79.         return 0;  
  80. }  


-- 执行结果 : 

 

 

[cpp] view plaincopy
 
  1. [root@ip28 pointer]# gcc memory.c   
  2. [root@ip28 pointer]# ./a.out   
  3. 主函数 局部变量 :   
  4. local1 : 0x7fff75f5eedc   
  5. local2 : 0x7fff75f5eed8   
  6. local3 : 0x7fff75f5eed4   
  7. const1 : 0x7fff75f5eed0   
  8. 主函数 指针变量 :   
  9. p1 : 0x19bad010   
  10. 全局变量 :   
  11. global1 : 0x600e14   
  12. global2 : 0x600e18   
  13. global3 : 0x600e1c   
  14. 主函数 静态变量 :   
  15. static1 : 0x600e34   
  16. static2 : 0x600e30   
  17. static3 : 0x600e2c   
  18. 字符串常量 :   
  19. char_p : 0x4009f7   
  20. 主函数地址 :   
  21. main : 0x40065f   
  22. = = = = = = = = = = = = = = =   
  23. 子函数 局部变量 :   
  24. local4 : 0x7fff75f5eea4   
  25. local5 : 0x7fff75f5eea0   
  26. local6 : 0x7fff75f5ee9c   
  27. 子函数 指针变量 :   
  28. p2 : 0x19bad030   
  29. 全局变量 :   
  30. global1 : 0x600e14   
  31. global2 : 0x600e18   
  32. global3 : 0x600e1c   
  33. 子函数 静态变量 :   
  34. static4 : 0x600e28   
  35. static5 : 0x600e24   
  36. static6 : 0x600e20   
  37. 子函数地址 :   
  38. function : 0x400528   

 

3. 指针与地址

 

[page]

(1) & 与 * 操作

 

取地址运算符 & : p = &c;

-- 表达式解析 : 将 c 的地址赋值给 变量 p, p 是指向 c 变量的指针;

-- & 可以使用的情况 : 取地址操作 只能用于内存中的对象, 如变量 或 数组, 栈内存 堆内存 都可以;

-- & 不适用的情况 : 不能用于 表达式, 常量, register类型变量; 

 

间接引用运算符 : * ;

-- 声明指针 : int *p ; 该表达式的含义是 *p 的结果是 int 类型, 声明变量 a, int a, 声明指针 *p , int *p;

-- 获取指针指向的值 : int a = *p ;

 

(2) 指针定义解析

 

声明指针 和 函数 : int *p, max(int a, int b), 声明指针变量 语法 与声明 变量语法类似, 同理声明函数也一样;

-- 原理 : *p 和 max()返回值 类型都是 int 类型;

 

指针指向 : 每个指针都必须指向某种特定类型;

-- 例外 : void *p 可以指向任何类型, 但是 p 不能进行取值运算, *p 是错误的, 因为不知道 p 指向的数据类型;

 

(3) 指针运算及示例

 

指针相关运算 : int x = 0; int *p = &x; 那么*p 就可以代表x;

-- 算数运算 : x = x + 1; 等价于 *p = *p + 1 ; int y = x + 1; 等价于 int y = *p + 1;

-- 自增运算 : 前提 : ++, * 运算顺序是自右向左;  ++*p 和 (*p)++, p 指向的值自增1, 注意要加上括号, 否则会将地址自增;

-- 指针赋值 : int *p, *q; int a = 0; p = &a; q = p; 最终结果 p 和 q 都指向了 变量 a;

 

示例程序 : 

 

 

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /************************************************************************* 
  2.     > File Name: pointer_address.c 
  3.     > Author: octopus 
  4.     > Mail: octopus_work.163.com  
  5.     > Created Time: Mon 10 Mar 2014 09:52:01 PM CST 
  6.  ************************************************************************/  
  7.   
  8. #include  
  9.   
  10. int main(int argc, char ** argv)  
  11. {  
  12.         int *p, *q;  
  13.         int a = 10, b;  
  14.   
  15.         //p指针指向a变量  
  16.         p = &a;  
  17.   
  18.         //*p 可以代替 a 进行运算  
  19.         ++*p;  
  20.   
  21.         b = *p + 5;  
  22.   
  23.         //指针之间可以直接相互赋值  
  24.         q = p;  
  25.   
  26.         //打印 p 和 q 指针指向的值  
  27.         printf("*p = %d  ", *p);  
  28.         printf("*q = %d  ", *q);  
  29.   
  30.   
  31.         return 0;  
  32. }  


执行结果 :

 

 

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. [root@ip28 pointer]# gcc pointer_address.c   
  2. [root@ip28 pointer]# ./a.out   
  3. *p = 11   
  4. *q = 11   

 

4. 函数参数的传值调用和传址调用

 

(1) 传值调用 和 传址调用

 

传值调用 : 以传值的方式将参数传递给函数, 不能直接修改主函数中变量的值, 仅仅是将副本传递给了函数;

 

传址调用 : 将 变量的指针 传递给函数, 当函数对指针进行操作的时候, 主函数中的值也进行了对应变化;

 

交换函数示例1 : 

 

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /************************************************************************* 
  2.     > File Name: swap.c 
  3.     > Author: octopus 
  4.     > Mail: octopus_work.163.com  
  5.     > Created Time: Mon 10 Mar 2014 11:07:18 PM CST 
  6.  ************************************************************************/  
  7.   
  8. #include  
  9.   
  10. void swap_1(int a, int b)  
  11. {  
  12.         int temp;  
  13.         temp = a;  
  14.         a = b;  
  15.         b = temp;  
  16.   
  17.         printf("swap_1 传值 函数 a = %d, b = %d  ", a, b);  
  18. }  
  19.   
  20. void swap_2(int *a, int *b)  
  21. {  
  22.         int temp;  
  23.         temp = *a;  
  24.         *a = *b;  
  25.         *b = temp;  
  26.   
  27.         printf("swap_2 传址 函数 a = %d, b = %d ", *a, *b);  
  28. }  
  29.   
  30. int main(int argc, char **argv)  
  31. {  
  32.         int a = 10, b = 5;  
  33.   
  34.         printf("初始值 : a = %d, b = %d  ", a, b);  
  35.   
  36.         swap_1(a, b);  
  37.         printf("执行 swap_1 函数, a = %d, b = %d  ", a, b);  
  38.   
  39.         swap_2(&a, &b);  
  40.         printf("执行 swap_2 函数, a = %d, b = %d  ", a, b);  
  41.   
  42.   
  43.         return 0;  
  44. }  


 

执行结果 : 

 

 

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. [root@ip28 pointer]# gcc swap.c   
  2. [root@ip28 pointer]# ./a.out   
  3. 初始值 : a = 10, b = 5   
  4.   
  5. swap_1 传值 函数 a = 5, b = 10   
  6. 执行 swap_1 函数, a = 10, b = 5   
  7.   
  8. swap_2 传址 函数 a = 5, b = 10  
  9. 执行 swap_2 函数, a = 5, b = 10   



 

示例解析 : 

-- 传值调用 : swap_1 是传值调用, 传入的是 main 函数中的 a b 两个变量的副本, 因此函数执行完毕后, 主函数中的值是不变的;

-- 传址调用 : swap_2 是传址调用, 传入的是 a , b 两个变量的地址 &a, &b, 当在swap_2 中进行修改的时候, 主函数中的 a,b变量也会发生改变;

 

 

(2) 高级示例

 

需求分析 : 调用getint()函数, 将输入的数字字符 转为一个整形数据;

 

getch 和 ungetch 函数 : 

-- 使用场景 : 当进行输入的时候, 不能确定是否已经输入足够的字符, 需要读取下一个字符, 进行判断, 如果多读取了一个字符, 就需要将这个字符退回去;

-- 使用效果 : getch() 和 ungetch() 分别是预读下一个字符, 和 将预读的字符退回去, 这样对于其它代码而言, 没有任何影响;

 

注意的问题 : 出现问题, 暂时编译不通过, 找个C语言大神解决;

 

代码 : 

 

 

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /************************************************************************* 
  2.     > File Name: getint.c 
  3.     > Author: octopus 
  4.     > Mail: octopus_work.163.com  
  5.     > Created Time: Mon 10 Mar 2014 11:40:19 PM CST 
  6.  ************************************************************************/  
  7. #include   
  8. #include   
  9. #include   
  10. #define SIZE 5  
  11.   
  12. int getint(int *p)  
  13. {  
  14.     //sign 是用来控制数字的正负  
  15.     int c, sign;  
  16.   
  17.     //跳过空白字符, 如果是空白字符, 就会进行下一次循环, 直到不是空白字符为止  
  18.   
  19.     while(isspace(c = getc(stdin)));  
  20.   
  21.     //如果输入的字符不是数字, 就将预读的数据退回到标准输入流中  
  22.     if(!isdigit(c) && c != EOF && c != '+' && c != '-')  
  23.     {  
  24.         ungetc(c, stdin);  
  25.         return 0;  
  26.     }  
  27.   
  28.     /* 
  29.      * 如果预读的是减号, 那么sign 标识就是 -1,  
  30.      * 如果预读的是加号, 那么sign 标识就是 1; 
  31.      */  
  32.     sign = (c == '-') ? -1 : 1;  
  33.     //如果 c 是 加号 或者 减号, 再预读一个字符&  
  34.     if(c == '+' || c == '-')  
  35.         c = getc(stdin);  
  36.   
  37.     for(*p = 0; isdigit(c); c = getc(stdin))  
  38.         *p = 10 * *p + (c - '0');  
  39.   
  40.     *p *= sign;  
  41.   
  42.     if(c != EOF)  
  43.         ungetc(c, stdin);  
  44.   
  45.     return c;  
  46.       
  47. }  
  48.   
  49. int main(int argc, char **argv)  
  50. {  
  51.     int n, array[SIZE], i;   
  52.     for(n = 0; n < SIZE && getint(&array[n]) != EOF; n++);  
  53.   
  54.     for(i = 0; i < SIZE; i++)  
  55.     {  
  56.         printf("array[%d] = %d  ", i, array[i]);  
  57.     }  
  58.     return 0;  
  59. }  

 

 

执行结果 : 

 

 

[plain] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. octopus@octopus-Vostro-270s:~/code/c/pointer$ ./a.out   
  2. 123  
  3. 123 43  
  4. 674 1  
  5. array[0] = 123   
  6. array[1] = 123   
  7. array[2] = 43   
  8. array[3] = 674   
  9. array[4] = 1   

 

 

 

5. 指针 和 数组

 

 

指针数组比较 : 

-- 可互相替代 : 数组下标执行的操作都可以使用指针替代;

-- 效率比较 : 使用指针操作效率比数组要高;

 

指针 与 数组初始化 : 

-- 声明数组 : int a[10]; 定义一个长度为10 的int数组;

-- 声明指针 : int *p; 定义一个指针, 该指针指向整型;

-- 相互赋值 : p = &a[0], 将数组第一个元素的地址赋值给指针变量;

-- 使用指针获取数组对象 : *p 等价于 a[0], *(p + 1) 等价于 a[1], *(p + i)等价于 a[i];

-- 注意地址的运算 : p + i , 在地址运算上, 每次增加 sizeof(int) * i 个字节;

 

将数组赋值给指针的途径 : 

-- 将数组第一个元素地址赋值给指针变量 : p = &a[0];

-- 将数组地址赋值给指针变量 : p = a;

 

指针 和 数组 访问方式互换 : 前提 int *p, a[10]; p = a;

-- 数组计算方式 : 计算a[i]的时候, 先将数组转化为 *(a + i)指针, 然后计算该指针值;

-- 取值等价 : a[i] 等价于 *(p + i);

-- 地址等价 : &a[i] 与 a + i 是等价的;

-- 指针下标访问 : p[i] 等价于 *(p + i);

-- 结论 : 通过数组和下标 实现的操作 都可以使用 指针和偏移量进行等价替换;

 

指针 和 数组 的不同点 : 

-- 指针是变量 : int *p, a[10]; p = a 和 p++ 没有错误;

-- 数组名不是变量 : int *p, a[10]; a = p 和 a++ 会报错;

 

数组参数 : 

-- 形参指针 : 将数组传作为参数传递给函数的时候, 传递的是数组的首地址, 传递地址, 形参是指针;

 

数组参数示例 : 

-- 函数参数是数组 : 函数传入一个字符串数组参数, 返回这个字符串长度;

 

 

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /************************************************************************* 
  2.     > File Name: array_param.c 
  3.     > Author: octopus 
  4.     > Mail: octopus_work.163.com  
  5.     > Created Time: Sat 15 Mar 2014 12:46:57 AM CST 
  6.  ************************************************************************/  
  7.   
  8. #include  
  9.   
  10. //计算字符串长度  
  11. int strlen(char *s)  
  12. {  
  13.         int n;  
  14.         for(n = 0; *s != ''; s++)  
  15.                 n++;  
  16.         return n;  
  17. }  
  18.   
  19. int main(int argc, char** argv)  
  20. {  
  21.         printf("strlen(djdhaj) = %d  ", strlen("djdhaj"));  
  22.         printf("strlen(12) = %d  ", strlen("12"));  
  23.         printf("strlen(dfe) = %d  ", strlen("dfe"));  
  24. }  


-- 执行结果 : warning: conflicting types for built-in function ‘strlen’, 原因是 C语言中已经有了 strlen 函数了, 如果改一个函数名, 就不会有这个警告了;

 

 

[plain] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. [root@ip28 pointer]# gcc array_param.c   
  2. array_param.c:12: warning: conflicting types for built-in function ‘strlen’  
  3. [root@ip28 pointer]# ./a.out             
  4. strlen(djdhaj) = 6   
  5. strlen(12) = 2   
  6. strlen(dfe) = 3   



 

数组和指针参数 : 将数组名传给参数, 函数根据情况判断是作为数组还是作为指针;

-- 实参 : 指针偏移量 和 数组下标 都可以作为 数组或指针函数形参, 如 数组情况fun(&array[2]) 或者 指针情况fun(p + 2);

-- 形参 : 函数的形参可以声明为 fun(int array[]), 或者 fun(int *array), 如果传入的是数组的第二个元素的地址, 可以使用array[-2]来获数组取第一个元素;

 

数组指针参数示例 : 

 

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /************************************************************************* 
  2.     > File Name: param_array_pointer.c 
  3.     > Author: octopus 
  4.     > Mail: octopus_work.163.com  
  5.     > Created Time: Sat 15 Mar 2014 01:28:33 AM CST 
  6.  ************************************************************************/  
  7.   
  8. #include  
  9.   
  10. //使用指针做形参 取指针的前两位 和 当前位  
  11. void fun_p(int *p)  
  12. {  
  13.         printf("*(p - 2) = %d  ", *(p - 2));  
  14.         printf("*p = %d  ", *p);  
  15. }  
  16.   
  17. //使用数组做形参 取数组的 第-2个元素 和 第0个元素  
  18. void fun_a(int p[])  
  19. {  
  20.         printf("p[-2] = %d  ", p[-2]);  
  21.         printf("p[0] = %d  ", p[0]);  
  22. }  
  23.   
  24. int main(int argc, char **argv)  
  25. {  
  26.         int array[] = {1,2,3,4,5};  
  27.         //向指针参数函数中传入指针  
  28.         printf("fun_p(array + 2) :  ");  
  29.         fun_p(array + 2);  
  30.   
  31.         //向数组参数函数中传入数组元素地址  
  32.         printf("fun_a(&array[2]) :  ");  
  33.         fun_a(&array[2]);  
  34.   
  35.         //向指针参数函数中传入数组元素地址  
  36.         printf("fun_p(&array[2]) :  ");  
  37.         fun_p(&array[2]);  
  38.   
  39.         //向数组参数函数中传入指针  
  40.         printf("fun_a(array + 2) :  ");  
  41.         fun_a(array + 2);  
  42.         return 0;  
  43. }  


执行效果 : 

[plain] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. [root@ip28 pointer]# gcc param_array_pointer.c   
  2. [root@ip28 pointer]# ./a.out   
  3. fun_p(array + 2) :   
  4. *(p - 2) = 1   
  5. *p = 3   
  6. fun_a(&array[2]) :   
  7. p[-2] = 1   
  8. p[0] = 3   
  9. fun_p(&array[2]) :   
  10. *(p - 2) = 1   
  11. *p = 3   
  12. fun_a(array + 2) :   
  13. p[-2] = 1   
  14. p[0] = 3   


 http://blog.csdn.net/shulianghan/article/details/20472269

关键字:C语言  程序内存  分配 引用地址:C语言程序内存分配

上一篇:如何将将字库或图片以数组的形式储存在.c 文件中
下一篇:c语言定义结构体指针

推荐阅读最新更新时间:2024-03-16 14:32

小容量单片机系统的C语言程序结构
引 言:   2002年初,笔者着手写一个IC卡预付费电表的工作程序,该电表使用Philips公司的8位51扩展型单片机87LPC764,要求实现很多功能,包括熄显示、负荷计算与控制、指示闪烁以及电表各种参数的查询等,总之,要使用时间的单元很多。笔者当时使用ASM51完成了这个程序的编写,完成后的程序量是2KB多一点。后来,由于种种原因,这个程序并没有真正使用,只是作了一些改动之后用在一个老化设备上进行计时与负荷计算。约一年后,笔者又重新改写了这些代码。 1 系统的改进   可以说,这个用ASM51实现的代码是没有什么组织性可言的,要什么功能就加入什么功能,弄得程序的结构非常松散,其实这也是导致笔者最终决定重新改写这些代
[应用]
msp430的一些常用的C语言控制程序---ZLG7289
/**********************************************************/ //MSP430高级实验开发组件 - ZLG7289键盘显示组件示例程序 //时钟设置: ////ACLK=N/A,MCLK=SMCLK=default(DCO~800k) //硬件连接: //// MSP430 Broad ZLG7289 Broad //// ------------------ ------------------ //// | VCC | ------ | VCC | //// | P65 | ------ | CS
[单片机]
单片机常用的14个C语言算法,看过的都成了大神!
算法(Algorithm): 计算机解题的基本思想方法和步骤。 算法的描述: 是对要解决一个问题或要完成一项任务所采取的方法和步骤的描述,包括需要什么数据(输入什么数据、输出什么结果)、采用什么结构、使用什么语句以及如何安排这些语句等。通常使用自然语言、结构化流程图、伪代码等来描述算法。 一、计数、求和、求阶乘等简单算法 此类问题都要使用循环,要注意根据问题确定循环变量的初值、终值或结束条件,更要注意用来表示计数、和、阶乘的变量的初值。 例:用随机函数产生100个 范围内的随机整数,统计个位上的数字分别为1,2,3,4,5,6,7,8,9,0的数的个数并打印出来。 本题使用数组来处理,用数组a 存放产生的确100个随机整数,
[单片机]
单片机常用的14个<font color='red'>C语言</font>算法,看过的都成了大神!
51单片机C语言教程(二) 认识51单片机
上一课我们的第一个项目完成了,可能有懂C语言的朋友会说, 这和PC机上的C语言没有多大的区别呀 。的确没有太大的区别,C语言只是一种程序语言的统称,针对不同的处理器相关的C语言都会有一些细节的改变。编写PC机的C程序时,如要对硬件编程你就必须对硬件要有一定的认识,51 单片机 编程就更是如此,因它的开发应用是不可与硬件脱节的,所以我们先要来初步认识一下51苾片的结构和引脚功能。MSC51架构的芯片种类很多,具体特点和功能不尽相同(在以后编写的附录中会加入常用的一些51芯片的资料列表),在此后的教程中就以Atmel公司的AT89C51和AT89C2051为中心对象来进行学习,两者是AT89系列的典型代表,在爱好者中使用相当的多,应用
[单片机]
51单片机<font color='red'>C语言</font>教程(二) 认识51单片机
Ubuntu、stm32下的C程序中堆、栈、全局、局部等变量的分配地址
一、内存分配 栈区(stack):指那些由编译器在需要的时候分配,不需要时自动清除的变量所在的储存区,如函数执行时,函数的形参以及函数内的局部变量分配在栈区,函数运行结束后,形参和局部变量去栈(自动释放)。栈内存分配运算内置与处理器的指令集中,效率高但是分配的内存空间有限。 堆区(heap):指哪些由程序员手动分配释放的储存区,如果程序员不释放这块内存,内存将一直被占用,直到程序运行结束由系统自动收回,c语言中使用malloc,free申请和释放空间。 静态储存区(static):全局变量和静态变量的储存是放在一块的,其中初始化的全局变量和静态变量在一个区域,这块空间当程序运行结束后由系统释放。 常量储存区(const
[单片机]
Ubuntu、stm32下的C<font color='red'>程序</font>中堆、栈、全局、局部等变量的<font color='red'>分配</font>地址
51单片机C51程序
C51程序 学习C51程序之前需要对C语言有一定的了解和学习;这里只介绍与C语言有区别的地方; 具体的不同如下: 1、C51中增加了几种针对MSC-51单片机的特有数据类型 a、sfr和sfr16:用于定义特殊功能寄存器,如:sfr PSW = 0xd0(一般情况下这些特殊功能寄存器不需要用户自定义,通常包含在 reg51.h 头文件中) b、bit和sbit: 定义位变量,只占用一个二进制位;bit是位寻址区中的变量(20H~2FH,关于位寻址区详见51单片机之存储器篇);sbit指特殊功能寄存器中的可以位寻址的变量 ,如:sbit P1_0 = P1^0 sbit OV = 0xd2 2、C51的存储模式与存储器密
[单片机]
51单片机C51<font color='red'>程序</font>
AVR单片机的C语言开发简介
Avr单片机和其他单片机一样,有自己的指令和寄存器,特定的IO口操作方法,有定时器,AD转换,串行口,PWM输出,EEPROM等。为了缩短大家学习的时间,快速的了解avr编程思路,拟定avr程序设计系列教程。我使用avr也不是很长时间,技术有限,错误再所难免,还请指出,以期改正。 【说明】:本页是针对从来没有碰过单片机或者用过单片机而没有用c语言的朋友,如果你已经用过其他单片机,此页说明可能对你没有很大帮助,且显得有些幼稚,请你跳过。 本站所有程序都是在icc+avrstudio下进行的,如果你没有配置好环境 采用icc的原因之一是它自带一个非常好用的application builder,生成的代码结构清晰,层次清楚,特别
[单片机]
AVR单片机的<font color='red'>C语言</font>开发简介
单片机程序存储器 ROM
  英文简称ROM。ROM所存数据,一般是装入整机前事先写好的,整机工作过程中只能读出,而不像随机存储器那样能快速地、方便地加以改写。ROM所存数据稳定 ,断电后所存数据也不会改变;其结构较简单,读出较方便,因而常用于存储各种固定程序和数据。除少数品种的只读存储器(如字符发生器)可以通用之外,不同用户所需只读存储器的内容不同。为便于使 用和大批 量 生产 ,进一步发展了可编程只读存储器(PROM)、可擦可编程序只读存储器(EPROM)和电可擦可编程只读存储器   ROM   (EEPROM)。例如早期的个人电脑如Apple II或IBM PC XT/AT的开机程序(操作系统)或是其他各种微电脑系统中的轫体(Firmware)。
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved