c++指向指针的指针与 c++指针作为函数参数传递问题
一直搞不明白,c++中指针到底是个啥东西,今天遇到到c++,指向指针的指针的问题,突然有点开窍了。
举个例子:
int main(int argc, char** argv)
{
int a[5]={1,2,3,4,5};
int *p=a;
int **pointer=&p;
std::cout<<"a="<<a<<std::endl;
std::cout<<"p="<<p<<std::endl;
std::cout<<"&a[0]="<<&a[0]<<std::endl;
std::cout<<"*pointer="<<*pointer<<std::endl;
std::cout<<std::endl;
std::cout<<"poiner="<<pointer<<std::endl;
std::cout<<"&p="<<&p<<std::endl;
std::cout<<std::endl;
std::cout<<"**pointer="<<**pointer<<std::endl;
std::cout<<"*p="<<*p<<std::endl;
std::cout<<"a[0]="<<a[0]<<std::endl;
std::cout<<std::endl;
std::cout<<"&pointer="<<&pointer<<std::endl;
}
运行结果:
对于指针来说,用 int *p=a举例子, 这句代码:std::cout<<"p="<<p<<std::endl,输出的是指针p指向的变量的地址,也就是p指针所保存的内容,也就是数组a的首地址,也就是a[0]的地址。所以从运行结果可以看出来,它们的值都是相同的。对于*p来说,这句代码:std::cout<<"*p="<<*p<<std::endl;输出的是p指向的变量所保存的内容,也就是a[0]的内容。
对于&p,则表示的是指针p自身在内存中的地址。这个地址可被指向指针的指针所指。比如,int **pointer=&p;跟上面一样,pointer指向p指针在内存中的地址,也就是pointer保存p指针在内存中的地址。*pointer则保存p指针所保存的内容,即p指针所指向的a[0]的地址。其实可以这么认为,*pointer也是个地址,这个地址其实就是a[0]的地址。**pointer则是指向a[0] 中的内容。&pointer则是pointer指针在内存中的地址。
一句话,对于一个指针符号来说,它表示的是指向的对象的内存地址,*+指针符号,则表示所指向对象保存的内容。
例如:
int main(int argc,char* argv[])
{
int *a = new int;
*a = 5;
int **ptr = &a;
cout << "&a: " << &a<<" *a: "<<*a<<" a: "<<a << endl;
cout << "*ptr: " << *ptr << " **ptr " << **ptr << " ptr: "<<ptr<<endl;
system("pause");
}
a指向5的内存地址
*a表示a指向的这个地址中的值即5
&a表示a指针的地址
**ptr为指向指针的指针
ptr的值为a指针的地址
*ptr表示表示a指针的这个地址值存放的值,即5的内存地址
**ptr则表示以a指针的这个地址值存放的值为地址的内存中存放的值,即5.
c++指针作为函数参数传递问题
作者:清林,博客名:飞空静渡
博客地址:http://blog.csdn.net/fjb2080
其实,对于C 或者C++ ,最难的一块地方估计就是指针了。指针是强大的,但也是很多人载在这里的地方。
前段时间写了一篇文章《C ++之 数组与指针的异同 》对C 和C ++中的指针做了一个初步的讲解。这次将讲解一下指针作为函数参数传递的问题。
很多人对于指针的使用是有所了解的,但还是经常会载在指针的问题上,是因为还不够了解指针的本质,其实如果了解指针的本质,对指针的使用也就一目了然了。
作为C 的初学者,经常会遇到指针作为函数参数传递的两个经典的问题。这里,我将透过指针的本质来来讲解这两个问题,这样以后无论你遇到什么样的指针问题,如果你以这样的方法来分析指针也许就迎刃而解了!
首先,第一个问题是这样的:
写一个函数,交换两个参数中的值。
初学者往往会这样写:
void exchange(int x, int y)
{
int p=x;
x = y;
y = p;
}
之后,你会查找资料了解到应该这样写:
void exchange(int *x, int *y)
{
int *p=x;
*x = *y;
*y = *p;
}
第二个问题是,写一个给某个指针分配内存的函数:
初学者往往是这样写:
void my_malloc(void* p, int size)
{
p = malloc(sizeof(int)*size);
}
然后又查在资料,知道应该这么写:
void my_malloc(void** p, int size)
{
*p = malloc(sizeof(int)*size);
}
虽然,网上很多这样的讨论,也有很多人做过很多的解释,但始终都无法给出一个令人一目了然,并可以长久记住的说法,这篇文章就是想试图解决这样的问题,给初学者一个原理性的了解!
首先,一定一定记住一点, 指针和变量一样,也是有地址的,只不过变量的值被解释成一个值,而指针的值被解释成一个地址。
下面,我们看一下代码:
void main()
{
int x;
int *p;
}
我们看这个函数的内存结构:
这是一个函数的栈结构,我们可以看到,变量和指针都占用了4 个字节。而且,由于我们对它们没有初始化,所以变量x 和指针p 里的内容都是随机的,就是说x 的值是不确定的,p 有可能指向某个内存地址,如果现在对p 操作也许会导致程序崩溃。
<!-- @page { margin: 2cm } P { margin-bottom: 0.21cm } -->
其实,我们记住了,指针也是有地址的 这个概念,很多问题就迎刃而解了。
下面,我来分析一下,指针作为函数参数传递的情况。
<!-- @page { margin: 2cm } P { margin-bottom: 0.21cm } -->
如果,我们的代码是这样的,你看会怎么样:
int main(int argc, char* argv[])
{
int *a = new int(10);
func(a);
return 0;
}
第一个要说的当然是:指针也是有地址的。
第二个要说的是:当给一个函数的参数传递一个变量是,这个变量是复制过去的。
对于第二点,我们在理解void exchange(int x, int y) 函数想交换这两个变量的的值时就应该理解了。
例如:
int a;
int b;
exchange(a,b);
不能交换a 和b 的值,因为此时exchange(a,b) 中的a 和b 并不是原来的a 和b 变量,它们只不过是被复制过去了。
有了这两个概念,就不难理解指针作为函数参数传递的问题。
首先,我们来看下上面的代码中的a 指针和p 指针的内存结构。
我们看到,当我们以a 作为func 函数的参数传递进去的时候,函数复制了这个指针,但这两个指针的内容是一样的,也就是说是指向同一个内存,即10 。
如果你还不了解的话,我就通过一段代码和测试再来说明:
- #include <stdio.h>
- void func(int* p)
- {
- printf("*p = %d/n", *p);
- printf("&p = %p/n", &p);
- }
- int main(int argc, char *argv[])
- {
- int *a = new int(10);
- printf("*a = %d/n", *a);
- printf("&a = %p/n", &a);
- func(a);
- return 0;
- }
编译:g++ -g -Wall test1.cpp
运行:./a.out
输出:
*a = 10
&a = 0xbfd4447c
*p = 10
&p = 0xbfd44460
我们看到输出,a 指向的地址的值和p 指向的地址里的值是一样的,都是10 。然而,对于指针a 和p 来说,它们自身的地址是不一样的,所以我们看到,函数func 复制了指针a 给p ,它们的值一样,但有不同的地址,是不同的指针。
我们再进一步:
- #include <stdio.h>
- void func(int* p)
- {
- printf("*p = %d/n", *p);
- printf("&p = %p/n", &p);
- printf("&*p = %p/n", &*p);
- }
- int main(int argc, char *argv[])
- {
- int *a = new int(10);
- printf("*a = %d/n", *a);
- printf("&a = %p/n", &a);
- printf("&*a = %p/n", &*a);
- func(a);
- return 0;
- }
编译输出:
*a = 10
&a = 0xbfe1c77c
&*a = 0x94b6008
*p = 10
&p = 0xbfe1c760
&*p = 0x94b6008
我们可以进一步看到,a 指针所指向的值的地址和p 指针所指向的值的地址是一样的,都是 0x94b6008 ,就如同上图所示,为了加深印象,再看一下这个图 ,然后再对比一下程序输出 ,然后在体会一下我在上面提到的两点 ,一点是:指针是有地址的 。另一点是:函数的参数是复制过去的 。
说到这里,我们再回到文章开始时提到的两个问题,一个是交换问题:
void exchange(int *x, int *y)
{
int *p=x;
*x = *y;
*y = *p;
}
那么这样为什么可以交换:
int a = 2;
int b = 3;
exchange(&a, &b);
上我们以a 和b 的地址传递给exchange 函数时,函数复制了这两个地址,并赋值给x 和y 这个两个指针,这两个指针是指向变量a 和b 的,它们的图形如下:
那么,当我们反引用指针时:
int *p=x;
*x = *y;
*y = *p;
我们操作的是a 和b 里面的变量的值,所以,我们交换a 和b 的值就成功了。
我们再来看下第二个问题:
void my_malloc(void* p, int size)
{
p = malloc(sizeof(int)*size);
}
当这样时:
int *a;
my_malloc(a, 10);
为什么这个会失败!
下面,我来分析一下:
当我们调用my_malloc(a, 10); 函数,而函数还没执行到p = malloc(size); 语句时,情况是这样的:
我们看到a 和p 的指针的值都是一样的,都是指向某个不确定的地址。
这时,我们执行这个语句:
p = malloc(sizeof(int)*size);
我们把这个语句分开两部分来看,一个是先执行malloc(sizeof(int)*size) ,然后在执行赋值语句,把malloc(sizeof(int)*size) 的返回值付给p 。
第一步:先执行malloc(sizeof(int)*size) ;(这里我们只考虑malloc 分配内存成功的情况)
第二步:把执行malloc(sizeof(int)*size) 的返回值付给了p ,如下图:
由上图,我们可以知道,这就是为什么,我们还是不能给a 分配地址的了。
下面我们来分析这个:
void my_malloc(void** p, int size)
{
*p = malloc(sizeof(int)*size);
}
int *a;
my_malloc(&a , 10);
这样执行,为什么会成功!
我们看到,当执行函数
my_malloc(void** p, int size);
但还没有执行
*p = malloc(sizeof(int)*size);
语句时,它们的内存结构图如下所示:
其实这里,我们可以把二维指针和一维指针当成和变量一样,也是有地址的。只不过它的解释不一样而已。
变量:里面的值是一个数值。
一维指针:里面的值是个地址,而这个地址里的值是个数值。
二维指针:里面的值是个地址,而这个地址里的值也是个地址。
那么,我看着图来解释p :
p 里面是一个地址,这个地址是&a ,即是a 指针的地址值,而a 指针地址里面的值也是个地址,这个地址是指向一个不确定的地方,说得坳口,慢慢对比图来理解就会好了!
执行malloc(size) 后的图如下:
然后在执行赋值语句:
*p = malloc(sizeof(int)*size);
后,如下图所示:
然后,我们就给指针a 分配内存成功了。
c++指向指针的指针与 c++指针作为函数参数传递问题的更多相关文章
- 不可或缺 Windows Native (18) - C++: this 指针, 对象数组, 对象和指针, const 对象, const 指针和指向 const 对象的指针, const 对象的引用
[源码下载] 不可或缺 Windows Native (18) - C++: this 指针, 对象数组, 对象和指针, const 对象, const 指针和指向 const 对象的指针, con ...
- ”危险“的RESTRICT与GCC的编译优化(编程者对编译器所做的一个“承诺”:使用restrict修饰过的指针,它所指向的内容只能经由该指针修改)
restrict是C99标准中新添加的关键字,对于从C89标准开始起步学习C语言的同学来说(包括我),第一次看到restrict还是相当陌生的.Wikipedia给出的解释如下: In the C p ...
- C基础知识(3):指针--概念、数组中指针的递增/递减、指针数组&数组指针、指向指针的指针
指针是一个变量,其值为另一个变量的地址. 所有指针的值的实际数据类型,不管是整型.浮点型.字符型,还是其他的数据类型,都是一样的,都是一个代表内存地址的长的十六进制数. 下面从4个代码例子分别讲述以下 ...
- 指向结构的指针 struct结构名称 *结构指针变量名
//指向结构的指针 struct结构名称 *结构指针变量名 //(*结构指针变量名).成员变量名//结构指针变量->成员变量名 1 #include<stdio.h> 2 #incl ...
- 一个包含n个结点的四叉树,每一个节点都有4个指向孩子节点的指针,这4n个指针有(3*n+1)个空指针. 4*n-(n-1) = 3*n+1
因为每个树都有一个头结点.头结点下面是4个子结点,然后每个子结点又有4个子节点.例如一个2层的四叉树,就会有5个结点,但头结点并不能计算进去.他的4个子节点下面接的都是空指针,可以得出空指针的个数为4 ...
- C和指针 第十三章 高级指针话题
高级声明: int (*f)(); 这里声明有两个括号,第二个括号是函数调用,第一个括号是聚组作用.(*f)是一个函数,所以f是指向返回整型的函数的指针.程序中的每个函数都位于,内存中某个位置,所以存 ...
- C++C++ 指针(二)--c++ 指针(二)--c++
一.内存管理:new和delete 1.new操作符:从操作系统获得内存块,并返回该内存块的首地址. delete操作符:将new申请的内存返还给操作系统. 开始一个简单的例子: #include & ...
- Qt 智能指针学习(7种指针)
Qt 智能指针学习 转载自:http://blog.csdn.net/dbzhang800/article/details/6403285 从内存泄露开始? 很简单的入门程序,应该比较熟悉吧 ^_^ ...
- C语言堆内存管理上出现的问题,内存泄露,野指针使用,非法释放指针
C语言堆内存管理上出现的问题,内存泄露,野指针使用,非法释放指针 (1)开辟的内存没有释放,造成内存泄露 (2)野指针被使用或释放 (3)非法释放指针 (1)开辟的内存没有释放.造成内存泄露,以下的样 ...
随机推荐
- 线段树(成段更新,区间求和lazy操作 )
hdu1556 Color the ball Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/O ...
- 小技巧------教你释放C盘空间
1.删除休眠文件hiberfil.sys. 该文件在c盘根部目录为隐藏的系统文件,隐藏的这个hiberfil.sys文件大小正好和自己的物理内存是一致的,当你让电脑进入休眠状态时,windows7在关 ...
- open live writer 安装 markdown 插件
我自己用的是 OpenLiveWriter ,所以本篇只讲 OLW 的,WindowsLiveWriter 戳 MarkdownInWindowsLiveWriter,OpenLiveWriter 戳 ...
- 使用_Capistrano_进行自动化部署(2)
之前的一篇文章是为了解决问题而写的,很多东西都没有介绍清楚,这一篇文章就是完整介绍一下 Capistrano,主要的参考来源是 Modern PHP 这本书. Capistrano 是用于自动部署应用 ...
- SQL Server 2008 R2数据库镜像部署图文教程
数据库镜像是一种针对数据库高可用性的基于软件的解决方案.其维护着一个数据库的两个相同的副本,这两个副本分别放置在不同的SQL Server数据库实例中 概述 “数据库镜像”是一种针对数据库高可用性的基 ...
- Ubbeditor的使用
简单介绍: 作为一种放在客户端文本编辑器,此时不能支持将JS.Html代码直接发送给服务器,这样将会给服务器带来极大的危险,比如UMEditor(富文本编辑器),它的使用需要关闭服务器端的代码检查的, ...
- linux memory dump--http://www.forensicswiki.org/wiki/Tools:Memory_Imaging
Linux provides two virtual devices for this purpose, '/dev/mem' and '/dev/kmem', though many distrib ...
- Spark集群安装和WordCount编写
一.Spark概述 官网:http://spark.apache.org/ Apache Spark™是用于大规模数据处理的统一分析引擎. 为大数据处理而设计的快速通用的计算引擎. Spark加州大学 ...
- 巧用Salt,实现CMDB配置自动发现
随着互联网+新形势的发展,越来越多的企业步入双态(稳敏双态)IT时代,信息化环境越来越复杂,既有IOE三层架构,也有VCE.Openstack等云虚拟化架构和互联网化的分布式大数据架构.所以,企业急需 ...
- MySQL和MSSQL差异(增量)备份的原理
MySQL和MSSQL差异(增量)备份的原理 对于真正的增量备份来说,只需要记录当前每页最后的检查点的LSN,如果大于之前全备时的LSN,则备份该页面,否则不用备份 这大大加快了备份速度和恢复时间,同 ...