C 语言指针与地址

来源:互联网 发布:常用的网络拓扑结构是 编辑:程序博客网 时间:2024/05/16 16:13

近段复习C语言,感觉C语言相对于C++、Java等高级语言最大的特点就是:简洁紧凑、灵活方便。C语言特征不多,带有面向硬件思维,小巧而精炼。

C语言非常贴近底层,要真正理解指针的本质,还需要些微机原理、计算机组成与原理、操作系统和编译原理等背景知识。
  • 物理地址是外部连接使用的、唯一的,它是“与地址总线相对应”;
  • 逻辑地址是内部和编程使用的、并不唯一。逻辑地址就是逻辑段管理内存而形成的。
C语言中使用的地址是逻辑地址,和操作系统密切相关。
  • Windows中, 对于一个地址的转换要经过段式和页式, 逻辑地址先通过段式变换转换成线性地址, 再通过页式转换变换成物理地址。
  • Linux中, 由于其段式变换其实并没有真正使用,线性地址和逻辑地址相等。
 在进行C语言指针编程中,可以读取指针变量本身值(&操作),实际上这个值就是逻辑地址,它是相对于你当前进程数据段的地址(偏移地址),不和绝对物理地址相干。只有在Intel实模式下,逻辑地址才和物理地址相等(因为实模式没有分段或分页机制,Cpu不进行自动地址转换),逻辑地址也就是在Intel 保护模式下程序执行代码段限长内的偏移地址(假定代码段、数据段如果完全一样)。

  •  8086体系的CPU一开始是20根地址线,寻址寄存器是16位,16位的寄存器可以访问64K的地址空间, 如果程序要想访问大于64K的内存, 就要把内存分段,每段64K,用段地址+偏移量的方法来访问 。
  • 386CPU出来之后,,采用了32条地址线,地址寄存器也扩为32位,这样就可以不用分段了,直接用一个地址寄存器来线性访问4G的内存了, 这就叫平面模式。

简单來说,计算机中所有的数据都是存放在存储器中的,一般把存储器中的一个字节称为一个存储单元。为了正确的访问这些存储单元,需要为每个存储单元编个号(即逻辑地址由操作系统来决定)。根据编号即可准确的找到该内存单元。内存单元的编号就称为地址 。

  • 变量可以理解为分配的一块存储单元,编译后就变成了对应内存的地址,大小由这个变量类型决定。但是,int类型长度通常由CPU+OS+Compiler共同决定,这也是编写跨平台软件需要注意的地方。
  • 指针也是一种变量,存放的是地址,32位程序就是占4B,不妨理解为无符号int,这个还是由CPU+OS+Compiler共同决定。

变量声明后,相当于和一块存储区的绑定,即把这块存储区暂时(变量生命周期内)叫做这个名字(变量),编译后就变成了对应内存的地址。这块存储区有其编址(编译器和操作系统)决定,对变量赋值,即往这块存储区写数据。


指针是这样一种变量,存放的是指定数据类型的首字节地址(例如32位程序可理解为4B无符号int)。通过指针可以对其指向的变量(指针存放了其指向变量的地址)进行操作。既然指针也是一种变量,那么其也有所占用的存储区,这快存储区地址也可以存放在指向其的指针中,这就是指向指针的指针。


一个典型的C程序中:

  • :系统自动分配自动回收,分配局部变量空间,速度较快,但无法人工控制。在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域,即栈顶的地址和栈的最大容量是系统预先规定的,是编译时就确定的常数,Windows预留栈空间是2M。如果申请的空间超过栈的剩余空间时,将提示overflow。所以,能从栈获得的空间较小。 栈空间可以通过设置来改变(具体根据相应的OS)来进行。详见:Stack的三种含义。
  • :是向上增长的用于分配程序员申请的内存空间,用起来方便,但易产生碎片。堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存,获得的空间比较灵活,也比较大。

C语言中结构体和指针就可以描述很多经典数据结构,下面通过一个链表的例子来总结:

#include <stdio.h>#include <stdlib.h>typedef int ElemType;typedef struct Node {ElemType data;struct Node *next;} Node;  //c语言中的定义,c语言的一些语法细节和c++区别还较大//data属性是用来存放数据的,其他的都是载体而已,从设计的角度出发typedef Node *LinkList;  //定义LinkList,自定义链表的指针变量,指向链表的地址//typedef struct Node *LinkList;  //这句也正确,上面的typedef定义了Node/* typedef 对已经存在的类型加一个别名 e.g.  {typedef int NUM[100]; NUM n;} <=> int n[100]; */// 初始化一个Node,开辟地址int initList(LinkList *L) {   //这里的传递的参数是指向指针(结构体指针)的指针//c语言不可以传递引用,c++才可以,严蔚敏数据结构传递的是c++的引用,使用&符号//在主函数中,实参可以是:要传递的地址,一个本类型的指针变量,本质都是拷贝地址到调用函数的指针变量中*L = (LinkList) malloc(sizeof(Node)); //*L就是指针L指向的数据if (!(*L))return 0;    //内存不足是就会FALSE(*L)->data = 0;(*L)->next = NULL;   // 这里的 *L 后就是取得 指向结构体 的指针return 1;}//结构体指针:结构体变量.成员名;(*p).成员名;p->成员名  [p为结构体指针变量]int creatList(LinkList *L, int n) {LinkList p;int i;*L = (LinkList) malloc(sizeof(Node)); //数据存放在变量中,变量存放在地址中(*L)->next = NULL;for (i = 0; i < n; i++) {p = (LinkList) malloc(sizeof(Node));  //创建一个节点p->data = rand() % 100 + 1;  //来自库 stdlib.hp->next = (*L)->next;(*L)->next = p;}printf("Creat LinkList succeed.");return 1;}int visitList(LinkList L) {printf("\nVisit LinkList: ");L = L->next; while (L != NULL) {printf("%d ", L->data);L = L->next;}return 1;}int insertList(LinkList L, ElemType e, int n) {int i;  //由于L是头,不算是第一个节点,不存放元素LinkList node;node = (LinkList) malloc(sizeof(Node));  //面向对象中的new,开辟在堆中node->data = e;for (i = 0; i < n - 1 && L != NULL; i++) {  //查到 n-1号节点的后面L = L->next;     //L初始是头结点,不存放数据,查到n循环n-1次}if (n < 1) {  printf("\nInsert index must >0");return 0;}<span style="font-family: Arial, Helvetica, sans-serif;">//判断边界条件</span>if (L == NULL) {printf("\nInsert index is out of length");return 0;}node->next = L->next;L->next = node;printf("\nInsert into index: %d, value is:%d", n, node->data);return 1;}int main() {LinkList L;  //L是一个指针变量,指向结构体printf("&L is %d, L is %d\n", &L, L);printf("size of int is %d\n", sizeof(int));printf("size of char is %d\n", sizeof(char));printf("size of int* is %d\n", sizeof(int*));printf("size of LinkList is %d\n", sizeof(LinkList));printf("size of LinkList* is %d\n", sizeof(LinkList*));ElemType e;e = 99;creatList(&L, 10); //创造一个链表printf("\n&L is %d,L is %d, &(L->data) is %d", &L, L, &(L->data));printf("\n&L is %d,L is %d, &(L->next) is %d", &L, L, &(L->next));visitList(L); //遍历每个元素insertList(L, e, 3); // 这里的ElemType传递了复制(也可以传递地址通过指针)visitList(L); //遍历每个元素return 0;}
输出的结果:

&L is 2686696, L is 0size of int is 4size of char is 1size of int* is 4size of LinkList is 4size of LinkList* is 4Creat LinkList succeed.&L is 2686696,L is 7614288, &(L->data) is 7614288&L is 2686696,L is 7614288, &(L->next) is 7614292Visit LinkList: 65 63 59 79 25 70 1 35 68 42 Insert into index: 3, value is:99Visit LinkList: 65 63 99 59 79 25 70 1 35 68 42 

0 0
原创粉丝点击