1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > C语言 内存分配 地址 指针 数组 参数 解析

C语言 内存分配 地址 指针 数组 参数 解析

时间:2019-08-27 02:15:30

相关推荐

C语言 内存分配 地址 指针 数组 参数  解析

指针简介: 指针式保存变量地址的变量;

--增加阅读难度: 指针 和 goto 语句会增加程序的理解难度, 容易出现错误;

--ANSI C: American National Standards Institute 美国国家标准学会, 即标准C;

--通用指针类型: ANSI C中使用 void* 作为通用指针类型, 即指向void的指针, void 是空类型,void* 是空类型指针, 可以指向任意类型的地址;

1. void 与 void*

(1) void 简介

void 作用:

--限定参数: 函数没有返回值, 需要使用 void 声明, 否则默认返回 int 类型;

--限定返回值: 函数不接收参数, 使用 void 作为参数, 如果传入参数, 编译器就会报错;

使用void注意点:

--void不能表示变量: void a, 这样定义是错误的;

--默认返回值: C 中, 如果没有标明返回值类型, 默认的返回值不是 void, 是 int 类型;

--void参数: C 语言中参数是void, 传入参数不会出错, C++中传入参数会出错, 因此这里我们统一规定, 如果函数没有参数, 就定义为void;

.

(2) void*简介

void * 作用:

--通用数据类型: void * 指针可以存放任意类型数据的地址, 任何数据类型的指针都可以赋值给 void * 通用类型指针;

--任意类型: 如果 函数 的 参数 和 返回值 可以是任意类型, 就可以使用 void * 作为函数的 参数 或者 返回值;

使用void* 注意点:

--void * 与 其它类型互相赋值: int * 变量可以赋值给 void * 变量, 但是void * 变量如果赋值给 int * 变量需要强转为 int * 类型;

--void * 不允许进行 算数操作: 标准C 中规定 void * 类型不允许进行 加减乘除 算数运算, 因为我们不知道这个类型的大小, GUN 中void * 等价于 char * ;

2. 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 plain copy print? /*************************************************************************>FileName:memory.c>Author:octopus>Mail:>CreatedTime:Mon10Mar08:34:12PMCST************************************************************************/#include<stdio.h>#include<stdlib.h>intglobal1=0,global2=0,global3=0;voidfunction(void){intlocal4=0,local5=0,local6=0;staticintstatic4=0,static5=0,static6=0;int*p2=(int*)malloc(sizeof(int));printf("子函数局部变量:\n");printf("local4:%p\n",&local4);printf("local5:%p\n",&local5);printf("local6:%p\n",&local6);printf("子函数指针变量:\n");printf("p2:%p\n",p2);printf("全局变量:\n");printf("global1:%p\n",&global1);printf("global2:%p\n",&global2);printf("global3:%p\n",&global3);printf("子函数静态变量:\n");printf("static4:%p\n",&static4);printf("static5:%p\n",&static5);printf("static6:%p\n",&static6);printf("子函数地址:\n");printf("function:%p\n",function);}intmain(intargc,char**argv){intlocal1=0,local2=0,local3=0;staticintstatic1=0,static2=0,static3=0;int*p1=(int*)malloc(sizeof(int));constintconst1=0;char*char_p="char";printf("主函数局部变量:\n");printf("local1:%p\n",&local1);printf("local2:%p\n",&local2);printf("local3:%p\n",&local3);printf("const1:%p\n",&const1);printf("主函数指针变量:\n");printf("p1:%p\n",p1);printf("全局变量:\n");printf("global1:%p\n",&global1);printf("global2:%p\n",&global2);printf("global3:%p\n",&global3);printf("主函数静态变量:\n");printf("static1:%p\n",&static1);printf("static2:%p\n",&static2);printf("static3:%p\n",&static3);printf("字符串常量:\n");printf("char_p:%p\n",char_p);printf("主函数地址:\n");printf("main:%p\n",main);printf("===============\n");function();return0;}

/*************************************************************************> File Name: memory.c> Author: octopus> Mail: > Created Time: Mon 10 Mar 08:34:12 PM CST************************************************************************/#include<stdio.h>#include<stdlib.h>int global1 = 0, global2 = 0, global3 = 0;void function(void){int local4 = 0, local5 = 0, local6 = 0;static int static4 = 0, static5 = 0, static6 = 0;int *p2 = (int*)malloc(sizeof(int));printf("子函数 局部变量 : \n");printf("local4 : %p \n", &local4);printf("local5 : %p \n", &local5);printf("local6 : %p \n", &local6);printf("子函数 指针变量 : \n");printf("p2 : %p \n", p2);printf("全局变量 : \n");printf("global1 : %p \n", &global1);printf("global2 : %p \n", &global2);printf("global3 : %p \n", &global3);printf("子函数 静态变量 : \n");printf("static4 : %p \n", &static4);printf("static5 : %p \n", &static5);printf("static6 : %p \n", &static6);printf("子函数地址 : \n");printf("function : %p \n", function);}int main(int argc, char **argv){int local1 = 0, local2 = 0, local3 = 0;static int static1 = 0, static2 = 0, static3 = 0;int *p1 = (int*)malloc(sizeof(int));const int const1 = 0;char *char_p = "char";printf("主函数 局部变量 : \n");printf("local1 : %p \n", &local1);printf("local2 : %p \n", &local2);printf("local3 : %p \n", &local3);printf("const1 : %p \n", &const1);printf("主函数 指针变量 : \n");printf("p1 : %p \n", p1);printf("全局变量 : \n");printf("global1 : %p \n", &global1);printf("global2 : %p \n", &global2);printf("global3 : %p \n", &global3);printf("主函数 静态变量 : \n");printf("static1 : %p \n", &static1);printf("static2 : %p \n", &static2);printf("static3 : %p \n", &static3);printf("字符串常量 : \n");printf("char_p : %p \n", char_p);printf("主函数地址 : \n");printf("main : %p \n", main);printf("= = = = = = = = = = = = = = = \n");function();return 0;}

--执行结果:[cpp]view plain copy print? [root@ip28pointer]#gccmemory.c[root@ip28pointer]#./a.out主函数局部变量:local1:0x7fff75f5eedclocal2:0x7fff75f5eed8local3:0x7fff75f5eed4const1:0x7fff75f5eed0主函数指针变量:p1:0x19bad010全局变量:global1:0x600e14global2:0x600e18global3:0x600e1c主函数静态变量:static1:0x600e34static2:0x600e30static3:0x600e2c字符串常量:char_p:0x4009f7主函数地址:main:0x40065f===============子函数局部变量:local4:0x7fff75f5eea4local5:0x7fff75f5eea0local6:0x7fff75f5ee9c子函数指针变量:p2:0x19bad030全局变量:global1:0x600e14global2:0x600e18global3:0x600e1c子函数静态变量:static4:0x600e28static5:0x600e24static6:0x600e20子函数地址:function:0x400528

[root@ip28 pointer]# gcc memory.c [root@ip28 pointer]# ./a.out 主函数 局部变量 : local1 : 0x7fff75f5eedc local2 : 0x7fff75f5eed8 local3 : 0x7fff75f5eed4 const1 : 0x7fff75f5eed0 主函数 指针变量 : p1 : 0x19bad010 全局变量 : global1 : 0x600e14 global2 : 0x600e18 global3 : 0x600e1c 主函数 静态变量 : static1 : 0x600e34 static2 : 0x600e30 static3 : 0x600e2c 字符串常量 : char_p : 0x4009f7 主函数地址 : main : 0x40065f = = = = = = = = = = = = = = = 子函数 局部变量 : local4 : 0x7fff75f5eea4 local5 : 0x7fff75f5eea0 local6 : 0x7fff75f5ee9c 子函数 指针变量 : p2 : 0x19bad030 全局变量 : global1 : 0x600e14 global2 : 0x600e18 global3 : 0x600e1c 子函数 静态变量 : static4 : 0x600e28 static5 : 0x600e24 static6 : 0x600e20 子函数地址 : function : 0x400528

3. 指针与地址

(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 plain copy print? /*************************************************************************>FileName:pointer_address.c>Author:octopus>Mail:>CreatedTime:Mon10Mar09:52:01PMCST************************************************************************/#include<stdio.h>intmain(intargc,char**argv){int*p,*q;inta=10,b;//p指针指向a变量p=&a;//*p可以代替a进行运算++*p;b=*p+5;//指针之间可以直接相互赋值q=p;//打印p和q指针指向的值printf("*p=%d\n",*p);printf("*q=%d\n",*q);return0;}

/*************************************************************************> File Name: pointer_address.c> Author: octopus> Mail: > Created Time: Mon 10 Mar 09:52:01 PM CST************************************************************************/#include<stdio.h>int main(int argc, char ** argv){int *p, *q;int a = 10, b;//p指针指向a变量p = &a;//*p 可以代替 a 进行运算++*p;b = *p + 5;//指针之间可以直接相互赋值q = p;//打印 p 和 q 指针指向的值printf("*p = %d \n", *p);printf("*q = %d \n", *q);return 0;}

执行结果:[cpp]view plain copy print? [root@ip28pointer]#gccpointer_address.c[root@ip28pointer]#./a.out*p=11*q=11

[root@ip28 pointer]# gcc pointer_address.c [root@ip28 pointer]# ./a.out *p = 11 *q = 11

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

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

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

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

交换函数示例1:

[cpp]view plain copy print? /*************************************************************************>FileName:swap.c>Author:octopus>Mail:>CreatedTime:Mon10Mar11:07:18PMCST************************************************************************/#include<stdio.h>voidswap_1(inta,intb){inttemp;temp=a;a=b;b=temp;printf("swap_1传值函数a=%d,b=%d\n",a,b);}voidswap_2(int*a,int*b){inttemp;temp=*a;*a=*b;*b=temp;printf("swap_2传址函数a=%d,b=%d\n",*a,*b);}intmain(intargc,char**argv){inta=10,b=5;printf("初始值:a=%d,b=%d\n\n",a,b);swap_1(a,b);printf("执行swap_1函数,a=%d,b=%d\n\n",a,b);swap_2(&a,&b);printf("执行swap_2函数,a=%d,b=%d\n",a,b);return0;}

/*************************************************************************> File Name: swap.c> Author: octopus> Mail: > Created Time: Mon 10 Mar 11:07:18 PM CST************************************************************************/#include<stdio.h>void swap_1(int a, int b){int temp;temp = a;a = b;b = temp;printf("swap_1 传值 函数 a = %d, b = %d \n", a, b);}void swap_2(int *a, int *b){int temp;temp = *a;*a = *b;*b = temp;printf("swap_2 传址 函数 a = %d, b = %d\n", *a, *b);}int main(int argc, char **argv){int a = 10, b = 5;printf("初始值 : a = %d, b = %d \n\n", a, b);swap_1(a, b);printf("执行 swap_1 函数, a = %d, b = %d \n\n", a, b);swap_2(&a, &b);printf("执行 swap_2 函数, a = %d, b = %d \n", a, b);return 0;}

执行结果:

[cpp]view plain copy print? [root@ip28pointer]#gccswap.c[root@ip28pointer]#./a.out初始值:a=10,b=5swap_1传值函数a=5,b=10执行swap_1函数,a=10,b=5swap_2传址函数a=5,b=10执行swap_2函数,a=5,b=10

[root@ip28 pointer]# gcc swap.c [root@ip28 pointer]# ./a.out 初始值 : a = 10, b = 5 swap_1 传值 函数 a = 5, b = 10 执行 swap_1 函数, a = 10, b = 5 swap_2 传址 函数 a = 5, b = 10执行 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 plain copy print? /*************************************************************************>FileName:getint.c>Author:octopus>Mail:>CreatedTime:Mon10Mar11:40:19PMCST************************************************************************/#include<stdio.h>#include<stdlib.h>#include<ctype.h>#defineSIZE5intgetint(int*p){//sign是用来控制数字的正负intc,sign;//跳过空白字符,如果是空白字符,就会进行下一次循环,直到不是空白字符为止while(isspace(c=getc(stdin)));//如果输入的字符不是数字,就将预读的数据退回到标准输入流中if(!isdigit(c)&&c!=EOF&&c!='+'&&c!='-'){ungetc(c,stdin);return0;}/**如果预读的是减号,那么sign标识就是-1,*如果预读的是加号,那么sign标识就是1;*/sign=(c=='-')?-1:1;//如果c是加号或者减号,再预读一个字符&if(c=='+'||c=='-')c=getc(stdin);for(*p=0;isdigit(c);c=getc(stdin))*p=10**p+(c-'0');*p*=sign;if(c!=EOF)ungetc(c,stdin);returnc;}intmain(intargc,char**argv){intn,array[SIZE],i;for(n=0;n<SIZE&&getint(&array[n])!=EOF;n++);for(i=0;i<SIZE;i++){printf("array[%d]=%d\n",i,array[i]);}return0;}

/*************************************************************************> File Name: getint.c> Author: octopus> Mail: > Created Time: Mon 10 Mar 11:40:19 PM CST************************************************************************/#include <stdio.h>#include <stdlib.h>#include <ctype.h>#define SIZE 5int getint(int *p){//sign 是用来控制数字的正负int c, sign;//跳过空白字符, 如果是空白字符, 就会进行下一次循环, 直到不是空白字符为止while(isspace(c = getc(stdin)));//如果输入的字符不是数字, 就将预读的数据退回到标准输入流中if(!isdigit(c) && c != EOF && c != '+' && c != '-'){ungetc(c, stdin);return 0;}/** 如果预读的是减号, 那么sign 标识就是 -1, * 如果预读的是加号, 那么sign 标识就是 1;*/sign = (c == '-') ? -1 : 1;//如果 c 是 加号 或者 减号, 再预读一个字符&if(c == '+' || c == '-')c = getc(stdin);for(*p = 0; isdigit(c); c = getc(stdin))*p = 10 * *p + (c - '0');*p *= sign;if(c != EOF)ungetc(c, stdin);return c;}int main(int argc, char **argv){int n, array[SIZE], i; for(n = 0; n < SIZE && getint(&array[n]) != EOF; n++);for(i = 0; i < SIZE; i++){printf("array[%d] = %d \n", i, array[i]);}return 0;}

执行结果:

[plain]view plain copy print? octopus@octopus-Vostro-270s:~/code/c/pointer$./a.out123123436741array[0]=123array[1]=123array[2]=43array[3]=674array[4]=1

octopus@octopus-Vostro-270s:~/code/c/pointer$ ./a.out 123123 43674 1array[0] = 123 array[1] = 123 array[2] = 43 array[3] = 674 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 plain copy print? /*************************************************************************>FileName:array_param.c>Author:octopus>Mail:>CreatedTime:Sat15Mar12:46:57AMCST************************************************************************/#include<stdio.h>//计算字符串长度intstrlen(char*s){intn;for(n=0;*s!='\0';s++)n++;returnn;}intmain(intargc,char**argv){printf("strlen(djdhaj)=%d\n",strlen("djdhaj"));printf("strlen(12)=%d\n",strlen("12"));printf("strlen(dfe)=%d\n",strlen("dfe"));}

/*************************************************************************> File Name: array_param.c> Author: octopus> Mail: > Created Time: Sat 15 Mar 12:46:57 AM CST************************************************************************/#include<stdio.h>//计算字符串长度int strlen(char *s){int n;for(n = 0; *s != '\0'; s++)n++;return n;}int main(int argc, char** argv){printf("strlen(djdhaj) = %d \n", strlen("djdhaj"));printf("strlen(12) = %d \n", strlen("12"));printf("strlen(dfe) = %d \n", strlen("dfe"));}

--执行结果:warning: conflicting types for built-in function ‘strlen’, 原因是 C语言中已经有了 strlen 函数了, 如果改一个函数名, 就不会有这个警告了;[plain]view plain copy print? [root@ip28pointer]#gccarray_param.carray_param.c:12:warning:conflictingtypesforbuilt-infunction‘strlen’[root@ip28pointer]#./a.outstrlen(djdhaj)=6strlen(12)=2strlen(dfe)=3

[root@ip28 pointer]# gcc array_param.c array_param.c:12: warning: conflicting types for built-in function ‘strlen’[root@ip28 pointer]# ./a.out strlen(djdhaj) = 6 strlen(12) = 2 strlen(dfe) = 3

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

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

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

数组指针参数示例:

[cpp]view plain copy print? /*************************************************************************>FileName:param_array_pointer.c>Author:octopus>Mail:>CreatedTime:Sat15Mar01:28:33AMCST************************************************************************/#include<stdio.h>//使用指针做形参取指针的前两位和当前位voidfun_p(int*p){printf("*(p-2)=%d\n",*(p-2));printf("*p=%d\n",*p);}//使用数组做形参取数组的第-2个元素和第0个元素voidfun_a(intp[]){printf("p[-2]=%d\n",p[-2]);printf("p[0]=%d\n",p[0]);}intmain(intargc,char**argv){intarray[]={1,2,3,4,5};//向指针参数函数中传入指针printf("fun_p(array+2):\n");fun_p(array+2);//向数组参数函数中传入数组元素地址printf("fun_a(&array[2]):\n");fun_a(&array[2]);//向指针参数函数中传入数组元素地址printf("fun_p(&array[2]):\n");fun_p(&array[2]);//向数组参数函数中传入指针printf("fun_a(array+2):\n");fun_a(array+2);return0;}

/*************************************************************************> File Name: param_array_pointer.c> Author: octopus> Mail: > Created Time: Sat 15 Mar 01:28:33 AM CST************************************************************************/#include<stdio.h>//使用指针做形参 取指针的前两位 和 当前位void fun_p(int *p){printf("*(p - 2) = %d \n", *(p - 2));printf("*p = %d \n", *p);}//使用数组做形参 取数组的 第-2个元素 和 第0个元素void fun_a(int p[]){printf("p[-2] = %d \n", p[-2]);printf("p[0] = %d \n", p[0]);}int main(int argc, char **argv){int array[] = {1,2,3,4,5};//向指针参数函数中传入指针printf("fun_p(array + 2) : \n");fun_p(array + 2);//向数组参数函数中传入数组元素地址printf("fun_a(&array[2]) : \n");fun_a(&array[2]);//向指针参数函数中传入数组元素地址printf("fun_p(&array[2]) : \n");fun_p(&array[2]);//向数组参数函数中传入指针printf("fun_a(array + 2) : \n");fun_a(array + 2);return 0;}

执行效果:[plain]view plain copy print? [root@ip28pointer]#gccparam_array_pointer.c[root@ip28pointer]#./a.outfun_p(array+2):*(p-2)=1*p=3fun_a(&array[2]):p[-2]=1p[0]=3fun_p(&array[2]):*(p-2)=1*p=3fun_a(array+2):p[-2]=1p[0]=3

[root@ip28 pointer]# gcc param_array_pointer.c [root@ip28 pointer]# ./a.out fun_p(array + 2) : *(p - 2) = 1 *p = 3 fun_a(&array[2]) : p[-2] = 1 p[0] = 3 fun_p(&array[2]) : *(p - 2) = 1 *p = 3 fun_a(array + 2) : p[-2] = 1 p[0] = 3

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。