C语言基本教程 第8课: 指针
2016-08-19 16:01
357 查看
指针确实是C语言的精髓,但是也很不容易理解.很多初学者 都是卡在这里的.
因为 指针跟很多部分有交集, 我想写好这个教程确实也很有难度.
不过放心吧, 我会尽力做好的.
先列个提纲,介绍一下 指针的相关方面:
1.内存,内存地址, 和指针
2.指针的使用
3.指针和数组
4.指针和字符串
5.函数的传值调用和传地址调用
6.函数指针
7.多级指针
<一>内存,内存地址, 和指针
我们现在大部分的电脑安装的都是 4GB 的内存条, 少数是 2G,8G,16G等.
这里只谈 4GB 的内存条.
32位处理器(CPU),计算机中的位数指的是CPU一次能处理的最大位数,这是总线的宽度,
CPU寄存器的位数,
那么我们看一下 CPU一次访问内存地址的大小: 就是 2的32 次方, 4294967296
那么这个数字是什么为单位呢?bit ,也可以称为比特,或者位,这个bit里面包含的值,只有两个,就是0和1
好了,那么这个bit怎么样能转换为GB呢,很简单:将它连续除以3次1024.
4294967296/1024/1024/1024 = 4
为什么除3次?第一次转换为KB,第二次转换为MB,第三次就转换为GB了.
也就是说 32 位cpu 的寻址能力 范围是 4GB,
而32位 cpu 一般只能安装 32位的操作系统, 只能使用 最大4GB 内存的原因.
以下是4GB内存的示意图:
我们看看 32位的操作系统怎么使用 这个 4GB的内存.
现代的应用程序都运行在一个虚拟内存空间里,在32位的系统里,这个内存空间拥有4GB的寻址能力。现代的应用程序可以直接使用32位的地址进行寻址,整个内存是一个统一的地址空间,用户可以使用一个32位的指针访问任意内存位置。
在进程的不同地址区间上有着不同的地位,Windows在默认情况下会将高地址的2GB空间分配给内核,而Linux默认将高地址的1GB空间分配给内核,具体的内存布局如下图:
1.代码段(code或text)
通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读,某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。代码段由各个函数产生,函数的每一个语句将最终经过编绎和汇编生成二进制机器代码(具体生生哪种体系结构的机器代码由编译器决定)。
2.只读数据段(RO Data)
只读数据段的特点是在运行中不需要改变。只读全局变量,只读局部变量,程序中使用的常量等会在编译时被放入到只读数据区。
3.读写数据段(RW Data)
读写数据段又称已初始化数据段,表示可以读也可以写的数据区。通常已初始化的全局变量和局部静态变量被放在了读写数据段,如:在函数中定义static char b[ 100]={“ABCDEFG”};读写数据区的特点是必须在程序经过初始化,如果只定义,没初始值,则不会生成读写数据区,而会定位为未初始化数据区(BSS)。
4. 未初始化数据段(BSS)
通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。
该段中的数据没有经过初始化,不是目标文件中的一段。未初始化数据段只在运行的初始化阶段才会产生,因此它的大小不会影响目标文件的大小。
5.堆(heap)
堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc/free等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张)/释放的内存从堆中被剔除(堆被缩减).堆内存只在程序运行时出现,一般由程序员分配和释放(malloc、free)。分配内存的函数所分配的内存空间在堆上,程序必须保证使用free释放,否则会发生内存泄漏。
6.栈(stack)
栈又称堆栈,存放程序的局部变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,栈用来传递参数和返回值。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。
栈内存只在程序运行时出现,在函数内部使用的变量、函数的参数以及返回值将使用栈空间,栈空间由编译器自动分配和释放。
说了这么多,指针和内存有什么关系呢?
先看下图:
内存就是存储数据的地方,就像是 之前说的 一排 望不到边际的水桶,上边的 0X00000000 到 0XFFFFFFFF 是水桶的编号.
指针呢,就是指针变量,里边的值就是 这些内存地址的编号,而不是 我们之前存储的 整数之类的东西,性质不一样.
<二>指针的定义和使用
int *pnum=NULL; //这是定义一个指针类型的变量,指针变量,简称指针, 并初始化指针为 NULL(其实就是0)
pnum=#//这是给指针赋值
以上两句可由以下一句代替:
int *pnum=#
总结一下:
&是取地址符,可以获得一个变量的内存地址.
*有三个作用:
1. 乘号
2.定义一个指针时,说明定义的这个变量是 指针型 变量. 如 int *pnum= # 但是 以后如果想表示指针时不需要 *, pnum直接用来表示指针变量,简称指针
3.间接运算符,用来获取 [指针型变量] 所指向的内存地址里边的值.
* 的第三个作用 刚好和 &的作用相反, 他们是一对逆运算.
<三>.指针和数组
int a[5];
这里有5个元素, 分别是 a[0], a[1], a[2], a[3], a[4],
在这里
a 代表元素 a[0]的地址. 它不是一个指针变量, 所以也不能进行 如 a++的操作.
指针就是指针,数组就是数组,
二者是两个东西,也不能互相转化.
很多人经常弄混是因为
数组有两种访问方式:
1)以下标的形式访问,如 a[0], a[1] 编译器总是把以下标的形式操作解析为以指针形式的操作
2)以指针的形式访问 *(a+1) 先取出a的地址,再加上4个字节(sizeif(int) )的偏移量,得到新的地址,再取出 新地址里边的值.
综上,两种形式在访问本质上并没有区别
另外需要注意的是 a 代表数组第一个元素的地址, 即 &a[0].
&a 则代表 数组(作为一个整体)的首地址.
<四>.指针和字符数组
char carray[10]="Hello";
char * str="Hello";
同上.
<五>.函数的传值调用和传地址调用
栈(stack)
栈又称堆栈,存放程序的局部变量(但不包括static声明的变量,static意味着在数据段中存放变量)。
在函数被调用时,栈用来传递参数和返回值。
栈内存只在程序运行时出现,在函数内部使用的变量、函数的参数以及返回值将使用栈空间,栈空间由编译器自动分配和释放。
<A>函数调用过程中的参数传递本质是 用实参来初始化 形参, 而不是 实参替换形参.
<B>函数调用必须通过栈来完成.
函数堆栈实际上使用的是程序的堆栈段内存空间,虽然程序的堆栈段是系统分配的一种静态数据区,
但是函数堆栈却是在调用时才分配好的,堆栈是系统自动管理的,创建和销毁都不需要人为干涉.
局部变量在程序运行到它定义的位置时创建,退出其程序块时销毁.
<C>函数堆栈的三个用途 : 在进入函数前保存环境变量和返回地址,在进入函数时保存实参的copy,在函数体内保存局部变量.
举例:
提问:
假如 子函数如下,则主函数的 a,b在主函数运行到 return 0; 时, a,b的值是多少?
int get_sum(int a, int b)
{
int x =0, y=0;
x=a;
y=b;
a=15;
b=23;
return x+y;
}
函数的传 地址调用,
实际上 也是传值调用.
<六>.函数指针
暂时不做介绍.回调函数常用.
<七>.多级指针
int num=10;
int *p1=#
int ** p2= &p1;
p2 为二级指针.
因为 指针跟很多部分有交集, 我想写好这个教程确实也很有难度.
不过放心吧, 我会尽力做好的.
先列个提纲,介绍一下 指针的相关方面:
1.内存,内存地址, 和指针
2.指针的使用
3.指针和数组
4.指针和字符串
5.函数的传值调用和传地址调用
6.函数指针
7.多级指针
<一>内存,内存地址, 和指针
我们现在大部分的电脑安装的都是 4GB 的内存条, 少数是 2G,8G,16G等.
这里只谈 4GB 的内存条.
32位处理器(CPU),计算机中的位数指的是CPU一次能处理的最大位数,这是总线的宽度,
CPU寄存器的位数,
那么我们看一下 CPU一次访问内存地址的大小: 就是 2的32 次方, 4294967296
那么这个数字是什么为单位呢?bit ,也可以称为比特,或者位,这个bit里面包含的值,只有两个,就是0和1
好了,那么这个bit怎么样能转换为GB呢,很简单:将它连续除以3次1024.
4294967296/1024/1024/1024 = 4
为什么除3次?第一次转换为KB,第二次转换为MB,第三次就转换为GB了.
也就是说 32 位cpu 的寻址能力 范围是 4GB,
而32位 cpu 一般只能安装 32位的操作系统, 只能使用 最大4GB 内存的原因.
以下是4GB内存的示意图:
我们看看 32位的操作系统怎么使用 这个 4GB的内存.
现代的应用程序都运行在一个虚拟内存空间里,在32位的系统里,这个内存空间拥有4GB的寻址能力。现代的应用程序可以直接使用32位的地址进行寻址,整个内存是一个统一的地址空间,用户可以使用一个32位的指针访问任意内存位置。
在进程的不同地址区间上有着不同的地位,Windows在默认情况下会将高地址的2GB空间分配给内核,而Linux默认将高地址的1GB空间分配给内核,具体的内存布局如下图:
C语言程序的段介绍
1.代码段(code或text)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读,某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。代码段由各个函数产生,函数的每一个语句将最终经过编绎和汇编生成二进制机器代码(具体生生哪种体系结构的机器代码由编译器决定)。
2.只读数据段(RO Data)
只读数据段的特点是在运行中不需要改变。只读全局变量,只读局部变量,程序中使用的常量等会在编译时被放入到只读数据区。
3.读写数据段(RW Data)
读写数据段又称已初始化数据段,表示可以读也可以写的数据区。通常已初始化的全局变量和局部静态变量被放在了读写数据段,如:在函数中定义static char b[ 100]={“ABCDEFG”};读写数据区的特点是必须在程序经过初始化,如果只定义,没初始值,则不会生成读写数据区,而会定位为未初始化数据区(BSS)。
4. 未初始化数据段(BSS)
通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。
该段中的数据没有经过初始化,不是目标文件中的一段。未初始化数据段只在运行的初始化阶段才会产生,因此它的大小不会影响目标文件的大小。
5.堆(heap)
堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc/free等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张)/释放的内存从堆中被剔除(堆被缩减).堆内存只在程序运行时出现,一般由程序员分配和释放(malloc、free)。分配内存的函数所分配的内存空间在堆上,程序必须保证使用free释放,否则会发生内存泄漏。
6.栈(stack)
栈又称堆栈,存放程序的局部变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,栈用来传递参数和返回值。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。
栈内存只在程序运行时出现,在函数内部使用的变量、函数的参数以及返回值将使用栈空间,栈空间由编译器自动分配和释放。
说了这么多,指针和内存有什么关系呢?
先看下图:
内存就是存储数据的地方,就像是 之前说的 一排 望不到边际的水桶,上边的 0X00000000 到 0XFFFFFFFF 是水桶的编号.
指针呢,就是指针变量,里边的值就是 这些内存地址的编号,而不是 我们之前存储的 整数之类的东西,性质不一样.
<二>指针的定义和使用
int *pnum=NULL; //这是定义一个指针类型的变量,指针变量,简称指针, 并初始化指针为 NULL(其实就是0)
pnum=#//这是给指针赋值
以上两句可由以下一句代替:
int *pnum=#
总结一下:
&是取地址符,可以获得一个变量的内存地址.
*有三个作用:
1. 乘号
2.定义一个指针时,说明定义的这个变量是 指针型 变量. 如 int *pnum= # 但是 以后如果想表示指针时不需要 *, pnum直接用来表示指针变量,简称指针
3.间接运算符,用来获取 [指针型变量] 所指向的内存地址里边的值.
* 的第三个作用 刚好和 &的作用相反, 他们是一对逆运算.
<三>.指针和数组
int a[5];
这里有5个元素, 分别是 a[0], a[1], a[2], a[3], a[4],
在这里
a 代表元素 a[0]的地址. 它不是一个指针变量, 所以也不能进行 如 a++的操作.
指针就是指针,数组就是数组,
二者是两个东西,也不能互相转化.
很多人经常弄混是因为
数组有两种访问方式:
1)以下标的形式访问,如 a[0], a[1] 编译器总是把以下标的形式操作解析为以指针形式的操作
2)以指针的形式访问 *(a+1) 先取出a的地址,再加上4个字节(sizeif(int) )的偏移量,得到新的地址,再取出 新地址里边的值.
综上,两种形式在访问本质上并没有区别
另外需要注意的是 a 代表数组第一个元素的地址, 即 &a[0].
&a 则代表 数组(作为一个整体)的首地址.
<四>.指针和字符数组
char carray[10]="Hello";
char * str="Hello";
同上.
<五>.函数的传值调用和传地址调用
栈(stack)
栈又称堆栈,存放程序的局部变量(但不包括static声明的变量,static意味着在数据段中存放变量)。
在函数被调用时,栈用来传递参数和返回值。
栈内存只在程序运行时出现,在函数内部使用的变量、函数的参数以及返回值将使用栈空间,栈空间由编译器自动分配和释放。
<A>函数调用过程中的参数传递本质是 用实参来初始化 形参, 而不是 实参替换形参.
<B>函数调用必须通过栈来完成.
函数堆栈实际上使用的是程序的堆栈段内存空间,虽然程序的堆栈段是系统分配的一种静态数据区,
但是函数堆栈却是在调用时才分配好的,堆栈是系统自动管理的,创建和销毁都不需要人为干涉.
局部变量在程序运行到它定义的位置时创建,退出其程序块时销毁.
<C>函数堆栈的三个用途 : 在进入函数前保存环境变量和返回地址,在进入函数时保存实参的copy,在函数体内保存局部变量.
举例:
提问:
假如 子函数如下,则主函数的 a,b在主函数运行到 return 0; 时, a,b的值是多少?
int get_sum(int a, int b)
{
int x =0, y=0;
x=a;
y=b;
a=15;
b=23;
return x+y;
}
函数的传 地址调用,
实际上 也是传值调用.
<六>.函数指针
暂时不做介绍.回调函数常用.
<七>.多级指针
int num=10;
int *p1=#
int ** p2= &p1;
p2 为二级指针.
相关文章推荐
- SQL查询语言基本教程(2)
- 非典型性C语言教程-1.4 指针,字符串,数组
- JavaScript基本教程之JavaScript语言的基础
- C语言指针5分钟教程
- JavaScipt基本教程之JavaScript语言的基础(收藏学习)
- C语言教程---第六章:指针
- C语言指针5分钟教程
- 为指针动态分配内存 | C语言教程 | C语言系列教程
- 指针和数组、字符串 | C语言教程 | C语言系列教程
- C语言学习7 :二级指针定义,强制转换,多级指针初步,6级指针构造,错误应用*p=&a,错误应用 二级p2,void型指针的兼容性,malloc函数基本用法,malloc分配空间和堆栈空间的区别,验证malloc函数内存的分配,验证malloc函数的越界,内存泄漏,指针不能返回局部变量地址,内存分配
- 非典型性C语言教程-1.5 函数指针
- SQL查询语言基本教程(3)
- C# 3.0语言详解之基本的语言增强_C#教程
- JavaScript基本教程之JavaScript语言的基础
- 二级c语言辅导教程及考点分析:第六章指针
- SQL查询语言基本教程(3)
- C# 3.0语言详解之基本的语言增强_C#教程
- Java语言入门教程(十二):Java语言中继承之基本概念
- C语言文件指针的基本函数介绍包含了fpoen、fclose、fgetc、fputc、fscanf、fprintf、fgets、fputs、fread、fwrite函数以及文件定位函数.
- 基本语言细节--指针与引用