<script language="JavaScript" src="/tech/js/ad/con_s15.js" type="text/javascript"></script><!--IFRAME id=contentFRM name=contentFRM align=left marginWidth=0 marginHeight=0 src=http://ad.6to23.com/tech/ad360x300.htm frameBorder=0 width=350 scrolling=no height=250></IFRAME--> 在学习c/c+过程中,指针是一个比较让人头痛的问题,稍微不注意将会是程序编译无法通过,甚至造成死机。在程序设计过程中,指针也往往是产生隐含bug的原因。下面就来谈谈指针的应用以及需要注意的一些问题,里面也许就有你平时没有注意到的问题,希望能帮助各位读者理解好指针。
一、我们先来回忆一下指针的概念吧,方便下面的介绍
指针是存放地址值的变量或者常量。例如:int a=1;&a就表示指针常量(“&”表示取地址运算符,也即引用)。int *b,b表示的是指针变量(注意,是b表示指针变量而不是*b),*表示要说明的是指针变量。大家注意int *b[2]和int(*b)[2]是不同的,int *b表示一个指针数组,而int (*b)[2]表示含有两个元素的int指针,这里要注意运算优先级问题,有助于理解指针问题。
在这里大概介绍基本概念就够了,至于具体使用方法,如赋值等,很多书都有介绍,我就不多说了。
二、应用以及注意的问题
1、 理解指针的关键所在――对指针类型和指针所指向的类型的理解
①、 指针类型:可以把指针名字去掉,剩下的就是这个指针
例如:int *a;//指针类型为int *
int **a;//指针类型为int **
int *(*a)[8];//指针类型为 int *(*)[8] |
②、 指针所指向的类型:是指编译器将把那一片内存所看待成的类型。这里只要把指针声明语句中的指针名字和名字右边的“*”号去掉就可以了,剩下的就是指针所指向的类型。
我之所以把他们放在第一位,是因为弄清楚他们是学c/c++指针的重点,正确理解他们才能使你打好c/c++的编程基础。
2、 指针的应用――传递参数。
其实它可以相当于隐式的返回值,这就比return的方法更加灵活了,可以返回更多的值,看看下面的例子自然就明白了:
#include "iostream.h"
void example(int *a1,int &b1,int c1)
{
*a1*=3;
++b1;
++c1;
}
void main()
{
int *a;
int b,c;
*a=6;
b=7;c=10;
example(a,b,c);
cout <<"*a="<<*a< cout <<"b="< cout <<"c="< }
输出:*a=18
b=8
c=10 |
注意到没有,*a和b的值都改变了,而c没有变。这是由于a1是指向*a(=6)的指针,也即与a是指向同一个地址,所以当a1指向的值改变了,*a的值也就改变了。在函数中的参数使用了引用(int &b1),b1是b的别名,也可以把它当作特殊的指针来理解,所以b的值会改变。函数中的参数int c1只是在函数中起作用,当函数结束时候便消失了,所以在main()中不起作用。
3、 关于全局变量和局部变量的一个问题
先不废话了,先看看程序:
#include “iostream.h”
int a=5;
int *example1(int b)
{
a+=b;
return &a;
}
int *example2(int b)
{
int c=5;
b+=c;
return &b;
}
void main()
{
int *a1=example1(10);
int *b1=example2(10);
cout <<”a1=”<<*a1< cout <<”b1=”<<*b1< }
输出结果:
a1=15
b1=4135 |
*b1怎么会是4135,而不是15呢?是程序的问题?没错吧?
由于a是全局变量,存放在全局变量的内存区,它一直是存在的;而局部变量则是存在于函数的栈区,当函数example2()调用结束后便消失,是b指向了一个不确定的区域,产生指针悬挂。
下面是对example1()和example2()的反汇编(用TC++ 3.0编译):
example1():
push bp;入栈
mov bp,sp
mov ax,[bp+04];传递参数
add [00AA],ax;相加
mov ax,00AA ;返回了结果所在的地址
.
.
.
pop bp;恢复栈,出栈
ret;退出函数
example2():
push bp;入栈
mov bp,sp
sub sp,02
mov word ptr [bp-02],0005
mov ax,[bp-02];传递参数
add [bp+04],ax;相加
lea ax,[bp+04];问题就出在这里
.
.
.
mov sp,bp
pop bp;恢复栈,出栈
ret;退出函数 |
对比之后看出来了吧?ax应该是存储的是结果的地址。而在example2()中,返回的却是[bp+04]的内容,因此指针指向了一个不确定的地方,由此产生的指针悬挂。example1()中,ax返回了正确的结果的地址。
4、 内存问题:使用指针注意内存的分配和边界。
使用指针过程中应该给变量一个适当的空间,以免产生不可见的错误。
请看以下代码:
#include “iostream.h”
void main()
{
char *a1;
char *a2;
cin >>a1;
cin >>a2;
cout <<”a1=”< cout <<”a2=”< }
输入:abc
123
输出:
a1=123
a2=
Null pointer assignment |
指针指向了“空”。解决办法就是分配适当的内存给这两个字符串。修正后的代码如下:
#include “iostream.h”
void main()
{
char *a1;
char *a2;
a1=new char [10];
a2=new char [10];
cin >>a1;
cin >>a2;
cout <<”a1=”< cout <<”a2=”< delete(a1);注意,别忘了要释放内存空间
delete(a2);
} |
到此就能输出正确的结果了。
分配了适当的内存之后要注意释放内参空间,同时还应该注意不要超出所分配的内存的大小,否则会有溢出现象产生,导致不可预料的结果。
5、 关于特殊的指针――引用
引用有时候应用起来要比指针要灵活,用它做返回的时候是不产生任何变量的副本的这样减小了内存的占用,提高执行的速度。引用使用起来要比指针好理解,比较直观。当引用作为参数时,不会改变参数的地址,因此可以作为左值。
下面请看一个例子:
#include “iostream.h”
char ch[5]=”ABCD”;
char &example(int b)
{
return ch;
}
void main()
{
cout <<”ch=”< example(2)=”c”;
cout<<”ch=”< }
输出结果:
ch=ABCD
ch=ABcD |
在实际编程过程中,可以灵活地引用或指针,尽量提高程序的可读性和执行效率。
三、小结: 指针是学习c/c++的重点难点,主要原因是指针较为抽象,不容易理解。使用指针千万要明白让指针指向什么地方,如何让指针指向正确的地方。在深入系统底层之中需要应用到大量的指针,因此需要理解好指针的基本概念,例如:指针类型和指针所指向的类型。平时应该对留心观察,了解程序的工作过程,必要时候可以对程序进行反汇编,加深对指针的理解,这种方法同样适合学别的编程方面的知识。
四、结束:
指针的应用是很广泛的,利用指针可以做很多事情,要成为一个好的程序员,必须对指针有比较深刻的了解。写本文的目的在于让大家对指针有更深一层的了解,提高指针的应用能力,内容大都是我在实际编程中遇到的问题。相信能给大家一定的帮助。 |
相关推荐
正确理解和区分这两者是掌握C/C++指针的关键。 接下来,我们探讨指针在实际应用中的作用。一个常见的应用场景是作为函数参数传递。通过指针,函数可以改变外部变量的值,实现非局部副作用。例如: ```cpp void ...
首先我们知道~ class Test { public: Test() { return this; //返回的当前对象的地址 ...return this返回当前对象的地址(指向当前对象的指针) 我们再来看看返回拷贝那个的地址~ #include <io
在C/C++编程中,动态链接库(DLL, Dynamic Link Library)是一种重要的代码复用机制。DLL文件包含可被多个程序同时使用的代码和数据,它允许开发者将功能模块化,从而减少应用程序的体积,提高系统资源利用率。这篇...
C/C++支持可变参数个数的函数定义,这一点与C/C++语言函数参数调用时入栈顺序有关,首先引用其他网友的一段文字,来描述函数调用,及参数入栈: ———— 引用开始 ———— C支持可变参数的函数,这里的意思是C支持...
在C/C++的编程世界中,static关键字是一个不可或缺的存在,它在不同的编程范式中扮演着不同的角色。本文将深入探讨C/C++中static关键字的多种用途及其重要性,帮助读者全面理解这一关键字在程序设计中的应用。 一、...
在C/C++编程语言中,函数参数的传递方式有三种:值传递、引用传递和指针传递。每种方式都有其独特的特性和应用场景,理解它们的差异和使用规则是编写高效且安全代码的关键。 首先,**值传递**是将变量的副本传入...
- 在函数声明`void f(int a[10])`中,尽管`a`在函数参数列表中看似是一个数组,但在C/C++中,这种写法实际上等价于`void f(int *a)`,传递的是一个指向数组首元素的指针。因此,当在函数内部使用`sizeof(a)`时,它...
在C/C++编程中,排序是一项常见的任务,用于组织数据并便于查找或分析。STL(Standard Template Library,标准模板库)提供了一个名为`sort`的高效函数,它可以帮助我们快速地对各种类型的数据进行排序。`sort`函数...
在C/C++编程中,了解变量在内存中的分布对于优化代码和排查问题至关重要。本文将深入探讨这个主题,基于给出的标题、描述和部分内容。 首先,我们要知道内存主要分为四个区域:堆(Heap)、栈(Stack)、代码区...
### 浅析C++中的引用与指针 #### 引言 在C++编程语言中,指针和引用是两个非常重要的概念。正确理解和运用它们能够显著提高代码的效率和可读性。本文旨在深入剖析C++中的引用与指针,并通过具体的例子来对比和解释...
在Borland C++ Builder中使用GDI+时,需要注意的是,所有GDI+对象都是引用计数的智能指针,这意味着它们会在不再被引用时自动释放。这是通过IUnknown接口实现的,它提供了AddRef和Release方法来管理对象的生命周期。...
- Void类型在C/C++中用于函数指针或未指定类型的占位符,它不存储任何数据。 6. 枚举类型(Enumeration) - 枚举类型在某些语言中如C++和C#中,可以看作是整型的别名,具体大小取决于底层的整型类型。 7. 字符串...
在C++编程语言中,函数和指针是两个非常重要的概念,它们的结合使用能够实现灵活的代码设计和高效的数据处理。本篇文章将深入探讨C++中的函数与指针,帮助初学者理解这一基础概念。 首先,我们来看函数。函数在C++...
JNI使得开发者可以充分利用Java和C/C++的优点,例如Java的跨平台性和C/C++的高效性能。在Android系统中,JNI扮演着连接Java层与Native层的关键角色。 首先,我们来理解如何通过JNI告诉虚拟机(VM)加载和使用本地库...
在本文中,我们将深入探讨C++类的封装性,以及其背后的构造函数、静态变量、虚函数等核心概念。 首先,我们来理解C++中的封装性。封装是面向对象编程的三大特性之一,它允许我们将数据和操作这些数据的函数捆绑在...
### C++程序设计内存泄漏浅析 #### 一、引言 C++作为一种广泛使用的编程语言,因其灵活性和高效性而备受青睐。然而,在实际开发过程中,如果不慎使用其动态内存管理功能,很容易引发内存泄漏问题。内存泄漏不仅会...