C++快速讲解(四):指针


前言:主要介绍指针的使用,动态内存的使用,数组和指针的区别,指针的运算以及指针与常量,指针与函数,二级指针,函数指针,引用的介绍。


1.指针使用

1.1 初始化指针

#include <iostream>
#include <string>

using namespace std;

int main() {

    //初始化指针
    int age = 10;
    int *p = &age;//p是一个指针,指向的是一个int类型的数据,这个数据是3
    string name = "zhangsan";
    string *pstr;//声明一个指针
    pstr = &name;//指向一个string类型的数据,这个数据是zhangsan

    //空指针声明.
    int *p1 = nullptr;//推荐使用这种方法
    int *p2 = NULL;
    int *p3 = 0 ;

    return 0;
}

  • 空指针注意:指针在声明的时候必须初始化,指针声明然后没有初始化,是很危险的.因为它会指向随机的区域,如果未来使用这个指针修改了指向区域的数据。

1.2 指针地址和大小

#include <iostream>

using namespace std;

int main() {

    //指针大小和大小
    int age = 88;
    int *p = &age;
    cout << "指针的地址是: " << &p <<endl;
    cout << "指针存储的是: " << p <<endl;
    cout << "指针大小是: " << sizeof p <<endl;
    cout << "age的大小是: " << sizeof age <<endl;
    return 0;
}

在这里插入图片描述

1.3 指针的解引用

#include <iostream>

using namespace std;

int main() {

    //指针解引用
    int age = 88;
    int *p = &age;
    //通过指针,获取到指向位置的数据 打印88
    cout << "*p:" << *p <<endl;
    //使用指针修改原来的age
    *p = 100;
    //使用指针和变量的方式打印age
    cout << "*p:" << *p <<endl;
    cout << "age: " << age <<endl;
    return 0;
}

在这里插入图片描述

2. 动态内存

2.1 申请和释放内存

#include <iostream>

using namespace std;
/*
 * 申请内存和释放内存
 */
int main() {

    //定义一个int类型的指针,并没有指向任何地方。
    int * int_ptr = nullptr;
    //在堆中申请内存,使用指针指向
    int_ptr = new int;
    //由于未赋值,所以输出的可能是未知的值
    cout << *int_ptr << endl;
    //修改开辟空间的数据值为100
    *int_ptr = 100;
    //解引用,输出100
    cout << *int_ptr << endl;
    delete int_ptr;  //释放内存,因为在堆中申请的内存,不会随着程序执行结束,内存会自动回收,必须进行手动释放内存才可以,
    return 0 ;
}

2.2 数组操作

#include <iostream>
using namespace std;
/*
 * 使用new int[] 来给数组申请动态内存 , 然后使用 delete[] 释放申请的内存
 */
int main() {

    //开辟了堆内存
    int *scores = new int[10]{55,66,77,88,99,1,2,3,4,5};
    cout<<"使用指针打印scores"<<endl;
    for (int i = 0;i<10;++i){
        cout<<*(scores+i)<<endl;
    }
    for (int i = 0; i < 10; ++i) {
         scores[i] = 90 +i;
    }
    cout<<"使用指针打印修改后的scores"<<endl;
    for (int i = 0;i<10;++i){
        cout<<*(scores+i)<<endl;
    }
    //只有在删除申请的数组内存,才需要跟上 []  delete[]xxx
    delete []scores;
    return 0 ;
}

在这里插入图片描述

2.3 栈和堆的使用

栈和堆的使用注意:定义的变量存储的位置位于栈内存中,栈内存的数据,当函数执行结束后即会被释放。堆内存中的容量相比栈内存要大多了,但是堆内存并不提供回收释放的工作,允许程序申请内存空间,但是同时也要自己负责内存空间的释放工作。

3.数组和指针

#include <iostream>
using namespace std;
/*
 * 数组与指针
 */
int main() {

    int age []{18,9,20,33,55,66};
    cout << "直接打印age:" <<age  << endl; //实际上是打印数组第一个元素的地址
    cout << "打印*age:" << *age  << endl;//取到的是第一个元素 :18
    int *age_ptr{age};//声明指针,存放的是数组第一个元素的地址
    cout << "打印age_ptr:" << age_ptr  << endl;
    cout <<"解引用:" <<*age_ptr  << endl;
    return 0 ;
}

在这里插入图片描述

4.指针运算

4.1 指针运算

#include <iostream>
using namespace std;
/*
 * 指针运算
 */
int main() {

    int age []{18,9,20,33,55,66};
    int *age_ptr{age};//声明指针,存放的是数组第一个元素的地址

    //使用数组的手法打印数组
    cout << age_ptr[0] << endl;
    cout << age_ptr[1] << endl;
    cout << age_ptr[2] << endl;
    cout << age_ptr[3] << endl;
    cout << age_ptr[4] << endl;
    cout << age_ptr[5] << endl;


    //对指针进行加法运算。由于score_ptr 是int类型,
    //而int类型占用4个字节,所以每次相加打印出来的地址都会变长4个字节
    cout << age_ptr  << endl;
    cout << (age_ptr + 1) << endl;
    cout << (age_ptr + 2) << endl;
    cout << (age_ptr + 3) << endl;
    cout << (age_ptr + 4) << endl;
    cout << (age_ptr + 5) << endl;

    //指针解引用取值
    cout << *age_ptr  << endl;
    cout << *(age_ptr + 1) << endl;
    cout << *(age_ptr + 2) << endl;
    cout << *(age_ptr + 3) << endl;
    cout << *(age_ptr + 4) << endl;
    cout << *(age_ptr + 5) << endl;
    return 0 ;
}

在这里插入图片描述

4.2 指针递增递减

#include <iostream>
using namespace std;
/*
 * 指针递增递减
 */
int main() {

    int age []{18,9,20,33,55,66};
    int *age_ptr{age};//声明指针,存放的是数组第一个元素的地址

    //指针递增
    for(int i = 0; i<sizeof(age)/sizeof (int); i++){
        cout << age_ptr << endl;
        cout << *age_ptr << endl;
        age_ptr++; // 指针移动指向下一个元素
    }

    cout << "------------" << endl;

    //指针递减
    int age2[] = {18,9,20,33,55,66};
    int lenghts = sizeof (age2) / sizeof (int);
    int *age_ptr2 = &age2[lenghts-1];//声明指针,存放的是数组最后一个元素的地址
    for(int i = lenghts-1; i>=0; --i){
        cout << age_ptr2 << endl;
        cout << *age_ptr2 << endl;
        age_ptr2--; // 指针移动指向下一个元素
    }
    return 0 ;
}

在这里插入图片描述

4.3 指针的等价判断

#include <iostream>
using namespace std;
/*
 * 等价判断
 */
int main() {

    string s1{"张三"};
    string s2{"张三"};

    string *p1 {&s1};
    string *p2 {&s2};
    string *p3 {&s1};


    cout << (p1 == p2) <<endl; 
    cout << (p1 == p3) <<endl;

    cout << (*p1 == *p2) <<endl;
    cout << (*p1 == *p3) <<endl;
    return 0 ;
}

在这里插入图片描述

5.指针与常量

5.1 指针常量

#include <iostream>
using namespace std;
/*
 * 指针常量
 */
int main() {

    //定义高分和低分的变量
    int high = 20;
    int low = 2;

    //使用指针常量指向高。这里的const修饰的是指向的数据
    const int *p = &high;
    //不允许修改值,因为此时编译器会认为high是一个常量
//    *p = 86 ; // 错误
    //可以指向其他位置。
    p = &low ; // 正确
    //当然也可以通过变量方式来修改值。
    high = 88;
    return 0 ;
}

5.2 常量指针

#include <iostream>
using namespace std;
/*
 * 常量指针
 */
int main() {

    //定义高分和低分的变量
    int high = 20;
    int low = 2;
    //表示这个指针是一个常量,这里的const修饰的是指针
    int *const p = &high;
    //允许修改值,但是不能做其他指向
    *p = 86;
//    p = &low ; // 错误
    return 0 ;
}

5.3 常量指针指向常量

#include <iostream>
using namespace std;
/*
 * 常量指针指向常量
 */
int main() {

    //定义高分和低分的变量
    int high = 20;
    int low = 2;
    //第一个const是修饰指向的数据,第二个const是修饰指针。
    //表示不管是指针还是指向的数据,都是常量
    const int *const p = &high;
    //既不允许修改指向,也不允许修改指向的值
    *p = 86 ; // 错误
    p = &low ; // 错误
    return 0 ;
}

6.函数参数传递指针

函数的参数,除了可以传递普通的变量和引用之外,还可以把指针当成参数来传递。

#include <iostream>
#include <string>

using namespace std;

/*
 * 函数传递指针
 */
void changedata(int *p){
    *p = 10;
}

void sway(int *m,int *n){
    int temp = *m;
    *m = *n;
    *n = temp;
}

int main() {
    
    int data = 2;
    cout<<"修改前data的值:"<<data<<endl;
    changedata(&data);
    cout<<"修改后data的值:"<<data<<endl;

    //实现两个值的交换
    int x = 100,y = 200;
    cout<<"X的值:"<<x<<" Y的值:"<<y<<endl;
    sway(&x,&y);
    cout<<"修改后X的值:"<<x<<" 修改后Y的值:"<<y<<endl;
    return 0;
}

在这里插入图片描述

7.函数返回指针

#include <iostream>
#include <string>

using namespace std;

/*
 * 函数返回指针
 */
int *get_larger(int *m, int *n){
    //利用解引用获得数据进行比较
    if(*m > *n){
        return m;
    } else{
        return n;
    }
}

int main() {
    int i = 100;
    int j = 200;
    int *larger = get_larger(&i,&j);
    cout<<*larger<<endl;
    return 0;
}

在这里插入图片描述

  • 注意:不要返回一个函数内部的一个局部变量指针 , 因为本地变量的声明周期应该只位于函数内部。一旦函数执行完毕则被释放。
#include <iostream>
#include <string>

using namespace std;

/**
 * 返回一个函数内部的局部变量指针
 */
int *fun(){
    int number = 99;
    return &number;
}

int main() {

    //会报错
    int *result = fun();
    cout<<*result<<endl;

    return 0;
}

8.二级指针

8.1 二级指针的定义使用

#include <iostream>
#include <string>

using namespace std;

/**
 * 二级指针的使用
 */
int main() {

    int age = 10;
    int *p1 = &age;
    int **p2 = &p1;
    cout<<"age:"<<age<<endl;
    cout<<"&age:"<<&age<<endl;
    cout<<"p1:"<<p1<<endl;
    cout<<"*p1:"<<*p1<<endl;
    cout<<"&p1:"<<&p1<<endl;
    cout<<"p2:"<<p2<<endl;
    cout<<"*p2:"<<*p2<<endl;
    cout<<"**p2:"<<**p2<<endl;

    return 0;
}

在这里插入图片描述

8.2 二级指针的应用

  • 通常出现的地方是作为函数参数传递。如果在函数的内部想要修改外部一级指针指向的数据值,那么则需要二级指针了。
  • 如下所示,如果传递的是一个一级指针,那么在外部的p依然没有被分配空间,传递进去的依然是一份值的拷贝而已 。
#include <iostream>
#include <string>

using namespace std;

/**
 * 二级指针的应用
 */
 void createP(int **p){
     *p = new int();//在堆内存中开辟一块内存。
    cout<<"函数中*p:"<<*p<<endl;
 }
int main() {

    //声明一个空指针
    int *p = nullptr;
    createP(&p);//使用二级指针在函数中完成在堆内存中申请内存
    *p = 100;
    cout<<"p:"<<p<<endl;
    cout<<"*p:"<<*p<<endl;

    //释放指针
    delete p;
    return 0;
}

在这里插入图片描述

9.函数指针

9.1 函数指针的基本使用

#include <iostream>
#include <string>

using namespace std;

/**
 * 使用函数指针方式调用函数
 */

int add(int m,int n){
    return m+n;
}
int main() {

    //使用函数指针调用函数 前半段表示声明一个函数指针add_ptr  该函数指针指向的函数返回值是int,并且有两个int类型的参数。
    int (*add_ptr)(int ,int) = add;
    int result = add_ptr(10,20);

    cout<<"使用函数指针调用的函数:"<<result<<endl;

    //使用普通方法
    int result2 = add(10,20);
    cout<<"使用普通方法调用函数:"<<result2<<endl;

    return 0;
}

在这里插入图片描述

9.2 函数指针作为参数的使用

函数虽然不能直接作为参数来进行传递,但是函数指针可以。

#include <iostream>
#include <string>

using namespace std;

int add(int m, int n);
int calc(int a,int b, int(*fun)(int ,int));//函数指针作为参数
int main() {

    int result = calc(10,20,add);
    cout<<"result:"<<result<<endl;
    return 0;
}

int add(int m,int n){
    return m+n;
}


int calc(int a,int b,int(*fun)(int,int)){
    return fun(a,b);
}

在这里插入图片描述

9.3 函数指针的作用

如果一个通用的函数,需要使用到另一个函数,但是这个函数并没有确定名称,是由其他组织或者个人开发的,那么这时候可以预留一个位置,做成函数指针虚位以待。

#include <iostream>
#include <string>
#include <vector>

using namespace std;
/**
 *函数指针的作用
 */

//函数原型
void listPrint(vector<int> scores, void(*ps)(vector<int>));
void print1(vector<int> s);
void print2(vector<int> s);
void print3(vector<int> s);//未知的函数实现

int main() {
    vector<int> s = {10,20,30,50,66,99};
    listPrint(s,print1);//调用的时候只需要输入数据和使用的函数名字就行
    listPrint(s,print2);
    return 0;
}

/**
 * 接收函数指针的函数
 * @param scores 数据
 * @param ps 函数的指针
 */
void listPrint(vector<int> scores, void(*ps)(vector<int>)){
    ps(scores);
}
/**
 * 打印函数
 * @param s
 */
void print1(vector<int> s){
    cout << "使用for遍历" << endl;
    for(int i : s){
        cout << i << endl;
    }
}
/**
 * 打印函数
 * @param s
 */
void print2(vector<int> s){
    cout <<"使用fori的方式"<< endl;
    for (int i = 0; i < s.size(); ++i) {
        cout <<s[i] << endl;
    }
}

void print3(vector<int> s){
    //未知的函数实现
}

在这里插入图片描述

10.引用

10.1 引用的简单使用

#include <iostream>
#include <string>
#include <vector>

using namespace std;
/**
 *引用
 */

int main() {
    int a = 30;
    int &b = a;
    b = 33;//同时也改变了a的值
    cout<<"a:"<<a<<endl;
    cout<<"b:"<<b<<endl;


    vector<string> names = {"zhangsan","lisi"};
    for(auto n:names){
        n = "aa";//这里不会更改vector的值
    }
    for(auto n:names){
        cout<<"n:"<<n<<endl;
    }
    for(auto &n:names){
        n = "bb";
    }
    for(auto n:names){
        cout<<"n:"<<n<<endl;
    }
    return 0;
}

在这里插入图片描述

10.2 左值引用

平常所说的引用,实际上指的就是左值引用 lvalue reference , 常用单个 & 来表示。 左值引用只能接收左值,不能接收右值。const 关键字会让左值引用变得不同,它可以接收右值

#include <iostream>
#include <string>
#include <vector>

using namespace std;
/**
 *左值引用
 */


//只能把左值赋值给引用,不能把右值赋给引用
int square(int &n){
    return n * n ;
}

int main(){
    int x = 1000;  // x是一个左值, 而 1000 是一个右值。
    x = 1000 + 20 ;  //x 是左值, 1000 + 20 是右值,

    int b = 10; // b 是一个左值, 10 是一个右值
    b = x ;   //b是一个左值, 而 x依然是一个左值。


    int num = 10 ;
    square(num) ; //正确

    square(5); //错误。因为5是右值 ,不能赋值引用。
    return 0;
}

10.3 右值引用

右值引用一般用于绑定到一个即将销毁的对象,所以右值引用又通常出现在移动构造函数中。

#include <iostream>
#include <string>
#include <vector>

using namespace std;
/**
 *右值引用
 */

int main() {
    int i = 66;
    int &r = i ; //r 是一个左引用,绑定左值 i

    int &&rr = i ; //rr是一个右引用,绑定到左值i , 错误!
    int &r2 = i*42 ; //  r2 是一个左引用, 而i*42是一个表达式,计算出来的结果是一个右值。 错误!

    const int &r3 = i*42; // 可以将const的引用,绑定到右值 正确
    int &&rr2 = i*42 ; // 右引用,绑定右值 正确

    return 0 ;
}

10.4 左右值引用总结

左值具有持久的状态,有独立的内存空间,右值要么是字面常量,要么就是表达式求值过程中创建的临时对象。


结束

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
C++指针是一种非常重要的概念,它是一种用于存储内存地址的变量类型,可以用于直接访问内存中的数据。指针的基本概念需要理解以下几个方面: 1. 指针的定义和声明:指针的定义需要指定指针的类型,它可以指向任何数据类型,包括整数、字符、浮点数、结构体等。例如,int *p; 声明了一个指向整数类型的指针变量p。 2. 指针的赋值:指针变量可以通过赋值来指向某个变量的地址,例如,int a = 10; int *p = &a; 将指针变量p指向变量a的地址。 3. 指针的解引用:指针变量可以通过解引用(*)操作符来访问它所指向的变量的值,例如,*p = 20; 修改了指针变量p所指向的变量a的值。 4. 指针的算术运算:指针变量可以进行加、减等算术运算,例如,p++; 将指针变量p指向下一个整数类型的地址。 5. 指针和数组:数组名本质上是一个指向数组首元素的指针,可以通过指针和数组名来访问数组中的元素,例如,int a[5] = {1, 2, 3, 4, 5}; int *p = a; cout << *p << endl; 输出数组a的第一个元素1。 6. 指针和函数:指针可以作为函数的参数来传递变量的地址,从而在函数内部对变量进行修改,例如,void swap(int *a, int *b) { int temp = *a; *a = *b; *b = temp; } 可以通过调用swap(&a, &b)来交换a和b的值。 需要注意的是,指针在使用时需要注意指针变量的值是否为空,避免出现野指针的情况,同时需要注意指针变量的生命周期,避免指针变量指向的内存已经被释放的情况。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

等待着冬天的风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值