数据结构学习笔记之C指针基础
2014-05-07 12:03
369 查看
之所以称为C指针基础,因为这些关于指针的操作是基础,但是我们又经常出错,在做数据结构的实验时,又遇到不少,我在这里做下总结,希望能帮到大家。另:这个不像之前的算法,也可能仍会有很多认识不足的地方,希望大家指正!
变量的地址即指针,存储地址值的内存单元称为指针单元。指针的类型由其类型决定,基类型是指指针变量所指的数据类型。所以指向整型变量的指针我们称之为整型指针;指向字符变量的指针称为字符指针等等
一、下面说一点最最基本的
1.指针声明:int *p, *q; //整型指针
char *ch; //字符指针
void *n; //这个指针也是对的,可以指向任何数据,要规定长度,这个到进阶的时候再说吧
2.指针使用:a.取地址单目运算符:int sum, *psum; psum = ∑即取sum的地址作为指针psum
b.指针的赋值:上面的psum = ∑ 是一个赋值语句;同样类型的指针 p = q; 这样赋值也是对的
c.间访单目运算符 * :sum = *q; 就可以将q所指单元的值赋给sum。
3.针对结构体指针,访问数据项的操作,方式有两种:
a. p->name (如果p是双重指针,则应该为 (*p)->name )
b. (*p).name
Ps:假设p是指向一个结构的指针,该结构内部有一个名为name的数据项,对它的访问可以使用上面两种方式
这里就有一个必须要理解的问题,指针的所指,赋值操作p = q;他们的指针单元是不一样的,存储单元的地址是不一样的,所以有时候在一些链表操作中,会犯这个错误,虽然你认真考虑了,但是你会认为 q = s; p
=q; 那么 p 的指向就变成了s, 这个本身是没有太大问题,但是如果你要修改的是p指针的存储单元,那就绝对错了!下面给出例子说明。
二、指针操作常见错误
引用我自己写的基于链表的顺序表的插入操作。
下面是链表的头结点和数据项结构定义:
下面是ListInsert函数实现代码:
第一个容易犯的错误:如上面代码的34、35行,和注释写的一样,前面我有将L->FirstNode(链表的首元结点)赋值给p,是赋值,不是连存储单元也改了,我们的目的是修改L的首元结点,很显然要修改其指针单元,所以如果在这里你写的是p = N, 就百分百错了!(你这样做编译器是不会报错的!某种程度上说,指针的风险性就在于这里,它很灵活)
第二个容易犯的错误:就是我们在参数列表中定义了一个指向数据元素的结构指针,我们用它返回我们前面插入的数据元素,那么如何返回?
错误写法: e = N;
错误好像还不是很明显哈,很多人就会问,为什么在函数体内可以用e指针访问所指的结构体,返回到主函数就不行了(我觉得这和汇编里面的子程序调用差不多,传的是指针,我们明显会对它进行压栈保护,也就是说在子函数e改变了指向,指向N所指的结构(某块存储空间),但是返回时,e所指的结构体(另一块存储空间)恢复了)。那么当然就没有预期效果了。
正确的写法:如上面代码的38行 (*e) = (*N); 这是两个同类型的结构体直接赋值,你也可以一个个的数据项赋值,但是明显那么做不明智。
关于结构体的赋值,也会有很多的小细节,一并说一下,因为这些细节有时候也很容易被忽略:
第三个容易犯的错误:就像前面的(*e) = (*N), e在主函数里定义,如果你只是定义了,没有分配存储空间给它,如果悬挂了,那么显然,前面这个语句就是错误的,因为e悬挂,没有所指,没有实体存储单元,就会出错!
另外就是前面说的结构体赋值的两种方式,一定要清楚等式两端的数据类型;如果结构体定义了字符串,用->来访问数据项或者赋值会更加麻烦且容易出错!
小结:前面三个错误可简述为:1.对指针所指地址和本身的存储地址理解不透彻;2.对指针参数传值理解不透彻;3.对指针赋值混淆,不清楚数据类型。所以大家要多练习,加深理解就好啦!
此外,再添加一点,定义在不同的头文件下的结构体,在main函数下include后,是可以引用另一个结构声明变量的,可以构成嵌套的结构:
以上是在usr_bintree.h的二叉树节点定义,下面是usr_stack.h的堆栈数据元素节点定义:
在上面在栈结点我就采用了另一个头文件的二叉树定义来定义了一个数据项: struct BiTree *pbtnode; 这样是没有任何问题的,实际可行,这是我的实验,通过测试的!
变量的地址即指针,存储地址值的内存单元称为指针单元。指针的类型由其类型决定,基类型是指指针变量所指的数据类型。所以指向整型变量的指针我们称之为整型指针;指向字符变量的指针称为字符指针等等
一、下面说一点最最基本的
1.指针声明:int *p, *q; //整型指针
char *ch; //字符指针
void *n; //这个指针也是对的,可以指向任何数据,要规定长度,这个到进阶的时候再说吧
2.指针使用:a.取地址单目运算符:int sum, *psum; psum = ∑即取sum的地址作为指针psum
b.指针的赋值:上面的psum = ∑ 是一个赋值语句;同样类型的指针 p = q; 这样赋值也是对的
c.间访单目运算符 * :sum = *q; 就可以将q所指单元的值赋给sum。
3.针对结构体指针,访问数据项的操作,方式有两种:
a. p->name (如果p是双重指针,则应该为 (*p)->name )
b. (*p).name
Ps:假设p是指向一个结构的指针,该结构内部有一个名为name的数据项,对它的访问可以使用上面两种方式
这里就有一个必须要理解的问题,指针的所指,赋值操作p = q;他们的指针单元是不一样的,存储单元的地址是不一样的,所以有时候在一些链表操作中,会犯这个错误,虽然你认真考虑了,但是你会认为 q = s; p
=q; 那么 p 的指向就变成了s, 这个本身是没有太大问题,但是如果你要修改的是p指针的存储单元,那就绝对错了!下面给出例子说明。
二、指针操作常见错误
引用我自己写的基于链表的顺序表的插入操作。
下面是链表的头结点和数据项结构定义:
typedef int status; typedef struct{ int length; struct LNode *FirstNode; }HNode; typedef struct LNode{ char name[16]; int age; char phone[12]; char mail[64]; struct LNode *next; }LNode;
下面是ListInsert函数实现代码:
第一个容易犯的错误:如上面代码的34、35行,和注释写的一样,前面我有将L->FirstNode(链表的首元结点)赋值给p,是赋值,不是连存储单元也改了,我们的目的是修改L的首元结点,很显然要修改其指针单元,所以如果在这里你写的是p = N, 就百分百错了!(你这样做编译器是不会报错的!某种程度上说,指针的风险性就在于这里,它很灵活)
第二个容易犯的错误:就是我们在参数列表中定义了一个指向数据元素的结构指针,我们用它返回我们前面插入的数据元素,那么如何返回?
错误写法: e = N;
错误好像还不是很明显哈,很多人就会问,为什么在函数体内可以用e指针访问所指的结构体,返回到主函数就不行了(我觉得这和汇编里面的子程序调用差不多,传的是指针,我们明显会对它进行压栈保护,也就是说在子函数e改变了指向,指向N所指的结构(某块存储空间),但是返回时,e所指的结构体(另一块存储空间)恢复了)。那么当然就没有预期效果了。
正确的写法:如上面代码的38行 (*e) = (*N); 这是两个同类型的结构体直接赋值,你也可以一个个的数据项赋值,但是明显那么做不明智。
关于结构体的赋值,也会有很多的小细节,一并说一下,因为这些细节有时候也很容易被忽略:
第三个容易犯的错误:就像前面的(*e) = (*N), e在主函数里定义,如果你只是定义了,没有分配存储空间给它,如果悬挂了,那么显然,前面这个语句就是错误的,因为e悬挂,没有所指,没有实体存储单元,就会出错!
另外就是前面说的结构体赋值的两种方式,一定要清楚等式两端的数据类型;如果结构体定义了字符串,用->来访问数据项或者赋值会更加麻烦且容易出错!
小结:前面三个错误可简述为:1.对指针所指地址和本身的存储地址理解不透彻;2.对指针参数传值理解不透彻;3.对指针赋值混淆,不清楚数据类型。所以大家要多练习,加深理解就好啦!
此外,再添加一点,定义在不同的头文件下的结构体,在main函数下include后,是可以引用另一个结构声明变量的,可以构成嵌套的结构:
//-------二叉树的二叉链表存储表示------- typedef struct BiTNode { char ch; struct BiTNode *lchild, *rchild; //左右孩子指针 }BiTNode, *BinTree;
以上是在usr_bintree.h的二叉树节点定义,下面是usr_stack.h的堆栈数据元素节点定义:
//堆栈的链栈表示--栈结点 typedef struct SNode { char ch; //堆栈结点保存的数据 struct SNode *next; //指向下一个链栈结点的指针 struct BiTNode *pbtnode; //指向二叉树结点的指针,这个结构定义在usr_bintree.h中 }SNode; //堆栈的链栈表示--栈元素 typedef struct { SNode *top; //栈顶指针 SNode *base; //栈底指针 int stacksize; //栈当前大小 }SqStack;
在上面在栈结点我就采用了另一个头文件的二叉树定义来定义了一个数据项: struct BiTree *pbtnode; 这样是没有任何问题的,实际可行,这是我的实验,通过测试的!
相关文章推荐
- 数据结构基础【学习笔记】
- 数据结构学习笔记一:数据结构基础
- (C/C++ 学习笔记)函数指针语法基础
- 奶爸业余单片机学习之:C语言基础——指针(指针变量)学习笔记之二
- 数据结构学习笔记---基础篇
- 奶爸业余单片机学习之:C语言基础——指针(指针变量)学习笔记
- 数据结构 学习笔记之:关于顺序栈中给结构体类型指针分配内存时,使用malloc和不使用malloc的疑惑之解惑!
- 数据结构 学习笔记之:线性表——顺序表、静态链表、动态链表(单链表、双链表、单循环链表、双循环链表,链式栈、链式队列)——基础概念大扫盲!
- [学习笔记]C和C++中指针的基础知识点(二)
- [学习笔记]C和C++中指针的基础知识点(一)
- C++学习笔记:函数指针语法基础以及函数指针做函数参数的思想剖析
- C++基础学习笔记---指针
- 深层次两张图解经典6大排序与6大基础数据结构——学完这些,妈妈再也不用担心我的排序算法与数据结构,学习笔记大放送
- C++基础学习笔记:指针
- C++基础教程 学习笔记(二) 数组、字符串和指针
- C语言-指针的基础学习笔记(2)
- python 基础数据结构-学习笔记之list
- 智能指针基础std::auto_ptr与new、delete的重载学习笔记
- 数据结构学习笔记一--基础
- 【算法零基础入门】 学习笔记一 什么是数据结构