分类
Level4

指针

一、指针 指向存放变量的值的地址。因此我们可以通过指针来修改变量的值。指针运算符*:获取该指针所指空间的数据。

#include <iostream> 
using namespace std;

int main(){
    int a=10,b=a;//将a的值赋值给b,值拷贝
    int *p=&a;//定义指针p(整数指针),指向a的地址
    // &表示取变量的地址;p是int*类型(整数指针类型)
    cout<<a<<' '<<&a<<endl;
    cout<<b<<' '<<&b<<endl;
    cout<<*p<<' '<<p<<endl;//p是地址,*p表示指向地址对应的值  
    *p=*p+2;// *p+=2 相当于 a=a+2
    cout<<a<<endl;
    cout<<*p<<' '<<p<<endl;
    return 0;
}   /* 注意拷贝值和拷贝地址的不同! */

&和*的嵌套使用

#include<iostream>
using namespace std;
int main(){
    int a=10;
    int *p=&a;
    cout<<p<<' '<<&p<<' '<<&a<<endl;//a地址,p地址,a地址                    
    cout<<*p<<endl;                                   
    cout<<&(*p)<<endl;                               
    cout<<*&(*p)<<endl;    
    int **q=&p; //指向指针的指针
    cout<<q<<' '<<*q<<' '<<' '<<**q<<endl;                        
    return 0;
}

*p++(*p) ++的区别:指针变量可以进行++和–运算,相当于右移/左移一个存储单元

#include<iostream>
using namespace std;
int main(){
    int x = 10;
    int *p = &x;
    cout<<p<<" "<<*p<<endl;
    (*p)++;
    cout<<p<<" "<<*p<<endl;
    *p=*p+1;
    cout<<p<<" "<<*p<<endl;
    *p++; //++的优先级高于*,因此*p++相当于p++
    cout<<p<<" "<<*p<<endl;
     return 0;
}

注意:*p++并不能通过指针修改x的值,*p++相当于p++是在对指针的值自增(相当于 地址值自增)。
指针变量的存储空间:指针变量大小与其指向的数据类型无关,而与编译器有关空指针:赋值为NULL或者nullptr的指针变量,空指针不指向任何变量,不能通过空指针访问内存数据种的数据。

结构体指针:

#include <bits/stdc++.h>
using namespace std;
struct stu{
    char name[100];
    double score;
};
int main(){
    stu s; //定义结构体变量 struct 
    strcpy(s.name,"ZhangSan");
    s.score = 99.99;
    cout<<s.name<<" "<<s.score<<endl;  //输出结构体
     
    //定义指针,指向结构体struct stu *stu = &s;//struct可以省略 
    stu *stu1 = &s;
    //在结构体指针中,不能用.来指向结构体成员变量(stu是地址) 
    cout<<stu1->name<<" "<<stu1->score<<endl;
    //(*stu)是结构体 
    cout<<(*stu1).name<<" "<<(*stu1).score<<endl; 
     
    //new结构体对象,直接赋值给一个指针
    stu *stu2 = new stu;
    strcpy(stu2->name,"ZhangSan");
    stu2->score = 99.8;
    cout<<stu2->name<<" "<<stu2->score<<endl;
    delete stu2;  //释放内存 
     
    //malloc结构体对象,直接赋值给一个指针
    //动态内存分配,用于申请一块连续的指定大小的内存块区域并返回分配的内存区域地址 
    stu *stu3 = NULL;
    stu3 = (stu*)malloc(sizeof(stu));
    strcpy(stu3->name,"WangWu");
    stu3->score = 98;
    cout<<stu3->name<<" "<<stu3->score<<endl;
    free(stu3);  //释放内存 
    return 0;
}

注意:理解malloc和free函数的作用!
(1) malloc:动态内存分配,用于申请一块连续的指定大小的内存块区域并返回分配的内存区域地址。
(2) free函数能释放某个动态分配的地址,表明不再使用这块动态分配的内存了,实现把之前动态申请的内存返还给系统。
(3) 如果结构体中有string类型的成员,那么不能使用malloc动态分配内存(可以 使用new),这是因为malloc/free和new/delete的重要区别之一是:new会先调用operator new函数,申请足够的内存(通常底层使用malloc实现)。然 后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存(通常底层使用free实现)。malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对 象构造和析构工作。所以string的构造函数并没有被调用,name没有被初始化,改为new能正常运行。

二、指针作用
1、通过函数借助指针修改变量的值

#include<iostream>
using namespace std;
void change1(int x){
    x++;
}
void change2(int *p){ 
    (*p)++;
}
int main(){
    int x = 1; 
    change1(x); 
    cout<<x<<endl;   //输出1 
    int p = 1; 
    change2(&p); 
    cout<<p<<endl;   //输出2 
    return 0;
}

2、让函数返回多个值
例子:通过函数计算2个数的较大、较小和平均值。

#include <bits/stdc++.h>
using namespace std;
//函数作用:计算2个数的较大、较小和平均值
double num(int a,int b,int *max,int *min){
	if(a>b){
		*max=a;
		*min=b;
	}else{
		*max=b;
		*min=a;
	}
	return (a+b)/2.0;
}
int main(){
	int a,b,max,min;
	cin>>a>>b;
	double r=num(a,b,&max,&min);
	cout<<max<<" "<<min<<" "<<r<<endl;
	return 0;
}

3 scanf 中使用指针

scanf (格式,地址):
%d:整数 %f:float %lf:double

%c:字符 %s:字符串 %p:指针

#include <bits/stdc++.h>
using namespace std;
int main(){
	int a=10,b=22;
	printf("%d\n",a+b);
	printf("%d\n",a-b);
	printf("%d+%d=%d\n",a,b,a+b);
	int *p=&a;
	printf("%p %d\n",p,*p);   // %p:指针
	return 0;
}

三、数组名是一种特殊的指针。指针可以做运算:

#include <iostream> 
using namespace std;
int main(){
    int a[5]={0,1,2,3,4};//数组的本质:a是指向a[0]的指针
    for(int i=0;i<5;i++) 
    	cout<<*(a+i)<<endl;//同cout<<a[i],注意不加括号的结果差别
    return 0;
}

数组指针变量和整数的加减法运算,相当于存储位置左移或者右移。

四、引用:一种特殊类型的变量,可以被认为是另一个变量的别名。引用和指针类似,相当于给变量起了个别名。
引用和其绑定的变量是同一个变量,操作引用即为操作变量本身。已存在的引用不能再作为其他变量的别名。

#include <iostream> 
using namespace std;

int main(){
    int a=10;
    int &p=a; //引用,p相当于a的一个别名
    p+=5;  //其实就是a+=5
    cout<<a<<' '<<&a<<endl;
    cout<<p<<' '<<&p<<endl;    //p相当于a的别名
    return 0;
}

五、指针在结构体中的使用,如单链表

//链表:头插法
#include <iostream> 
using namespace std;
int n,x;
struct Node{  //结构体
	int val;      //值
	Node *next;   //下一个结点
}*head;
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){ //生成n个结点的链表
        cin>>x;
    	Node *p=new Node();  //定义一个新的结点
    	p->val=x;
    	p->next=head;
    	head=p;
    }
	
    for(Node *p=head;p;p=p->next){ //从头开始输出每个node的val
	cout<<p->val<<' ';
    }
    cout<<endl;
    return 0;
}
//链表:尾插法
#include <iostream> 
using namespace std;
int n,x;
struct Node{  //结构体
    int val;      //值
    Node *next;   //下一个结点
}*tail,*head;
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){ //生成n个结点的链表
        cin>>x;
    	Node *p=new Node(); //定义一个新的结点
    	p->val=x;  
    	if(i==1){
    	    head=p;  //头结点指向第一个结点
	}else{
	    tail->next=p;   //新结点接在尾部
	}
	tail=p;   //尾结点更新
    }
	
    for(Node *p=head;p;p=p->next){//从头开始输出每个node的val
	cout<<p->val<<' ';
    }
    cout<<endl;
    return 0;
}

数据类型小结: