Lesson 3:复合类型

来源:互联网 发布:台湾也用淘宝吗 编辑:程序博客网 时间:2024/06/06 03:49

        复合类型是常用的最基础的数据类型,学好这些是理解高级数据结构的基础。

        一、数组

       在计算机的内存中,储存空间是一个地址连续的块,创建一个数组也就是分配一块连续的存储空间,因此数组有一个很明显的有点,就是访问的速度快,不像其他的数据结构需要花费一个遍历的时间。打个比方,假如你需要到一栋高楼里去找一个人,但是他没告诉你住在几楼,那么你需要从一楼开始往上敲门,直到找到为止;但是如果你知道他住在几楼,那么你就可以直接找到他,跟数组中的下标一样,可以直接定位到目标。       

       创建一个数组需要声明:值的类型;数组名;元素个数。数组的长度最好是用常量来声明,而且尽量比所需的长度大一点,来避免发生越界访问。如果数组很大的话,那么最好在main函数外定义,作为一个全局变量。对于一个长度为n的数组,下标的有效范围从0到n-1,编译器不会检查使用的下标是否有效,但是运行后可能会对数据或程序造成破坏。另外,如果对数组名用sizeof函数,那么返回的字节数为每个元素的字节数乘以数组长度。

       初始化:

       1、int array[5]

       2、int array[5] = {1, 2, 3, 4, 5}

       3、int array[] = {1, 2, 3, 4, 5}

       4、int array[5] = {0}

       第一种定义方法会给元素分配随机值;第二种会依次给元素赋值,如果花括号中的数少于数组长度,则其他元素会被初始化为0;第三种不用指定数组长度,可以用sizeof(array) / sizeof(int)来获得;第四种将所有元素初始化为0,如果花括号中的值不是0,则将该数赋给第一个元素,其余元素初始化为0。另外,在C++11中可以用初始化列表,即在以上方法中去掉“=”效果一样。


        二、C-风格字符数组

        字符数组一定要以空字符结尾(null character),即 '\0',ASC码为0,否则就会出错。例如:

char a[5] = {'a', 'b', 'c', 'd', '\0'};char b[5] = {'a', 'b', 'c', 'd', 'd'};char c[5] = "abcd";char d[] = "abcd";cout << (int)d[4];cout << strlen(a) << " " << strlen(b) << " "      << strlen(c) << " " << strlen(d) << endl;

        字符数组b没有空字符结尾,因此它不是一个字符串。相比较a和b,输出a时当碰到空字符时就会停止,输出b时由于不会碰到空字符,那么5个字符输出完之后不会停止,会接着将内存中随后的各个字节解释为要打印的字符,直到遇到空字符为止,因此我们可以用空字符来将一个字符数组进行截短。由于空字符(其实也就是值为0的字节)在内存中很常见,因此这一过程将会快停止。a和b的初始化方式比较复杂,因此有c和d这两种初始化方式。一种是指定的长度,这样编译器会自动在末尾补上空字符,一种是不指定长度,同样编译器也会自动加上空字符并计算长度。

输出字符数组d的最后一个字符会发现什么都没有,就一个空行,加上强制转换后就可以得到空字符的ASC码0。利用cstring中的strlen函数可以输出四个字符数组的长度分别为4 11 4 4,显然长度为11正好说明了没有空字符结尾带来的错误,同时也可以看出空字符虽然占用一个长度单位,但是在计算长度时并不会算上它。

        类似于数组,如果初始化的数量为达到其长度,那么剩余的所有字符都会被初始化为0,也就是空字符。


        关于输入

        由于不能通过键盘输入空字符,因此cin需要别的方法来确定字符串的结尾位置。cin使用空白(空格、制表符、换行符)来确定字符串的结束位置,那么每次只能输入一个单词。因此要特别留心输入的字符数组中包含有空格的情况,这会产生多个字符串。所以必须使用cin的较高级特性。

        面向行的输入:getline()

        getline函数读取整行,通过使用回车键输入的换行符来确定输入结尾,可以使用cin.getline()函数来实现。 这个函数有两个参数,例如

        cin.getline(name, 10);

        第一个参数是用来存储输入行的数组的名称,第二个参数是要读取的字符数。如果这个参数是20,那么这个函数最多读取19个字符,超过19个字符的部分则不会读取。当遇到换行符或者读取到指定数目的字符时会停止读取。另外,如果输入的字符数目少于参数值,敲击换行符也会停止读取,并且会自动补充一个空字符。所以在写参数的时候可以尽量大一点。(需要着重理解的是:getline函数会读取并直接丢弃换行符)

        面向行的输入:get()

        get()函数有两种用法,第一种参数和getline一样,用法也一样,但是我们需要注意的是,get函数不会读取并丢弃换行符,而是将其留在输入队列中,所以当我们连续两次使用get函数来输入名字的时候,由于第一次调用后,换行符会留在输入队列中,因此第二次调用时看到的第一个字符便是换行符,它会认为已经到达行尾,而没有发现任何可以读取的内容。解决的方法可以用不带参数的get()函数,调用cin.get()可读取下一个字符,即使是换行符。所以,一共有三种解决方法:

char name1[20], name2[20];// method 1cin.get(name1, 10);cin.get();cin.get(name2, 10);// method 2cin.get(name1, 10).get();cin.get(name2, 10).get();// method 3cin.getline(name1, 10).getline(name2, 10);

        第一种方法使用不含参数的get()函数来处理换行符;第二种方法cin.get()函数返回一个cin对象,该对象随后被用来调用get()函数,其效果和cin.getline()一样。

        通过以上我们可以看出getline和get函数之间的区别,老式没有getline,get函数的输入更仔细,可以用来对每一个字符进行判断。当getline()或get()函数读取空行时(也就是连续使用输入函数碰到的换行符问题),最初的做法是下一条输入语句将在前一条getline或get结束读取的位置开始读取;当前的做法是当get()读取空行后将设置失效位(failbit),表示接下来的输入将被阻断,可以使用cin.clear()来进行恢复。另外,如果输入的字符比指定的数目多,那么getline()和get()将把余下的字符留在输入队列中,而getline()还会设置失效位,并关闭后面的输入。


        三、string类

        string类是很常用的一种数据类型,是字符数组的改进版,一些基本写法不变,但是使用更方便。string的大小可以让程序自动处理;可以使用+符号进行拼接;不能直接将一个数组赋给另一个数组,但可以将一个string对象赋给另一个string对象。对应在C-风格字符串中,有拷贝函数strcpy和拼接函数strcat。


        四、结构体

        直接举个例子吧:

struct student {int id;double chinese;double math;double english;double total;}stu1, stu2;student stu3;


        这里定义了一个学生成绩的结构体,id、语数外成绩和总成绩是5个成员变量。student是一种数据类型,创建该变量的方式有两种,一种就是和stu1、stu2一样直接跟在结构体后面定义,另一种就是和stu3一样直接定义。同样,结构体变量可以用赋值运算符将结构赋给另一个同类型的结构。初始化成员变量有以下方式:

student stu4;stu4.id = 0;stu4.chinese = 100;stu4.math = 100;stu4.english = 100;stu4.total = stu4.chinese + stu4.math + stu4.english;student stu5 = {1,100,100,100,300};student *stu6 = new student {2,90,90,90,270};cout << stu4.id << " " << stu6->id << endl;// another method struct student {int id;double chinese;double math;double english;double total;}stu7 = {1,100,100,100,300};

        第一种分别对每个成员变量赋值,比较难写,第二种和第三种需要区分开和指针变量的区别,普通变量直接用 ' . '来访问成员变量,指针变量要用' ->' 来访问并且需要使用new关键字来开辟空间。注意花括号之后的分号不能漏写。

        结构数组的使用和数组一样,初始化也可以和一个变量的初始化来类比,例如:

student stu[2] = {{1, 100, 100, 100, 300},{2, 101, 101, 101, 303}};

        同样也可以用sizeof函数来计算结构体的大小,根据分配空间的原理,最好在定义结构体的时候需要将成员变量按照字节从小到大的顺序来定义,这样是最节省空间的(至于具体原因在这里不做详细的讨论)。


        五、共用体(union)

        共用体是一种数据格式,它能够存储不同的数据类型,但只能同时存储其中的一种类型。由于共用体每次只能存储一个值,因此它必须有足够的空间来存储最大的成员,所以共用体的长度为其最大乘员的长度。很显然,共用体的优势就是节省空间。例如:

struct widget {char brand[20];int type;union id {long id_num;char id_char[20];}id_val;};widget prize;//......if (prize.type == 1)cin >> prize.id_val.id_num;else cin >> prize.id_val.id_char;

        匿名共用体(anonymous union),其成员将成为位于相同地址处的变量,因此不需要定义名称。

struct widget {char brand[20];int type;union {long id_num;char id_char[20];};};widget prize;//......if (prize.type == 1)cin >> prize.id_num;else cin >> prize.id_char;

        六、枚举(enum)
        枚举是一种创建符号常量的方式,这种方式可以代替const,定义的格式跟结构体差不多。在下例中,one,two,three,four这些常量叫做枚举量(enumerator):
// method 1enum digit {one, two, three, four};// method 2enum digit {one = 1, two = 2, three = 4, four = 8};// method 3enum digit {one, two = 100, three, four};
        第一种方法中,这些符号常量对应的整数值为0,1,2,3;第二种方法中,是自己设置的枚举量的值;第三种方法中,one默认为0,由于two是100,那么后面的值需要比其大1,则three为101,four为102。定义之后,one,two,three,four就可以直接作为常量用了。
       
       








       

       




       



       

       


0 0
原创粉丝点击