指针再回首

本文深入探讨了C语言中指针的基本概念及其常见应用误区,包括指针的定义、内存分配问题及函数参数传递等方面的知识。
指针再回首:
今天遇到一个问题,倒腾了半天,还是一个指针的问题,现在把指针的概念再拿出来重温一遍吧。
指针的定义:
int* p;
这里p为指针变量,它是代表一个“内存地址”的值,也就是说不论什么时候,p都是一个内存中的地址。那*p代表的是指向p所存储的内存地址的值,这里是一个整型变量。而*p是指向的内存地址代表什么类型由int等数据类型来修饰。
下面针对经常爱出错的地方做一下介绍:
1、分配内存
void GetMemory(char *p)
{
  p=malloc(4*sizeof(char));
}
这种写法是错误的,因为在这里p是一个变量,在这里作为形参的时候是不会被函数体所改变的,即在函数体内所改变的不会被保存下来。
第一个参数对char  来说是传址,但对char  *来说是传值.也就是说函数体内p对所指向的内容所作的改变可以得到保留,但对p来说就不是这样.  
 如果想要实现这个功能,有以下几种方法:

1、用二级指针
void GetMemory(char **pszCanoFile)
{
  * pszCanoFile=(pszCanoFile*)malloc(4*sizeof(char));
}
由于malloc的返回值为void*所以要用相应的数据类型进行强制转换
调用的时候为int *p;
   GetMemory(&p);
参数char  **pszCanoFile被声明成二级指针,其对char  *pszCanoFile来说是传址.也就是对一个char  *的传址.这样,在此函数体内对pszCanoFile所作的任何变动都可以得到保留 
2、用局部变量
char* GetMemory(int size)
{
   char*p=(char*)malloc(size*sizeof(char));
   return p;
}  调用的时候为p=GetMemory(4);
要注意以下几种写法是错误的,主要注意不要返回栈上的地址。
char* GetMemory(void)
{
  char p[]=”hello world”
    retrunp;
}
char* p=GetMemory;
printf(“%s”,p);将得到乱码,因为返回了一个栈上的地址。
用函数返回值来传递动态内存这种方法虽然好用,但是常常有人把return语句用错了。这里强调不要用return语句返回指向“栈内存”的指针,因为该内存在函数结束时自动消亡,见示例7-4-4。
 
char *GetString(void)
{
    char p[] ="hello world";
    returnp;    //编译器将提出警告
}
void Test4(void)
{
char *str = NULL;
str = GetString();   // str 的内容是垃圾
cout<< str << endl;
}
示例7-4-4 return语句返回指向“栈内存”的指针
 
用调试器逐步跟踪Test4,发现执行str =GetString语句后str不再是NULL指针,但是str的内容不是“helloworld”而是垃圾。
如果把示例7-4-4改写成示例7-4-5,会怎么样?
 
char *GetString2(void)
{
    char *p ="hello world";
    returnp;
}
void Test5(void)
{
    char *str =NULL;
    str =GetString2();
    cout< }
示例7-4-5 return语句返回常量字符串
 
函数Test5运行虽然不会出错,但是函数GetString2的设计概念却是错误的。因为GetString2内的“helloworld”是常量字符串,位于静态存储区,它在程序生命期内恒定不变。无论什么时候调用GetString2,它返回的始终是同一个“只读”的内存块。
3、函数作为形参
6.3.1指针运算符与指针表达式
在C中有两个关于指针的运算符:
"&运算符:取地址运算符,&m即是变量m的地址。
"*运算符:指针运算符,*ptr表示其所指向的变量。
[例6-2]从键盘输入两个整数,按由大到小的顺序输出。
main()
{
int*p1,*p2,a,b,t;
scanf("%d,%d",&a,&b);
p1=&a;
p2=&b;
if(*p1<*p2)
{
t=*p1;
*p1=*p2;
*p2=t;
}
printf("%d,%d/",a,b);
}
在程序中,当执行赋值操作p1=&a和p2=&b后,指针实实在在地指向了变量a与b,这时
引用指针*p1与*p2,就代表了变量a与b。
运行程序:
RUN
3,4
4,3
在程序运行过程中,指针与所指的变量之间的关系如图6-4所示:指针运算符与指针表达式(图一)
 
当指针被赋值后,其在内存的安放如a),当数据比较后进行交换,这时,指针变量与所指向的变量的关系如b)所示,在程序的运行过程中,指针变量与所指向的变量其指向始终没变。
下面对程序做修改。
[例6-3]
main()
{
int*p1,*p2,a,b,*t;
scanf("%d,%d",&a,&b);
p1=&a;
p2=&b;
if(*p1<*p2)
{
t=p1;
p1=p2;
p2=t;
}
printf("%d,%d/",*p1,*p2);
}
程序的运行结果完全相同,但程序在运行过程中,实际存放在内存中的数据没有移动,而是将指向该变量的指针交换了指向。其示意如图6-5:
 
当指针交换指向后,p1和p2由原来指向的变量a和b改变为指向变量b和a,这样一来,*p1就表示变量b,而*p2就表示变量a。在上述程序中,无论在何时,只要指针与所指向的变量满足p=&a;我们就可以对变量a以指针的形式来表示。此时p等效于&a,*p等效于变量a。
6.3.2指针变量作函数的参数
函数的参数可以是我们在前面学过的简单数据类型,也可以是指针类型。使用指针类型做函数的参数,实际向函数传递的是变量的地址。由于子程序中获得了所传递变量的地址,在该地址空间的数据当子程序调用结束后被物理地保留下来。
[例6-4]利用指针变量作为函数的参数,用子程序的方法再次实现上述功能。
main()
{
void chang();
int*p1,*p2,a,b,*t;
scanf("%d,%d",&a,&b);
p1=&a;
p2=&b;
chang(p1,p2);
printf("%d,%d/",*p1,*p2);
return0;
}
void chang(int*pt1,int*pt2)
{
intt;
if(*pt1<*pt2)
{
t=*pt1;*pt1=*pt2;*pt2=t;}
return;
}
由于在调用子程序时,实际参数是指针变量,形式参数也是指针变量,实参与形参相结合,传值调用将指针变量传递给形式参数pt1和pt2。但此时传值传递的是变量地址,使得在子程序中pt1和pt2具有了p1和p2的值,指向了与调用程序相同的内存变量,并对其在内存存放的数据进行了交换,其效果与[例6-2]相同。
思考下面的程序,是否也能达到相同的效果呢?
main()
{
void chang();
int*p1,*p2,a,b,*t;
scanf("%d,%d",&a,&b);
p1=&a;
p2=&b;
chang(p1,p2);
printf("%d,%d/",*p1,*p2);
}
void chang(int*pt1,int*pt2)
{
int*t;
if(*pt1<*pt2)
{
t=pt1;pt1=pt2;pt2=t;
}
return;
}
程序运行结束,并未达到预期的结果,输出与输入完全相同。其原因是对子程序来说,函数内部进行指针相互交换指向,而在内存存放的数据并未移动,子程序调用结束后,main()函数中p1和p2保持原指向,结果与输入相同。
 



欢迎您使用http://Blogmove.cn提供的"博客搬家"和"博文三窟"服务.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值