参考
指针
适用于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函数
// }