网易博客转载UNIX C学习(4部分)
来源:互联网 发布:韩国足球 知乎 编辑:程序博客网 时间:2024/06/05 23:59
1.链表实现:#include <iostream>
#include <stdlib.h>
using namespace std;
//链表实现
struct LST
{
int data;
LST *pnext;
};
class CList
{
public:
CList(){p1=NULL;p2=NULL;head=NULL;end=NULL;cnt=0;}
void create(const int n);//创建链表,链表初始化。
const int getcount()const;//得到链表中数目
void pt()const;//打印链表中节点。
void insert(const int n);//在链表中第n个数据前插入数据。
const int find(const int n);//查找n是否在链表里面,若找到,返回是第几个数据,否则返回0;
void revers();//对数据进行旋转
void sort();//链表升序排序
void dete(const int n);//删除第n个结点
private:LST *p1,*p2,*head,*end;int cnt;
};
void CList::dete(const int n)
{
p1=head;
for (int i=1;i<n-1;i++)
p1=p1->pnext;
p2=p1->pnext;p1->pnext=p2->pnext;
cnt--;
}
void CList::sort()
{p1=head;p2=p1->pnext;int temp;
for (int i=0;i<cnt-1;i++)
{for(int j=i+1;j<cnt;j++)
{if(p1->data>p2->data) {temp=p1->data;p1->data=p2->data;p2->data=temp;}
p2=p2->pnext;}
p1=p1->pnext;p2=p1->pnext;
}
}
void CList::revers()
{
int a[200];//这里定义了链表的最大长度。
int temp;p1=head;p2=head;
for (int i=0;i<cnt;i++){a[i]=p1->data;p1=p1->pnext;}
for(i=0;i<cnt/2;i++) {temp=a[i];a[i]=a[cnt-i-1];a[cnt-i-1]=temp;}
for (i=0;i<cnt;i++){p2->data=a[i];p2=p2->pnext;}
}
const int CList::find(const int n)
{ p1=head;
for (int i=0;i<cnt;i++)
{if(p1->data==n) return i+1;
else p1=p1->pnext;
}
return 0;
}
void CList::insert(const int n)
{
int data;
if (n<=1)//在链表头插入
{cout<<"请输入要插入的数据:"<<endl;cin>>data;p1=new LST;p1->data=data;p1->pnext=head;
head=p1;cnt++;}
if(n>1 && n<=cnt)//在中间插入
{cout<<"请输入要插入的数据:"<<endl;cin>>data;
p1=head;
for (int i=1;i<n-1;i++)
p1=p1->pnext;
p2=new LST;p2->data=data;p2->pnext=p1->pnext;p1->pnext=p2;cnt++;
}
if(n>=cnt+1)//链尾插入
{cout<<"请输入要插入的数据:"<<endl;cin>>data;p1=new LST;p1->data=data;p1->pnext=NULL;
end->pnext=p1;end=p1;;cnt++;}
}
void CList::create(int n)
{
int data;
cnt=n;
if (n<=0){p1=p2=NULL;cout<<"请输入>0的数!";}
if (n==1) {cout<<"输入唯一一个节点的数据"<<endl;cin>>data;
p1=new LST;p1->data=data;p1->pnext=NULL;head=end=p1;}
if (n>1)
for (int i=1;i<=n;i++)
{
if(1==i)
{cout<<"输入第一个节点的数据"<<endl;cin>>data;p1=new LST;p1->data=data;
p2=p1;p1=new LST;p2->pnext=p1;head=p2;}
if(i<n && i>1)
{cout<<"输入要创建第"<<i<<"个节点的数据"<<endl;cin>>data;
p1->data=data;p2=p1;p1=new LST;p2->pnext=p1;}
if(i==n)
{cout<<"输入最后一个节点的数据"<<endl;cin>>data;
p1->data=data;p1->pnext=NULL;end=p1;
}
}
}
const int CList::getcount()const
{
return cnt;
}
void CList::pt()const
{
LST *p=head;
for(int i=0;i<cnt;i++)
{cout<<p->data<<'\t';p=p->pnext;}
cout<<endl;
}
int main()
{
CList ls1;
int n;
do
{cout<<"请输入要创建的节点数目:"<<endl;cin>>n;
} while (n<=0);
ls1.create(n);
ls1.pt();
ls1.sort();
ls1.dete(3);
ls1.pt();
return 0;
}
2.线性表实现:
#include <iostream>
using namespace std;
class CLine//线性表类
{
public:
CLine():p(NULL) {max=0;cnt=0;}
void create(const int n);//初始化,若为0则为空线性表。
void pt();//打印值
void sort();//排序升序
void del(int n);//删除第n个元素
const int getcnt(){return cnt;}//返回实际数目
void rverse();//倒序
const int find(const int n);//查找n是否在线性表里面,若在返回它的位置,不在返回-1;
void insert(int address,int value);//将值value添加到第address个数据前面
private:int max;int cnt; int * p;//线性表最大数目和实际数目
};
const int CLine::find(const int n)
{for(int i=0;i<cnt;i++) if(n==p[i]) return i;
return -1;}
void CLine::rverse()
{
int *pnew=new int[cnt];int j=0;
for(int i=cnt-1;i>=0;i--)
pnew[j++]=p[i];
p=pnew;
}
void CLine::insert(int address,int value)
{
cnt++;int *pnew=new int[cnt];
for (int i=0;i<address-1;i++)
pnew[i]=p[i];
pnew[i++]=value;
for(int j=address-1;j<cnt-1;j++)
pnew[i++]=p[j];
p=pnew;
}
void CLine::del(int n)
{
int *pi=new int[cnt-1];
for (int i=0;i<n-1;i++)
pi[i]=p[i];
for (int j=n;j<cnt;j++)
pi[i++]=p[j];
cnt--;p=pi;
}
void CLine::sort()
{
int temp;
for(int i=0;i<cnt-1;i++)
for(int j=i+1;j<cnt;j++)
if(p[i]>p[j]) {temp=p[i];p[i]=p[j];p[j]=temp;}
}
void CLine::pt()
{
for(int i=0;i<cnt;i++)
cout<<p[i]<<'\t';
cout<<endl;
}
void CLine::create(int n)
{
p=new int[n];int data;
for (int i=0;i<n;i++)
{
cout<<"输入第"<<i+1<<"个数:"<<endl;
cin>>data;p[i]=data;
}
max=cnt=n;
}
int main()
{CLine line1;
line1.create(5);
//line1.sort();
cout<<line1.find(2)<<endl;
line1.pt();
return 0;
}
3.堆栈实现:
#include <iostream>
using namespace std;
class CStack
{
public:CStack(){top=NULL;cnt=0;max=0;p=NULL;}
void init(const int n);//初始化栈为n个空间;
void pt();//打印栈元素
void push(int n);//将元素n压入栈中
const int pop();//弹出栈顶值。
private:int *top;int max;int cnt;int *p;//指向栈顶的指针和栈中元素个数。
};
const int CStack::pop()
{
if(0==cnt) {cout<<"栈中无元素!"<<endl; return 999999999;}
else {max--;cnt--;int a=*p;p=p+1;return a;};
}
void CStack::push(int n)
{int j=0;
if(0==cnt) {top[max-1]=n;cnt++;p=top+max-1;}
else if(max==cnt)
{int *pnew=new int[max+1];
for(int i=0;i<max;i++) {pnew[++j]=top[i];} pnew[0]=n;max++;cnt++;p=pnew;top=pnew; }
else {top[max-cnt-1]=n;p=top+max-cnt-1;cnt++;}
}
void CStack::pt()
{if(0==max) cout<<"空栈!";
else
if(cnt==0) cout<<"栈中无元素!";
else for(int i=0;i<cnt;i++) cout<<p[i]<<'\t';cout<<endl;
}
void CStack::init(const int n)
{top=new int[n];max=n;}
int main()
{
CStack s1;
s1.init(5);
s1.push(10);s1.push(11);s1.push(12);s1.push(13);s1.push(14);
s1.pt();
cout<<s1.pop()<<s1.pop()<<s1.pop()<<s1.pop()<<s1.pop()<<s1.pop()<<endl;
s1.pt();
return 0;
}
下面是栈的模板实现:
#include <iostream>
using namespace std;
template<class _T>
class CStack
{
public:CStack(){top=NULL;cnt=0;max=0;p=NULL;}
void init(const int n);//初始化栈为n个空间;
void pt();//打印栈元素
void push(_T n);//将元素n压入栈中
const _T pop();//弹出栈顶值。
private:_T *top;int max;int cnt;_T *p;//指向栈顶的指针和栈中元素个数。
};
template<class _T>
const _T CStack<_T>::pop()
{
if(0==cnt) {cout<<"栈中无元素!"<<endl; return (_T)0;}
else {max--;cnt--;_T a=*p;p=p+1;return a;};
}
template<class _T>
void CStack<_T>::push(_T n)
{int j=0;
if(0==cnt) {top[max-1]=n;cnt++;p=top+max-1;}
else if(max==cnt)
{_T *pnew=new _T[max+1];
for(int i=0;i<max;i++) {pnew[++j]=top[i];} pnew[0]=n;max++;cnt++;p=pnew;top=pnew; }
else {top[max-cnt-1]=n;p=top+max-cnt-1;cnt++;}
}
template<class _T>
void CStack<_T>::pt()
{if(0==max) cout<<"空栈!";
else
if(cnt==0) cout<<"栈中无元素!";
else for(int i=0;i<cnt;i++) cout<<p[i]<<'\t';cout<<endl;
}
template<class _T>
void CStack<_T>::init(const int n)
{top=new _T[n];max=n;}
int main()
{
CStack<char> s1;
s1.init(5);
s1.push('a');s1.push('b');s1.push('c');s1.push('d');
s1.pt();
return 0;
}
4.关于枚举类型的一个小程序:
enum BREED{YORKIE, CAIRN, DANDIE, SHETLAND, DORERMAN,LAB};
class CDog
{BREED b;
public:
CDog(BREED be){b=be;}
void pt()
{char *s[6]={"jiao1","jiao2","jiao3","jiao4","jiao5","jiao6"};;
cout<<s[b]<<endl;}
};
int main()
{
CDog d1(CAIRN);
d1.pt();//输出jiao2.
return 0;
}
5.关于虚析构函数例子:
class base
{ char *pbase;
public:
base(){pbase=new char[5];}
virtual ~base()
{delete []pbase;cout<<"调用base析构函数"<<endl;}
};
class derived:public base
{ char *pderived;
public:
derived(){pderived=new char[100];}
virtual ~derived()
{delete []pderived;cout<<"调用derived析构函数"<<endl;}
};
void main()
{ derived *pd1=new derived;
delete pd1;//输出结果为 "调用derived析构函数" "调用base析构函数"
}
事实上,关于virtual虚析构函数是没有意义的,因为虚函数就为了让继承类重写,而析构函数的名字要和类一样,继承类的名字和基类名字不可能一样,也就不会实现析构函数。所以上面这个例子加不加virtual效果都是一样的。
6.关于const, static, extern, volatile。
Const:const修饰参数,表示参数不可改变。给这个参数赋值时,只能在构造函数用 :号赋值,而且必须要赋值。不能在外面赋值,在外面赋值是static的变量的风格。
Const在函数前面,表示函数返回值为常量,不可改变。
Const在函数后面,只可以访问类中变量(包括const成员和非const成员,但是不可以对它们进行操作,只能用,),const在后的函数只能访问后面也有const的函数。
Const类对象:const对象只能访问const在后面的成员函数,只能访问为const的成员变量。
用static修饰的成员变量和成员函数只在本文件中可见,在外部是不能访问的。用static修饰的成员和成员函数在类的多个对象中只有一份拷贝,类的静态成员函数只能访问类的静态变量或函数static成员变量只有在它的文件范围内初始化后才能使用。分配内存时,存储在静态区,不存储在栈上面。
Extern:在一个文件中将自己的成员变量定义成extern,它是静态分配空间的,此extern变量和函数在其它函数中可见。若引用另一个文件中的变量或者成员函数,用extern声明,可以有多处声明,但是初始化只能有一次。可以多次声明,但类型必须一样。它也分配在静态区。
基类的成员函数可以被派生类继承并覆盖,那么假如我们将基类的成员函数体前面加修饰符const的话,可防止覆盖函数改变数据成员的值。
const与static都有静态的含义,当声明一个变量在其前加写const或static,它会将值一直保持下去。
但const实际实际上声明了一个完全静态的常量,除了在声明时可以给它赋值一次,在之后的任何代码中都不能再对它进行赋值。所以在声明const常量时,必须对其赋值,否则这个常量将没有任何意义。当然,const是左结合的,所以在用到指针时,要注意它只使得右侧紧挨着的量成为静态。比如int const *a 变量值是静态的,但地址是可变的,反过来,int *const a地址是静态的,但变量值是可变的。Const修饰成员函数时不能用在构造函数和析构函数。
const用于函数也是类似的。Typedef char* pstr; const pstr p2=”world”;p2++是错误的。虽然const char* p2中p2++是正确的,但是这个是错误的,因为主要看const后面的修饰。与typedef无关。用const声明的可以放在头文件中。
而static的最大特点就是,它可变。比如有static int a = 2;,这个值在以后的代码中可以再次赋值。所以说static全局静态变量不必在声明时就一定初始化。那么static的意义何在呢?其实这个全局静态的意义在于它的时效性。通常来说,一个变量在函数或者一些语句当中,一旦运行结束就会被系统干掉,这样这段代码在第二次运行时变量的值会被重新初始化。而如果加了关键字static,则这个变量在之后不会被重新建立,而是继续之前的值和地址.
Volatile定义:int volatile n。可以被外部进程(操作系统、硬件或并发线程等)改变。经常用在多线程中,表示优化器在用到这个变量值的时候要小心的重新读取这个变量的值,而不是使用保存在寄存器里的备份。当结构或类用这个值时,表示结构或者类的所有成员都被视为volatile.一个参数既可以是volatile,也可以是const,前者表示程序不应该试图去改变它,后者表示它可能被意想不到的改变。
Extern “C”来指定使用另一语言进行链接,表示支持C编译器链接。
变量声明4种类型:
Auto:声明变量的生存期为自动,所有变量默认就是auto的。表明变量(自动)具有本地范围,块范围的变量声明如FOR默认为AUTO存储类型。
Register:命令编译器尽可能将变量存在CPU内部寄存器而不是通过内存寻址访问以提高效率。在可能的情况下会直接存放在机器的寄存器里,但对32位编译器不起作用,当global optimizations(全局优化)开的时候,它会做出选择是否放在自己的寄存器中,不过其它与register关键字有关的其它符号都对32位编译器有效。
Static:常见用途:统计函数被调用次数和当数组比较大时,减少建立或者初始化这些变量的开销。在全局静态储存区。
Const:
关于这个详细的说明在:
http://wenku.baidu.com/view/842f5318964bcf84b9d57b66.html
7.一个程序小代码:
代码如下:
#include <stdio.h>
int main()
{
const short int c1 = 49920;
const int c2 = 1073742008;
int (*pf)() = (int (*)())&c2;
printf("%c%c\n", *(char*)pf()-19, *((char*)pf()+1)-49);
return 0;
}
运行这个程序,屏幕上会出现一个:)。
很多人不懂其中的道理,在这里我给大家分析下代码。
先看这两句:
const short int c1 = 49920;
const int c2 = 1073742008;
定义了两个局部变量,数值转换成16进制为:
const short int c1 = 0xc300;
const int c2 = 0x400000b8;
其中变量c1的地址为:0x0012FF7C,占两个字节,c2的地址为:0x0012FF78,占四个字节。这两个变量占据了连续的空间。变量赋值后,从0x12ff78开始的内存单元存储的字节码为:B8 00 00 40 00 C3 。对应的汇编码是:
mov eax,400000h
ret
接下来的这句:
int (*pf)() = (int (*)())&c2;
分析如下:
定义了一个函数指针,参数为NULL,返回值为int类型。这个函数指针,指向上面的汇编码。这样,后面执行pf(),就执行了这段汇编码。
继续分析下面这句代码:
printf("%c%c\n", *(char*)pf()-19, *((char*)pf()+1)-49);
先看*(char*)pf()-19这个表达式,执行了了pf指向的汇编代码,从汇编代码看,
这个函数调用后的返回值是0x400000,pf()前面的char *是把函数的返回值转换成一个
char*型指针,这个指针指向0x400000,前面再加个*号,表示取0x400000地址的内容,
由于是char *型指针,因此从这个地址取一个字节。
*(char*)pf()-19 表示的是从0x400000取出的字节内容再减去19。
接下来:*((char*)pf()+1)-49代表的意思是从0x400000 + 1的地址取出一个字节内容在减去49。
熟悉PE文件结构的朋友一定知道,对于exe文件0x400000是内存加载的基地址。
也就是说,0x400000 字节的内容对应的是0x4D,0x400001字节的内容对应的是0x5A.
这是我们常说的pe文件起始的两个字节,"MZ"
这样,表达式*(char*)pf()-19的结果是0x3A ,表达式*((char*)pf()+1)-49的结果是0x29
察看ascii码表,输出就是我们看到的样子。
#include <stdio.h>
/*
int main()
{
const short int c1 = 49920;
const int c2 = 1073742008;
int (*pf)() = (int (*)())&c2;
//printf("%c%c\n", *(char*)pf()-19, *(char*)pf()-36);
printf("%c%c\n", *(char*)pf()-19, *((char*)pf()+1)-49);
return 0;
}
*/
#include <stdlib.h>
void print()
{
printf("怎么调用我了?\n");
exit(0);
}
int main(int argc,char* argv[])
{
*(&argc-1)=(int)print;
return 0;
}
7.String实现:
#include <iostream.h>
class CStr
{
char *ps;
public:
CStr(const char* cstr);
CStr(const char* cstr,int nLength);
CStr(char ch);//用ch这个字符初始化
CStr(char ch,int nLength);//用ch这个字符去填充长度nLength
CStr(const CStr& str);
CStr& operator=(const char* cstr);
CStr &operator=(const CStr &str);
CStr &operator+= (const char* pszSrc);
CStr &operator+=(const CStr &pszSrc);
void pt()const {if(ps) cout<<ps<<endl;}
char* getps()const {return ps;}
int glength()const {if(ps==NULL) return 0;
else {for(int i=0;*(ps+i)!='\0';i++); return i;}}
friend ostream &operator<<(ostream &osm,CStr &str);
friend istream &operator>>(istream &ism,CStr &str);
friend bool operator!=(const CStr &str1,const CStr &str2);
friend bool operator==(const CStr &str1,const CStr &str2);
friend bool operator<(const CStr &str1,const CStr &str2);
friend bool operator>(const CStr &str1,const CStr &str2);
CStr();
virtual ~CStr(){if(ps) delete[]ps;};
};
CStr::CStr() {ps=NULL;}
CStr::CStr(const char* cstr)
{for (int i=0;*(cstr+i)!='\0';i++);
ps=new char[i+1]; for (int j=0;j<i;j++) *(ps+j)=*(cstr+j);*(ps+j)='\0';}
CStr::CStr(const char* cstr,int nLength)//用cstr的前nLength来填充
{ps=new char[nLength+1];for (int j=0;j<nLength;j++) *(ps+j)=*(cstr+j);*(ps+j)='\0';}
CStr::CStr(char ch)
{ps=new char[2];ps[0]=ch;ps[1]='\0';}
CStr::CStr(char ch,int nLength)
{ps=new char[nLength+1];for (int j=0;j<nLength;j++) *(ps+j)=ch;*(ps+j)='\0';}
CStr::CStr(const CStr& str)
{ int i=str.glength(); if(i!=0) {ps=new char[i+1];
for (int j=0;j<str.glength();j++) ps[j]=*(str.ps+j); ps[j]='\0';}else ps=NULL;}
CStr& CStr::operator=(const char* cstr)
{if(ps) delete []ps;for (int i=0;*(cstr+i)!='\0';i++);
ps=new char[i+1];for(int j=0;j<i;j++) ps[j]=*(cstr+j); ps[j]='\0';return *this;}
CStr &CStr::operator=(const CStr &str)
{if(ps) delete[]ps;int i=str.glength();ps=new char[i+1];
for(int j=0;j<i;j++) ps[j]=*(str.ps+j); ps[j]='\0';return *this;}
CStr &CStr::operator+= (const char* pszSrc)
{for(int k=0;*(pszSrc+k)!='\0';k++);
if(k==0) return *this;
else if(ps==NULL) return *this;
else{
CStr s2(pszSrc); CStr s1(ps);
if(ps) delete []ps;
ps=new char[s1.glength()+s2.glength()+1];
for(int i=0;i<s1.glength();i++) ps[i]=*(s1.ps+i);
for(int j=0;j<s2.glength();j++) ps[i+j]=*(s2.ps+j);
ps[i+j]='\0';return *this;} }
CStr &CStr::operator+=(const CStr &pszSrc)
{if(pszSrc.glength()==0) return *this;
else if(ps==NULL) return *this;
else
{CStr s1(ps); CStr s2(pszSrc);
if(ps) delete []ps; ps=new char[s1.glength()+s2.glength()+1];
for(int i=0;i<s1.glength();i++) ps[i]=*(s1.ps+i);
for(int j=0;j<s2.glength();j++) ps[i+j]=*(s2.ps+j); ps[i+j]='\0';
return *this;}}
bool operator!=(const CStr &str1,const CStr &str2)
{if(str1.glength() != str2.glength()) return true;
else for(int i=0;i<str2.glength();i++)
if(str1.ps+i==str2.ps+i) return false;
return true;}
bool operator==(const CStr &str1,const CStr &str2)
{if(str1.glength() != str2.glength()) return false;
else for(int i=0;i<str2.glength();i++)
if(str1.ps+i!=str2.ps+i) return false;
return true;}
bool operator<(const CStr &str1,const CStr &str2)
{ int i=str1.glength();int j=str2.glength();int min=i<j?i:j;int m;
if(str1.glength()==0 && str2.glength()==0) return true;
if(str1.glength()==0 && str2.glength()!=0) return true;
if(str2.glength()==0 && str1.glength()!=0) return false;
else
{for(m=0;m<i;m++)
if(*(str1.ps+m)>*(str2.ps+m)) return false;
if(i<j) return true;
return false;
}
}
bool operator>(const CStr &str1,const CStr &str2)
{ int i=str1.glength();int j=str2.glength();int min=i<j?i:j;int m;
if(str1.glength()==0 && str2.glength()==0) return false;
if(str1.glength()==0 && str2.glength()!=0) return false;
if(str2.glength()==0 && str1.glength()!=0) return true;
else
{for(m=0;m<i;m++)
if(*(str1.ps+m)>*(str2.ps+m)) return true;
if(i<j) return false;
return true;
}
}
ostream &operator<<(ostream &osm,CStr &str)
{
osm<<str.ps<<endl;return osm;
}
istream &operator>>(istream &ism,CStr &str)
{
ism>>str.ps;return ism;
}
int main()
{
//CStr s1("hello!");s1.pt();
//CStr s2("love you!",20);s2.pt();cout<<s2.glength()<<endl;
//CStr s3('u');s3.pt();cout<<s3.glength()<<endl;
//CStr s4('i',9);s4.pt();cout<<s4.glength()<<endl;
CStr s2("9iooofff");
CStr s1("3iooof");
cin>>s1;
cout<<s1<<endl;
if(s1>s2) cout<<">"<<endl;
else cout<<"<"<<endl;
//cout<<s1!=s2<<endl;
// s1.pt();//cout<<s1.glength()<<endl;
return 0;
}
- 网易博客转载UNIX C学习(4部分)
- 网易博客转载UNIX C学习(1部分)
- 网易博客转载UNIX C学习(2部分)
- 网易博客转载UNIX C学习(3部分)
- 网易博客转载C学习(1部分)
- 网易博客转载C++学习(1部分)
- 从今天开始,我将开始转载网易博客部分文章
- 网易博客#include<cstdio>转载
- unix c学习总结--线程部分(apue)
- 【转载网易博客】select @@identity的用法
- uml开发过程转载自网易博客
- 我的网易学习博客
- 学习日记+部分转载
- 学习博客指南-转载
- (iOS 开发) 证书部分的博客转载
- 【转载】网易博客完美支持Word写日志
- 浏览器 cookie 详解 -- 转载自网易博客gkecenter
- 二叉树遍历——转载于网易博客summer
- 网易博客转载UNIX C学习(3部分)
- Leetcode : ZigZag Conversion
- Ubuntu挂载共享文件夹
- UI学习回想日记
- Js setInterval
- 网易博客转载UNIX C学习(4部分)
- 关于配置php Apache mysql的网络文档
- Extjs Gird 支持中文拼音排序
- [转]字符串拷贝函数
- 《MFC Windows程序设计》中Accel的改进
- android4.1编译之后运行Emulator 失败
- 在PowerDesigner中设计概念模型
- 用户只可输入整数 并求出它们的乘积
- 第一周:项目3 学生成绩的结构体数组(包括学号、姓名、三门课成绩、总分、均分)