【编程语言】C++基础大杂烩(上篇)

本博客之前有C语言的教程,在C语言的基础上进行C++的学习,会轻松蛮多。此文就简单地将一些不太同的地方进行点一点,主要涉及的章节就是C语言的那几个部分:数据类型、运算符、表达式、输入输出、流程控制、函数、预编译处理、结构体、共同体、枚举、指针和引用。


数据类型、运算符和表达式

1、C语言中的头引用#include <stdio.h>,代表着standard input&output;而C++中的头引用为#include <iostream>,代表着input&output stream。

并且要注意开头的区别:

#include <stdio.h>        //C语言

#include <iostream>
using namespace std;        //C++

所谓namespace,是指标识符的各种可见范围。C++标准程序库中的所有标识符都被定义于一个名为std的namespace中。

<iostream>和<iostream.h>是不一样,前者没有后缀,实际上,在你的编译器include文件夹里面可以看到,二者是两个文件,打开文件就会发现,里面的代码是不一样的。

后缀为.h的头文件c++标准已经明确提出不支持了,早些的实现将标准库功能定义在全局空间里,声明在带.h后缀的头文件里,c++标准为了和C区别开,也为了正确使用命名空间,规定头文件不使用后缀.h。

因此,当使用<iostream.h>时,相当于在C中调用库函数,使用的是全局命名空间,也就是早期的c++实现;当使用<iostream>的时候,该头文件没有定义全局命名空间,必须使用namespace std;这样才能正确使用cout。

2、C语言中是没有try、catch、throw关键字的;而C++中是有的。

3、在C++中有两种方法定义标识符常量:一种是使用预编译处理指令;另一种是使用C++的常量说明符const。例如:

#define PI 3.1415926
const float pi=3.1415926;

注意:标识符常量在C++程序中只能引用,不能修改其值。

4、关系运算符的两个操作数可以是任意的基本类型的数据。由于实数在计算机内进行计算和存储时,都会产生误差,在进行两个实数的比较时,不能采用精确的比较。

例如:假设有两个实数变量x,y,经过一系列的计算后,理论上两个变量的值都应该是100.0,但计算机内的实际值可能是99.999999和10.000001:

x==y                //结果为0
fabs(x-y)<=1e-5            //结果为1

其中,fabs()函数为C++中求实数绝对值的库函数。

5、指数表示法(科学计数法),表示10的多少次方。例如:

123E12            //正确
0.12e-2            //正确

需要注意的是:在E或者e的前面必须有数字,且E或者e的后面(指数部分)必须为正数。

6、赋值类型转换和逻辑表达式优化时的副作用。例如:

int i,j;
float x,y;
x=(i=4.8)+(j=5.9);

此时x的结果是9.0,而不是10.7。因为在进行赋值运算的时候进行了类型转换,直接导致后来表达式的求值精度。

例如:

int a=16,b=10,i;
float x=10;
i=a<b&&(x=25)>b;

此时i的结果为0,这没有什么争议。只是需要注意一下:x的结果为10。在C++中,在求逻辑表达式的过程中,一旦能够确认逻辑表达式的值,就不必再逐步求值了。也就是说,a<b为0,求&&运算,无论右边的结果是什么,结果一定为0。这个时候,就直接将0赋值给i,而右边的表达式不运行了。

7、C++中,在每一个字符串常量的结尾,存放的都是一个“\0”,表示字符串的结束符。比如:

"a"            //存放值为0x6100
'a'            //存放值为0x61


简单的输入/输出

1、当使用C++提供的输入/输出时,必须在程序的开头添加一行:

#include <iostream>

2、当程序执行期间,要给变量输入整数、实数、字符数据的时候,使用cin来完成。其一般格式为:

cin>>变量名;

其中,运算符“>>”称为提取运算符,表示将暂停程序的执行,等待用户从键盘上输入相应的数据。在一个提取运算符后面只能跟一个变量名,但“>>”可以重复多次使用。

回车键的作用:

  • 告诉cin已经输入一行数据,cin开始从输入行中提取输入的数据,并以此将所提取的数据赋值给cin中所列举的变量;
  • 起输入数据之间的分隔符作用,当cin遇到回车键时,若仍有变量等待输入,则继续等待用户输入。

例如:

#include <iostream>
using namespace std;

void main() {
	char c1, c2, c3;
	cin >> c1 >> c2 >> c3;

	system("pause");
}

这个时候,如果输入Abc,那么也照样将三个字符依次赋值给c1、c2、c3。

注意:cin不能将输入的空格、回车键赋值给字符变量。若想要从键盘上输入的每一个字符,包括空格和回车键都作为一个输入字符赋值给字符型变量,必须使用函数cin.get()。其一般格式为:

cin.get(字符型变量);

这个函数从输入行中取出一个字符,并将它赋值给字符型变量。该语句每执行一次只能从输入行中提取一个字符。

另外,如果想要输入十六进制或者八进制数据,在cin中必须指明对于的数据类型:hex为十六进制,oct为八进制,dec为十进制。比如:

cin >> hex >> i;
cin >> oct >> j;
cin >> dec >> k;

或者使用:

cout<<setf( ios::hex, ios::basefield );

要注意的是:如果在cin中指明使用的数制输入后,则所指明的数制会一直有效,直到在接下来的cin中指明使用另一种数制为止。

3、与cin输入流相对应的是cout输出流,cout的用法与cin类似,就不多介绍了。

只提一下科学计数法的输出方式(只对实数有效)

	float a = 3.14;
	cout.setf(ios::scientific, ios::floatfield);
	cout << a;

当然,一旦指明按科学计数法输出实数之后,则其后的输出均按照科学计数法输出。如果要解除科学计数法的方式,需要使用:

	cout.setf(ios::fixed, ios::floatfield);

如果想要确定输出实数保留小数的位数,可以使用以下的方式:

cout.setf(ios::fixed, ios::floatfield);
cout.precious(2);        //小数点后两位

如果想要指明输出项占用的字符宽度,需要使用setw()函数

cout<<setw(6)<<i<<endl;

使用setw()函数的注意事项:

  • 要使用setw()函数之前,必须包含头文件iomanip.h,且函数的参数必须为一个正整数;
  • setw()函数只对其后的一个输出项有效。


C++流程控制语句

1、求Fibonacci数列的前40项(1,1,2,3,5,8,13……)

#include <iostream>
using namespace std;

void main() {
	int a = 1, b = 1;
	int i, j;
	for (i = 0; i < 40; i++) {
		if (i == 0)
			j = a;
		else if (i == 1)
			j = b;
		else
		{
			j = a + b;
			a = b;
			b = j;
		}
		cout << j << endl;
	}

	system("pause");
}

或者采用函数的递归也是可以实现的。

2、exit()和abort()函数都是C++的库函数,其功能都是终止程序的执行,将控制返回给操作系统。通常,前者用于正常终止程序的执行,而后者用于异常终止程序的执行。显然,执行到这两个函数中的任一函数时,都将改变程序的执行次序。当使用这两个函数中的任意函数时,都应该包含头文件cstdlib。

exit()函数,其一般的格式为:

exit(整数);

通常exit()函数的参数为一个整数,这个数的值作为中止程序执行的原因。执行该函数时,将无条件地终止程序的执行而不管该函数处于程序的什么位置,并将控制返回给操作系统。

通常整数的表示:用0表示正常退出,而其他的整数作为异常退出。

当执行exit()函数时,系统要做中止程序执行前的收尾工作,如关闭该程序打开的文件,释放变量所占用的存储空间(不包括动态分配的存储空间)等。

abort()函数,其一般的格式为:

abort();

该函数没有任何参数。在执行该函数时,系统不做结束程序前的收尾工作,直接终止程序的执行。故一般使用的没有exit()函数广泛。

3、一些函数介绍:

  • sqrt()函数:取根号,引用cmath;
  • abs()函数:绝对值函数(多用于整数),引用cstdlib;
  • fabs()函数:绝对值函数(多用于double、float),引用cmath;
  • rand(a,b)函数:随机数函数,生成a-b之间的随机数,引用cstdlib;

但是一般都用无参的rand()函数,则产生一定范围随机数的通用表示公式是:

  • 取得(0,x)的随机整数:rand()%x;
  • 取得(a,b)的随机整数:rand()%(b-a);
  • 取得[a,b)的随机整数:rand()%(b-a)+a;
  • 取得[a,b]的随机整数:rand()%(b-a+1)+a;
  • 取得(a,b]的随机整数:rand()%(b-a)+a+1;
  • 取得0-1之间的浮点数:rand()/double(RAND_MAX)。


函数和编译预处理

1、在C++中,当函数定义在前,函数调用在后时,程序能正确编译执行。

我们知道,在C语言中,由于隐式声明的存在,下述的函数调用是成立的:

#include <stdio.h>  
  
int main()  
{  
    int a;  
    a=fun1();  
    return 0;  
}   
  
int fun1()  
{  
    return 0;  
}  
然而, 在C++中,同样的这样表述是不成立的:
#include <iostream>
using namespace std;

int main()
{
	int a;
	a = fun1();
	return 0;
}

int fun1()
{
	return 0;
}

也就是说,在C++中,如果函数的调用在前,定义在后,那么应该在主函数中添加函数原型(函数声明):

#include <iostream>
using namespace std;

int main()
{
	int a;
	int fun1();
	a = fun1();
	return 0;
}

int fun1()
{
	return 0;
}

也就是说,当出现函数调用在前,函数定义在后的情况下,在函数调用之前,必须要对被调用函数作函数原型说明。

2、在C++,形参和实参的结合方式有三种:传值调用、传地址调用、引用调用。而在C语言中,仅支持前两种,不支持引用调用。

3、在C++中,作用域分为五类:块作用域、文件作用域、函数原型作用域、函数作用域、类的作用域。

块作用域:局部更优先原则。

在for语句中说明的循环控制变量具有块作用域,其作用域为包含for语句的那个内层块,而不仅仅作用于for。比如:

for(int i=0;i<10;i++){
    cout<<i<<'\t';
}
cout<<i<<endl;        //此时输出i是允许的,且输出为10。

也就是说,上面这段程序等价于:

int i;
for(i=0;i<10;i++){
    cout<<i<<'\t';
}
cout<<i<<endl;        //此时输出i是允许的,且输出为10。

文件作用域:在函数外定义的变量(标识符)或用ertern说明的变量(标识符)称为全局变量(标识符)。全局变量的作用域称为文件作用域,即在整个文件中都是可以访问的。其缺省的作用域是:从定义全局变量的位置到源程序文件的结束,即符合说明在前、使用在后的原则。

当块作用域内的变量和全局变量同名的时候,局部变量优先。但与块作用域不同的是,在块作用域内可以通过作用域运算符“::”来引用与局部变量同名的全局变量。比如:

#include <iostream>
using namespace std;

int i=100;

int main(){
    int i=50;
    cout<<i<<endl;            //访问局部变量i
    cout<<::i<<endl;            //访问全局变量i

    return 0;
}

函数原型作用域:在函数原型的参数表中说明的标识符所具有的作用域称为函数原型作用域,它从其说明处开始,到函数原型说明的结束处结束。正因为如此,在函数原型中说明的标识符与该函数的定义及调用无关,所以可以在函数原型说明中只做参数的类型说明,而省略参数名。例如:

int max(int a,int b);            //正确
int max(int,int);        //正确

函数作用域:在函数内定义的标识符在其定义的函数内均有效,即不论在函数内的某一个地方定义,均可以引用这种标识符。在C++中,只有标号具有函数作用域。

在一个函数中定义的标号,在其整个函数内均可以引用。所以在同一函数内不允许标号同名。同时,不允许在一个函数内使用goto语句转移到另一个函数内的某一个标号去执行。比如:

#include <iostream>
using namespace std;

int main(){
    int a;
    label:cout<<"输入一个整数:"<<endl;
    cin>>a;
    cout<<"你刚刚输入的是:"<<a<<endl;
    goto label;

    return 0;
}

4、存储类是针对变量而言的,它规定了变量的生存期,即何时为变量分配内存空间及何时回收为变量分配的内存空间。变量的额存储类反映了变量占用内存空间的期限。

在C语言程序运行时,占用的内存空间被分成三部分:程序代码区、静态存储区、动态存储区

变量的存储类型具体来说分成4种:自动类型(auto)、寄存器类型(register)、静态类型(static)、外部类型(extern)。其中,自动类型、寄存器类型的变量属于动态变量,静态类型、外部类型属于静态变量。

extern的作用:

  • 在同一个源程序中定义的全局变量,属于定义在后而使用在前的情况下,使用ertern说明为外部变量;
  • 在不同的源程序文件中,若引用其他源文件下定义的全局变量,使用ertern说明为外部变量。

5、内联函数

程序的执行过程中要调用一个函数,系统需要进行保护当前的现场、参数入栈等工作,然后转去执行被调用函数的函数体。当执行完被调用函数后,要恢复现场,再接着执行函数调用后的语句。

当函数体比较简短的时候,这种函数的调用方式的开销是比较大的。

C++提供了一种方法,将函数体的代码直接插入到调用处,将调用函数的方式改为顺序执行直接插入的程序代码,这样可以减少程序的执行时间。这一过程称为内联函数的扩展。内联函数的实质就是用存储空间来换取时间的方法。

内联函数的定义方法是在函数定义时,在函数的类型前增加修饰词inline。

内联函数需要注意的是:

  • 在C++中,除了函数体内含有循环、switch分支和复杂嵌套循环的if豫剧之外,所有的函数都可以定义为内联函数;
  • 对于用户指定的内联函数,编译器是否作为内联函数来处理由编译器来自行决定。

6、在C++中定义函数时,允许给参数指定一个缺省的值。在调用函数的时候,如果明确地给出了实参的值,就使用相应实参的值;如果没有给出,就使用缺省值。比如:

void delay(int n=1000){
    for(; n>0; n--);
}

delay(100);            //延时100
delay();            //延时1000

除了上面的这个方法之外,还可以在函数的原型说明的时候列出缺省值;如果这样,那么函数定义的时候,就不能重复指定了。比如:

void delay(int n=1000);

void delay(int n){
    for(; n>0; n--);
}

delay(100);            //延时100
delay();            //延时1000

缺省参数的函数的注意点:

  • 在定义函数的时候,如果具有缺省值的参数有多个,那么,在函数定义的时候,缺省参数必须位于参数表中的最右边。这是因为:在C++中处理函数调用时,参数是自右向左依次入栈的。比如:
int fun(int a, int b=1, int c=1);            //正确
int fun(int a=1, int b=1, int c);            //错误
int fun(int a=1, int b, int c=1);            //错误

7、参数个数可变的函数。

#include <iostream>
#include <cstdarg>
using namespace std;

int max(int num, int b...) {
	va_list ap;
	int maxf, temp;
	va_start(ap, b);
	maxf = b;			//赋初值

	for (int i = 1; i < num; i++) {
		temp = va_arg(ap, int);
		if (maxf < temp)
			maxf = temp;
	}

	va_end(ap);
	return maxf;
}

int main()
{
	cout << max(2, 1, 3) << endl;
	cout << max(3, 2, 4, 3) << endl;
	cout << max(4, 2, 9, 5, 8) << endl;

	system("pause");
	return 0;
}

在定义一个参数可变的函数时,形参表中用省略号“...”表示后面可以没有参数,也可以由若干参数。

在定义参数可变的函数时,必定要用到三个库函数va_start()、va_arg()、va_end()。但是如果要想使用这三个函数,必须先包含头文件cstdarg。

  • va_list类型的变量:这种变量和int、float等类同,它是C++系统预定义的一个数据类型,只有通过这种类型的变量才能从实际参数表中取出可变参数;
  • va_start()函数有两个参数,一个是va_list类型的变量,另一个是参数形参列表中最后一个参数的变量名,即省略号之前的变量名。该函数的作用是:初始化参数个数可变的函数,使va_list类型的变量指向实参中第一个可变的参数;
  • va_arg()函数有两个参数,一个是va_list类型的变量,另一个是C++中预定义的数据类型名(如int、float)。该函数的作用是:将va_list类型的变量指向的可变参数转换为C++中预定义的数据类型名,并将该数据作为返回值返回。同时,还将va_list类型的变量指向下一个可变的参数;
  • va_end()函数只有一个参数,就是va_list类型的变量。该函数的作用是:做好可变参数的收尾工作(不能省略这一步,否则可能直接死机)。

即:使用va_start()来初始化可变参数,va_arg()一次取出各个可变参数,va_end()来做结束工作。

注意:一般在设计参数个数可变的函数时,必定有一个参数指明可变参数的个数或者总的实参个数。如上例中的num,b可有可不有,就是为了赋初值而已。

8、C++中提供了两种重载:函数的重载和运算符的重载。

仅仅返回值不同时,不能定义为重载函数。

9、文件包含时,使用#include的时候,用包含文件的内容替换include指令行时,是在一个临时文件中进行的,并不改变原文件的内容。

猜你喜欢

转载自blog.csdn.net/qq_38410730/article/details/80389349