# 第10章 指针和结构体
10.1 指针
一、什么是指针
指针(Pointer):变量的地址,通过它能找到以它为地址的内存单元。
例子1:理解指针概念
#include <bits/stdc++.h>
using namespace std;
int main(){
int x=10;
int* p=&x; // 定义整数指针p,指向x的地址
cout<<p<<endl; // 输出x的地址(指针值)
cout<<*p<<endl; // 输出p指向地址对应的值(即x的值)
*p=*p+2; // 通过指针修改变量x的值(等价于x=x+2)
cout<<x<<endl;
return 0;
}
例子2:值拷贝与地址拷贝对比
#include <bits/stdc++.h>
using namespace std;
int main(){
int x=10;
// 值拷贝:y是新变量,修改y不影响x
int y =x;
y=y+2;
cout<<x<<endl; // 输出10
// 地址拷贝:p指向x的地址,修改*p即修改x
int* p=&x;
*p=*p+2;
cout<<x<<endl; // 输出12
return 0;
}
例子3:&和*的嵌套使用
#include <bits/stdc++.h>
using namespace std;
int main(){
int x=10;
int *p=&x;
cout<<p<<endl; // 输出x的地址
cout<<*p<<endl; // 输出x的值
cout<<&(*p)<<endl; // 等价于&x,输出x的地址
cout<<*&(*p)<<endl; // 等价于*p,输出x的值
return 0;
}
例子4:p++和(p)++的区别
#include <bits/stdc++.h>
using namespace std;
int main(){
int x=10;
int* p=&x;
cout<<p<<" "<<*p<<endl; // 输出x的地址和10
// ++优先级高于*,*p++等价于p++(指针地址自增,不修改x的值)
// *p++;
(*p)++; // 先取p指向的值,再自增(x变为11)
*p=*p+1; // x再自增1(变为12)
cout<<p<<" "<<*p<<endl; // 输出原地址和12
return 0;
}
二、指针作用
- 通过函数修改变量的值:普通参数传递是值拷贝,指针传递可直接操作原变量
#include <bits/stdc++.h>
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;
}
- 让函数返回多个值:通过指针输出额外结果
#include <bits/stdc++.h>
using namespace std;
// 返回平均值,通过指针输出最大值和最小值
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;
}
- 在scanf中使用指针:scanf需传入变量地址
#include <bits/stdc++.h>
using namespace std;
int main(){
int a,b;
// 变量读入:&取地址
scanf("%d%d",&a,&b);
// 变量输出
printf("%d+%d=%d\n",a,b,a+b);
printf("%d-%d=%d\n",a,b,a-b);
int *p =&a;
printf("%p %d\n",p,*p); // 输出a的地址和值
return 0;
}
格式说明:%d(整数)、%f(float)、%lf(double)、%c(字符)、%s(字符串)、%p(指针)
三、数组指针
数组本质是指向首元素(a[0])的地址,指针可遍历数组
例子1:数组与指针的关联
#include <bits/stdc++.h>
using namespace std;
int main(){
int a[5]={10,20,30,40,50};
cout<<a<<" "<<&a<<" "<<&a[0]<<endl; // 三者均为a[0]的地址
int *p =a; // 指针p指向数组首元素
cout<<p<<" "<<a[0]<<endl; // 输出首地址和10
*p=*p+2; // 修改a[0]为12
cout<<p<<" "<<a[0]<<endl;
p++; // 指针指向a[1]
*p=*p+2; // 修改a[1]为22
cout<<p<<" "<<a[1]<<endl;
return 0;
}
例子2:函数中通过指针操作数组
#include <bits/stdc++.h>
using namespace std;
// 数组形式参数
void fun1(int a[],int n){
for(int i=0;i<n;i++){
cout<<a[i]<<" ";
}
cout<<endl;
}
// 指针形式参数
void fun2(int* a,int n){
for(int i=0;i<n;i++){
cout<<*a<<" ";
a++;
}
cout<<endl;
}
// 字符数组遍历(指针方式)
void fun4(char* s){
while(*s!='\0'){
cout<<*s<<" ";
s++;
}
cout<<endl;
}
int main(){
int a[3]={10,20,30};
fun1(a,3);
fun2(a,3);
char s[10]="hello";
fun4(s);
return 0;
}
10.2 结构体
一、什么是结构体
结构体是用户自定义的数据结构,可存储不同类型数据项(数组仅存储相同类型)。
例子1:结构体定义与使用
#include <bits/stdc++.h>
using namespace std;
// 定义结构体Student(驼峰命名)
struct Student{
int num; // 学号
string name; // 姓名
double height; // 身高
}s1,s2; // 定义结构体时直接创建变量s1、s2
// 输出结构体成员(struct可省略)
void print(Student s){
cout<<s.num<<" "<<s.name<<" "<<s.height<<endl;
}
int main(){
Student s; // 创建结构体变量
// 赋值
s.num =1;
s.name="zhang";
s.height=178.5;
print(s);
// 直接给s1赋值
s1.num=2;
s1.name="wang";
s1.height=182.6;
print(s1);
return 0;
}
注意:引用结构体成员用“结构体变量.成员名”
例子2:结构体数组
#include <bits/stdc++.h>
using namespace std;
struct Student{
int num;
string name;
double height;
};
// 输出结构体数组
void print(Student a[],int n){
for(int i=0;i<n;i++){
cout<<a[i].num<<" "<<a[i].name<<" "<<a[i].height<<endl;
}
}
int main(){
int n;
Student a[100]; // 结构体数组
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i].num>>a[i].name>>a[i].height;
}
print(a,n);
return 0;
}
例子3:结构体指针
#include <bits/stdc++.h>
using namespace std;
struct Student{
int num;
string name;
double height;
};
int main(){
Student s;
s.num=1;
s.name="wang";
s.height=185.5;
// 直接访问成员
cout<<s.num<<" "<<s.name<<" "<<s.height<<endl;
// 结构体指针
Student *p =&s;
// 指针访问成员:-> 或 (*p).成员名
cout<<p->num<<" "<<p->name<<" "<<p->height<<endl;
cout<<(*p).num<<" "<<(*p).name<<" "<<(*p).height<<endl;
return 0;
}
二、结构体的应用
应用1:期末考试成绩排名(冒泡排序+sort排序)
// 解法一:冒泡排序
#include <bits/stdc++.h>
using namespace std;
struct Student{
int num;
string name;
int score;
};
int main(){
Student a[110];
int n,i,j;
cin>>n;
for(i=0;i<n;i++){
cin>>a[i].num>>a[i].name>>a[i].score;
}
// 冒泡排序:成绩降序,成绩相同学号升序
for(i=1;i<=n-1;i++){
for(j=0;j<=n-i-1;j++){
if(a[j].score <a[j+1].score || (a[j].score==a[j+1].score && a[j].num>a[j+1].num)){
swap(a[j],a[j+1]);
}
}
}
for(i=0;i<n;i++){
cout<<a[i].num<<" "<<a[i].name<<" "<<a[i].score<<endl;
}
return 0;
}
// 解法二:sort排序
#include <bits/stdc++.h>
using namespace std;
struct Student{
int num;
string name;
int score;
};
// 比较函数
bool cmp(Student s1,Student s2){
if(s1.score>s2.score || (s1.score==s2.score && s1.num<s2.num)){
return true;
}else{
return false;
}
}
int main(){
Student a[110];
int n,i;
cin>>n;
for(i=0;i<n;i++){
cin>>a[i].num>>a[i].name>>a[i].score;
}
sort(a,a+n,cmp);
for(i=0;i<n;i++){
cout<<a[i].num<<" "<<a[i].name<<" "<<a[i].score<<endl;
}
return 0;
}
应用2:遥控飞机争夺赛(求平均成绩并排序)
#include <bits/stdc++.h>
using namespace std;
struct Player{
int num; // 选手编号
double score; // 平均成绩
};
bool cmp(Player p1,Player p2){
if(p1.score>p2.score){
return true;
}else{
return false;
}
}
int main(){
int n,i,j,x,s,ma,mi;
Player a[110];
cin>>n;
for(i=0;i<n;i++){
cin>>a[i].num;
s=0;
ma=INT_MIN;
mi=INT_MAX;
for(j=0;j<5;j++){
cin>>x;
s+=x;
ma=max(ma,x);
mi=min(mi,x);
}
a[i].score=(s-ma-mi)/3.0; // 去掉最高分和最低分求平均
}
sort(a,a+n,cmp);
// 输出前三名,保留3位小数
for(i=0;i<3;i++){
cout<<a[i].num<<" "<<fixed<<setprecision(3)<<a[i].score<<endl;
}
return 0;
}
应用3:活动选择(贪心算法)
#include <bits/stdc++.h>
using namespace std;
struct Node{
int begin; // 活动开始时间
int end; // 活动结束时间
}a[110];
// 按结束时间升序排序
bool cmp(Node n1,Node n2){
if(n1.end<n2.end){
return true;
}else{
return false;
}
}
int main(){
int n,i,c=0,e;
cin>>n;
for(i=0;i<n;i++){
cin>>a[i].begin>>a[i].end;
}
sort(a,a+n,cmp);
c=1;
e=a[0].end; // 第一个活动必选
// 遍历后续活动,选择不冲突的
for(i=1;i<n;i++){
if(a[i].begin>=e){
c++;
e=a[i].end;
}
}
cout<<c<<endl;
return 0;
}
应用4:宇宙总统2(统计得票并排序)
#include <bits/stdc++.h>
using namespace std;
struct Node{
string name; // 名字
int count; // 得票数
}a[1100];
// 得票数降序,得票相同按名字字典码降序
bool cmp(Node n1,Node n2){
if(n1.count>n2.count || (n1.count==n2.count && n1.name>n2.name)){
return true;
}else{
return false;
}
}
int main(){
int n,k=0,i,j;
bool f;
string s;
cin>>n;
for(i=1;i<=n;i++){
cin>>s;
f=false;
// 查找是否已有该名字
for(j=1;j<=k;j++){
if(a[j].name==s){
a[j].count++;
f=true;
break;
}
}
// 不存在则新增
if(!f){
k++;
a[k].name=s;
a[k].count=1;
}
}
sort(a+1,a+k+1,cmp);
for(i=1;i<=k;i++){
cout<<a[i].name<<" "<<a[i].count<<endl;
}
return 0;
}
三、结构体作业
- 1730:【入门】购买贺年卡
- 1740:【基础】统计每个数出现的次数(提示:结构体存储数及出现次数)
- 1346:【入门】等比例缩放照片(提示:结构体存储宽、高及宽高比差值)
- 1347:【基础】游览动物园(提示:结构体存储坐标、与小红距离、与出口距离)
- 1561:【提高】买木头(提示:结构体存储木头长度和数量)
- 1330:【入门】求最大梯形的面积(提示:结构体存储上底、下底、高和面积)
- 1482:【提高】花生采摘(提示:结构体存储坐标和花生数量)

浙公网安备 33010602011771号