第四章 复合类型
2020-02-24 10:43:18 4 举报
AI智能生成
C++ plus
作者其他创作
大纲/内容
枚举
作用
提供一种创建符号常量的方式,可以代替const
常用来定义相关的符号常量
定义声明
enum spectrum {red, orange , yellow , green , violet , indigo};
说明
spectrum 被称为枚举
red,orange 为符号常量,叫做枚举量
red =0 ....
声明变量
spectrum band;
说明
enum 可省略
变量赋值
band= red;
band = (spectrum) 0;//valid<br>band=0;// invalid<br>
说明
变量只有赋值运算,没有算术运算
band 能取的值只能在枚举量中
设置枚举值
enum bits {one=1,two=2,four=4,eight=8};
enum bigstep {first ,second =100,third };
first =0, third=101
说明
指定的值一定要是整型
可以创建多个相同的枚举量
枚举取值范围
例子
enum bits {one=1,two=2,four=4,eight=8};
bits myflag;<br>myflag = (bits)6; //valid
范围
上限
找到大于这个最大值、最小的2的幂,再-1.
下限
大于o,为0
小于0 ,找到 小于这个最小值的 最大2的幂,+1
例子
bits 上限为16-1=15
其他
枚举量是整型,可被提升为 int 类型
int color =blue //valid;<br>spectrum band=3;// invalid<br>color = 3+red ;// valid ,color = 3
cout<<band; // 结果=3
如果要用int 来赋值给band,用强制类型转化可以。
指针
相关概念<br>不知道的点
*p_updates 是int ,不是指针
int *ptr, *两边的空格可选
C++ 中 ,int* 是一种复合类型,是指向int 的指针
char *tax_ptr<br> double *str <br>tax_ptr和str 两个变量本身的长度通常是相同的<br>char 和 double 的地址长度是一样的
int *ptr;<br>ptr 是int 值的地址不说明 ptr 本身的类型是int 值。int 2字节,地址4字节
指针本身也用内存。<br>int *ptr =&a;<br>ptr 、&ptr 都存在且不同
指针的危险
创建指针时,计算机分配存储地址的内存,但不分配用来存储指针所指向的数据的内存。<br>int *ptr;<br>*ptr=23333; 是不可以的。
指针和数字
int *ptr;<br>ptr = 0XB8000000
不能简单地将整数赋值给变量
但可以用强制类型转换<br>ptr =(int *)0XB8000000
new分配内存
指针的真正作用
在运行阶段分配未命名的内存以储存值<br><br> int n;<br>int *ptr =&n; 没什么实际意义
分配内存方式
malloc()
new 运算符
更好
格式
int *pn= new int
int *pn;<br>pn=new int ;
说明
原理: new int 告诉 程序需要适合存储int 的内存。<br>new 运算符根据类型确定需要多少字节的内存。它<br>找到这样的内存,并返回给其地址,
这里说pn 指向一个数据对象。 (因为没有内存名称)
两个地方指定数据类型<br>第一个——指针要指向的数据的类型<br>第二个——指定需要什么样的内存
好处
使程序在管理内存方面有更大的控制权
其他
new 分配的内存块和常规声明分配的内存块不同。<br>int a ;int *pa= new int;<br>a——栈的内存区域<br>*pa——堆、自由存储区的内存区域
new 可能会失败。C++提供了检测并处理内存分配失败的工具
delete 释放内存
作用
使用完内存后,归还给内存池,这部分内存可供程序其他部分使用,<br>是有效使用内存的关键。
格式
int *ps =new int ;<br>.....<br>delete ps;
说明
delete 只能与new 配合使用,否则内存泄漏。<br>(被分配的内存无法使用),对空指针使用delete <br>安全
释放ps,但不会删除ps 本身
不能释放已经释放的内存块,<br>不能用delete 释放声明变量的内存
int *ps =new int;<br>delete ps;//ok<br>delete ps://no<br>int a=5;<br>int *pi=&a;<br>free pi; //no
delete 用于new分配的内存。<br>不意味着使用new的指针,<br>而是用于new的地址
int *ps=new int;<br>int *pq=ps;<br>delete ps;<br>//这样可能增加错误地删除同一个内存块两次的可能。
数组指针
指针声明赋值
int a[10];<br>int *ptr ;<br>ptr=a; /ptr = &a[0];
元素表示
a[i];<br>*(a+i);<br>p[i];<br>*(p+i);
new创建动态数组
作用
对大型数据(数组、字符串、结构)使用,<br>是new 和指针的用武之地
分配内存方式
静态联编
声明时创建数组,程序在被编译时为它分配内存空间。<br>
要编写程序时指定数组长度
动态联编
用new 和指针程序运行时创建数组。数组称为动态数组,
程序在运行时确定数组长度
创建
格式
int * psome = new int[10];
说明
char * ptr =new char;//就可以了???<br>下面解释:
<div>char *ptr2=new char;</div><div>cin>>ptr2;</div><div>cout<<"the result is:"<<endl;</div><div>cout<<ptr2<<endl;<br>结果:<br>输入: i am a student<br>输出: the result is:<br> i</div>
<div>char *ptr2=new char;</div><div>ptr2="i am a student";//地址已经改变了,其实可以不要上句</div><div>cout<<"the result is:"<<endl;</div><div>cout<<ptr2<<endl;<br>结果: i am a student</div>
释放动态<br>数组
格式
int *psome =new int [10] ;<br>delete [] psome ;
new 和 delete 规则
元素的使用
数组法
psome[0]、psome[1]
说明
原因:C 和 C++内部都使用指针处理数组
指针法
psome + i
说明
psome +1 后,psome[0] = 原 psome[1];
psome +1 后,最好恢复 psome - 1 后再用delete [] psome;
why ? 不然会出现错误?
指针和数组都可以用这两种方法表示
其他
不能使用 sizeof运算符来确定动态分配的数组包含的字节数,sizeof 不可以
指针、数组和指针算术
指针算术
指针变量+1,增加的值等于指向的类型占用的字节数
指针、数组区别
指针值可改变,但是数组的值不可改变
int a[10];<br>int *p=a;<br>sizeof(a)=4*8=32;//这个时候C++不会把数组名解释为地址。<br>sizeof(p)=4 ;//指针的长度
数组的地址
数组名——第一个元素的地址;<br>对数组名取地址——整个数组的地址<br><br>
int a[10];<br>cout<<a<<endl;<br>cout<<&a<<end1; <br>两者值一样。<br><br>
&a[0] ——4字节的内存块的地址<br>&a——40字节内存块的地址。<br><br>
a 是一个int 指针。<br>&a 指向包含10个元素的Int 数组<br>可声明初始化这种指针:<br>int (*p)[10] =&a;<br>*p和a等价 (*p)[0]=a[0]
不太明白。。
数组名虽然为第一个元素的地址,但是<br>sizeof ()用于数组名——返回真个数组长度
指针和字符串
原理
在cout 、C++表达式中,char 数组名、char指针以及用<br>引号括起的字符串常量都被解释为字符串第一个字符的地址
字符串的表示
数组
char str[100]=" "
char str[100];<br>str[100]="i am a student";
不可以
指针
char *ptr="i am";
char *ptr;<br>ptr="i am a student";
可以,不用分配什么new.<br>
说明
但如果用的是cin>>ptr ;就要new;
“i am a student ”表示的是字符串地址,赋值给了ptr;
string 类
string str1="i am ";<br>
string str1;<br>str1="i am ...";
说明
可以直接用cin>>str1;
字符串副本
方法
用new 分配给指针,然后用strcpy赋值;
说明
<div>char *ptr1="i am a student";<span class="Apple-tab-span" style="white-space:pre"> </span><br>char *ptr2=new char;</div><div>ptr2="i am a student"; //改变了ptr2的地址。这样其实不用new 分配<br>/不可以 ,ptr1,ptr2的地址相同</div>
char *ptr2 = new char;<br>strcpy (ptr2,"i am a student");// 这样才能得到两个不同的副本。
其他
const char *bird = "wern"表示可以用bird<br>表示字符串且不可以改变它。
如果给cout一个指针,将打印地址。但是指针如果是char*则打印字符串,<br>如果想打印char*字符串的地址 :<br>——cout<<(int *)ptr;<br>——cout<<&ptr;//显示的是指针的地址,不是字符串的地址。<br>——cout<<&ptr[0]//显示的是字符串。!!!!
对比字符数组
cout<<str;//字符串<br>cout<<(int*)str;//数组地址<br>cout<<&str;//ditto<br>cout<<&str[0];//显示的是字符串!!
有些C++系统,字符串字面值是常量,如果修改,程序运行阶段会错误
int *ptr="i am ";<br>ptr[0]='o';<br>//程序不能运行
int *ptr=new char;
有些系统只使用字符串字面值的一个副本表示程序中多有该字面值
int *ptr1="i am a student";<br>int *ptr2 ="i am a student";<br>//cout<<(int *)ptr1; cout<<(int *)ptr2 是一样的;
strcpy原理
char food[20];<br>strcpy(food,"a picnic basket filled with many goodies");数组比字符串小。<br>这样会覆盖程序正在使用的其他内存。可能会使程序错误。——最好用strncpy
strncpy原理
strncpy(food,"a picnic basket filled with many goodies",19);<br>food[19]='\0';<br>19——表示最多将19个字符赋值给food 数组,满是不会自动添加\0;
new创建动态结构
单元素
创建
student *ptu=new student;
成员引用
箭头成员运算符
ptu->name;
句点运算符
(*ptu).name
字符串省内存方法
代码
说明
new 和 delete 可以分开使用,但是最好不要
这种方法可以节省内存,但是实际上使用string类更容易
存储类型
依据
分配内存的方法
类型
自动存储
使用范围
函数内部定义的常规变量——自动变量
自动变量
特点
在所属的函数被调用时候自动产生,函数结束时候自动消亡。<br>//自动消亡指的是什么自动消亡???书上说内存自动释放,但是<br>为什么再次用函数的时候,内存还是那个内存???
作用域
包含它的代码块。如果在函数其中的某个代码块定义了一个变量,<br>则变量只在这个代码块上适用。//刷新了我的认识。内存地址还存在吗??
<div><span class="Apple-tab-span" style="white-space:pre"> </span>int i,j;</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>for(i=0;i<2;i++)</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>{</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>int k;</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>k++;</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>}</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>cout<<k;<br> //[Error] 'k' was not declared in this scope </div>
代码
<div>using namespace std;<br>int main()</div><div>{</div><div><br></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>char *ptr1,*ptr2;</div><div><span class="Apple-tab-span" style="white-space:pre"> </span></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>ptr1=proove();</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>cout<<"ptr1="<<ptr1<<" 存储位置 : "<<(int *)ptr1<<endl;</div><div><span class="Apple-tab-span" style="white-space:pre"> </span></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>cout<<endl;</div><div><span class="Apple-tab-span" style="white-space:pre"> </span></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>ptr2=proove();</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>cout<<"ptr2="<<ptr2<<" 存储位置 : "<<(int *)ptr2<<endl;</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>cout<<"ptr1="<<ptr1<<" 存储位置 : "<<(int *)ptr1<<endl;</div><div><br></div><div><br></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>return 0;</div><div>} </div><div><br></div><div>char * proove()</div><div>{</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>char str[20];</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>cout<<"输入:"<<endl;</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>cin>>str;</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>cout<<"str="<<str<<endl;</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>return str;</div><div>}<br><br>//结果:<br><div>输入:</div><div>abc</div><div>str=abc</div><div>ptr1=S\F 存储位置 : 0x6bfebc</div><div><br></div><div>输入:</div><div>efg</div><div>str=efg</div><div>ptr2=S\F 存储位置 : 0x6bfebc</div><div>ptr1=S\F 存储位置 : 0x6bfebc</div></div>
说明
1.返回str ,返回了地址,但是里面的值消亡。这样是不可以的。<br><br>
2.第一次第二次存储位置是相同的,所以说虽然值消亡,<br>但是地址没有,还是那个地址???还是说还是分配到<br>了一样的地址???
存储
栈中
静态存储
含义
整个程序执行期间都存在
方式
函数外部声明
在函数内部用static 声明
但是作用域也只在函数内部
初始化
一般实现只能自动初始化静态数组和静态结构
动态存储
范围
new 和delete 运算符
变量特点
节省内存的代码。能在一个函数中分配内存,在一个函数中释放。<br>数据生命周期不完全受程序或函数的生存时间控制<br>
存储
自由存储空间/堆
线程存储(C++11)
内存泄漏
含义
使用运算符new 在自由存储空间上创建变量后,<br>没有使用delete ,即使使包含指针的内存由于作用域规则<br>和对象生命周期原因被释放,在自由存储空间上分配的<br>变量或结构继续存在。
内存被分配出去,无法收回。
应用程序内存被耗尽,出现内存耗尽错误,导致程序崩溃。
分支主题
指向结构体数组的指针
指向结构体的指针数组
指向 指向结构体指针数组 的指针
定义
初始化
赋值
表示元素
表示地址???
数组的替代品<br>(先不要深究。。)
模板类 vector
作用
是一种动态数组。可以运行阶段末尾加新数据,可以中间加。<br>是new创建动态数组的代替品。使用内置的new delete 管理内存
使用要求
头文件 vector
名称空间std
声明
vector<int> vi;<br>vector<double>vd(n);
说明
vi是vector<int>的一个对象.,vd....。
vi 初始化长度为0,其后可以调整。(不知道怎么调整)。
vd(n)中的n ,可以是常量,可以是变量,指明了数组vd 的长度
模板类 array
使用要求
array
std
声明
array<double,5>ad;<br>array<int ,4> ai={1,2,3,4}
元素使用
ai[1]....
可以将array对象赋值给另一个 array 对象。 //数组不可以。<br>vector 也不可以吧
对比
vector功能比数组强大,但效率低。因为n可变??需要长期固定的数组,用数组更好,<br>数组没那么安全和方便。
array 长度固定,使用栈(数组一样),不是自由存储区域(vector),<br>效率与数组一样,更方便,安全
数组
不知道的点
[]中的指定元素数目,必须是整型常数、const值,也可以是常量表达式。不可以是变量
float loans[20];不能说loans 是数组,而是
int a[5]; sizeof (a),sizeof(a[0]);
int hand[4]={1,2,3,4};<br>hand[4]={4,5,6,7};不可以。
int a[]={1,2,3,4};<br>char a[]="i am a student."
字符串最好不写下标,但是其它最好写下标
可以直接 sizeof( int ) ;
c++11初始化
double earning[4] { 1,2,3,4};
=号可以省略
int a[4] {};
大括号没有东西,初始化为0
禁止缩窄转换
数组的替代品——模板类vector、array
cout 输出
整型数组
int a[]={1,2,3,};<br>cout<<a;
结果:<br>输出的是 a的地址
字符数组
char a[3] = { 'a' , 'b' , 'c' } ;
结果:<br>abc0贎<br>
字符串
char a[]="i am a student";<br>cout<<a;
结果:<br>i am a student
字符串
处理字符串方式
C-风格字符串
基于string 类库
c-风格字符串
性质
以\0结尾
与字符常量区别
char shirt_size= 'S';
s。。表示的是83
char shirt_size="S"
s 。。即“S”表示的是字符串所在的内存地址,但是这是错误的
输出原理
逐个处理字符串中的字符,直到达到空字符为止
注意
存储字符串的字符数组应该足够大,数组比字符串长的害处只是会浪费一些空间
意思是不会降低处理数组时的效率。?
字符串常量的拼接
原则
任何两个由空白分隔的字符串常量将自动拼接成一个
例子
cout<<"i am a" "student"
cout<<"i am a"<br>"student";
结果
字符串之间没有空格
上 :i am astudent
字符串输入
cin 输入字符串
例子
cout<<"enter your name\n";<br>cin>>name ;<br>cout<<"enter your favourite food\n";<br>cin>>dessert ;<br>cout<<"Here is "<<dessert<<"for you "<<name;
cin >> name; <br>不用读取地址的符号&。
结果:<br>enter your name<br>Alex bob<br>enter you favourite food <br>Here is bob for you Alex
原理
用空白()确定字符串的结束位置。cin 读取 Alex 后把 bob 留在输入队列中 ,当cin 在输入队列中搜索用户喜欢的甜点时,发现bob ,因此cin 读取bob。
scanf 会 这样子吗??
是
<div>int main()</div><div>{</div><div><span class="Apple-tab-span" style="white-space:pre"> </span></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>using namespace std;</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>char name[20];</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>char food[20];</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>cout<<"enter your name \n";</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>cin>>name;</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>cout<<"enter your favourite food\n";</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>cin>>food;</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>cout<<"here some "<<food<<" for you "<<name<<endl;</div><div><span class="Apple-tab-span" style="white-space:pre"> </span></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>printf("enter your name \n");</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>scanf("%s",&name);</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>printf("enter your favourite food\n");</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>scanf("%s",&food);</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>printf("Here some %s for you %s",name ,food);</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>return 0;</div><div><span class="Apple-tab-span" style="white-space:pre"> </span></div><div>}<br><br>//结果都一样</div>
getline()
作用
类成员函数,能够输入整条字符串,直到换行符
getline()丢弃换行符,空字符来替代换行符
格式
char name[20];<br>cin.getline( name, 20 );
说明
第一个参数name
存储到的数组中的数组名
第二个参数 20
读取的字符数,20——最多读取19个字符数,最后一个\0
其他
可以进行拼接
例子
cin.getline(name1,20);<br>cin.getline(name2,20);
cin.getline(name1,20).getline(name2,20);
原理
cin.getline()完成后返回一个cin对象
get()
作用
类成员函数,能够输入整条字符串,直到换行符
换行符保留在输入序列中
不读取换行符,将其留在输入队列中
格式
cin.get(name ,20);
结果
cin.get(name ,20);<br>cin.get(food,20);
第二个get 看到第一个字符是换行符,认为已经达到行尾,不会读取任何内容
变体
cin.get()
作用
读取下一个字符即使是换行符
如果想接收这个字符,应该怎么办???
结果改进应用
cin.get( name , 20 ).get( ) ;
get()好处
能知道停止读取的原因是由于已经读取了整行
?? 觉得是getline()也可以
getline()\get()读取空行
做法
最初,下一条输入语句将在前一条getline()或get()结束读取的位置开始读取。
当前,当get(),(不是getline())读取空行后将设置失效位,接下来的输入被阻断,可用cin.clear();来恢复。
混合输入字符串和数字
导致的问题
int year ;<br>cout<<"enter year"<<endl;<br>cin>>year ;<br>char address[80];<br>cout<<"enter address"<<endl;<br>cin.getline(address,80);<br>cout<<"year="<<year<<endl;<br>cout<<"address="<<address<<endl;
结果:<br>enter year<br>1999<br>enter address<br>year=1999<br>address=
解决方法
cin>>year;<br>cin.get(); / cin.get(ch);<br>
(cin>>year).get(); (cin>>year).get(ch);
string 类简介
作用
用string 类型的变量来存储字符串
提供将字符串作为一种数据类型的表示方法
相关概念
#include<string>
string类位于名称空间std 中,必须使用using 指令,或者std::string 引用
srting 对象就是变量 ,如下的str1
声明
string str1;
= char cha1[ ];
初始化
string str1="i am a student";
string str1={"i am a student"};
string str1 {"i am a student"};
赋值
输入赋值
cin>>str ;
一个string对象赋值给另一个string 对象
str1=str2;
直接赋常量
str1="i am a student"
一个一个元素赋值
str1[0]=i;.....
说明
string 的大小能够自动地调整。声明时可以创建<br>一个长度为0的string 对象,输入读取到str1时,<br>会自动的调整长度
再输出的时候会调整吗???会的,会重新调整大小。<br>当作是一个普通变量就可以了。
元素的引用
str[1]...数组表示法一样
字符串合并
str3 = str1 + str2 ;
子主题
str1+=str2;
在字符串str1 后加字符
str1 += "i am a student";
与C-风格字符串对比
赋值
str1=str2 ;<br>
str1="i am a student"
strcpy(charr1,charr2) ;
strcpy(cahrr1,"i am a student");
合并
str1 += str2;
子主题
strcat(str1,str2);
strcat(str1,"a student")
求长度(字符数)
str1.size()
int len1 = str1.size( );<br> str2.size( );
str1 是一个对象,size()是一个类方法
未被初始化的str对象的长度为0
strlen()
int len2=strlen(charr1)
strlen 原理
从第一个元素开始计算字节数,直至遇到空字符。
好处
比字符数组安全
string类 I/O
getline
一般其它
cin.getline(cahrr1,20)
string
getline(cin,str1);
不用说明长度
说明
参数
cin
指出哪里去查找输入
其他形式字符串字面值
一般形式
char charr1[ ]=" i am a student ".
wchar_t<br>char16_t<br>char32_t
wchar_t title = L " Chief Astrogator ";<br> = u " "<br> =U " "
Unicode字符编码方案UTF-8
含义?
根据编码的数字值,字符可能存储为1~4 个八位组
前缀
u8
原始(raw)字符
作用
字符表示的是自己。eg/n 表示的是/和n
格式
R"( 字符串 )"
例子
cout<< R"( Jim "king" Tutt uses "\n" instead of endl. )"
Jim "king" Tutt uses "\n" instead of endl.
如果要显示 “( <br>和 )“ 怎么办?
在”和(之间添加其他字符
eg : cout << R"+*( "(who woudln`t )",she whispered. )+*"<<endl ;
结果:<br>"(who woudln`t )",she whispered.
结构简介
结构定义
main()前面
main()中,紧跟在开始括号的后面
结构变量
地位
是结构数据对象
声明创建
法1
struct student std ;
C++ 中的struct 可以省略
student std;
法2
法3
初始化
student std =<br>{<br> "wang",<br> 11,<br> 91.5<br>};
可以没有等号
student std ={"wang",11,91.5};
初始化为0
student std {};
成员引用
std.name
可以将string类作为成员
std::string name;
注意名称空间
结构赋值
结构数组
定义
变量创建
初始化变量
inflatable guests[2] =<br>{<br> {"ben",0.5,21.99} ,<br> {"John",2000,565.99} <br>} ;
引用成员
结构中的位字段
概念
每个成员称为位字段
格式/例子
struct torgle_register<br>{<br> unsigned int SN : 4;<br> unsigned int : 4;<br> bool goodIn : 1;<br> bool goodTorgle: 1;<br>};
共用体
声明
union ....<br>{<br> int int_val;<br> long long_val;<br>};
应用
struct widget<br>{<br> char brand[20];<br> int type;<br> union id <br> {<br> long id_num;<br> char id_char[20];<br> }id_val ;<br>} ;
widget prize;<br>prize.id_val.id_num
struct widget<br>{<br> char brand[20];<br> int type;<br> union <br> {<br> long id_num;<br> char id_char[20];<br> };<br>} ;
pirze.id.num
0 条评论
下一页