C++ 怎么把struct I[15]孙子兵法传入日本时间子函数与传回主函数?

C++ 教程-海文库
全站搜索:
您现在的位置:&>&&>&IT认证
1.1 C++程序结构(Structure of a program)下面我们从一个最简单的程序入手看一个C++程序的组成结构。// my first program in C++ Hello World!#include &iostream.h&
int main() {cout &&“Hello World!”;return 0;}上面左侧显示了我们的第一个程序的源代码,代码文件名称为hellowworld.cpp。右边显示了程序被编译执行后的输出结果。编辑和编译一个程序的方法取决于你用的是什么编译器,根据它是否有图形化的界面及版本的不同,编译方法也有可能不同,具体请参照你所使用的编译器的使用说明。以上程序是多数初学者学会写的第一个程序,它的运行结果是在屏幕上打出”Hello World!”这句话。虽然它可能是C++可写出的最简单的程序之一,但其中已经包含了每一个C++程序的基本组成结构。下面我们就逐个分析其组成结构的每一部分: // my first program in C++这是注释行。所有以两个斜线符号(//)开始的程序行都被认为是注释行,这些注释行是程序员写在程序源代码内,用来对程序作简单解释或描述的,对程序本身的运行不会产生影响。在本例中,这行注释对本程序是什么做了一个简要的描述。# include & iostream.h &以#标志开始的句子是预处理器的指示语句。它们不是可执行代码,只是对编译器作出指示。在本例中这个句子# include & iostream.h &告诉编译器的预处理器将输入输出流的标准头文件(iostream.h)包括在本程序中。这个头文件包括了C++中定义的基本标准输入-输出程序库的声明。此处它被包括进来是因为在本程序的后面部分中将用到它的功能。1 / 170
C++标准函数库的所有元素都被声明在一个名空间中,这就是std名空间。因此为了能够访问它的功能,我们用这条语句来表达我们将使用标准名空间中定义的元素。这条语句在使用标准函数库的C++程序中频繁出现,本教程中大部分代码例子中也将用到它。 int main()这一行为主函数(main function)的起始声明。main function是所有C++程序的运行的起始点。不管它是在代码的开头,结尾还是中间 C 此函数中的代码总是在程序开始运行时第一个被执行。并且,由于同样的原因,所有C++程序都必须有一个main function。 main 后面跟了一对圆括号(),表示它是一个函数。C++中所有函数都跟有一对圆括号(),括号中可以有一些输入参数。如例题中显示,主函数(main function)的内容紧跟在它的声明之后,由花括号({})括起来。cout && “Hellow World!”;这个语句在本程序中最重要。 cout 是C++中的标准输出流(通常为控制台,即屏幕),这句话把一串字符串(本例中为”Hello World”)插入输出流(控制台输出)中。cout 在的声明在头文件iostream.h中,所以要想使用cout 必须将该头文件包括在程序开始处。注意这个句子以分号(;)结尾。分号标示了一个语句的结束,C++的每一个语句都必须以分号结尾。 (C++ 程序员最常犯的错误之一就是忘记在语句末尾写上分号) 。 return 0;返回语句(return) 引起主函数 main()执行结束,并将该语句后面所跟代码(在本例中为0) 返回。这是在程序执行没有出现任何错误的情况下最常见的程序结束方式。在后面的例子中你会看到所有C++程序都以类似的语句结束。
你可能注意到并不是程序中的所有的行都会被执行。程序中可以有注释行(以//开头),有编译器预处理器的指示行(以#开头),然后有函数的声明(本例中main函数),最后是程序语句(例如调用cout &&),最后这些语句行全部被括在主函数的花括号({})内。本例中程序被写在不同的行中以方便阅读。其实这并不是必须的。例如,以下程序 int main (){cout && & Hello World &;return 0;}也可以被写成:int main () { cout && & Hello World &; return 0; }以上两段程序是完全相同的。
在C++中,语句的分隔是以分号(;)为分隔符的。分行写代码只是为了更方便人阅读。 以下程序包含更多的语句:2 / 170
// my second program in C++ Hello World! I'm a C++ program#include &iostream.h&
int main (){cout && &Hello World! &;cout && &I'm a C++ program&;return 0;}在这个例子中,我们在两个不同的语句中调用了cout &&函数两次。再一次说明分行写程序代码只是为了我们阅读方便,因为这个main 函数也可以被写为以下形式而没有任何问题:int main () { cout && & Hello World! &; cout && & I'm to C++ program &; return 0; }
为方便起见,我们也可以把代码分为更多的行来写:int main (){cout &&&Hello World!&;cout&& &I'm a C++ program&;return 0;}它的运行结果将和上面的例子完全一样。这个规则对预处理器指示行(以#号开始的行)并不适用,因为它们并不是真正的语句。它们由预处理器读取并忽略,并不会生成任何代码。因此他们每一个必须单独成行,末尾不需要分号(;)
注释 (Comments)注释(comments)是源代码的一部分,但它们会被编译器忽略。它们不会生成任何执行代码。使用注释的目的只是使程序员可以在源程序中插入一些说明解释性的内容。 C++ 支持两中插入注释的方法:// line comment/* block comment */3 / 170
第一种方法为行注释,它告诉编译器忽略从//开始至本行结束的任何内容。第二种为块注释(段注释),告诉编译器忽略在/*符号和*/符号之间的所有内容,可能包含多行内容。在以下我们的第二个程序中,我们插入了更多的注释。/* my second program in C++with more comments */
#include &iostream.h&
int main (){cout && &Hello World! &; // says Hello World!cout && &I'm a C++ program&; // says I'm a C++programreturn 0;}
Hello World! I'm a C++ program
如果你在源程序中插入了注释而没有用//符号或/*和*/符号,编译器会把它们当成C++的语句,那么在编译时就会出现一个或多个错误信息。
1.2 变量和数据类型(Variables and Data types )你可能觉得这个“Hellow World”程序用处不大。我们写了好几行代码,编译,然后执行生成的程序只是为了在屏幕上看到一句话。的确,我们直接在屏幕上打出这句话会更快。但是编程并不仅限于在屏幕上打出文字这么简单的工作。为了能够进一步写出可以执行更有用的任务的程序,我们需要引入变量(variable)这个的概念。让我们设想这样一个例子,我要求你在脑子里记住5这个数字,然后再记住2这个数字。你已经存储了两个数值在你的记忆里。现在我要求你在我说的第一个数值上加1,你应该保留6 (即5+1)和2在你的记忆里。现在如果我们将两数相减可以得到结果4。 所有这些你在脑子里做的事情与计算机用两个变量可以做的事情非常相似。同样的处理过程用C++来表示可以写成下面一段代码:a = 5;b = 2;a = a + 1;result = a -
很明显这是一个很简单的例子,因为我们只用了两个小的整数数值。但是想一想你的电脑可以同时存储成千上万这样的数值,并进行复杂的数学运算。因此,我们可以将变量(variable)定义为内存的一部分,用以存储一个确定的值。 每一个变量 (variable)需要一个标识,以便将它与其他变量相区别,例如,在前面的代码中,变量标识是a, b,和result。我们可以给变量起任何名字,只要它们是有效的标识符。
标识(Identifiers)5 / 170
有效标识由字母(letter),数字(digits)和下划线 ( _ )组成。标识的长度没有限制,但是有些编译器只取前32个字符(剩下的字符会被忽略)。空格(spaces),标点(punctuation marks)和符号(symbols) 都不可以出现在标识中。只有字母(letters),数字(digits) 和下划线(_)是合法的。并且变量标识必须以字母开头。标识也可能以下划线(_)开头,但这种标识通常是保留给为外部连接用的。标识不可以以数字开头。必须注意的另一条规则是当你给变量起名字时不可以和C++语言的关键字或你所使用的编译器的特殊关键字同名,因为这样与这些关键字产生混淆。例如,以下列出标准保留关键字,他们不允许被用作变量标识名称:asm, auto, bool, break, case, catch, char, class, const, const_cast, continue, default, delete, do, double, dynamic_cast, else, enum, explicit, extern, false, float, for, friend, goto, if, inline, int, long, mutable, namespace, new, operator, private, protected, public, register, reinterpret_cast, return, short, signed, sizeof, static, static_cast, struct, switch, template, this, throw, true, try, typedef, typeid, typename, union, unsigned, using, virtual, void, volatile, wchar_t, while另外,不要使用一些操作符的替代表示作为变量标识,因为在某些环境中它们可能被用作保留词:and, and_eq, bitand, bitor, compl, not, not_eq, or, or_eq, xor, xor_eq
你的编译器还可能包含一些特殊保留词,例如许多生成16位码的编译器(比如一些DOS编译器)把 far, huge和 near也作为关键字。非常重要:C++语言是“大小写敏感”(“case sensitive”) 的,即同样的名字字母大小写不同代表不同的变量标识。因此,例如变量RESULT,变量result和变量Result分别表示三个不同的变量标识.基本数据类型(Fundamental Data types)编程时我们将变量存储在计算机的内存中,但是计算机要知道我们要用这些变量存储什么样的值,因为一个简单的数值,一个字符,或一个巨大的数值在内存所占用的空间是不一样的。计算机的内存是以字节(byte)为单位组织的。一个字节(byte)是我们在C++中能够操作的最小的内存单位。一个字节(byte)可以存储相对较小数据:一个单个的字符或一个小整数(通常为一个0到255之间的整数)。但是计算机可以同时操作处理由多个字节6 / 170
组成复杂数据类型,比如长整数(long integers)和小数(decimals)。以下列表总结了现有的C++基本数据类型,以及每一类型所能存储的数据范围: 数据类型(DATA TYPES)
*字节数一列和范围一列可能根据程序编译和运行的系统不同而有所不同。这里列出的数值是多数32位系统的常用数据。对于其他系统,通常的说法是整型(int)具有根据系统结构建议的自然长度(即一个字one word的长度),而4中整型数据char, short, int, long的长度必须是递增的,也就是说按顺序每一类型必须大于等于其前面一个类型的长度。同样的规则也适用于浮点数类型float, double和 long double,也是按递增顺序。 除以上列出的基本数据类型外,还有指针(pointer)和void 参数表示类型,我们将在后面看到。7 / 170变量的声明(Declaration of variables)在C++中要使用一个变量必须先声明(declare)该变量的数据类型。声明一个新变量的语法是写出数据类型标识符(例如int, short, float...) 后面跟一个有效的变量标识名称。例如:以上两个均为有效的变量声明(variable declaration)。第一个声明一个标识为a 的整型变量(int variable),第二个声明一个标识为mynumber 的浮点型变量(float variable)。声明之后,我们就可以在后面的程序中使用变量a和 mynumber 了。 如果你需要声明多个同一类型的变量,你可以将它们缩写在同一行声明中,在标识之间用逗号(comma) 分隔。例如:int a, b,以上语句同时定义了a、b、c 3个整型变量,它与下面的写法完全等同:整型数据类型 (char, short, long 和 int) 可以是有符号的(signed)或无符号的(unsigned ),这取决于我们需要表示的数据范围。有符号类型(signed)可以表示正数和负数,而无符号类型(unsigned)只能表示正数和0。在定义一个整型数据变量时可以在数据类型前面加关键字 signed 或 unsigned 来声明数据的符号类型。例如: unsigned short NumberOfSsigned int MyAccountB如果我们没有特别写出signed或 unsigned,变量默认为signed,因此以上第二个声明我们也可以写成:int MyAccountB因为以上两种表示方式意义完全一样,因此我们在源程序通常省略关键字signed 。 唯一的例外是字符型(char)变量,这种变量独立存在,与signed char 和 unsigned char型均不相同。8 / 170
short 和 long 可以被单独用来表示整型基本数据类型,short 相当于 short int, long 相当于 long int。也就是说 和 两种声明是等价的。最后,signed 和 unsigned 也可以被单独用来表示简单类型,意思分别同signed int 和 unsigned int 相同,即以下两种声明互相等同:unsigned MyBirthYunsigned int MyBirthY下面我们就用C++代码来解决在这一节开头提到的记忆问题,来看一下变量定义是如何在程序中起作用的。// operating with variables
#include &iostream.h&
int main (){// declaring variables:int a,
// process:a = 5;b = 2;a = a + 1;result = a -
// print out the result:cout &&
// terminate the program:return 0;} 4如果以上程序中变量声明部分有你不熟悉的地方,不用担心,我们在后面的章节中很快会学到这些内容。
变量的范围(Scope of variables)所有我们要使用的变量都必须事先声明过。C和C++语言的一个重要区别是,在C++语言中我们可以在源程序中任何地方声明变量,甚至可以在两个可执行(excutable)语句的中间声明变量,而不象在C语言中变量声明只能在程序的开头部分。 然而,我们还是建议在一定程度上遵循C语言的习惯来声明变量,因为将变量声明放在一处对debug程序有好处。因此,传统的C语言方式的变量声明就是把变量声明放在每一个函数(function)的开头(对本地变量local variable)或直接放在程序开头所有函数(function)的外面(对全局变量global variable)。一个变量可以是本地(local)范围内有效,叫做本地变量,也可以是全局(global)范围内有效,叫做全局变量。全局变量要定义在一个源码文件的主体中,所有函数(包括主函数main())之外。而本地变量定义在一个函数甚至只是一个语句块单元中。如下图所示:
变量初始化(Initialization of variables)当一个本地变量( local variable)被声明时,它的值默认为未定(undetermined)。但你可能希望在声明变量的同时赋给它一个具体的值。要想达到这个目的,需要对变量进行初始化。C++中有两种初始化方法:第一种,又叫做类C (c-like) 方法,是在声明变量的时候加上一个等于号,并在后面跟上想要的数值:type identifier = initial_例如,如果我们想声明一个叫做a的int变量并同时赋予它0这个值,我们可以这样写: int a = 0;另外一种变量初始化的方法,又叫做构造函数(constructor)初始化,是将初始值用小括号(parenthesis ())括起来:type identifier (initial_value) ;例如:int a (0);在C++.中以上两种方法都正确并且两者等同。// 变量初始化
#include &iostream&
int main (){int a=5;
// 初始值为 5int b(2);
// 初始值为 2 // 不确定初始值
a = a + 3;result = a -11 / 1706cout &&
return 0;}
字符串(strings)字符串是用来存储一个以上字符的非数字值的变量。C++提供一个string类来支持字符串的操作,它不是一个基本的数据类型,但是在一般的使用中与基本数据类型非常相似。与普通数据类型不同的一点是,要想声明和使用字符串类型的变量,需要引用头文件&string&,并且使用using namespace语句来使用标准名空间(std),如下面例子所示: // C++字符串例题#include &iostream&#include &string&
int main (){string mystring = &This is astring&;cout &&return 0;} This is a string如上面例子所示,字符串变量可以被初始化为任何字符串值,就像数字类型变量可以被初始化为任何数字值一样。以下两种初始化格式对字符串变量都是可以使用的:string mystring = &This is a string&;string mystring (&This is a string&);字符串变量还可以进行其他与基本数据类型变量一样的操作,比如声明的时候不指定初始值,和在运行过程中被重新赋值。12 / 170
// C++字符串例题2#include &iostream&#include &string&
int main (){mystring =&This is the initial stringcontent&;cout && mystring &&mystring =&This is a different stringcontent&;cout && mystring &&return 0;} This is the initial string content This is a different string content要了解更加详细的C++字符串操作,建议参考Cplusplus上的string类reference。
1.3 常量(Constants )一个常量(constant)是一个有固定值的表达式。
字(Literals)字是用来在程序源码中表达特定的值。在前面的内容中我们已经用了很多的字来给变量赋予特定的值。例如:a = 5;
这句代码中5就是一个字常量。字常量(literal constant)可以被分为整数(Integer Numbers), 浮点数(Floating-Point Numbers),字符(Characters)和字符串(Strings)。
整数(Integer Numbers)1776707-273他们是整型常数,表示十进制整数值。注意表示整型常数时我们不需要些引号(quotes (&))或任何特殊字符。毫无疑问它是个常量:任何时候当我们在程序中写1776,我们指的就是1776这个数值。除十进制整数另外, C++还允许使用八进制(octal numbers)和十六进制(hexadecimal numbers)的字常量(literal constants)。如果我们想要表示一个八进制数,我们必须在它前面加上一个0字符(zero character),而表示十六进制数我们需要在它前面加字符0x (zero, x)。例如以下字常量(literal constants)互相等值:14 / 170
// 十进制 decimal0113 // 八进制 octal0x4b // 十六进制 hexadecimal所有这些都表示同一个整数: 75 (seventy five) ,分别以十进制数,八进制数和十六进制数表示。像变量一样,常量也是有数据类型的。默认的整数字常量的类型为int型。我们可以通过在后面加字母u或l来迫使它为无符号(unsigned)的类型或长整型(long)。 75
// unsigned int75l
// long75ul
// unsigned long
这里后缀u和l可以是大写,也可以是小写。
浮点数(Floating Point Numbers)浮点数以小数(decimals)和/或指数幂( exponents)的形式表示。它们可以包括一个小数点,一个e字符(表示&by ten at the Xth height&,这里X是后面跟的整数值) ,或两者都包括。3.14159 // 3.141596.02e23 // 6.02 x 10^10231.6e-19 // 1.6 x 10^-193.0
// 3.0以上是包含小数的以C++表示的4个有效数值。第一个是PI,第二个是Avogadro数之一,第三个是一个电子(electron)的电量(electric charge)(一个极小的数值) C 所有这些都是近似值。最后一个是浮点数字常量表示数3。浮点数的默认数据类型为double。如果你想使用float或long double类型,可以在后面加f或l后缀,同样大小写都可以:3.14159L
// long double6.02e23f
字符和字符串(Characters and strings)此外还有非数字常量,例如:'z''p'&Hello world&&How do you do?&前两个表达式表示单独的字符(character),后面两个表示由若干字符组成的字符串(string) 。注意在表示单独字符的时候,我们用单引号(single quotes (')),在表示字符串或多于一个字符的时候我们用双引号(double quotes (&))。当以常量方式表示单个字符和字符串时,必须写上引号以便把他们和可能的变量标识或保留字区分开,注意以下例子:x'x'x 指一个变量名称为 x, 而'x'指字符常量'x'。字符常量和字符串常量各有特点,例如escape codes,这些是除此之外无法在源程序中表示的特殊的字符,例如换行符 newline (\n) 或跳跃符tab (\t)。所有这些符号前面要加一个反斜杠inverted slash (\)。这里列出了这些escape codes:
例如:'\n''\t'&Left \t Right&&one\ntwo\nthree&另外你可以数字ASCII 码表示一个字符,这种表示方式是在反斜杠(\)之后加以8进制数或十六进制数表示的ASCII 码。在第一种(八进制octal)表示中,数字必需紧跟反斜杠(例如\23或\40),第二种(十六进制hexacedimal),必须在数字之前写一个x字符(例如\x20或\x4A)。如果每一行代码以反斜杠inverted slash (\)结束,字符串常量可以分多行代码表示: &string expressed in \two lines&你还可以将多个被空格blankspace、跳跃符tabulator、换行符newline或其他有效空白符号分隔开的字符串常量连接在一起:&we form& &a single& &string& &of characters&最后如果我们想让字符串使用宽字符(wchar_t),而不是窄字符(char),可以在常量的前面加前缀L:L&This is a wide character string&宽字符通常用来存储非英语字符,比如中文字符,一个字符占两个字节。布尔型常量(Boolean Literals)布尔型只有两个有效的值:true和false,其数据类型为bool。
定义常量Defined constants (#define)使用预处理器指令#define,你可以将那些你经常使用的常量定义为你自己取的名字而不需要借助于变量。它的格式是:17 / 170
#define identifier value例如:#define PI 3.#define NEWLINE '\n'#define WIDTH 100以上定义了三个常量。一旦做了这些声明,你可以在后面的程序中使用这些常量,就像使用其它任何常量一样,例如:circle = 2 * PI *cout && NEWLINE;实际上编译器在遇到#define指令的时候做的只是把任何出现这些常量名(在前面的例子中为PI, NEWLINE或WIDTH)的地方替换成他们被定义为的代码(分别为3., '\n'和100)。因此,由#define定义的常量被称为宏常量macro constants。 #define 指令不是代码语句,它是预处理器指令,因此指令行末尾不需要加分号semicolon (;) 。如果你在宏定义行末尾加了分号(;) ,当预处理器在程序中做常量替换的时候,分号也会被加到被替换的行中,这样可能导致错误。
声明常量declared constants (const)通过使用const前缀,你可以定义指定类型的常量,就像定义一个变量一样: const int width = 100;const char tab = '\t';const zip = 12440;如果没有指定类型(如上面最后例子中最后一行),编译器会假设常量为整型int。
1.4 操作符/运算符(Operators)前面已经学习了变量和常量,我们可以开始对它们进行操作,这就要用到C++的操作符。有些语言,很多操作符都是一些关键字,比如add, equals等等。C++的操作符主要是由符号组成的。这些符号不在字母表中,但是在所有键盘上都可以找到。这个特点使得C++程序更简洁,也更国际化。运算符是C++语言的基础,所以非常重要。你不需要背下所有这一小节的内容,这些细节知识仅供你以后需要时参考。赋值Assignation (=)赋值运算符的功能是将一个值赋给一个变量。a = 5;将整数5赋给变量a。= 运算符左边的部分叫做lvalue (left value),右边的部分叫做rvalue (right value)。lvalue 必须是一个变量,而右边的部分可以是一个常量,一个变量,一个运算(operation)的结果或是前面几项的任意组合。有必要强调赋值运算符永远是将右边的值赋给左边,永远不会反过来。a =将变量b (rvalue)的值赋给变量a (lvalue),不论a当时存储的是什么值。同时考虑到我们只是将b的数值赋给a,以后如果b的值改变了并不会影响到a的值.例如:如果我们使用以下代码(变量值的变化显示在绿色注释部分):// 赋值符号例子 a:4 b:7
#include &iostream&
int main (){int a,
b:?19 / 170
// a:10, b:?b = 4;
// a:10, b:4a =
cout && &a:&;cout &&cout && & b:&;cout &&
return 0;}以上代码结果是a的值为4, b的值为7。最后一行中b的值被改变并不会影响到a,虽然在此之前我们声明了a = (从右到左规则right-to-left rule)。C++拥有而其他语言没有的一个特性是赋值符 (=) 可以被用作另一个赋值符的rvalue (或rvalue的一部分) 。例如:a = 2 + (b = 5);等同于:b = 5;a = 2 +它的意思是:先将5赋给变量b,然后把前面对b的赋值运算的结果(即5)加上2再赋给变量a,这样最后a中的值为7。因此,下面的表达式在C++中也是正确的: a = b = c = 5; //将5同时赋给3个变量a, b和c。
数学运算符Arithmetic operators ( +, -, *, /, % )C++语言支持的5种数学运算符为:+ 加addition? - 减subtraction? * 乘multiplication? / 除division
? % 取模module加减乘除运算想必大家都很了解,它们和一般的数学运算符没有区别。唯一你可能不太熟悉的是用百分号(%)表示的取模运算(module)。取模运算是取两个整数相除的余数。例如,如果我们写a = 11 % 3;,变量a的值将会为结果2,因为2是11除以3的余数。
组合运算符Compound assignation operators (+=, -=, *=, /=, %=, &&=, &&=, &=, ^=, |=)C++以书写简练著称的一大特色就是这些组合运算符compound assignation operators (+=, -=, *= 和 /= 及其他) ,这些运算符使得只用一个基本运算符就可改写变量的值: value += 等同于 value = value +a -= 5; 等同于 a = a - 5;a /= 等同于 a = a /price *= units + 1; 等同于 price = price * (units + 1);其他运算符以此类推。例如:// 组合运算符例子 5
#include &iostream&
int main (){int a, b=3;a =a+=2;
// 相当于 a=a+2cout &&return 0;}
递增和递减Increase and decrease书写简练的另一个例子是递增(increase)运算符 (++)和递减(decrease)运算符(--)。它们使得变量中存储的值加1或减1。它们分别等同于+=1和-=1。因此:a++;a+=1;a=a+1;在功能上全部等同,即全部使得变量a的值加1。它的存在是因为最早的C编译器将以上三种表达式的编译成不同的机器代码,不同的机器代码运行速度不一样。现在,编译器已经基本自动实行代码优化,所以以上三种不同的表达方式编译成的机器代码在实际运行上已基本相同。这个运算符的一个特点是它既可以被用作prefix前缀,也可以被用作后缀suffix,也就是说它既可以被写在变量标识的前面(++a),也可以被写在后面(a++)。虽然在简单表达式如a++或++a中,这两种写法代表同样的意思,但当递增increase或递减decrease的运算结果被直接用在其它的运算式中时,它们就代表非常不同的意思了:当递增运算符被用作前缀prefix (++a) 时,变量a的值线增加,然后再计算整个表达式的值,因此增加后的值被用在了表达式的计算中;当它被用作后缀suffix (a++)时,变量a的值在表达式计算后才增加,因此a在增加前所存储的值被用在了表达式的计算中。注意以下两个例子的不同:
在第一个例子中,B在它的值被赋给A之前增加1。而在第二个例子中B原来的值3被赋给A然后B的值才加1变为4。
关系运算符Relational operators ( ==, !=, &, &, &=, &= )我们用关系运算符来比较两个表达式。如ANSI-C++ 标准中指出的,关系预算的结果是一个bool值,根据运算结果的不同,它的值只能是真true或false。例如我们想通过比较两个表达式来看它们是否相等或一个值是否比另一个的值大。以下为C++的关系运算符:22 / 170
下面你可以看到一些实际的例子:当然,除了使用数字常量,我们也可以使用任何有效表达式,包括变量。假设有a=2, b=3和c=6,
注意:运算符= (单个等号)不同于运算符== (双等号)。第一个是赋值运算符(将等号右边的表达式值赋给左边的变量);第二个(==)是一个判断等于的关系运算符,用来判断运算符两边的表达式是否相等。因此在上面例子中最后一个表达式((b=2) == a),我们首先将数值2赋给变量b,然后把它和变量a进行比较。因为变量a中存储的也是数值2,所以整个运算的结果为true。23 / 170
在ANSI-C++标准出现之前的许多编译器中,就像C语言中,关系运算并不返回值为真true或假false的bool值,而是返回一个整型数值最为结果,它的数值可以为0,代表&false&或一个非0数值(通常为1)来代表&true&。
逻辑运算符Logic operators ( !, &&, || )运算符 ! 等同于boolean运算NOT(取非),它只有一个操作数(operand),写在它的右边。它做的唯一工作就是取该操作数的反面值,也就是说如果操作数值为真true,那么运算后值变为假false,如果操作数值为假false,则运算结果为真true。它就好像是说取与操作数相反的值。例如:
逻辑运算符&&和||是用来计算两个表达式而获得一个结果值。它们分别对应逻辑运算中的与运算AND和或运算OR。它们的运算结果取决于两个操作数(operand)的关系:
例如:( (5 == 5) && (3 & 6) ) 返回false ( true && false ).( (5 == 5) || (3 & 6)) 返回true ( true || false ).条件运算符Conditional operator ( ? )条件运算符计算一个表达式的值并根据表达式的计算结果为真true或假false而返回不同值。它的格式是:24 / 170
condition ? result1 : result2 (条件?返回值1:返回值2)如果条件condition 为真true,整个表达式将返回esult1,否则将返回result2。
// 条件运算符例子 7
#include &iostream&
int main (){int a,b,c;
a=2;b=7;c = (a&b) ? a :
return 0;}上面的例子中a的值为2,b的值为7,所以表达式(a&b)运算值为假(false),所以整个表达式(a&b)?a:b要取分号后面的值,也就是b的值7。因此最后输出 c 的值为7。
逗号运算符 ( , )逗号运算符 (,) 用来分开多个表达式,并只取最右边的表达式的值返回。
例如有以下代码:25 / 170
a = (b=3, b+2);
这行代码首先将3赋值给变量b,然后将b+2赋值给变量a。所以最后变量a的值为5,而变量b的值为3。位运算符Bitwise Operators ( &, |, ^, ~, &&, && )位运算符以比特位改写变量存储的数值,也就是改写变量值的二进制表示:
变量类型转换运算符Explicit type casting operators变量类型转换运算符可以将一种类型的数据转换为另一种类型的数据。在写C++中有几种方法可以实现这种操作,最常用的一种,也是与C兼容的一种,是在原转换的表达式前面加用括号()括起的新数据类型:float f = 3.14;i = (int)以上代码将浮点型数字3.14转换成一个整数值(3)。这里类型转换操作符为(int)。在C++中实现这一操作的另一种方法是使用构造函数constructor 的形式:在要转换的表达式前加变量类型并将表达式括在括号中:i = int ( f );以上两种类型转换的方法在C++中都是合法的。另外ANSI-C++针对面向对象编程(object oriented programming)增加了新的类型转换操作符 (参考Section 5.4, Advanced class type-casting).26 / 170
sizeof()这个运算符接受一个输入参数,该参数可以是一个变量类型或一个变量自己,返回该变量类型(variable type) 或对象(object)所占的字节数:a = sizeof (char);这将会返回1给a,因为char是一个常为1个字节的变量类型。sizeof返回的值是一个常数,因此它总是在程序执行前就被固定了。
其它运算符在本教程后面的章节里我们将看到更多的运算符,比如指向指针的运算或面向对象编程特有的运算,等等,我们会在它们各自的章节里进行详细讨论。
运算符的优先度 Precedence of operators当多个操作数组成复杂的表达式时,我们可能会疑惑哪个运算先被计算,哪个后被计算。例如以下表达式:a = 5 + 7 % 2我们可以怀疑它实际上表示:a = 5 + (7 % 2) 结果为6,还是 a = (5 + 7) % 2 结果为0?正确答案为第一个,结果为6。每一个运算符有一个固定的优先级,不仅对数学运算符(我们可能在学习数学的时候已经很了解它们的优先顺序了),所有在C++中出现的运算符都有优先级。从最从最高级到最低级,运算的优先级按下表排列:
结合方向Grouping定义了当有同优先级的多个运算符在一起时,哪一个必须被首先运算,最右边的还是最左边的。所有这些运算符的优先级顺序可以通过使用括号parenthesis signs (和)来控制,而且更易读懂,例如以下例子: a = 5 + 7 % 2;根据我们想要实现的计算的不同,可以写成: a = 5 + (7 % 2); 或者 a = (5 + 7) % 2;所以如果你想写一个复杂的表达式而不敢肯定各个运算的执行顺序,那么就加上括号。这样还可以使代码更易读懂。28 / 170
1.5 控制台交互(Communication through console)控制台(console)是电脑的最基本交互接口,通常包括键盘(keyboard)和屏幕(screen)。键盘通常为标准输入设备,而屏幕为标准输出设备。在C++的iostream函数库中,一个程序的标准输入输出操作依靠两种数据流:cin给输入使用和cout给输出使用。另外,cerr和clog也已经被实现DD它们是两种特殊设计的数据流专门用来显示出错信息。它们可以被重新定向到标准输出设备或到一个日志文件(log file)。因此cout (标准输出流)通常被定向到屏幕,而cin (标准输入流)通常被定向到键盘。 通过控制这两种数据流,你可以在程序中与用户交互,因为你可以在屏幕上显示输出并从键盘接收用户的输入。
输出Output (cout)输出流cout与重载(overloaded)运算符&&一起使用:cout && &Output sentence&; // 打印Output sentence到屏幕上cout && 120; // 打印数字 120 到屏幕上cout && // 打印变量 x 的值到屏幕上运算符&&又叫插入运算符(insertion operator) 因为它将后面所跟的数据插入到它前面的数据流中。在以上的例子中,字符串常量Output sentence,数字常量120和变量x先后被插入输出流cout中。注意第一句中字符串常量是被双引号引起来的。每当我们使用字符串常量的时候,必须用引号把字符串引起来,以便将它和变量名明显的区分开来。例如,下面两个语句是不同的:cout && &Hello&; // 打印字符串Hello到屏幕上cout && H // 把变量Hello存储的内容打印到屏幕上插入运算符insertion operator (&&)可以在同一语句中被多次使用:cout && &Hello, & && &I am & && &a C++ sentence&;29 / 170
上面这一行语句将会打印Hello, I am a C++ sentence到屏幕上。插入运算符(&&) 的重复使用在我们想要打印变量和内容的组合内容或多个变量时有所体现:cout && &Hello, I am & && age && & years old and my zipcode is & &&
如果我们假设变量age的值为24,变量zipcode的值为90064,以上句子的输出将为:Hello, I am 24 years old and my zipcode is 90064必须注意,除非我们明确指定,cout并不会自动在其输出内容的末尾加换行符,因此下面的语句:cout && &This is a sentence.&;cout && &This is another sentence.&;将会有如下内容输出到屏幕:This is a sentence.This is another sentence.虽然我们分别调用了两次cout,两个句子还是被输出在同一行。所以,为了在输出中换行,我们必须插入一个换行符来明确表达这一要求。在C++中换行符可以写作\n: cout && &First sentence.\n &;cout && &Second sentence.\nThird sentence.&;将会产生如下输出:First sentence.Second sentence.Third sentence.另外,你也可以用操作符endl来换行,例如:cout && &First sentence.& &&cout && &Second sentence.& &&将会输出:First sentence.Second sentence.当操作符endl被用在buffered streams中时有一点特殊:它们被flushed。不过cout 默认为unbuffered,所以不会被影响。你可以暂时不管这一点。你可以使用\n或endl来指定cout输出换行,请注意前面所讲的两者的不同用法。 30 / 170
输入Input (cin)C++中的标准输入是通过在cin数据流上重载运算符extraction (&&) 来实现的。它后面必须跟一个变量以便存储读入的数据。例如:cin &&声明一个整型变量age然后等待用户从键盘输入到cin并将输入值存储在这个变量中。 cin只能在键盘输入回车键(RETURN)后才能处理前面输入的内容。因此即使你只要求输入一个单独的字符,在用户按下回车键(RETURN)之前cin将不会处理用户的输入的字符。 在使用cin输入的时候必须考虑后面的变量类型。如果你要求输入一个整数,extraction (&&) 后面必须跟一个整型变量,如果要求一个字符,后面必须跟一个字符型变量,如果要求一个字符串,后面必须跟一个字符串型变量。// i/o example#include &iostream.h&int main (){cout && &Please enter an integer value: &;cin &&cout && &The value you entered is & &&cout && & and its double is & && i*2 && &.\n&;return 0;}
Please enter an integer value: 702 The value you entered is 702 and its double is 1404.使用程序的用户可以使引起错误的原因之一,即使是在最简单的需要用cin做输入的程序中(就像我们上面看到的这个程序)。因为如果你要求输入一个整数数值,而用户输入了一个名字(一个字符串),其结果可能导致程序产生错误操作,因为它不是我们期望从用户处获得的数据。当你使用由cin 输入的数据的时候,你不得不假设程序的用户将会完全合作而不会在程序要求输入整数的时候输入他的名字。后面当我们看到怎样使用字符串的时候,我们将会同时看到一些解决这一类出错问题的办法。你也可以利用cin 要求用户输入多个数据:cin && a &&31 / 170
等同于:cin &&cin &&在以上两种情况下用户都必须输入两个数据,一个给变量a,一个给变量b。输入时两个变量之间可以以任何有效的空白符号间隔,包括空格,跳跃符tab或换行。cin和字符串我们可以像读取基本类型数据一样,使用cin和&&操作符来读取字符串,例如: cin &&但是,cin &&只能读取一个单词,一旦碰到任何空格,读取操作就会停止。在很多时候这并不是我们想要的操作,比如我们希望用户输入一个英文句子,那么这种方法就无法读取完整的句子,因为一定会遇到空格。要一次读取一整行输入,需要使用C++的函数 getline,相对于是用cin,我们更建议使用getline来读取用户输入。例如:// 读取字符串例子#include &iostream&#include &string&
int main (){cout && &What's your name? &;getline (cin, mystr);cout && &Hello & && mystr && &.\n&;cout && &What is your favorite color? &;getline (cin, mystr);cout && &I like & && mystr && & too!\n&;return 0;}32 / 170What's your name? Aqua Hello Aqua. What is your favorite color? blue I like blue too!你可能注意到在上面的例子中,两次调用getline函数我们都是用了同一个字符串变量(mystr)。在第二次调用的时候,程序会自动用第二次输入的内容取代以前的内容。字符串流 (stringstream)标准头文件&sstream&定义了一个叫做 stringstream的类,使用这个类可以对基于字符串的对象进行像流(stream)一样的操作。这样,我们可以对字符串进行抽取和插入操作,这对将字符串与数值互相转换非常有用。例如,如果我们想将一个字符串转换为一个整数,可以这样写:string mystr (&1204&);stringstream(mystr) &&这个例子中先定义了一个字符串类型的对象mystr,初始值为&1204&,又定义了一个整数变量myint。然后我们使用stringstream类的构造函数定义了这个类的对象,并以字符串变量mystr为参数。因为我们可以像使用流一样使用stringstream的对象,所以我们可以像使用cin那样使用操作符&&后面跟一个整数变量来进行提取整数数据。这段代码执行之后变量myint存储的是数值1204。// 字符串流的使用示例#include &iostream&#include &string&#include &sstream&
int main (){float price=0;int quantity=0;
cout && &Enter price: &;getline (cin,mystr);stringstream(mystr) &&cout && &Enter quantity: &;getline (cin,mystr);stringstream(mystr) &&33 / 170Enter price: 22.25 Enter quantity: 7 Total price: 155.75cout && &Total price: & && price*quantity &&return 0;}在这个例子中,我们要求用户输入数值,但不同于从标准输入中直接读取数值,我们使用函数getline从标注输入流cin中读取字符串对象(mystr),然后再从这个字符串对象中提取数值price和quantity。通过使用这种方法,我们可以对用户的输入有更多的控制,因为它将用户输入与对输入的解释分离,只要求用户输入整行的内容,然后再对用户输入的内容进行检验操作。这种做法在用户输入比较集中的程序中是非常推荐使用的。
2.1 控制结构(Control Structures)一个程序的语句往往并不仅限于线性顺序结构。在程序的执行过程中它可能被分成两支执行,可能重复某些语句,也可能根据一些判断结果而执行不同的语句。因此C++ 提供一些控制结构语句 (control structures) 来实现这些执行顺序。为了介绍程序的执行顺序,我们需要先介绍一个新概念:语句块(block ofinstructions)。一个语句块(A block of instructions) 是一组互相之间由分号semicolons (;) 分隔开但整体被花括号curly bracket signs: { and }括起来的语句。 本节中我们将看到的大多数控制结构允许一个通用的statement做参数,这个statement根据需要可以是一条语句,也可以是一组语句组成的语句块。如果我们只需要一条语句做statement,它可以不被括在花括号 ({}) 内。但如果我们需要多条语句共同做statement,则必须把它们括在花括号内 ({}) 以组成一个语句块。
条件结构Conditional structure: if and else条件结构用来实现仅在某种条件满足的情况下才执行一条语句或一个语句块。它的形式是:if (condition) statement这里 condition 是一个将被计算的表达式(expression)。如果表达式值为真,即条件(condition)为true,statement 将被执行。否则,statement 将被忽略(不被执行),程序从整个条件结构之后的下一条语句继续执行。例如,以下程序段实现只有当变量x存储的值确实为100的时候才输出&x is 100&: if (x == 100)cout && &x is 100&;如果我们需要在条件condition为真true的时候执行一条以上的语句,我们可以花括号{}将语句括起来组成一个语句块:35 / 170
if (x == 100){cout && &x is &;cout &&}我们可以用关键字else 来指定当条件不能被满足时需要执行的语句,它需要和if 一起使用,形式是:if (condition) statement1 else statement2例如:if (x == 100)cout && &x is 100&;elsecout && &x is not 100&;以上程序如果x的值为100,则在屏幕上打出x is 100,如果x不是100,而且也只有在x不是100的时候,屏幕上将打出x is not 100。多个if + else 的结构被连接起来使用来判断数值的范围。以下例子显示了如何用它来判断变量 x中当前存储的数值是正值,负值还是既不正也不负,即等于0 。 if (x & 0)cout && &x is positive&;else if (x & 0)cout && &x is negative&;elsecout && &x is 0&;记住当我们需要执行多条语句时,必须使用花括号{}将它们括起来以组成一个语句块block of instructions。
重复结构 Iteration structures 或循环loops循环Loops 的目的是重复执行一组语句一定的次数或直到满足某种条件。36 / 170
while 循环格式是:while (表达式expression) 语句statement它的功能是当expression 的值为真true时重复执行statement。例如,下面我们将用while循环来写一个倒计数程序:// custom countdown using while#include &iostream.h&int main (){cout && &Enter the starting number & &;cin &&while (n&0) {cout && n && &, &;--n;}cout && &FIRE!&;return 0;}
Enter the starting number & 8 8, 7, 6, 5, 4, 3, 2, 1, FIRE!程序开始时提示用户输入一个倒计数的初始值。然后while 循环开始,如果用户输入的数值满足条件n&0 (即 n 比0 大),后面跟的语句块将会被执行一定的次数,直到条件 (n&0) 不再满足(变为false)。以上程序的所有处理过程可以用以下的描述来解释:从main开始:1. 用户输入一个数值赋给n.2. while语句检查(n&0)是否成立,这时有两种可能:o true: 执行statement (到第3步)o false: 跳过statement. 程序直接执行第5步.3. 执行statement:cout && n && &, &;--n;37 / 170
(将n 的值打印在屏幕上,然后将n 的值减1).4. 语句块结束,自动返回第2步。5. 继续执行语句块之后的程序:打印 FIRE! ,程序结束。我们必须考虑到循环必须在某个点结束,因此在语句块之内(loop的statement之内) 我们必须提供一些方法使得条件condition 可以在某个时刻变为假 false,否则循环将无限重复下去。在这个例子里,我们用语句--n;使得循环在重复一定的次数后变为false :当 n 变为0,倒计数结束。do-while 循环格式:do 语句statement while (条件condition);它的功能与while 循环一抹一样,除了在do-while循环中是先执行statement 然后才检查条件condition ,而不想while循环中先检查条件然后才执行statement。这样,即使条件condition从来没有被满足过,statement 仍至少被执行一次。例如,下面的程序重复输出(echoes)用户输入的任何数值,直到用户输入0为止。// number echoer#include &iostream.h&int main (){do {cout && &Enter number (0 to end): &;cin &&cout && &You entered: & && n && &\n&;} while (n != 0);return 0;}
Enter number (0 to end): 12345 You entered: 12345 Enter number (0 to end): 160277 You entered: 160277 Enter number (0 to end): 0 You entered: 0do-while循环通常被用在判断循环结束的条件是在循环语句内部被决定的情况下,比如以上的例子,在循环的语句块内用户的输入决定了循环是否结束。如果用户永远不输入0,则循环永远不会结束。38 / 170
for 循环格式是:for ( increase)它的主要功能是当条件condition 为真true时重复执行语句statement ,类似while 循环。但除此之外,for 还提供了写初始化语句initialization 和增值语句increase 的地方。因此这种循环结构是特别为执行由计数器控制的循环而设计的。它按以下方式工作:1. 执行初始化initialization 。通常它是设置一个计数器变量(counter variable)的初始值,初始化仅被执行一次。2. 检查条件condition ,如果条件为真true,继续循环,否则循环结束循环中语句statement 被跳过。3. 执行语句statement 。像以前一样,它可以是一个单独的语句,也可以是一个由花括号{ }括起来的语句块。4. 最后增值域(increase field)中的语句被执行,循环返回第2步。注意增值域中可能是任何语句,而不一定只是将计数器增加的语句。例如下面的例子中计数器实际为减1,而不是加1。 下面是用for循环实现的倒计数的例子:// countdown using a for loop#include &iostream.h&int main (){for (int n=10; n&0; n--) {cout && n && &, &;}cout && &FIRE!&;return 0;}
10, 9, 8, 7, 6, 5, 4, 3, 2, 1, FIRE!初始化initialization 和增值increase 域是可选的(即可以为空)。但这些域为空的时候,它们和其他域之间间隔的分号不可以省略。例如我们可以写:for (;n&10;)来表示没有初始化和增值语句;或for (;n&10;n++) 来表示有增值语句但没有初始化语句。 另外我们也可以在for循环初始化或增值域中放一条以上的语句,中间用逗号 coma(,)隔开。例如假设我们想在循环中初始化一个以上的变量,可以用以下的程序来实现: 39 / 170
for ( n=0, i=100 ; n!= n++, i-- ){// whatever here...}这个循环将被执行50 次,如果n 和i 在循还内部都不被改变的话:
n初始值为0,i初始值为100,条件是(n!=i)(即n不能等于i)。因为每次循环n加1,而且i减1,循环的条件将会在第50次循环之后变为假false(n和i都等于50)。 分支控制和跳转(Bifurcation of control and jumps)break 语句通过使用break语句,即使在结束条件没有满足的情况下,我们也可以跳出一个循环。它可以被用来结束一个无限循环(infinite loop),或强迫循环在其自然结束之前结束。例如,我们想要在倒计数自然结束之前强迫它停止(也许因为一个引擎故障): // break loop example#include &iostream.h&int main (){for (n=10; n&0; n--) {cout && n && &, &;if (n==3){cout && &countdown aborted!&;}return 0;}
10, 9, 8, 7, 6, 5, 4, 3, countdown aborted!40 / 170
continue 语句
continue语句使得程序跳过当前循环中剩下的部分而直接进入下一次循环,就好像循环中语句块的结尾已经到了使得循环进入下一次重复。例如,下面例子中倒计数时我们将跳过数字5的输出:// continue loop example#include &iostream.h&int main (){for (int n=10; n&0; n--) {if (n==5)cout && n && &, &;}cout && &FIRE!&;return 0;}
10, 9, 8, 7, 6, 4, 3, 2, 1, FIRE!goto 语句通过使用goto语句可以使程序从一点跳转到另外一点。你必须谨慎只用这条语句,因为它的执行可以忽略任何嵌套限制。跳转的目标点可以由一个标示符(label)来标明,该标示符作为goto语句的参数。一个标示符(label)由一个标识名称后面跟一个冒号colon (:)组成。通常除了底层程序爱好者使用这条语句,它在结构化或面向对象的编程中并不常用。下面的例子中我们用goto来实现倒计数循环:// goto loop example#include &iostream.h&int main (){int n=10;41 / 17010, 9, 8, 7, 6, 5, 4, 3, 2, 1, FIRE!loop:cout && n && &, &;n--;if (n&0)cout && &FIRE!&;return 0;}exit 函数exit是一个在cstdlib (stdlib.h)库中定义的函数。exit的目的是一个特定的退出代码来结束程序的运行,它的原型(prototype)是: void exit (int exit code);exit code是由操作系统使用或被调用程序使用。通常exit code为0表示程序正常结束,任何其他值表示程序执行过程中出现了错误。
选择结构The selective Structure: switchswitch语句的语法比较特殊。它的目标是对一个表达式检查多个可能常量值,有些像我们在本节开头学习的把几个if 和else if 语句连接起来的结构。它的形式是: switch (expression) {case constant1:block of instructions 1case constant2:block of instructions 2...default:default block of instructions}它按以下方式执行:42 / 170
switch 计算表达式expression 的值,并检查它是否与第一个常量constant1相等,如果相等,程序执行常量1后面的语句块block of instructions 1 直到碰到关键字break ,程序跳转到switch 选择结构的结尾处。如果expression 不等于constant1,程序检查表达式expression 的值是否等于第二个常量constant2,如果相等,程序将执行常量2后面的语句块block of instructions 2 直到碰到关键字break。依此类推,直到最后如果表达式expression 的值不等于任何前面的常量(你可以用case语句指明任意数量的常量值来要求检查),程序将执行默认区default: 后面的语句,如果它存在的话。default: 选项是可以省略的。下面的两段代码段功能相同:
前面已经提到switch的语法有点特殊。注意每个语句块结尾包含的break语句。这是必须的,因为如果不这样做,例如在语句块block of instructions 1 的结尾没有break,程序执行将不会跳转到switch选择的结尾处 (}) ,而是继续执行下面的语句块,直到第一次遇到break语句或到switch选择结构的结尾。因此,不需要在每一个case 域内加花括号{ }。这个特点同时可以帮助实现对不同的可能值执行相同的语句块。例如: switch (x) {case 1:case 2:case 3:cout && &x is 1, 2 or 3&;default:cout && &x is not 1, 2 nor 3&;43 / 170
}注意switch只能被用来比较表达式和不同常量的值constants。因此我们不能够把变量或范围放在case之后,例如 (case (n*2):) 或 (case (1..3):) 都不可以,因为它们不是有效的常量。如果你需要检查范围或非常量数值,使用连续的if 和else if 语句。
2.2 函数I(Functions I)通过使用函数(functions)我们可以把我们的程序以更模块化的形式组织起来,从而利用C++所能提供的所有结构化编程的潜力。一个函数(function)是一个可以从程序其它地方调用执行的语句块。以下是它的格式:
这里:type 是函数返回的数据的类型? name 是函数被调用时使用的名? argument 是函数调用需要传入的参量(可以声明任意多个参量)。每个参量(argument)由一个数据类型后面跟一个标识名称组成,就像变量声明中一样(例如,int x)。参量仅在函数范围内有效,可以和函数中的其它变量一样使用,它们使得函数在被调用时可以传入参数,不同的参数用逗号(comma)隔开.? statement 是函数的内容。它可以是一句指令,也可以是一组指令组成的语句块。如果是一组指令,则语句块必须用花括号{}括起来,这也是我们最常见到情况。? 44 / 170
其实为了使程序的格式更加统一清晰,建议在仅有一条指令的时候也使用花括号,这是一个良好的编程习惯。下面看一下第一个函数的例子:// function example The result is 8#include &iostream.h&int addition (int a, int b){r=a+b;return (r);}
int main (){z = addition (5,3);cout && &The result is & &&return 0;}记得在我们教程开始时说过:一个C++程序总是从main函数开始执行。因此我们从那里开始。我们可以看到 main 函数以定义一个整型变量z 开始。紧跟着我们看到调用addition 函数。我们可以看到函数调用的写法和上面函数定义本身十分相似:
参数有明显的对应关系。在main 函数中我们调用addition 函数,并传入两个数值: 5 和3 , 它们对应函数addition 中定义的参数int a 和int b。当函数在main 中被调用时,程序执行的控制权从main转移到函数addition。调用传递的两个参数的数值 (5 和3) 被复制到函数的本地变量(local variables) int a 和int b 中。函数addition 中定义了新的变量(),通过表达式r=a+b;, 它把a 加b 的结果赋给r 。因为传过来的参数a 和b 的值分别为5 和3 ,所以结果是8。45 / 170
下面一行代码:return (r);结束函数addition,并把控制权交还给调用它的函数(main) ,从调用addition的地方开始继续向下执行。另外,return 在调用的时候后面跟着变量r (return (r);), 它当时的值为8, 这个值被称为函数的返回值。
函数返回的数值就是函数的计算结果,因此, z 将存储函数addition (5, 3)返回的数值, 即8。用另一种方式解释,你也可以想象成调用函数(addition (5,3)) 被替换成了它的返回值 (8)。接下来main中的下一行代码是:cout && &The result is & &&它把结果打印在屏幕上。
这里是另一个关于函数的例子:// function example#include &iostream.h&int subtraction (int a, int b){r=a-b;return (r);}
int main (){int x=5, y=3,z = subtraction (7,2);cout && &The first result is & && z && '\n';cout && &The second result is & && subtraction(7,2) && '\n';cout && &The third result is & && subtraction (x,y)&& '\n';z= 4 + subtraction (x,y);cout && &The fourth result is & && z && '\n';return 0;} The first result is 5 The second result is 5 The third result is 2 The fourth result is 6在这个例子中,我们定义了函数subtraction。这个函数的功能是计算传入的两个参数的差值并将结果返回。在 main 函数中,函数subtraction被调用了多次。我们用了几种不同的调用方法,因此你可以看到在不同的情况下函数如何被调用。为了更好的理解这些例子,你需要考虑到被调用的函数其实完全可以由它所返回的值来代替。例如在上面例子中第一种情况下 (这种调用你应该已经知道了,因为我们在前面的例子中已经用过这种形式的调用):z = subtraction (7,2);cout && &The first result is & &&47 / 170
如果我们把函数调用用它的结果(也就是5)替换,我们将得到:z = 5;cout && &The first result is & &&同样的cout && &The second result is & && subtraction (7,2);与前面的调用有同样的结果,但在这里我们把对函数subtraction 的调用直接用作cout的参数。这可以简单想象成我们写的是:cout && &The second result is & && 5;因为5 是subtraction (7,2)的结果。在cout && &The third result is & && subtraction (x,y);中,与前面的调用唯一的不同之处是这里调用subtraction 时的参数使用的是变量而不是常量。这样用时毫无问题的。在这个例子里,传入函数subtraction 的参数值是变量x 和y中存储的数值,即分别为5 和3,结果为2。第四种调用也是一样的。只要知道除了z = 4 + subtraction (x,y);我们也可以写成:z = subtraction (x,y) + 4;它们的结果是完全一样的。注意在整个表达式的结尾写上分号semicolon sign (;)。它并不需要总是跟在函数调用的后面,因为你可以有一次把它们想象成函数被它的结果所替代:z = 4 + 2;z = 2 + 4;没有返回值类型的函数,使用void.如果你记得函数声明的格式:48 / 170
type name ( argument1, argument2 ...) statement就会知道函数声明必须以一个数据类型(type)开头,它是函数由return 语句所返回数据类型。但是如果我们并不打算返回任何数据那该怎么办呢?假设我们要写一个函数,它的功能是打印在屏幕上打印一些信息。我们不需要它返回任何值,而且我们也不需要它接受任何参数。C语言为这些情况设计了void 类型。让我们看一下下面的例子:// void 函数示例 I'm a function!#include &iostream&
void printmessage (){cout && &I'm a function!&;}
int main (){printmessage ();return 0;}void还可以被用在函数参数位置,表示我们明确希望这个函数在被调用时不需要任何参数。例如上面的函数printmessage也可以写为以下形式:void printmessage (void){cout && &I'm a function!&;}虽然在C++ 中void可以被省略,我们还是建议写出void,以便明确指出函数不需要参数。你必须时刻知道的是调用一个函数时要写出它的名字并把参数写在后面的括号内。但如果函数不需要参数,后面的括号并不能省略。因此调用函数 printmessage 的格式是 printmessage();函数名称后面的括号就明确表示了它是一个函数调用,而不是一个变量名称或其它什么语句。以下调用函数的方式就不对:49 / 170
2.3 函数II(Functions II)参数按数值传递和按地址传递(Arguments passed by value and by reference)到目前为止,我们看到的所有函数中,传递到函数中的参数全部是按数值传递的(by value)。也就是说,当我们调用一个带有参数的函数时,我们传递到函数中的是变量的数值而不是变量本身。例如,假设我们用下面的代码调用我们的第一个函数addition : int x=5, y=3,z = addition ( x , y );在这个例子里我们调用函数addition 同时将x和y的值传给它,即分别为5和3,而不是两个变量:
这样,当函数addition被调用时,它的变量a和b的值分别变为5和3,但在函数addition内对变量a 或b 所做的任何修改不会影响变量他外面的变量x 和 y 的值,因为变量x和y并没有把它们自己传递给函数,而只是传递了他们的数值。50 / 170
但在某些情况下你可能需要在一个函数内控制一个函数以外的变量。要实现这种操作,我们必须使用按地址传递的参数(arguments passed by reference),就象下面例子中的函数duplicate:// passing parameters by reference x=2, y=6, z=14#include &iostream.h&
void duplicate (int& a, int& b, int& c){a*=2;b*=2;c*=2;}
int main (){int x=1, y=3, z=7;duplicate (x, y, z);cout && &x=& && x && &, y=& && y && &, z=& &&return 0;}第一个应该注意的事项是在函数duplicate的声明(declaration)中,每一个变量的类型后面跟了一个地址符ampersand sign (&),它的作用是指明变量是按地址传递的(by reference),而不是像通常一样按数值传递的(by value)。当按地址传递(pass by reference)一个变量的时候,我们是在传递这个变量本身,我们在函数中对变量所做的任何修改将会影响到函数外面被传递的变量。
用另一种方式来说,我们已经把变量a, b,c和调用函数时使用的参数(x, y和z)联系起来了,因此如果我们在函数内对a 进行操作,函数外面的x 值也会改变。同样,任何对b 的改变也会影响y,对c 的改变也会影响z&。这就是为什么上面的程序中,主程序main中的三个变量x, y和z在调用函数duplicate 后打印结果显示他们的值增加了一倍。51 / 170
如果在声明下面的函数:void duplicate (int& a, int& b, int& c)时,我们是按这样声明的:void duplicate (int a, int b, int c)也就是不写地址符 ampersand (&),我们也就没有将参数的地址传递给函数,而是传递了它们的值,因此,屏幕上显示的输出结果x, y ,z的值将不会改变,仍是1,3,7。这种用地址符 ampersand (&)来声明按地址&by reference&传递参数的方式只是在C++中适用。在C 语言中,我们必须用指针(pointers)来做相同的操作。 按地址传递(Passing by reference)是一个使函数返回多个值的有效方法。例如,下面是一个函数,它可以返回第一个输入参数的前一个和后一个数值。// more than one returning value Previous=99, Next=101
#include &iostream.h&void prevnext (int x, int& prev, int& next){prev = x-1;next = x+1;}
int main (){int x=100, y,prevnext (x, y, z);cout && &Previous=& && y && &, Next=& &&return 0;}
参数的默认值(Default values in arguments)当声明一个函数的时候我们可以给每一个参数指定一个默认值。如果当函数被调用时没有给出该参数的值,那么这个默认值将被使用。指定参数默认值只需要在函数声明时把一个数值赋给参数。如果函数被调用时没有数值传递给该参数,那么默认值将被使用。但如果有指定的数值传递给参数,那么默认值将被指定的数值取代。例如:52 / 170
// default values in functions 6#include &iostream.h& 5int divide (int a, int b=2) {r=a/b;return (r);}
int main () {cout && divide (12);cout &&cout && divide (20,4);return 0;}我们可以看到在程序中有两次调用函数divide。第一次调用:divide (12)只有一个参数被指明,但函数divide允许有两个参数。因此函数divide假设第二个参数的值为2,因为我们已经定义了它为该参数缺省的默认值(注意函数声明中的int b=2)。因此这次函数调用的结果是6 (12/2)。在第二次调用中:divide (20,4)这里有两个参数,所以默认值 (int b=2) 被传入的参数值4所取代,使得最后结果为 5 (20/4).
函数重载(Overloaded functions)两个不同的函数可以用同样的名字,只要它们的参量(arguments)的原型(prototype)不同,也就是说你可以把同一个名字给多个函数,如果它们用不同数量的参数,或不同类型的参数。例如:// overloaded function#include &iostream.h&
53 / 1702 2.5int divide (int a, int b) {return (a/b);}
float divide (float a, float b) {return (a/b);}
int main () {int x=5,y=2;float n=5.0,m=2.0;cout && divide (x,y);cout && &\n&;cout && divide (n,m);cout && &\n&;return 0;}在这个例子里,我们用同一个名字定义了两个不同函数,当它们其中一个接受两个整型(int)参数,另一个则接受两个浮点型(float)参数。编译器 (compiler)通过检查传入的参数的类型来确定是哪一个函数被调用。如果调用传入的是两个整数参数,那么是原型定义中有两个整型(int)参量的函数被调用,如果传入的是两个浮点数,那么是原型定义中有两个浮点型(float)参量的函数被调用。为了简单起见,这里我们用的两个函数的代码相同,但这并不是必须的。你可以让两个函数用同一个名字同时完成完全不同的操作。
Inline 函数(inline functions)inline指令可以被放在函数声明之前,要求该函数必须在被调用的地方以代码形式被编译。这相当于一个宏定义(macro)。它的好处只对短小的函数有效,这种情况下因为避免了调用函数的一些常规操作的时间(overhead),如参数堆栈操作的时间,所以编译结果的运行代码会更快一些。它的声明形式是:inline type name ( arguments ... ) { instructions ... }54 / 170
它的调用和其他的函数调用一样。调用函数的时候并不需要写关键字inline ,只有在函数声明前需要写。
递归(Recursivity)递归(recursivity)指函数将被自己调用的特点。它对排序(sorting)和阶乘(factorial)运算很有用。例如要获得一个数字n的阶乘,它的数学公式是:n! = n * (n-1) * (n-2) * (n-3) ... * 1更具体一些,5! (factorial of 5) 是:5! = 5 * 4 * 3 * 2 * 1 = 120而用一个递归函数来实现这个运算将如以下代码:// factorial calculator Type a number: 9#include &iostream.h& !9 = 362880
long factorial (long a){if (a & 1) return (a * factorial (a-1));else return (1);}
int main () {cout && &Type a number: &;cin &&cout && &!& && l && & = & && factorial (l);return 0;}注意我们在函数factorial中是怎样调用它自己的,但只是在参数值大于1的时候才做调用,因为否则函数会进入死循环(an infinite recursive loop),当参数到达0的时候,函数不继续用负数乘下去(最终可能导致运行时的堆栈溢出错误(stack overflow error)。这个函数有一定的局限性,为简单起见,函数设计中使用的数据类型为长整型(long)。在实际的标准系统中,长整型long无法存储12!以上的阶乘值。55 / 170
函数的声明(Declaring functions)到目前为止,我们定义的所有函数都是在它们第一次被调用(通常是在main中)之前,而把main 函数放在最后。如果重复以上几个例子,但把main 函数放在其它被它调用的函数之前,你就会遇到编译错误。原因是在调用一个函数之前,函数必须已经被定义了,就像我们前面例子中所做的。但实际上还有一种方法来避免在main 或其它函数之前写出所有被他们调用的函数的代码,那就是在使用前先声明函数的原型定义。声明函数就是对函数在的完整定义之前做一个短小重要的声明,以便让编译器知道函数的参数和返回值类型。它的形式是:type name ( argument_type1, argument_type2, ...);它与一个函数的头定义(header definition)一样,除了:它不包括函数的内容,也就是它不包括函数后面花括号{}内的所有语句。? 它以一个分号semicolon sign (;) 结束。? 在参数列举中只需要写出各个参数的数据类型就够了,至于每个参数的名字可以写,也可以不写,但是我们建议写上。 ?例如:// 声明函数原型#include &iostream.h&
void odd (int a);void even (int a);
int main () {do {cout && &Type a number: (0 to exit)&;cin &&odd (i);} while (i!=0);return 0;}
56 / 170Type a number (0 to exit): 9 Number is odd. Type a number (0 to exit): 6 Number is even. Type a number (0 to exit): 1030 Number is even. Type a number (0 to exit): 0 Number is even.void odd (int a) {if ((a%2)!=0) cout && &Number is odd.\n&;else even (a);}
void even (int a) {if ((a%2)==0) cout && &Number is even.\n&;else odd (a);}这个例子的确不是很有效率,我相信现在你已经可以只用一半行数的代码来完成同样的功能。但这个例子显示了函数原型(prototyping functions)是怎样工作的。并且在这个具体的例子中,两个函数中至少有一个是必须定义原型的。这里我们首先看到的是函数odd 和even的原型:void odd (int a);void even (int a);这样使得这两个函数可以在它们被完整定义之前就被使用,例如在main中被调用,这样main就可以被放在逻辑上更合理的位置:即程序代码的开头部分。尽管如此,这个程序需要至少一个函数原型定义的特殊原因是因为在odd 函数里需要调用even 函数,而在even 函数里也同样需要调用odd函数。如果两个函数任何一个都没被提前定义原型的话,就会出现编译错误,因为或者odd 在even 函数中是不可见的(因为它还没有被定义),或者even 函数在odd函数中是不可见的。很多程序员建议给所有的函数定义原型。这也是我的建议,特别是在有很多函数或函数很长的情况下。把所有函数的原型定义放在一个地方,可以使我们在决定怎样调用这些函数的时候轻松一些,同时也有助于生成头文件。
3.1 数组(Arrays)数组(Arrays) 是在内存中连续存储的一组同种数据类型的元素(变量),每一数组有一个唯一名称,通过在名称后面加索引(index)的方式可以引用它的每一个元素。也就是说,例如我们有5个整型数值需要存储,但我们不需要定义5个不同的变量名称,而是用一个数组(array)来存储这5个不同的数值。注意数组中的元素必须是同一数据类型的,在这个例子中为整型(int)。例如一个存储5个整数叫做billy的数组可以用下图来表示:
这里每一个空白框代表数组的一个元素,在这个例子中为一个整数值。白框上面的数字0 到4 代表元素的索引(index)。注意无论数组的长度如何,它的第一个元素的索引总是从0开始的。同其它的变量一样,数组必须先被声明然后才能被使用。一种典型的数组声明显示如下: type name [elements];58 / 170
这里type 是可以使任何一种有效的对象数据类型(object type),如 int, float...等,name 是一个有效地变量标识(identifier),而由中括号[]引起来的elements 域指明数组的大小,即可以存储多少个元素。因此,要定义上面图中显示的 billy 数组,用一下语句就可以了:int billy [5];备注:在定义一个数组的时候,中括号[]中的elements 域必须是一个常量数值,因为数组是内存中一块有固定大小的静态空间,编译器必须在编译所有相关指令之前先能够确定要给该数组分配多少内存空间。
初始化数组(Initializing arrays)当声明一个本地范围内(在一个函数内)的数组时,除非我们特别指定,否则数组将不会被初始化,因此它的内容在我们将数值存储进去之前是不定的。如果我们声明一个全局数组(在所有函数之外),则它的内容将被初始化为所有元素均为0。因此,如果全局范围内我们声明:int billy [5];那么billy 中的每一个元素将会被初始化为
另外,我们还可以在声明一个变量的同时把初始值付给数组中的每一个元素,这个赋值用花括号{ }来完成。例如:int billy [5] = { 16, 2, 77, 40, 12071 };这个声明将生成如下数组:
花括号中我们要初始化的元素数值个数必须和数组声明时方括号[ ]中指定的数组长度相符。例如,在上面例子中数组billy 声明中的长度为5,因此在后面花括号中的初始值也有5个,每个元素一个数值。59 / 170
因为这是一种信息的重复,因此C++允许在这种情况下数组[ ]中为空白,而数组的长度将有后面花括号{}中数值的个数来决定,如下例所示。int billy [] = { 16, 2, 77, 40, 12071 };存取数组中数值(Access to the values of an Array)在程序中我们可以读取和修改数组任一元素的数值,就像操作其他普通变量一样。格式如下:name[index]继续上面的例子,数组billy 有5个元素,其中每一元素都是整型int,我们引用其中每一个元素的名字分别为如下所示:
例如,要把数值75存入数组billy 中第3个元素的语句可以是:billy[2] = 75;又例如,要把数组billy 中第3个元素的值赋给变量a,我们可以这样写: a = billy[2];因此,在所有使用中,表达式billy[2]就像任何其他整型变量一样。注意数组billy 的第3个元素为billy[2],因为索引(index)从0开始,第1个元素是billy[0],第2个元素是billy[1],因此第3个是 billy[2]。同样的原因,最后一个元素是billy[4]。如果我们写billy[5],那么是在使用billy的第6个元素,因此会超出数组的长度。在C++ 中对数组使用超出范围的index是合法的,这就会产生问题,因为它不会产生编译错误而不易被察觉,但是在运行时会产生意想不到的结果,甚至导致严重运行错误。超出范围的index 之所以合法的原因我们在后面学习指针(pointer)的时候会了解。 学到这里,我们必须能够清楚的了解方括号[ ]在对数组操作中的两种不同用法。它们完成两种任务:一种是在声明数组的时候定义数组的长度;另一种是在引用具体的数组元素的时候指明一个索引号(index)。我们要注意不要把这两种用法混淆。60 / 170
int billy[5]; // 声明新数组(以数据类型名称开头)billy[2] = 75; // 存储数组的一个元素其它合法的数组操作:billy[0] = // a为一个整型变量billy[a] = 75;b = billy [a+2];billy[billy[a]] = billy[2] + 5;
// arrays example 12206#include &iostream.h&
int billy [ ] = {16, 2, 77, 40, 12071};int n, result=0;
int main () {for ( n=0 ; n&5 ; n++ ) {result += billy[n];}
cout &&return 0;}
多维数组(Multidimensional Arrays)多维数组(Multidimensional Arrays)可以被描述为数组的数组。例如,一个2维数组(bidimensional array)可以被想象成一个有同一数据类型的2维表格。
jimmy 显示了一个整型(int )的3x5二维数组,声明这一数组的的方式是: 61 / 170
int jimmy [3][5];而引用这一数组中第2列第4排元素的表达式为:jimmy[1][3]
(记住数组的索引总是从0开始)。多维数组(Multidimensional arrays)并不局限于2维。如果需要,它可以有任意多维,虽然需要3维以上的时候并不多。但是考虑一下一个有很多维的数组所需要的内存空间,例如:char century [100][365][24][60][60];给一个世纪中的每一秒赋一个字符(char),那么就是多于30亿的字符!如果我们定义这样一个数组,需要消耗3000M的内存。多维数组只是一个抽象的概念,因为我们只需要把各个索引的乘积放入一个简单的数组中就可以获得同样的结果。例如:int jimmy [3][5];效果上等价于int jimmy [15]; (3 * 5 = 15)唯一的区别是编译器帮我们记住每一个想象中的维度的深度。下面的例子中我们就可以看到,两段代码一个使用2维数组,另一个使用简单数组,都获得同样的结果,即都在内存中开辟了一块叫做jimmy的空间,这个空间有15个连续地址位置,程序结束后都在相同的位置上存储了相同的数值,如后面图中所示:// multidimensional array#include &iostream.h&#define WIDTH 5#define HEIGHT 3
int jimmy [HEIGHT][WIDTH];int n,m;
int main (){62 / 170// pseudo-multidimensional array #include &iostream.h& #define WIDTH 5
#define HEIGHT 3
int jimmy [HEIGHT * WIDTH]; int n,m;
int main (){for (n=0; n&HEIGHT; n++) {for (m=0; m&WIDTH; m++)jimmy[n][m]=(n+1)*(m+1);}
return 0;} for (n=0; n&HEIGHT; n++){ for (m=0; m&WIDTH; m++) jimmy[n * WIDTH + m]=(n+1)*(m+1); } return 0; }上面两段代码并不向屏幕输出,但都向内存中的叫做jimmy的内存块存入如下数值:
我们用了宏定义常量(#define)来简化未来可能出现的程序修改,例如,如果我们决定将数组的纵向由3扩大到4,只需要将代码行:#define HEIGHT 3修改为:#define HEIGHT 4而不需要对程序的其他部分作任何修改。
数组参数(Arrays as parameters)有时候我们需要将数组作为参数传给函数。在C++ 中将一整块内存中的数值作为参数完整的传递给一个函数是不可能的,即使是一个规整的数组也不可能,但是允许传递它的地址。它们的实际作用是一样的,但传递地址更快速有效。要定义数组为参数,我们只需要在声明函数的时候指明参数数组的基本数据类型,一个标识后面再跟一对空括号[]就可以了。例如以下的函数:void procedure (int arg[])接受一个叫做arg的整型数组为参数。为了给这个函数传递一个按如下定义的数组: 63 / 170
int myarray [40];其调用方式可写为:procedure (myarray);下面我们来看一个完整的例子:// arrays as parameters 5 10 15#include &iostream.h& 2 4 6 8 10
void printarray (int arg[ ], int length) {for (int n=0; n& n++) {cout && arg[n] && & &;}cout && &\n&;}
int main () {int firstarray[ ] = {5, 10, 15};int secondarray[ ] = {2, 4, 6, 8, 10};printarray (firstarray,3);printarray (secondarray,5);return 0;}可以看到,函数的第一个参数(int arg[ ])接受任何整型数组为参数,不管其长度如何。因此,我们用了第2个参数来告知函数我们传给它的第一个参数数组的长度。这样函数中打印数组内容的for 循环才能知道需要检查的数组范围。在函数的声明中也包含多维数组参数。定义一个3维数组 (tridimensional array) 的形式是:base_type[ ][depth][depth]例如,一个函数包含多维数组参数的函数可以定义为:void procedure (int myarray[ ][3][4])注意第一对括号[ ]中为空,而后面两对不为空。这是必须的,因为编译器必须能够在函数中确定每一个增加的维度的深度。64 / 170
数组作为函数的参数,不管是多维数组还是简单数组,都是初级程序员容易出错的地方。建议阅读章节3.3, 指针(Pointers),以便更好的理解数组(arrays)是如何操作的。
3.2 字符序列(Character Sequences)前面基础知识部分讲C++变量类型的时候,我们已经提到过C++的标准函数库提供了一个string类来支持对字符串的操作。然而,字符串实际就是一串连续的字符序列,所以我们也可以用简单的字符数组来表示它。例如,下面这个数组:char jenny [20];是一个可以存储最多20个字符类型数据的数组。你可以把它想象成:
理论上这数组可以存储长度为20的字符序列,但是它也可以存储比这短的字符序列,而且实际中常常如此。例如,jenny 在程序的某一点可以只存储字符串&Hello& 或者&Merry christmas&。因此,既然字符数组经常被用于存储短于其总长的字符串,就形成了一种习惯在字符串的有效内容的结尾处加一个空字符(null character)来表示字符结束,它的常量表示可写为0 或'\0'。我们可以用下图表示jenny (一个长度为20的字符数组) 存储字符串&Hello& 和&Merry Christmas& :65 / 170
注意在有效内容结尾是如何用空字符null character ('\0')来表示字符串结束的。后面灰色的空格表示不确定数值。
初始化以空字符结束的字符序列(Initialization of null-terminated character sequences)因为字符数组其实就是普通数组,它与数组遵守同样的规则。例如,如果我们想将数组初始化为指定数值,我们可以像初始化其它数组一样用:char mystring[] = { 'H', 'e', 'l', 'l', 'o', '\0' };在这里我们定义了一个有6个元素的字符数组,并将它初始化为字符串Hello 加一个空字符(null character '\0')。除此之外,字符串还有另一个方法来进行初始化:用字符串常量。在前几章的例子中,字符串常量已经出现过多次,它们是由双引号引起来的一组字符来表示的,例如:&the result is: &是一个字符串常量,我们在前面的例子中已经使用过。与表示单个字符常量的单引号(')不同,双引号 (&)是表示一串连续字符的常量。由双引号引起来的字符串末尾总是会被自动加上一个空字符 ('\0') 。因此,我们可以用下面两种方法的任何一种来初始化字符串mystring:char mystring [ ] = { 'H', 'e', 'l', 'l', 'o', '\0' };char mystring [ ] = &Hello&;在两种情况下字符串或数组mystring都被定义为6个字符长(元素类型为字符char):组成Hello的5个字符加上最后的空字符('\0')。在第二种用双引号的情况下,空字符('\0')是被自动加上的。66 / 170
注意:同时给数组赋多个值只有在数组初始化时,也就是在声明数组时,才是合法的。象下面代码现实的表达式都是错误的:mystring = &Hello&;mystring[ ] = &Hello&;mystring = { 'H', 'e', 'l', 'l', 'o', '\0' };因此记住:我们只有在数组初始化时才能够同时赋多个值给它。其原因在学习了指针(pointer)之后会比较容易理解,因为那时你会看到一个数组其实只是一个指向被分配的内存块的常量指针(constant pointer),数组自己不能够被赋予任何数值,但我们可以给数组中的每一个元素赋值。在数组初始化的时候是特殊情况,因为它不是一个赋值,虽然同样使用了等号(=) 。不管怎样,牢记前面标下画线的规则。
给字符序列的赋值因为赋值运算的lvalue 只能是数组的一个元素,而不能使整个数组,所以,用以下方式将一个字符串赋给一个字符数组是合法的:mystring[0] = 'H';mystring[1] = 'e';mystring[2] = 'l';mystring[3] = 'l';mystring[4] = 'o';mystring[5] = '\0';但正如你可能想到的,这并不是一个实用的方法。通常给数组赋值,或更具体些,给字符序列赋值的方法是使用一些函数,例如strcpy。strcpy (string copy) 在函数库cstring (string.h) 中被定义,可以用以下方式被调用:strcpy (string1, string2);这个函数将string2 中的内容拷贝给string1。string2 可以是一个数组,一个指针,或一个字符串常量constant string。因此用下面的代码可以将字符串常量&Hello&赋给mystring:strcpy (mystring, &Hello&);例如:67 / 170
// setting value to string J. Soulie#include &iostream.h&#include &string.h&
int main () {char szMyName [20];strcpy (szMyName,&J. Soulie&);cout && szMyNreturn 0;}注意:我们需要包括头文件才能够使用函数strcpy。虽然我们通常可以写一个像下面setstring一样的简单程序来完成与cstring中strcpy同样的操作:// setting value to string J. Soulie#include &iostream.h&
void setstring (char szOut [ ], char szIn [ ]) {int n=0;do {szOut[n] = szIn[n];} while (szIn[n++] != '\0');}
int main () {char szMyName [20];setstring (szMyName,&J. Soulie&);cout && szMyNreturn 0;}另一个给数组赋值的常用方法是直接使用输入流(cin)。在这种情况下,字符序列的值是在程序运行时由用户输入的。当cin 被用来输入字符序列值时,它通常与函数getline 一起使用,方法如下: cin.getline ( char buffer[], int length, char delimiter = ' \n');68 / 170
这里buffer 是用来存储输入的地址(例如一个数组名),length 是一个缓存buffer 的最大容量,而delimiter 是用来判断用户输入结束的字符,它的默认值(如果我们不写这个参数时)是换行符newline character ('\n')。下面的例子重复输出用户在键盘上的任何输入。这个例子简单的显示了如何使用cin.getline来输入字符串:// cin with strings#include &iostream.h&
int main () {char mybuffer [100];cout && &What's your name? &;cin.getline (mybuffer,100);cout && &Hello & && mybuffer && &.\n&;cout && &Which is your favourite team? &;cin.getline (mybuffer,100);cout && &I like & && mybuffer && & too.\n&;return 0;} What's your name? Juan Hello Juan. Which is your favourite team? Inter Milan I like Inter Milan too.注意上面例子中两次调用cin.getline 时我们都使用了同一个字符串标识(mybuffer)。程序在第二次调用时将新输入的内容直接覆盖到第一次输入到buffer 中的内容。你可能还记得,在以前与控制台(console)交互的程序中,我们使用extraction operator (&&) 来直接从标准输入设备接收数据。这个方法也同样可以被用来输入字符串,例如,在上面的例子中我们也可以用以下代码来读取用户输入:cin &&这种方法也可以工作,但它有以下局限性是cin.getline所没有的:它只能接收单独的词(而不能是完整的句子),因为这种方法以任何空白符为分隔符,包括空格spaces,跳跃符tabulators,换行符newlines和回车符arriage returns。? 它不能给buffer指定容量,这使得程序不稳定,如果用户输入超出数组长度,输入信息会被丢失。 ?因此,建议在需要用cin来输入字符串时,使用cin.getline来代替cin &&。69 / 170
字符串和其它数据类型的转换(Converting strings to other types)鉴于字符串可能包含其他数据类型的内容,例如数字,将字符串内容转换成数字型变量的功能会有用处。例如一个字符串的内容可能是&1977&,但这一个5个字符组成序列,并不容易转换为一个单独的整数。因

我要回帖

更多关于 c struct 的文章

 

随机推荐