C/C++中指针的深入理解

目录
  • 计算机的内存模型
  • 指针与指针常量
  • 指针变量和指针常量
  • 指针变量和数组
  • 函数指针
  • C++中的引用
  • 传值还是传引用
  • C++中的new关键词
  • 总结

计算机的内存模型

CPU是计算机的核心部件,要想让一个CPU工作,就必须向它提供指令和数据,指令和数据在存储器中存放,也就是我们平时说的内存。

内存分为:物理内存和虚拟内存,物理内存对应着计算机中的内存条,虚拟内存是操作系统内存管理系统假想出来的,由于这些不是我们本文的重点,我们就不做区分。

在不考虑cpu缓存的情况下,计算机运行程序本质上是对内存中的数据的操作,存储器被划分为多个存储单元,存储单元从零开始顺序编号,CPU要从内存中读取数据,首先要指定存储单元的地址。

CPU从内存中读取数据的过程如图所示:

计算机为了方便管理内存,将内存的每个单元用一个数字编号

指针与指针常量

指针的本意就是内存地址,我们可以通俗的理解成内存编号,既然计算机通过编号来操作内存单元,这就造成了指针的高效率

指针变量:

  • 通俗理解为存储指针的变量,也就是存储内存地址(内存编号)的变量
  • 指针变量和int,float,char等类型一样同属变量类型,指针变量类型占四个字节(32位机器下),存储的是32位的内存地址
    星号:
  • 在C\C++中(*)被定义为取内容符号
  • 虽然所有指针变量占的内存大小和存储的内存地址大小都是一样的,但是由于存储的只是数据的内存首地址,所以指针变量存储的内存地址所指向的数据类型决定着如何解析这个首地址
  • 比如int型指针变量,我们需要从该指针变量存储的首地址开始向后一直搜索4个字节的内存空间
  • 所以当我们使用*p,必须知道p是一个什么类型的指针

指针变量和指针常量

指针变量首先是一个变量,由于指针变量存储了某个变量的内存首地址,我们通常认为"指针变量指向了该变量",同时指针变量时一个变量,它的值是可以变动的。

相反,指针常量可通俗地理解为存储固定的内存单元地址编号的量,它一旦存储了某个内存地址以后,不可再改存储其他的内存地址了

举个例子:

void f(const int *x,int *y)
{
	*x=2;//错误,由于x前面有个const修饰,所以不可以修改x所指向的内存单元的内容
	//正确写法
	*y=3;
}

指针变量和数组

先看一个例子:

	int a[5]={1,2,3,4,5};
	int *ptr=(int*)(&a+1);
	cout<<*(a+1)<<endl;
	cout<<*(ptr-1)<<endl;

输出结果为2和5,首先我们看一下&a+1的含义:

  • 我们知道C\C++中规定数组名表示这个数组的首地址,而这里出现了&a这样的符号,本来a就是指针常量,再次取地址难道不是非法操作?
  • 这时我们可以将这个&a看成是指向数组的指针,也称为行指针,&a的类型是int (*p)[5],一个步长即5个元素的长度,&a + 1代表往后移动一个步长

分析:

  • a表示的是第一个元素的首地址,那么a+1指向的就是下一个元素的内存首地址,所以*(a+1)=2
  • 而&a则表示整个数据的首地址,那么&a+1移动的内存数目就是整个数组所占字节数,假如原先数组中第一个元素的首地址是0,那么&a+1表示的就是20,而这个地址已经不属于数组了,接着通过(int*)(&a+1)将数组指针转换成整型指针,这样原先&a+1表示的数据范围20-39就缩小为20-23,正好是一个int型的大小,而ptr-1就是16了,表示的数据内存范围是16-19,这样*(ptr-1)正好是最后一个元素5了

上面的例子,只是通过简单的数据类型来说明内存分布,但是实际上一些复杂的数据类型,尤其是一些自定义的类或者结构体类型,内存分布还要充分考虑到字节对齐。

函数指针

函数指针是指向函数的指针变量,C\C++程序在编译时,每个函数都有一个入口地址,该入口地址就是函数指针所指向的地址,有了指向函数的指针变量后,可用该指针变量调用函数,同时也可以做函数的参数

我们先看函数指针调用函数,如下:

int f(int x, int y) {
	return x + y;
}
//申明一个函数指针
typedef int (*pf)(int, int);
int main()
{
	int a = 1;
	int b = 2;
	//将函数f地址赋给函数指针pf
	pf = f;
	//利用函数指针调用函数
	int c = (*pf)(a, b);
	cout << c << endl;
}

需要注意的是,定义的函数指针类型时的函数签名(包括函数返回值和函数参数列表的类型,个数,顺序)要将赋值给该类型变量的函数签名保持一致,不然可能会发生很多无法预料的情况,还有C\C++规定函数名就表示函数入口地址,所以,函数名赋值时函数名前面加不加取地址符&都一样,但是在C++中取类的方法函数的地址时,这个&符号不能省略。

函数指针还有另外一个用处,就是作为一个函数的参数,在Windows编程中作为回调函数很常见:

typedef int (*PF)(int, int);
int f1(int x, int y)
{
	return x + y;
}
int f2(PF pf, int t)
{
	return (*pf)(3, t);
}
int main()
{
	//将函数f1作为参数传递给函数f2
	int c = f2(f1, 4);
	cout << c << endl;
	return 0;
}

C++中的引用

所谓引用,使用另外一个变量名来代表某一块内存,这就相当于同一个人有不同的名字,但是不管哪个名字,指的都是同一个人。

int a=1;
//通过&符号,将b定义为a的引用
int &b=a;
//b和a完全一样,等价于int c=a
int c=b;

注意,C++规定,定义一个引用的时候,必须马上初始化

传值还是传引用

如果变量类型是基元数据类型,比如int,float,bool,char等小数据类型被称为基元数据类型,那么赋值时传的是值,这时候b的值是a的拷贝,那么更改b不会影响到a,但是,如果变量数据类型是复杂数据类型,比如数组,类对象,那么赋值时传的就是引用,这个时候,a和b指向的都是同一个内存区域,那么无论更改a或者b都会相互影响。

最后,在利用C++中拷贝构造函数复制对象时需要注意,基元数据类型可以直接复制,但是对于引用类型数据,我们需要实现引用类型的真正复制

C++中的new关键词

在c++中通过new关键词定义一个对象,不能直接得到对象的实例,我们需要用一个指针去接收这个new出来的对象,我们引用这个对象必须使用指针引用运算符->

#include <iostream>
using namespace std;
class Person
{
public:
	Person()
	{

	}
	Person(int a, int b)
	{
		this->m_a = a;
		this->m_b = b;
	}
	int m_a;
	int m_b;
};
int main()
{
	//在C++中可以用以下形式来实例化一个对象,per1和per2为实例化的person类对象
	Person per1;
	int i = 1;
	int j = 2;
	Person per2(i, j);
}

在C++中,this关键词是一个指针,而不像在java中是一个类实例,在C++中*this才等价于java

class Person
{
public:
	Person(int number)
	{
		//C++中需要使用指针引用符号
		this->m_number = number;
	}
	//返回对象本身,,需要使用引用,因为返回值时会创建一个新的对象,使用引用的方式不会创建新的对象
	Person& getSelf()
	{
		return *this;
	}
	int m_number;
};

总结

到此这篇关于C/C++中指针的文章就介绍到这了,更多相关C/C++指针理解内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C/C++指针和取地址的方法

    先看下面的程序: 复制代码 代码如下: void main() {     int a = 100;     int *ap = &a;     printf("%p\n",&a);//输出:002AF744     printf("%p\n",ap);//输出:002AF744     printf("%d\n",*ap);//输出:100     printf("%p\n",&ap);//输出:00

  • C++中this指针的用法及介绍

    this指针只能在一个类的成员函数中调用,它表示当前对象的地址.下面是一个例子:   复制代码 代码如下: void Date::setMonth( int mn )     {      month = mn; // 这三句是等价的      this->month = mn;      (*this).month = mn;     } 1. this只能在成员函数中使用.全局函数,静态函数都不能使用this.实际上,成员函数默认第一个参数为T* const register this.如:

  • C++11新特性之智能指针(shared_ptr/unique_ptr/weak_ptr)

    shared_ptr基本用法 shared_ptr采用引用计数的方式管理所指向的对象.当有一个新的shared_ptr指向同一个对象时(复制shared_ptr等),引用计数加1.当shared_ptr离开作用域时,引用计数减1.当引用计数为0时,释放所管理的内存. 这样做的好处在于解放了程序员手动释放内存的压力.之前,为了处理程序中的异常情况,往往需要将指针手动封装到类中,通过析构函数来释放动态分配的内存:现在这一过程就可以交给shared_ptr去做了. 一般我们使用make_shared来

  • C++指针数组、数组指针、数组名及二维数组技巧汇总

    本文较为详细的分析了关于理解C++指针数组,数组指针,数组名,二维数组的一些技巧.是比较重要的概念,相信对于大家的C++程序设计有一定的帮助作用. 一.关于数组名 假设有数组: int a[3] = {1, 2, 3} 1.数组名代表数组第一个元素的地址,注意,不是数组地址(虽然值相等),是数组第一个元素地址,a 等同于 &a[0]; a+1是第二个元素的地址.比第一个元素地址a(或者&a[0])超出了一个整型指针的大小,在这里是4个字节(byte) cout << a <

  • C++中用指向数组的指针作函数参数

    1.一维数组名作为函数参数传递一位数组名,就相当于该数组的首个元素的地址: 复制代码 代码如下: int a[10];int *p;p=a;//p=a与p=&a[0]是等价的 实例代码: 复制代码 代码如下: #include<iostream>using namespace std;int main(){ int a[10]={1,2,3,4,5,6,7,8,9,10}; void Print(int *p ,int n); Print(a,10); cout<<endl

  • C++中的对象指针总结

    指向对象的指针在建立对象的时候,变异系统会给每一个对象分配一定的存储空间,以存放其成员.对象空间的起始地址就是对象的指针.可以定义一个指针变量,用来存放对象的指针. 一个简单的示例1.1: 复制代码 代码如下: #include<iostream>using namespace std;class Student{ public:  int num;  int score;  Student(int ,int );//声明构造函数  void Print();//声明输出信息函数};Stude

  • C++智能指针实例详解

    本文通过实例详细阐述了C++关于智能指针的概念及用法,有助于读者加深对智能指针的理解.详情如下: 一.简介 由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete.程序员忘记 delete,流程太复杂,最终导致没有 delete,异常导致程序过早退出,没有执行 delete 的情况并不罕见. 用智能指针便可以有效缓解这类问题,本文主要讲解参见的智能指针的用法.包括:std::auto_ptr.boost::scoped_ptr.boost::shared_p

  • 深入理解c++指针的指针和指针的引用

    展示一下使用指针的指针和指针的引用修改传递给方法的指针,以便更好的使用它.(这里说的指针的指针不是一个二维数组) 为什么需要使用它们 当我们把一个指针做为参数传一个方法时,其实是把指针的复本传递给了方法,也可以说传递指针是指针的值传递. 如果我们在方法内部修改指针会出现问题,在方法里做修改只是修改的指针的copy而不是指针本身,原来的指针还保留着原来 的值.我们用下边的代码说明一下问题: int m_value = 1; void func(int *p) { p = &m_value; } i

  • C++指向类成员函数的指针详细解析

    首先 函数指针是指向一组同类型的函数的指针:而类成员函数我们也可以相似的认为,它是指向同类中同一组类型的成员函数的指针,当然这里的成员函数更准确的讲应该是指非静态的成员函数.前者是直接指向函数地址的,而后者我们从字面上也可以知道 它肯定是跟类和对象有着关系的. 函数指针实例: 复制代码 代码如下: typedef int (*p)(int,int);//定义一个接受两个int型且返回int型变量的函数指针类型int func(int x,int y){ printf("func:x=%d,y=%

  • 简单总结C++中指针常量与常量指针的区别

    我们先回顾下,什么是指针?什么是常量?指针是一种特殊的变量,它里面存储的内容是内存地址.常量是指其里面存储的内容不能发生改变的量.明白了这两个概念后,我们现在正式进入指针常量与常量指针. 1.指针常量与常量指针的概念 指针常量就是指针本身是常量,换句话说,就是指针里面所存储的内容(内存地址)是常量,不能改变.但是,内存地址所对应的内容是可以通过指针改变的. 常量指针就是指向常量的指针,换句话说,就是指针指向的是常量,它指向的内容不能发生改变,不能通过指针来修改它指向的内容.但是,指针自身不是常量

随机推荐