Skip to content

Instantly share code, notes, and snippets.

@htfy96
Created October 3, 2015 14:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save htfy96/9e9260f2c8562212a88f to your computer and use it in GitHub Desktop.
Save htfy96/9e9260f2c8562212a88f to your computer and use it in GitHub Desktop.
#include <iostream>
#include <cstddef> //size_t类型在cstddef中被定义
#include <cassert> //assert宏在cassert中被定义
#include <cstring> //strlen函数
using namespace std;
const size_t SLEN=30;
/* 这一行定义了一个值为30,类型是int的名叫SLEN的常量
* 正确的定义方式是
* const int SLEN = 30;
* 而不是
* const intSLEN=30;
*
* 出于区分常量和非常量的考虑,常量一般会用全大写字母来命名。
* 由于下述的原因,类型改为size_t
*/
struct Student{ //一般来说类型名首字母大写! student -> Student
char fullname[SLEN];
char hobby[SLEN];
int ooplevel;
};
/*
* 从这里可以看出,SLEN充当的是字符数组定义时的维度的作用。
* 这样做,我是不赞同的,原因有二:
* 1) 这样的定长数组,一旦实际存储内容超过SLEN,就会发生溢出,很难处理。如果存储内容远远小于SLEN,则不仅浪费了空间,在拷贝的时候也浪费时间。
* 2) 这样的数组长度应该使用内置的size_t类型,类型与实际意义相符
*
* 因此:
* 1) 应该使用标准的std::string类型来存储字符串,省时省力。
* 当然在这里本身就是为了训练基本技巧的时候故意使用char数组,但请注意,在实际生产中大多数时候char数组是一个非常拙劣的注意。
* 2) 把SLEN改为size_t
*/
int getinto(Student pa[],int n);
void display1(Student st);
void display2(const Student* ps);
void display3(const Student pa[],int n);
/*在C++中,传递数组的方式有不少:
* 1) (Student arg[])
* 2) (Student* arg)
* 3) (Student arg[100])
* 这三者是完全等价的,写在参数表的时候本质上都是接受一个Student*的内容,下标约束(100)并没有任何作用
*
* 然而,数组和指针本质上是两种类型,为什么:
* Student arr[200];
* Student* ptr_stu = new Student[class_size];
* arr和ptr_stu一个是数组,一个是指针,却都能够调用display2(...)呢?
* 原因在于,数组在这个时候会隐式地转换成一个指针,丢失掉它所携带的维度信息,变成一个Student*。
* 这种转换被称作decay,是优先级相当高的一种转换。
* 出于这种转换的原因,我们默认情况下很难知道一个数组的维数,所以一般需要传一个辅助量n人为告知它的维数
*
* 这个时候我们要想在编译期获得一个数组的维度,应该采取如下做法:
*
#include <iostream>
#include <cstddef>
using namespace std;
template<size_t si>
void foo(int (*pt)[si])
{
cout << "The size of this array is" << si <<endl;
}
int main()
{
int a[20];
foo(&a);
}
*/
int main()
{
cout<< "Enter class size:";
int class_size;
cin>>class_size;
while (cin.get() !='\n');
/* continue;*/ // 这个continue是不需要的
assert(class_size>0);
//这里class_size不是表明一个size吗?为什么不用size_t类型而是使用int类型?
//答:这里我们是希望在输入负数class_size的时候崩溃,而不是尝试开一个很大的数组勉强运作下去(size_t 一般就是unsigned int类型,在输入负数的时候会溢出变成一个大正数,有可能将错就错运行下去;而new int[负数] 会直接崩溃,当然这只是一个很差的弥补方法)
//这是软件设计中的一个原则:如果不正确,越早崩溃越好,而不是让它产生新的问题。
//这里可以#include <cassert>库后调用assert(class_size>0)语句,该条语句表示如果class_size 非正则会自动结束程序。这是最好的解决方案
Student* ptr_stu=new Student[class_size];
// 改为Student* ptr_stu
//
for (int i=0; i<class_size; ++i)
{
ptr_stu[i].ooplevel= 0;
ptr_stu[i].hobby[0]='\0';
ptr_stu[i].fullname[0]= '\0';
} //因为new出来的东西都是未定义内容的,所以一定要初始化!
//接下来应该读取内容了
for (int i=0; i<class_size; ++i)
{
cin>>ptr_stu[i].fullname >> ptr_stu[i].hobby>> ptr_stu[i].ooplevel ;
cout<<ptr_stu[i].fullname << ptr_stu[i].hobby<< ptr_stu[i].ooplevel ;
}
int entered=getinto(ptr_stu,class_size);
//!这里有大问题,刚刚new出来的ptr_stu 内容是未定义的,你不应该尝试读取里面有什么,正常情况下应该读入到ptr_stu中去
for(int i=0;i<entered;i++)
{
display1(ptr_stu[i]);
display2(&ptr_stu[i]);
//这一段演示了值传参和指针传参两种方式,在C++引用出现之前,C语言只能用这两种方式传参。因而要么忍受display1拷贝大对象的痛苦,要么像display2一样被迫加入一个中间层指针和语法上不自然的&。之后引用的出现解决了这个问题。
}
display3(ptr_stu,entered);
delete []ptr_stu;
/* cout <<"DONE\n";*/
cout<< "DONE" << endl;//\n很不好,因为不是所有系统的换行符都是\n
return 0;
}
int getinto(Student pa[],int n)
{
int sum=0;
for(int i=0;i<n;i++)
{
/* if(pa[i] !={" ";" "; }) */ // ??? 不能读取刚new出来的ptr_stu的内容!而且也不能这么比较大小
if ( strlen(pa[i].fullname) && strlen(pa[i].hobby) && pa[i].ooplevel ) //有姓名,有爱好,而且ooplevel不为0
sum=sum+1;
else
break;
}
return sum;
}
void display1(Student st) //这里应该使用const Student& st比较妥当
{
cout<<"This student's fullname is:"<<st.fullname; //输出时可以直接输出一个char数组
}
void display2(const Student*st)
{
cout<<"This student's hobby is:"<<(*st).hobby; //同上
}
void display3(const Student pa[],int n)
{
//for(int i=0;i<n;n++) //不是n++,是i++!
for(int i=0; i<n; i++)
{
cout<<"This student's ooplevel is:"<<pa[i].ooplevel;
}
}
/*
* Testcase:
* a aa 1
* b bb 2
* c cc 0
* d dd 0
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment