有关于C指针的一些知识点(又名C指针编程之道读书笔记)

来源:互联网 发布:js调用手机相册插件 编辑:程序博客网 时间:2024/04/30 20:15

指针的基本语法,指针与内存分配,指针在数据结构中的应用

一,指针的语法

指针的意义:我们希望可以通过地址快速的操作变量,因此需要使用指针(地址)。

指针类型变量中存储的地址,即指针变量的值就是地址。

指针定义的一般形式:类型说明符*指针变量名;类型说明符:指针所指向的数据的数据类型。*:表示这里定义的是一个指针变量。指针变量名:该指针变量的名称。

两个运算符号:&

取地址符号&用于获得某个变量的地址。取值符号*用于获得某个地址中存放的数据。

指针在被使用前,必须赋值,换句话说,指针变量必须在使用前指向某一地址(使用前必须定义且要初始化)。否则,由于指针的值不定,可以对不该操作的数据单元数据进行了篡改,造成不可预计的错误。

指针的操作:1)赋值:给指针赋值,只能赋予定义时指定的数据类型的数据单元的地址。(例如char*p;int s=2;p=&s;错误)

                      2)取值:*p

                      3)指针与整数的加减法:pp+n的意义是,p的地址值增加了n*数据类型所占数据单元(如果每个数据单元是1字节,那么p指向的地址挪动了n个字节。ps:指向数组是+-n不要越过数组边界,否则会造成不可预计的错误)。

                      4)指针与指针的减法:差值为两个指针之间的距离(地址差值)。

二,数组与指针

数组:数组在内存中,就是一组连续的内存单元,长度为数组长度乘以数据类型所占的数据单元个数(例如int a7];int占两个字节,编译器为数组开辟7*2个字节大小的内存空间)。

数组的引用有两种方式:1)下标:数组名[下标],是指数组的第下标值个元素的值

                                        2)指针:数组名就是数组的首地址

数组指针:指向的数据单位为数组某个元素的指针。(如char str[]="hello world!"char*p,p=str或者p=&str[6]等,这里的p就是数组指针)

数组指针的引用:定义好数组指针并初始化,直接用*来取值就可以引用数组指针(取数组指针所指向数组元素的值)了


指针数组:一组指针的集合。常用来表示一组字符串。

指针数组的定义:类型说明符 *数组名[数组长度];类型说明符:指针所指向变量的类型,*数组名:表示数组为指针数组,数组长度:数组元素的个数

C指针编程之道中举的例子很清楚:char *name[]={"china","pk","Longmai"};name[0]->"china",name[1]->"pk",name[2]->"Longmia".

数组指针的引用:输出一个字符串,需要一个字符串的地址。每一给数组元素都是一个字符串的地址。例如name0]是数组china的首地址。

三,字符串与指针

字符串有两种处理方式:1)数组  2)指针

字符串指针的定义的一般格式:char*变量名;指向字符串的首地址

数组处理字符串:整体赋值只能在初始化时进行,之后就要一个一个元素的赋值。

四,函数与指针

形参与实参:如果函数的形参是变量,只是复制了传入的变量的值。要想改变传入的变量,那么必须穿入地址,也就是函数指针要为形参,这样实参也会跟着形参改变。

函数指针:指向函数的指针,指针(地址)是函数空间的首地址。定义格式:数据类型(*变量名)();数据类型表示函数返回值的数据类型;函数名称即函数首地址(类似字符串的设定)。

函数指针的使用:(*指针名)();直接使用指针就调用了指针所指向的函数

指针函数:返回值是指针的函数,称为指针函数。定义格式:数据类型*函数名();

五,指向指针的指针

地址也是数据,也要存储在相应的地址上,所以我们也可以通过地址访问它。这个地址就是指针的指针,即指向指针的指针变量。

指针的指针的定义格式:**变量名;*的结合方向是从右到左,*变量名是一个一级指针,*一级指针就是一个二级指针。

多维数组:数组[i]相当于*(数组名+i);二维数组总,数组名[i][j]相当于*(数组名[i]+j)或者*(*(数组名+i)+j)

六,指针与内存

C语言中,将内存划分为全局区、代码区、常量区、堆栈区。全局区存放在整个程序运行期间都生效的全局变量。代码区存放代码。常量区存放运行期间不会改变的的数据。堆:堆区的内存分配是编程人员控制的,用于存放临时的数据。栈:用于存放局部变量,调用函数的形参等。调用结束后自动释放。

动态内存分配函数1malloc

       void *malloc(unsigned int size);调用这个函数可以给用户分配一块内存空间,大小为size个字节。返回值是void类型的指针,因此,在使用时需要把该指针强制转换为需要的类型。分配成功后,这块内存区为随机值。分配失败,返回NULL

                             2calloc

                                  void *calloc(unsigned int num,unsigned int size);分配大小为size*num个字节的空间,并初始化为0或者NULL;分配失败返回NULL

  3realloc

void *realloc(void *ptr,size_t size);ptr指针所指向的内存空间重新分配,size是分配后的空间大小。成功返回新空间的指针,失败返回NULL

这三种方式都是成功,返回新空间的首地址。失败返回NULL;因此在使用分配的空间之前,最好检查一下是否分配成功。


释放内存函数:free

void free(void *ptr);ps:释放内存后,指针仍然指向该段内存,但是内存中的数据已经是无效数据了,因此为了防止误操作,建议释放内存后,将指针也释放掉,即及时将指针指向NULL


七,指针与队列

队列,是一种先进先出的线性数据结构,由于单向队列有假溢出现象,所以我们通常采用循环队列。

#include "iostream"using namespace std;/* 循环队列 循环队列结构 操作:1)初始化 2)入队 3)出队 4)队空 5)队满 */#define QueueSize 8typedef struct{    int front;//指向队头    int rear;//指向队尾    int counter;//纪录队中元素个数    int QData[QueueSize];//存放队元素}CQueue;int CQueueEmpty(CQueue *queue)//采用纪录队列中元素个数的方式判断队空{    return queue->counter==0;}int CQueueFull(CQueue *queue)////采用纪录队列中元素个数的方式判断队满{    return queue->counter==QueueSize;}void InitCQueue(CQueue *queue)//函数的形参是CQueue类型的指针变量{    queue->front=0;    queue->rear=0;    queue->counter=0;}int InCQueue(CQueue *queue,int elem){    if(CQueueFull(queue))    {        return 0;    }else{        queue->QData[queue->rear]=elem;        queue->counter++;        queue->rear=(queue->rear+1)%QueueSize;        return 1;    }}int OutCQueue(CQueue *queue,int *elem){    if(CQueueEmpty(queue))    {        return 0;    }else    {        *elem=queue->QData[queue->front];        queue->counter--;        queue->front=(queue->front+1)%QueueSize;        return 1;    }}


链表队列:在单链表的基础上,加上队列的特性,除了头指针还要有尾指针。不需要事先定义队列长度,且不需判断队满,因为内存是动态分配和释放的,出队入队是要动态的释放和分配空间。

#include "iostream"using namespace std;/* 链式队列 队和队结点的链式定义结构 操作:1)初始化 2)入队(动态分配空间) 3)出队(释放内存) 4)队空 5)队满 */typedef struct LinkNode_t{    int data;    struct LinkNode_t *next;}LinkNode;typedef  struct LinkList_t{    LinkNode *front;    LinkNode *rear;}LinkQueue;LinkQueue *queue;LinkNode *node;LinkQueue* QueueInit(){    node=(LinkNode*)malloc(sizeof(LinkNode));    queue =(LinkQueue*)malloc(sizeof(LinkQueue));    queue->front=queue->rear=node->next=NULL;    return queue;}void InQueue(LinkQueue *queue,int data){    node=(LinkNode*)malloc(sizeof(LinkNode));    node->data=data;    node->next=queue->rear->next;    queue->rear->next=node;    queue->rear=node;}int QueueEmpty(LinkQueue *queue){    if(queue->rear==queue->front)        return 0;    else        return 1;}int OutQueue(LinkQueue *queue){    int data;    if(QueueEmpty(queue))    {        cout<<"the queue is already empty!!!";        return 1;    }else    {        node=queue->front->next;        queue->front->next=node->next;        data=node->data;        if(node==queue->rear)        {            queue->rear=queue->front;        }        free(node);        return data;    }}int main(){        return 0;}

八,指针与栈

只能在一端(栈顶)进行操作,另一端(栈底)不允许进行操作的线型结构叫做栈。

栈按照存储结构的不同可分为两种:一是顺序栈,二是链栈。链栈,常用到两个指针,一是栈底指针bottom,二是栈顶指针top。由于我们只对栈顶操作,所以常忽略栈底指针。栈顶可以指向栈顶元素位置,也可以指向元素待插入位置。


九,指针与动态链表

动态链表就是用指针的方式将链表元素链接起来的线型表,比如:单链表,双链表,循环链表。

/*

 循环链表

 结点有两个域:一,值二,下结点地址(链域)

 操作:1)初始化 2)寻找节点 3)插入 4)删除

 */


十,指针与二叉树









0 0
原创粉丝点击