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); // 取值

指针修改

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);
#include <stdio.h>

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,指针的值+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

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

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

数组指针

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

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

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

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

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

int arr[]={1,2,3};
int* p=arr;
p arr &a[0] // 三个是等价的,都是指向数组的第一个元素
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]);
}

多维数组指针

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]);
}

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

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

下面才是指针与二维数组之间的关系,因为数组是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)

字符串指针

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

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

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));
}

另一种字符串表示方法

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

char *s="hello";

char *s;
s="hello";

s="world"; // 正确
s[2]='1';// 错误

两种创建方式的不同

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

指针与函数

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

二级指针

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

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

空指针

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

char* s=NULL;

if (p==NULL){
	// ...
}

void指针

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

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

函数指针

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

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

char *strlong(char* s1,char* s2){
    if (strlen(s1)>=strlen(s2)){
        return s1;
    }
    else{
        return s2;    
    }
}

下面是函数指针

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;
}

结构体指针

一个指针变量指向结构体,叫做结构体指针
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; // 直接通过结构体指针获取成员,这种方法更有效

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

见例子

结构体数组指针

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

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);
}

???

#include <stdio.h>
#include <stdlib.h>

#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;
}

this指针

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

#include <iostream>
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;
}

this指针的机制

#include <iostream>
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 指针存放的是谁的地址"<<this<<endl; //0x61fe1c
        }
        */
    
    private:
        int age;
}; // 注意分号

int main(void)
{
    Student s;
    // s.setAge(3);
    // cout<<s.getAge()<<endl;
    
    s.test();
    /*
    调用的时候相当于传入了这个对象的地址,void test(Student* this)
    s.test(&s) 
    */

    cout<<"s 实例的地址"<<&s<<endl; //0x61fe1c
    return 0;
}

静态函数不能访问this指针

// static void lazy(){
//     cout<<this->age<<endl; // 静态成员函数不能使用this函数
// }
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值