• C/C++指针


    参考

    https://www.bilibili.com/video/BV1s5411J78m?p=3&spm_id_from=pageDriver&vd_source=7155082256127a432d5ed516a6423e20 – 入门 讲的不错

    指针

    适用于C/C++

    指针与内存/地址

    数据以二进制的形式存在内存中,每个数据占n个字节
    每个字节都有一个内存地址

    指针:就把他理解为一个数据,只要是数据就以二进制的形式存储在内存中
    指针(在内存中存的值是 被指向的 那个数据的 内存地址值) – 见下图
    且每个指针在内存中也有一个地址
    总结:指针就是被指向数据的内存地址

    CPU访问内存时需要的是地址,而不是变量名和函数名,变量名和函数名只是地址的一种助记符,当源文件被编译和链接成可执行程序后,变量名和函数名都被替换成地址。
    编译和连接过程的一项重要的任务就是找到这些名称所对应的地址

    通常:变量名表示的是数据本身;
    函数名、字符串名、数组名是表示的是代码块和数据块的首地址

    地址本质上是一个整数

    其他概念

    一个变量A 存放的是 另一个变量B的指针,那么将A变量 称为 指针变量

    指针创建

    dataType* name=value dataType表示该指针所指向的数据的类型
    注意指针的类型是 dataType*而不是dataType

    int a;
    int *p; // *表示指针变量
    p=&a; //p的值 时a的地址值
    
    int *p=&a; // 等价
    int* p=&a; // 等价 ; 感觉这种更符合实际的意思
    
    printf("%d",*p); // 取值
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    指针修改

    int a;
    int *p;
    p=&a; //p的值 时a的地址值
    // 等价于 int *p=&a;
    // 等价于 int* p=&a; //感觉这种更符合实际的意思
    
    a=1;
    &p // p的地址值
    *p // *表示取消引用,*p 取了p的值(地址)所指向的数据
    *p=10; // a此时变成了5
    
    char c;
    char *pc;
    
    double d;
    double *pd;
    
    // *p++ 等价于*(p++)
    int a=10;
    int* p=&a;
    printf("%p\n",p);
    printf("%d\n",*p++);
    printf("%d\n",a);
    printf("%p\n",p);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    #include 
    
    int main(){
        int a;
        a=10;
        int *p;
        p=&a;
        printf("%d\n",p); // 6422036 'python':hex(6422036) =='0x61fe14'
        printf("%p\n",p); //000000000061FE14 
        printf("%p\n",&a); //000000000061FE14
    
        int b=20;
        *p=b; // 只是值的修改
        printf("%d\n",a); // 20
        printf("%p\n",p); // 000000000061FE14
        printf("%p\n",&a); // 000000000061FE14 不会改变a的地址
        printf("%p\n",&b); // 000000000061FE10
    
        p=&b;
        printf("%p\n",p); // 000000000061FE10,指向了b
        printf("%p\n",&a); // 000000000061FE14
        printf("%p\n",&b); // 000000000061FE10
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    在这里插入图片描述

    指针算数

    指针的值+1,指针的值+4个字节
    尽量不要对指针进行运算,这样没有意义

        int a;
        a=10;
        int *p;
        p=&a;
        printf("%p\n",&a); // 000000000061FE14
        printf("%p\n",p+1); // 000000000061FE18
        printf("%p\n",&a); // 000000000061FE14
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    *运算符优先级高于双目运算符

    int a=1;
    int* p=&a;
    printf("%d\n",*p+1); // 2
    
    • 1
    • 2
    • 3

    数组指针

    数组指针:指向数组的指针
    不同于指针数组!!!

    数组名可以认为是做一个指针,指向数组的第0个元素
    数组本身是指针这个说法不准确!!!
    第0个元素的地址称为数组的首地址

    数组指针 指向的是数组中的一个具体元素,而不是整个数组,所以指针数组的类型和数组元素的类型有关

    数组指针与数组名不同,数组名不可以改变,而数组指针可以改变
    *p++ 等价于*(P++),不能使用*arr++,因为数组名不能改变
    *++p 等价于*(++P) 等价于*(p+1)
    (*p)++只对数值进行改变

    *运算符优先级高于双目运算符

    int arr[]={1,2,3};
    int* p=arr;
    p arr &a[0] // 三个是等价的,都是指向数组的第一个元素
    
    • 1
    • 2
    • 3
    int a[]={1,2,3};
    int num=sizeof(a)/sizeof(int);
    
    int* p=a;
    for (int i=0;i<num;i++){
        printf("%d,%d\n",*(a+i),*(p+i));
    }
    
    // 方法2
    int* p=a;
    for (int i=0;i<num;i++){
        printf("%d,%d\n",*(a+i),p[i]);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    多维数组指针

    https://blog.csdn.net/angiusc/article/details/106599792

    // 可以定义1xX的数组
    int a[1][3]={1,2,3};
    for (int i=0;i<1;i++){
        // for (int j=0;j<3;j++){
        //     printf("%d\n",a[i][j]);
        // }
        printf("%p\n",a[i]);
        printf("%p\n",&a[i][0]); // &a[i][0] 与a[i] 等价
        printf("%d\n",a[i][0]);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    补充:注意与指针数组的区别

    int* arr[3]={&a,&b,&c};
    
    • 1

    下面才是指针与二维数组之间的关系,因为数组是int类型

    int a[][2]={1,2,3,4};
    int (*p)[2]=a; // 这个写法要记住!!!
    // p+1 跨越一行数据
    
    // 下面三个是等价的
    printf("%p\n",p);
    printf("%p\n",a[0]);
    printf("%p\n",&a[0][0]);
    
    // 下面三个是等价的
    printf("%p\n",p+1);
    printf("%p\n",a[1]);
    printf("%p\n",&a[1][0]);
    
    // 理解
    对于1维数组
    int a={1,2,3};
    int* p=a;
    a[1]==*(P+1)==*(a+1)
    &a[1]==p+1==a+1
    
    // 二维数组
    // 看成嵌套的一维数组
    int a[][2]={1,2,3,4};
    int (*p)[2]=a; 
    *(*(p+1)+1) ==a[1][1]
    p指向了一个都是指针的数组(首地址),而数组元素的每个指针,指向了一个一维数组(首地址)
    p+1==a[1]==&a[1][0]
    *(p+1)==a[1]==&a[1][0] // 这个记忆方法放到下面可以理解
    
    // 下面三个方法是等价的
    printf("%d\n",*(a[1]+1)); //a[1]也是一个指针,表示一维数组
    printf("%d\n",*(*(p+1)+1));  // *(p+1) p+1表示一个一维数组,
    printf("%d\n",a[1][1]);
    
    a+i==p+i
    a[i]==p[i]==*(a+i)==*(p+i)
    a[i][j]==p[i][j]==*(a[i]+j)==*(p[i]+j)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    字符串指针

    指向字符串的指针
    通常利用字符数组表示字符串

    字符数组归根结底还是一个数组,所以数组大部分的功能都适用

    char s[]="hello";
    char* p=s;
    for (int i=0;i<sizeof(s)-1;i++){
        printf("%c,%c,%c\n",s[i],p[i],*(p+i));
    }
    
    for (int i=0;i<strlen(s);i++){
        printf("%c,%c,%c\n",s[i],p[i],*(p+i));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    另一种字符串表示方法

    直接使用一个指针指向字符串
    被称为:字符常量
    能够修改指针指向的地址,但是不能修改内容

    char *s="hello";
    
    char *s;
    s="hello";
    
    s="world"; // 正确
    s[2]='1';// 错误
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    两种创建方式的不同

    char s[]="xxx",以字符数组存储在全局数据区或栈区,具有读写权限
    char* s="xxx",存储在常量区,只有读取权限,一旦定义不能修改

    *运算符优先级高于双目运算符

    char a='c';
    char* p=&a;
    printf("%d\n",a); //99
    printf("%c\n",*p+1); //d
    printf("%d\n",*p+1); //100
    
    • 1
    • 2
    • 3
    • 4
    • 5

    指针与函数

    https://blog.csdn.net/L_fengzifei/article/details/126291514

    二级指针

    指向指针的指针
    指针存放的是一个变量的地址,而指针的地址也可以被另一个指针存放
    指针变量也是变量,因此也需要存储空间

    int a=100;
    int* p1=&a;
    int** p2=&p1; // 可以理解为 (int*)* p2=&p1 因为指向的是指针,而指针的类型是int*,而创建的又是一个指针,所以是(int*)* 
    
    • 1
    • 2
    • 3

    空指针

    对未初始化的指针赋值为NULL
    空指针是不指向任何数据的指针,是无效之战

    char* s=NULL;
    
    if (p==NULL){
    	// ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    void指针

    void* 表示一个有效指针,指向实实在在的数据,只是数据的类型尚未确定,在后序使用过程中需要进行强制类型转换

    char* s=(char*)malloc(sizeof(char)*30);
    
    • 1

    函数指针

    使指针变量指向函数坐在的内存区域,然后通过指针变量就可以找到并调用该函数
    returnType (*p)(param list)returnType为函数返回值类型
    注意:与指针函数的区别

    下面是指针函数,返回值是指针

    char *strlong(char* s1,char* s2){
        if (strlen(s1)>=strlen(s2)){
            return s1;
        }
        else{
            return s2;    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    下面是函数指针

    int max(int a,int b){
        return a>b?a:b;
    }
    
    int main(){
        int x=1;
        int y=2;
        int maxval;
        int (*pmax)(int,int)=max;
    
        maxval=(*pmax)(x,y);    
        printf("%d\n",maxval);
        
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    结构体指针

    一个指针变量指向结构体,叫做结构体指针
    struct struct_name* var

    struct str{
    	char* name;
    	int num;
    } stu1={"tom",1};
    
    struct stu* pstu=&stu1;
    
    // 直接创建指针
    struct str{
    	char* name;
    	int num;
    } stu1={"tom",1},*pstu=&stu1;
    
    // 使用指针获取结构体成员
    (*pstu).name; // 必须加括号
    pstu->name; // 直接通过结构体指针获取成员,这种方法更有效
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    结构体指针作为函数参数传递

    见例子

    结构体数组指针

    因为是数组,所以数组名可以作为元素的首地址

    struct stu class[]={
        {"li",2},
        {"wang",3}
    };
    
    // 下面两个是等价的,都表示地址
    printf("%p\n",class);
    printf("%p\n",&class[0]);
    
    // 所以可以利用指针指向地址
    struct stu* pstu=class; 
    
    // 下面这两种访问方法都是等价的
    struct stu* pstu=class;
    for (int i=0;i<2;i++){
        printf("%s,%d\n",(pstu+i)->num,(pstu+i)->age);
    }
    
    for (int i=0;i<2;i++,pstu++){
        printf("%s,%d\n",pstu->num,pstu->age);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    ???

    #include 
    #include 
    
    #define N 2
    
    struct stu{
        char name[10];
        int num;
    }boya[N],boyb[N],*pa,*pb;
    
    
    int main(){
        FILE* fp;
        int i;
        if ((fp=fopen("ex4.txt","wt+"))==NULL){
            puts("fail to open file");
            exit(0);
        }
    
        printf("input data\n");
    
        pa=boya;
        for (i=0;i<N;i++,pa++){
            scanf("%s %d",pa->name,&pa->num);
        }
        pa=boya; // 为什么要写两遍??
        for (i=0;i<N;i++,pa++){
            fprintf(fp,"%s %d\n",pa->name,pa->num);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    this指针

    this指针,实际上存放的是对象的首地址
    https://www.bilibili.com/video/BV1C7411Y7Kb?spm_id_from=333.337.search-card.all.click&vd_source=7155082256127a432d5ed516a6423e20 – 重要参考

    #include 
    using namespace std;
    
    class Student{
        public:
            int getAge(){
                return age;
            }
    
            void setAge(int age){
                this->age=age; // this->age 表示的是当前类中的属性,(特别用于属性与形参名相同的时候)
                			   // this->age 有点像self.age的作用
             }
    		
    		// 第二种用法
            Student setAge(int age){
                this->age=age;
                return *this; // this指针,实际上存放的是对象的首地址,引入如果要返回一个类对象,所以要*this
            }
    
        
        private:
            int age;
    }; // 注意分号
    
    int main(void)
    {
        Student s;
        s.setAge(3);
        cout<<s.getAge()<<endl;
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    this指针的机制

    #include 
    using namespace std;
    
    class Student{
        public:
            int getAge(){
                return age;
            }
    
            // void setAge(int age){
            //     this->age=age; // this->age 表示的是当前类中的属性,(特别用于属性与形参名相同的时候)
            // }
    
            Student setAge(int age){
                this->age=age;
                return *this;
            }
    
            void test(){
                cout<<"this 指针存放的是谁的地址"<<this<<endl; //0x61fe1c
            }
            
            /*
            实际上编译器是自动加上了一个该对象类型的 指针
            void test(Student* this){
                cout<<"this 指针存放的是谁的地址"<
        
        private:
            int age;
    }; // 注意分号
    
    int main(void)
    {
        Student s;
        // s.setAge(3);
        // cout<
        
        s.test();
        /*
        调用的时候相当于传入了这个对象的地址,void test(Student* this)
        s.test(&s) 
        */
    
        cout<<"s 实例的地址"<<&s<<endl; //0x61fe1c
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    静态函数不能访问this指针

    // static void lazy(){
    //     cout<age<
    // }
    
    • 1
    • 2
    • 3
  • 相关阅读:
    算法排序之冒泡排序及优化
    如何在IDEA上使用Git克隆下载创建好的项目和分支、提交项目
    《热题100》字符串、双指针、贪心算法篇
    Android Java反射与Proxy动态代理详解与使用基础篇(一)
    C++ —— 继承
    中国酶制剂市场预测与投资战略研究报告(2022版)
    数据库错误知识集2
    IOS开发学习日记(十七)
    libcurl域名检测超时段错误分析
    servlet -> spring-mvc -> spring-boot-> spring-security目录
  • 原文地址:https://blog.csdn.net/L_fengzifei/article/details/126411708