您的位置:首页 > 编程语言 > C语言/C++

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空间分配给内核,具体的内存布局如下图:




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 为二级指针.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: