C++扫盲篇之指针详解

目录
  • 前言
  • 指针为什么要有类型
  • 指针和数组
  • 二级指针
  • 指针与多态绑定
  • 函数指针
  • 类成员指针
  • 补充:用指针的指针指向指针数组
  • 总结

前言

指针对于学习C/C++的人来说是一道必须迈过去的坎,就像学习九阳神功必须要打通任督二脉一样的道理。虽然说随着智能指针的普及,很少需要程序员再手动操作原始指针, 但是如果你连原始指针的都没学好,那你怎么可能用好智能指针呢?

无论是原始指针还是智能指针,要想用好它就一定要做到知其然,知其所以然。

因为本文阅读对象是有了一定指针基础的童鞋,所以如果你对指针如果是处于一无所知的状态的话,建议先去温习下指针的基础知识,不然可能读起来会打击你求知的欲望。

指针为什么要有类型

是为了指针运算和取值。

当使用指针取值的时候需要知道怎么取值,比如按照多少个字节去取值,这是需要确定才能取到正确的值的,要知道用多少个字节去取就得知道指针的类型是什么。

我们知道指针的运算增加或者减少1意味着需要偏移指针所表示的类型的大小个字节数,比如说一个int字节的指针增加1,表示偏移4个字节(一般情况下int都是4个字节),所以这也是需要知道指针的类型。

指针和数组

本来从字面上来说指针和数组是八竿子打不着的,它们理应是井水不犯河水的,怎么就扯上了呢?我们经常听说数组指针、指针数组,这些都是什么意思呢?他们到底是指针还是数组呢?下面将一一为你解答。

指针数组,首先它是一个数组,数组里面的每个元素都是一个指针,例如比如int *p[4] 就是一个指针数组,因为运算符[]的优先级运算符*的优先级高,所以p优先和[]组成数组,然后*和类型int组合成数组元素的类型。 例如以下程序就是一个指针数组的示例:

main.c
#include <stdio.h>
int main(){
    char *str[3] = {
        "我是数组1",
        "我是数组2",
        "我是数组3"
    };
    printf("%s\n%s\n%s\n", str[0], str[1], str[2]);
    return 0;
}

数组指针,首先它是一个指针,这个指针所指向的对象是数组,比如这个指针是p,那么通过解引用*p获得内容就是一个数组,例如int (*p)[4],主意带上括号, 通常数组指针也作为一个二维数组来使用。

二级指针

所谓的二级指针其实就是一个指向指针的指针,例如int **p就是一个二级指针,它内部存放的对象是一个指针,通过一次解引用获得的是内存存放的指针的地址,需要再次对这个内部的指针进行解引用才能获取到 这个真是内容的值。

理解起来有点绕,那么这个拗口的二级指针有什么作用呢?二级指针在C++中可能用的不多,但是在C中是经常使用的一把利器,它通常作为一个函数的参数,起到在函数内部对一个指针进行初始化的作用, 比如经典的音视频处理工具FFmpeg中就大量使用了二级指针。 以下例子展示如何通过二级指针对指针形式赋值:

main.cpp
void initP(int **p){
    *p = new int(10);
}

int main() {
    int *p = nullptr; // 一个空的指针
    initP(&p); // 通过二级指针初始化指针p
    std::cout << "*p的值:" << *p << endl;
    delete p;
    return 0;
}

可能在这里就有人和当初笔者刚接触C语言一样迷惑了,难道不能通过给函数传递一级指针给指针初始化吗?这是不行的,这是因为值传递的缘故,像深入探讨的童鞋们可以写个例子打印下实参的具体地址对比下研究下其背后的原理。

指针与多态绑定

我们都知道C++是一门面向对象的设计语言,支持多态就是它的一个重要特性之一,在学习C++类的相关知识的时候老师就告诉我们:*在C++语言中,当我们使用基类的引用(或指针)调用一个虚函数时将发生动态绑定。*也就是 说使用通过父类的指针或引用就能按照实参的实际类型是父类还是子类调用不同的虚函数。

例如如以下代码:

main.cpp
class Base{
public:
    virtual void print() const{
        std::cout << "base print" << endl;
    }
    virtual ~Base(){
    }
};

class Child:public Base{
public:
    void print() const override{
        std::cout << "Child print" << endl;
    }
};

void testPrint(const Base &base){
    base.print();
}

int main() {
    Base a = Child();
    testPrint(a);// 打印Base print
    Child b = Child(); // 注意,不能写成Base b = Child(),否则打印的是Base的print
    testPrint(b); // 打印Child print
    Base *c = new Child(); // 指针,动态类型与静态类型不一致
    testPrint(*c); // 打印Child print

    Base &&r = Child(); // 表达式是右值引用,动态类型与静态类型不一致
    testPrint(r); // 打印Child print
    return 0;
}

为什么在上面的程序中变量a的实际类型是Child,但是函数testPrint内部调用的却是父类的打印方法呢?不是说引用会触发多态吗?函数testPrint也是通过引用传递的呀, 真是百思不得其jie呀。

要解开这个疑惑就得了解下静态类型和动态类型的知识了。静态类型在编译时总是已知的,首先静态类型是变量声明时的类型或表达式生成的类型;动态类型则是变量或表达式表示的内存中的对象的类型,动态类型直到运行时才可知。 如果变量在定义时表达式既不是引用也不是指针,则它的动态类型永远与静态类型一致的,也就是声明时所指的类型,否则的话静态类型可能与动态类型不一致。

那么有了静态类型与动态类型的概念之后再结合注释看上面的示例代码是不是就有一种拨开云雾见青天的感觉了呢?

函数指针

函数指针顾名思义就是指向函数的指针,它的定义:函数指针是指向函数的指针变量。因此“函数指针”本身首先应是指针变量,只不过该指针变量指向函数。

其声明方式是:

返回值类型 (*函数名) (参数)

函数指针的一个重要用途就是作为函数的参数,用于在函数内部进行指针函数的调用,一般用作回调函数,比如在创建一个POSIX线程的就需要传递一个函数指针用于指明该线程做点什么事情。

以下代码展示了一个简单的函数指针的使用方法:

void testFunc(int a,int b,void (*func)(int c,int d) ){
    // do something
    func(a,b);
}

void callback(int a,int b){
}

int main() {
    testFunc(1,2,callback);
    return 0;
}

类成员指针

这里类成员指针表示的是指向类的某个对象的非静态成员的指针,而不是表示类成员变量的指针,首先需要区分好这是两个不同的概念,如果不能好好区分这两个概念的童鞋,需要再好好思考一下。

成员指针的类型囊括了类的类型以及成员的类型。当初始化一个这样的指针时,我们令其指向类的某个成员,但是不指定该成员所属的对象;直到使用成员指针时,才提供成员所属的对象。

和其他指针一样,在声明成员指针时我们也使用*来表示当前声明的名字是一个指针。与普通指针不同的是,成员指针还必须包含成员所属的类。下面是一个使用的示例:

class Person{
public:
    virtual void print() const{
        std::cout << "base print" << endl;
    }

    virtual ~Person(){
    }

public:
    string lastName;
    string firstName;
};

int main() {
    string Person::*p; // 声明了一个类成员指针
    p = &Person::firstName; // 成员变量的指针指向了Peron的name
    Person person;
    person.*p = "hello"; // 使用成员指针
    p = &Person::lastName; // 成员变量的指针指向了Peron的lastName
    person.*p = "world"; // 使用成员指针
    std::cout << person.firstName << " " << person.lastName << std::endl;
    return 0;
}

至于这个成员指针有什么用处,给笔者的感觉就是重新命了一个别名的感觉,甚至有点脱裤子放屁?但是存在即合理,不是没有用处,只是笔者见过那种场景而已吧。。。

除了有指向类成员变量的指针外还有指向类成员函数的指针,这里就不多展开探讨了!!!

补充:用指针的指针指向指针数组

#include<stdio.h>
int change(char **p)
{
	int i, j;
	for (i = 0; i < 5; i++)
	{
		for (j = 0; *(*(p + i) + j) != '\0'; j++)//利用指针的指针取二维数组的元素
		{
			*(*(p + i) + j) = 'c';
			printf("%c", *(*(p + i) + j));
		}
		printf("\n");
	}
	return 0;
}

int main(void)
{
	char *a[5] = { "hello", "zhuyu", "jiajia", "linux","Ubuntu" };//如果想使用 需使用指针数组即*a[5] 声明一个有五个字符串指针的数组。
	                                                              //但是由于每个元素都是指针字符串,所以只能够读取,而不能够写入。
	change(a);
	return 0;
}

总结

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

(0)

相关推荐

  • C++中指针函数与函数指针的使用

    指针函数 指针函数是一个函数,只不过指针函数返回的类型是某一类型的指针. 格式: 类型名* 函数名(函数参数列表) 使用: /* * 指针函数,返回int* 指针变量 */ int* add(int a, int b) { int *p; int c = a + b; p = &c; return p; } int main() { int* p; p = add(1, 4); printf("%d\n", *p); getchar(); return 1; } 函数指针 函数

  • C++中的函数指针与函数对象的总结

    篇一.函数指针函数指针:是指向函数的指针变量,在C编译时,每一个函数都有一个入口地址,那么这个指向这个函数的函数指针便指向这个地址. 函数指针的用途是很大的,主要有两个作用:用作调用函数和做函数的参数. 函数指针的声明方法:数据类型标志符 (指针变量名) (形参列表):一般函数的声明为: int func ( int x );而一个函数指针的声明方法为:int (*func) (int x);前面的那个(*func)中括号是必要的,这会告诉编译器我们声明的是函数指针而不是声明一个具有返回型为指针

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

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

  • C++中指向结构体变量的指针

    定义: 结构体变量的指针就是该变来那个所占据的内存段的起始地址.可以设一个指针变量,来指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址. 设p是指向结构体变量的数组,则可以通过以下的方式,调用指向的那个结构体中的成员: (1)结构体变量.成员名.如,stu.num. (2)(*p).成员名.如,(*p).num. (3)p->成员名.如,p->num. 复制代码 代码如下: #include<iostream>#include<string>using na

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

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

  • 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++智能指针实例详解

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

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

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

  • C++中字符串以及数组和指针的互相使用讲解

    C++字符串与指针 在C++中可以用3种方法访问一个字符串(在第5章介绍了前两种方法). 用字符数组存放一个字符串 [例]定义一个字符数组并初始化,然后输出其中的字符串. #include <iostream> using namespace std; int main( ) { char str[]="I love CHINA!"; cout<<str<<endl; return 0; } 运行时输出: I love CHINA! 用字符串变量存放

  • C++扫盲篇之指针详解

    目录 前言 指针为什么要有类型 指针和数组 二级指针 指针与多态绑定 函数指针 类成员指针 补充:用指针的指针指向指针数组 总结 前言 指针对于学习C/C++的人来说是一道必须迈过去的坎,就像学习九阳神功必须要打通任督二脉一样的道理.虽然说随着智能指针的普及,很少需要程序员再手动操作原始指针, 但是如果你连原始指针的都没学好,那你怎么可能用好智能指针呢? 无论是原始指针还是智能指针,要想用好它就一定要做到知其然,知其所以然. 因为本文阅读对象是有了一定指针基础的童鞋,所以如果你对指针如果是处于一

  • C语言学习进阶篇之万字详解指针与qsort函数

    目录 前言 函数指针 代码一 代码二 函数指针数组 函数指针数组的用途 计算器的基本代码 函数指针实现简单的计算机 函数指针数组实现简单计算机 指向函数指针数组的指针 回调函数 简单的冒泡排序 冒泡排序的优化 qsort函数 qsort函数介绍 qsort实现冒泡排序 qsort排序结构数据 模拟实现qsort函数 写在最后 前言 前面学到了字符指针,指针数组是一个存储指针的数组,数组指针是一个指向函数的指针,数组参数和指针参数.其中不乏有很多需要注意的知识点,例如:&数组名和数组名表示的含义,

  • C语言指针详解

    前言:复杂类型说明     要了解指针,多多少少会出现一些比较复杂的类型,所以我先介绍一下如何完全理解一个复杂类型,要理解复杂类型其实很简单,一个类型里会出现很多运算符,他们也像普通的表达式一样,有优先级,其优先级和运算优先级一样,所以我总结了一下其原则:从变量名处起,根据运算符优先级结合,一步一步分析.下面让我们先从简单的类型开始慢慢分析吧: int p; //这是一个普通的整型变量   int *p; //首先从P 处开始,先与*结合,所以说明P 是一个指针,然后再与int 结合,说明指针所

  • C++函数指针详解

    函数指针基础: 1. 获取函数的地址 2. 声明一个函数指针 3.使用函数指针来调用函数 获取函数指针: 函数的地址就是函数名,要将函数作为参数进行传递,必须传递函数名. 声明函数指针 声明指针时,必须指定指针指向的数据类型,同样,声明指向函数的指针时,必须指定指针指向的函数类型,这意味着声明应当指定函数的返回类型以及函数的参数列表. 例如: double cal(int); // prototype double (*pf)(int); // 指针pf指向的函数, 输入参数为int,返回值为d

  • C++智能指针详解

    目录 一. unique_ptr独占指针 特点 创建方式 传递方式 简单使用 隐藏危险 二. shared_ptr 计数指针 特点 传递方式 隐藏危险 三. weak_ptr 优缺点: 智能指针由原始指针的封装,优点是可以自动分配内存,不用担心内存泄漏问题. 用于解决独占/共享所有权指针的释放,传输等问题. 但是没有原始指针方便. 一. unique_ptr独占指针 特点 都是围绕独占展开 特点一: 如其名,独占.也就是说同一个内存空间同时只能有一个指针来管理. int* pi = new in

  • C++ boost scoped_ptr智能指针详解

    目录 一.智能指针-唯一所有者 二.接口类分析 一.智能指针-唯一所有者 boost::scoped_ptr 是一个智能指针,它是动态分配对象的唯一所有者. boost::scoped_ptr 无法复制或移动.此智能指针在头文件 boost/scoped_ptr.hpp 中定义. 二.接口类分析 scoped_array 分析 scoped_array 的类部分原始代码如下: template<class T> class scoped_array // noncopyable { priva

  • C++ Boost PointerContainer智能指针详解

    目录 一.提要 二.智能指针Boost.PointerContainer 三.练习 一.提要 在 C++11 中,Boost.PointerContainer是另一个智能指针,一般是用来生成集合数据的,本文阐述这种指针的特点和用法. 二.智能指针Boost.PointerContainer 库 Boost.PointerContainer 提供专门用于管理动态分配对象的容器.例如,在 C++11 中,您可以使用 std::vector<std::unique_ptr<int>> 创

  • Go语言基础学习之指针详解

    目录 1. 什么是指针 2. 指针地址 & 指针类型 3. 指针取值 4. 空指针 5. make 6. new 7. make 和 new 的区别 8. 问题 今天来说说 Go 语言基础中的指针. Go 语言中指针是很容易学习的,Go 语言中使用指针可以更简单的执行一些任务. 1. 什么是指针 Go 语言中,一个指针变量指向了一个值的内存地址.和 C.C++ 中的指针不同,Go 语言中的指针不能进行计算和偏移操作. Go 语言中的函数传参都是值拷贝,当我们想要修改某个变量的时候,我们可以创建一

  • C语言指针详解及用法示例

    新手在C语言的学习过程中遇到的最头疼的知识点应该就是指针了,指针在C语言中有非常大的用处.下面我就带着问题来写下我对于指针的一些理解. 指针是什么? 指针本身是一个变量,它存储的是数据在内存中的地址而不是数据本身的值.它的定义如下: int a=10,*p; p=&a int a=10; int *p=&a; 首先我们可以理解 int* 这个是要定义一个指针p,然后因为这个指针存储的是地址所以要对a取地址(&)将值赋给指针p,也就是说这个指针p指向a. 很多新手都会对这两种定义方法

  • C++中函数指针详解及代码分享

    函数指针 函数存放在内存的代码区域内,它们同样有地址.如果我们有一个int test(int a)的函数,那么,它的地址就是函数的名字,如同数组的名字就是数组的起始地址. 1.函数指针的定义方式:data_types (*func_pointer)( data_types arg1, data_types arg2, ...,data_types argn); c语言函数指针的定义形式:返回类型 (*函数指针名称)(参数类型,参数类型,参数类型,-); c++函数指针的定义形式:返回类型 (类名

随机推荐