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

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


数组

1、C++除了基本数据类型之外,还提供了构造数据类型,以满足不同应用的需要。构造数据类型包括:数组、结构体、共同体、类。C语言中没有类的概念。

2、在C++中不提供可变化大小的数组,即数组定义中的常量表达式不能包含变量,但是可以使用宏定义标识符常量或用const说明的标识符常量。比如:

#define ASD 256
const int SIZE=50;

int x[ASD];            //正确
int y[ASD+SIZE];            //正确
int z['a'];            //正确,代表97

3、不能将数组作为一个整体进行直接赋值、输入、输出;且同类型的数组之间不能相互直接赋值。但是在数组的初始化的时候,可以同时对数组的所有元素赋初值。

4、当把数组定义为全局变量或者静态变量时,C++编译器会自动的将所有元素的初值置为0。当把数组定义为其他存储类型的局部变量的时候,数组的元素没有确定的初值,其值是随机的。

5、在C++中,可以将字符的值作为整数来处理,整数也可以作为字符来处理(整数的值应该为0-255之间)。从这个角度上来说,字符型和整数型是相通的,但两者又是有区别的。例如:

char s1[100];
int s2[100];

前者s1分配空间100字节,后者s2分配空间400字节。

6、字符数组的输入/输出方法有两种:逐个字符的输入/输出,将字符数组作为字符串输入/输出。

char str[20];                        //C++逐个字符输入
for(int i=0;i<sizeof(str),i++){
    cin >> str[i];                    //忽略空格
    cin.get(str[i]);                //不忽略空格和回车
}

char str[]="Hello";                //C++逐个字符输出
for(int i=0;i<sizeof(str),i++){
    cout << str[i];
}

逐个字符输入/输出的时候,控制循环结束可以使用sizeof()函数或者strlen()函数,但是后者必须要引用头文件cstring。

char str[20];                 //C语言输入
gets(str); 

char str[] = "Hello";              //C语言输出
puts(str); 

char str[20];              //C++输入
cin >> str;            //遇到空格和回车认为字符串结束
cin.getline(str,20);        //将输入的一行作为一个字符串,不忽略空格

char str[] = "Hello";            //C++输出  
cout << str;

注意:这里使用cin.getline()函数将输入的一行作为一个字符串送到字符数组中,该函数的第一个参数为字符数组名,后一个参数为允许输入的最大字符个数。

扫描二维码关注公众号,回复: 1639575 查看本文章

但是这样会有一个问题,如下:

#include <iostream>
using namespace std;

int main()
{
	char s3[81];
	char s4[8] = { 's','j','f','s','k','l','f','j' };

	cin.getline(s3, 80);
	cout << s3 << endl;
	cout << s4 << endl;

	system("pause");
	return 0;
}

该程序运行的结果为:

HELLO WORLD
HELLO WORLD
sjfsklfj烫烫烫烫HELLO WORLD
请按任意键继续. . .

是不是很意外!!为什么s4的输出就这么奇怪呢?

cin.getline()函数从标准输入(键盘)读入一行数据,所谓读取一行,就是遇到换行符就返回。但是,cin.getline()函数并不读取换行符'\n',它会把换行符替换成空字符'\0',作为字符串结束的标志。

然而,s4是按照逐个字符的方式给初始化的,而不是按照字符串的方式初始化的,所以s4中没有字符串的结束符!所以将s4的字符作为字符串输出的时候,由于最后一个字符'j'没有字符串的结束符,就会将紧跟其后的存储空间的值都作为字符输出,直至遇到字符串结束字符为止。

所以,当把字符数组中的字符作为字符串输出的时候,必须保证在数组中含有字符串结束符。也就是说,如果程序改成:

#include <iostream>
using namespace std;

int main()
{
	char s3[81];
	char s4[9] = { 's','j','f','s','k','l','f','j','\0' };

	cin.getline(s3, 80);
	cout << s3 << endl;
	cout << s4 << endl;

	system("pause");
	return 0;
}

这段程序的运行结果如下:

HELLO WORLD
HELLO WORLD
sjfsklfj
请按任意键继续. . .

所以C语言当时说puts()函数一般和gets()函数一起使用,也不是没有道理的。

7、字符串比较函数strcmp(字符串1,字符串2)的实现:

int stringcomp(char s1[],chars2[]){
    int j=0,k;
    while((k=s1[j]-s2[j])==0&&s1[j])
        j++;
    return k;
}

解析:字符串比较跳出循环的条件,相减的结果不等于0(减出大小了),或者减到最后有一个结束了(为字符串结束符'\0')。


结构体、共同体和枚举类型

1、结构体是一种构造数据类型,编译程序并不为任何数据类型分配存储空间,只有定义了结构体类型的变量时,系统才为这种变量分配存储空间。

2、在定义结构体类型的成员时,不能指定成员的存储类型为auto、register、extern。这是由于系统不为结构体类型分配任何存储空间,如果想要指定,就去指定结构体类型的变量的存储类型。

但是,可以指定成员的存储类型为static。比如:

struct student{
    auto int i,j;                //错误
    register int s;                //错误
    extern int f;                //错误
    static int m;                //正确
}

auto student s1;                //正确
register student s2;                //正确
extern student s3;                //正确
static student s4;                //正确

当把结构体类型中的某一个成员变量的存储类型定义为static时,表示在这种结构体类型的所有变量中,编译程序为该成员只分配一个存储空间,即这种结构体类型的所有变量共享同一个该成员的存储空间。比如:

#include <iostream>
using namespace std;

struct s {
	static int id;                    //引用性说明
	int age;
};
int s::id = 50;                            //定义性说明

int main()
{
	s s1, s2;
	cout << s1.id << endl;
	s1.id = 100;
	cout << s1.id << endl;
	s2.id = 200;
	cout << s2.id << endl;
	cout << s1.id << endl;

	system("pause");
	return 0;
}

这段程序的运行结果为:

50
100
200
200
请按任意键继续. . .

首先,在结构体中说明的静态成员属于引用性说明,必须在文件作用域中的某个位置对静态的成员进行定义性说明,且只能说明一次。对结构体静态成员定义性说明的格式为:

数据类型 结构体类型名::静态成员名;

其中,“::”属于作用域运算符,表示静态成员名是的属于结构体类型的成员的。当然,在定义性说明的同时进行初始化也是可以的,要是不初始化,就按照静态变量的默认初始值来定。

其次,可以看到,当我们修改s2的id的时候,s1的id也随之而变化,所以可以看出结构体的静态成员只有一块内存空间;

还有,对于静态成员名的初始化要么在定义性说明的时候进行,要么就不要进行。尤其是定义结构体变量的时候,不要初始化!比如:

s s1 = {100};                    //正确,为age成员初始化
s s1 = {100,100};                    //错误,重复初始化
s1.id = 100;                    //正确

因为:结构体的静态成员变量内存只有一份,就需要在定义性说明的时候对这一块区域赋初值。但要是结构体变量也可以对其进行赋初值,就势必会导致初值冲突的情况。

3、结构体类型变量可以作为函数的参数,函数也可以返回结构体类型的值。当函数的实参和形参为结构体类型的变量时,这种结合方式属于值传递

4、不同的枚举可以取相同的值。比如:

enum colors { A=2,B=1,C,D,E=2 };

这五个元素的值分别为2、1、2、3、2。不过通常不同的枚举元素取相同的值是没有什么实用意义的。

因此:C++只允许将枚举表中列举的某一个枚举常量作为枚举变量的值,而不能使用枚举常量代表的值。

5、对枚举类型的变量只能使用两种运算符:赋值运算符和关系运算符。枚举类型变量不能从键盘上输入,但是却可以直接输出,输出的值就是枚举常量对于的整数值。


指针和引用

1、指针变量的值只能是某一个变量的地址(起始地址),在说明指针变量时,通常其值是不确定的(静态存储类型、文件作用于类型的变量除外,默认为0,也就是空)。只有对指针变量赋值后,才能使用它。

2、编译程序也要为指针变量分配内存单元,因为指针变量的值是一个地址,其取值范围是不变的,通常用4个字节来表示地址(32位系统:4字节;64位系统:8字节),所以不同类型的指针变量所分配到的内存单元的大小是相同的。

3、在C++中,允许将一个整型常数经强制转换后,来初始化指针变量。比如:

int* p = (int*) 0x5600;

也同样允许,将0直接赋值给任一指针变量,其含义是初始化指针变量,使其值为“空”。但是除了0之外的任何非地址数据,不能直接赋值给任一指针变量。

4、向任何一个未初始化的指针变量所指向的内容赋值是极其危险的,并且是不允许的。比如:

#include <iostream>
using namespace std;

int main()
{
	int* p;
	cin >> *p;
	cout << *p << endl;

	system("pause");
	return 0;
}

该程序可以被编译和连接,输入100,输出的值也是100。乍看是没有任何问题的,但是却是不允许的。因为指针变量p是一个局部变量,未被初始化。也就是说,p的值是一个随机值。如果输入100,那么100存储的地址就是一个随机的内存空间。可能是空闲空间,也可能是某一个关键内存空间。所以,当程序复杂的时候,极有可能出问题。如果改成:

#include <iostream>
using namespace std;

int main()
{
	int i, *p = &i;            //相当于两句话int *p; p = &i;
	cin >> *p;
	cout << *p << endl;

	system("pause");
	return 0;
}

此时,指针变量p被初始化了,p的值是变量i的地址。这样就不会出问题了。

5、指针变量执行“++”或者“--”操作,其含义不是指针变量的值进行加1或减1的操作,而是使指针变量指向下一个或上一个元素。计算机内是按下式计算的:

指针变量 = 指针变量 +/- sizeof(指针变量类型)

6、注意区分* p++和(* p)++的区别:

  • * p++:因为运算符*和++的优先级相同,左结合性(从右向左)。先取出p的值,p再++;
  • (* p)++:因为运算符*和++的优先级相同,左结合性(从右向左)。先取出p的值,值再++。

注意:*(指针运算符)、&(取地址运算符)、++、--,这四个运算符的优先级都相同,且都是左结合性(从右向左)。

7、在C++中说明了一个数组后,数组名可以作为一个指针来使用,它的值为整个数组的起始地址,即数组第0个元素的起始地址。但数组名不同于指针变量,编译程序要为指针变量分配内存空间,而并不为数组名分配内存空间。

因此在程序中可把数组名作为指针来使用,但不能对数组名进行赋值运算、++,--运算,但是却可以做加、减法运算。比如:

int a[10], *p;
p = a;

for(int i=0;i<10;i++){
    cout << *(a+i) << endl;            //允许,仅仅是指针移动,并没有赋值
    cout << * a++ << endl;            //不允许,因为a++表示a=a+1,已经包含了赋值运算
    cout << a[i] << endl;            //允许,直接就是数组
    cout << *(p+i) << endl;            //允许
    cout << * p++ << endl;            //允许
    cout << p[i] << endl;            //允许
}

也就是可以看出:一旦用指针变量指向数组的起始地址,那么指针变量就可以代替数组名进行运算。*(p+i)、*(a+i)、a[i]、p[i]彼此相等,都是表示元素a[i]。

必须强调的是,用指针来访问数组元素时,编译程序不作下标是否越界的检查。

8、二维数组的行地址和元素地址的各种表示方法和含义总结:

数组地址和元素的表示法
表示形式 含义
a 二维数组名,数组的起始地址,数组第0行的地址
a+0 第0行的起始地址
a[0] 第0行第0列元素的起始地址

*a,*(a+0)

第0行第0列元素的地址
**a,**(a+0),*a[0],*(*(a+0)+0) 元素a[0][0]
a+i,&a[i] 第i行的起始地址
a+i+j,&a[i]+j 第i+j行的起始地址
a[i],*(a+i) 第i行第0列元素的起始地址
*(a+i)+j,a[i]+j,&a[i][j],*&a[i]+j 第i行第j列元素的地址
*(*(a+i)+j),*(a[i]+j),*(&a[i][j]),*(*&a[i]+j),a[i][j] 第i行第j列元素的值

解释:在C++中,允许这样理解二维数组a[4][4]:a是一个二维数组名,类同于一维数组,它可以表示该二维数组的起始地址;并可将二维数组的每一行看成是一个元素,即数组a包含4个元素a[0]、a[1]、a[2]、a[3];这四个元素分别又是一个数组,a[0]、a[1]、a[2]、a[3]分别为这四个一维数组的起始地址,并等同于四个一维数组的数组名,而每个一维数组又包含四个元素。

a[i]和&a[i]都表示地址,且值相同,但两者表示的意义不同。前者为第i行第0列元素的地址,后者为第i行的地址。因为编译器不给数组名a和a[i]分配内存空间,只有为某一变量分配了内存空间后,变量名前的“&”才表示取地址运算符。所以在a[i]前面是否有“&”,是用来区分元素地址和行地址的。

更体现在:a[i]和&a[i]都表示地址,且值相同;但是a[i]+j表示第i行第j列元素的起始地址,而&a[i]+j表示第i+j行的起始地址。或者说:

int a[4][4], *p;
for(int i=0;i<4;i++){
    p = a[i];            //正确,p指向int,a[i]指向int
    p = &a[i];            //错误,p指向int,&a[i]指向行
}

9、对于字符数组和字符型指针变量的区别:

char a[10] = "Hello";        //正确
a = "World";            //错误
char* p= "Hello";            //正确
p = "World";            //正确

  • 存储器空间分配不一样。编译程序要为字符数组a分配10个内存空间,它最多只能存放10个字符;而对于字符型指针变量,pc只需要分配4个字节的内存,它只能存放一个内存单元的地址;
  • 赋初值方式不一样。对与字符数组,是把字符串送到为数组分配的存储空间去;而对于字符型指针变量,是先把字符串存放到内存中,然后将存放字符串的起始地址送到指针变量中;
  • 赋值方式不一样。对于字符数组赋值,须逐个元素赋值;对于字符型指针变量,可以将任一指针值赋给字符指针变量。因为编译器不给数组名分配内存空间;
  • 可以给字符数组直接输入字符串,而在给字符指针变量赋初值前,不允许将输入的字符串送到指针变量所指向的内存区域。也就是说,字符串指针变量必须先赋初值:
char s1[10], *p;
cin >> s1;            //正确
cin >> p;            //错误,因为不知道p指向的什么样的内存

  • 在执行程序的过程中,字符数组的起始地址是不能改变的,而字符指针的值是可以改变的。

10、指针数组,常用来表示一组字符串。例如,字符串排序(选择排序):

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

int main()
{
	char* str[] = { "Follow me","Basic","Great Wall","Department","Computer Design" };
	char* p;
	int i, j, k;
	for (i = 0; i < 4; i++) {
		k = i;
		for (j = i + 1; j < 5; j++) {
			if (strcmp(str[k], str[j]) > 0)
				k = j;
		}
		p = str[k];
		str[k] = str[i];
		str[i] = p;
	}
	for (i = 0; i < 5; i++)
		cout << str[i] << endl;

	system("pause");
	return 0;
}

11、数组指针,本质上是一个行指针。

int a[4][4], (*p1)[4];
for(int i=0;i<4;i++){
    p1 = &a[i];
}

解释:(*p)指明p是一个指针变量,再与4结合,表示该指针变量所指向的数据是一个一维数组,该数组由4个元素组成,或者说p指向包含四个元素的一维数组。此时(*p)[0]表示的就是第i行第0个元素的值。

12、数组作为函数参数总结

若一维数组作为函数参数的方法:

  • 函数的形参用数组或一级指针型变量,实参用数组或一级指针型变量。

若多维数组作为函数参数的方法:

  • 函数的形参定义为多维数组,调用函数时的实参为多维数组名;
  • 把多维数组作为一维数组来处理,函数的形参定义为一维数组或一级指针变量,另一个参数指明数组的元素个数,对应的实参给出数组的起始地址;
  • 用指向一位数组的指针变量作为函数的参数。

13、实现字符串的逆序输出:

#include <iostream>
using namespace std;

char* flip(char* ptr) {
	char* p1, *p2, temp;
	p1 = p2 = ptr;
	while (*p2++);                //p2指向字符串结束字符'\0'后面的一个字符位置
	p2 -= 2;                    //p2指向字符串结束字符'\0'前面的一个字符位置,结束符不参与逆序
	while (p1 < p2) {
		temp = *p2;
		*p2 = *p1;
		*p1 = temp;
		p1++;
		p2--;
	}
	return ptr;
}

int main()
{
	char str[200];
	cin.getline(str, 200);
	cout << flip(str) << endl;

	system("pause");
	return 0;
}

14、带参数的main()函数及命令行参数

C++为了增加程序的灵活性和可适应性,允许main()函数带有两个或三个形参,其函数的一般原型为:

int main(int argc, char* argv[], char* eve[]);
int main(int argc, char** argv, char** eve);
int main(int argc, char* argv[]);

其中,第一个参数为实际命令行所带的参数个数;第二个参数是一个指向字符串的指针数组,它的每一个元素依次指向该命令的一个参数;而第三个参数也是一个指向字符串的指针变量,它的每一个元素指向当前运行系统的环境变量。

14、指向函数的指针

一个函数的入口地址称为函数的指针,一个指针变量的值为一个函数的入口地址时,称其为指向函数的指针变量。定义如下:

数据类型 (*函数指针名)(形参列表);  
int (*f)(int,int);  

其中:数据类型是函数的返回类型;(*函数指针名)的括号不能省去,如果省去了,代表函数的返回值是一个指针类型,这就成了指针函数了;形参列表只要标注数据类型即可。

函数名表示该函数的入口地址(在内存中的起始地址),可以将函数名赋给指向函数的指针变量。在不作强制类型转换时,只能将与指向函数的指针变量具有相同返回值和相同的参数表的函数名赋给指向函数的指针变量。换言之,指向函数的指针变量只能指向与该指针变量具有相同返回值类型和相同参数(个数及顺序一致)的任一函数。

对指向函数的指针变量进行赋值后,可用该指针变量来调用函数。赋值、调用函数的格式为:

指针变量名 = 函数名;  
f = fun;                /* 函数指针的赋值 */  

(* 指针变量名)(实参表)
(*f)(a,b);                /* 等价于调用函数fun(a,b); */

同样,指向函数的指针变量也可以作为新的函数的参数。比如:

float max(float* p, int n){
    ...
}

void process(float* p, int n, float (*fp)(float*,int)){
    (*fp)(p,n);
}

void main(){
    float x[]={1.2,2.3,3.4,4.5};
    process(x,4,max);
}

15、new和delete运算符的用法:

在程序的执行过程中,希望根据输入的值来确定一个数组的大小,一般就需要用到new运算符。比如:

int n;
cin >> n;
float *p;
p = new float[n];

在C++中,new和delete运算符分别用于为指针变量动态分配内存空间和动态回收指针所指向的内存空间。其格式如下:

指针变量 = new 数据类型;                //分配由数据类型决定大小的一篇连续的内存空间
指针变量 = new 数据类型(初值);            //分配由数据类型决定大小的一篇连续的内存空间,并用初值来初始化
指针变量 = new 数据类型[表达式];        //分配指定类型的数组空间
delete 指针变量;                    //将指针变量指向的内存空间归还系统
delete [表达式]指针变量;            //将指针变量指向的数组内存空间归还系统
delete [表达式]指针变量;            //将指针变量指向的数组内存空间归还系统

例如:

int* p1;
float* fp1, (*p)[10];

p1 = new int;
fp1 = new float(2.3);
p = (float(*)[10])new float[10];            //p是一个行指针(数组指针),所以一定要强制转换!

使用new和delete的注意事项:

  • new动态分配到的内存空间的内容是一些随机值,系统不对它们做任何的初始化工作,其初值是不确定的;
  • 对于很长的程序,用new运算符动态分配内存后,要判断其指针的值是否为0。若new运算符运算的结果为0,则表示动态分配内存失败,应该中断程序的执行。比如:
float* p;
p = new float[10000];
if(p==0){
    cout<<"动态分配内存失败!"<<endl;
    exit(3);
}

  • 动态分配存放数组的内存空间,或为结构体分配内存空间时,不能在分配空间时进行初始化。比如:
int* p;
p = new int[10](1,2,3,4,5,6,7,8,9,10);        //错误

  • 若new运算符计算的指针类型与赋值运算符左操作数类型不一致时,必须进行强制类型转换。比如:
float (*p)[10];
p = new float[10];                //错误
p = (float(*)[10])new float[10];    //正确,指向一个行指针,1行的二维数组   ---> delete p
p = new float[2][10];                //正确,指向一个二维数组,2行的二维数组   ---> delete [2]p

  • 用new运算符分配的内存空间的指针值必须保存起来,以便于用delete运算符归还已动态分配的内存空间,否则会有不可预测的错误。也就是说,必须确认delete归还的一定要是new出来的内存空间,而不是别的内存空间;
  • 当动态分配了二维数组的存储空间后,在释放这部分存储空间时,要指明数组的行数。例如:
int (*p)[100];
p = new int[30][100];
...
delete p;                //仅释放二维数组的第0行所占用的存储空间
delete [30]p;            //p指向的二维数组空间都释放

16、引用基础

C++中提供了一个与指针密切相关的特性——引用,引用是一种特殊的数据类型。定义引用类型变量,其本质是给一个已定义的变量起一个别名,系统不为引用类型变量分配内存空间,只是使得引用类型变量与其相关联的变量使用同一个内存空间。

引用主要用于函数之间传递数据。其定义的一般形式为:

数据类型 &引用变量名=变量名;

int count;
int &refcount = count;

count++;                    //与下句等效
refcount++;                //与上句等效

对引用类型变量的注意点:

  • 定义引用类型变量时,必须将它初始化。为它初始化的变量类型必须与引用类型变量的类型相同;
  • 引用类型变量的初始化值不能是常数,但是如果该引用类型变量是const类型的引用类型常量,就可以用常数初始化。比如:
int &ref = 5;                //错误
const int &ref = 5;                //正确

  • 能说明为引用类型变量的引用,不能说明为引用类型数组。但引用数组中的某一个元素是可以的。比如:
int i, &ref = i;            //正确
int &ref1 = ref;            //正确,相当于int &ref1;   ref1=ref;
int &ref2 = &ref1;            //错误
int a[10];
int &ref=a;                //错误
int &ref[10]=a;                //错误
int &ref=a[0];                //正确
int &*ref=a;                //错误

  • 可以用动态分配的内存来初始化一个引用变量。比如:
float &ref = * new float;            //需要注意:*
ref = 200;
cout << ref;
delete &ref;                //需要注意:&

由于new运算符的运算结果是一个指针类型,所以在前面需要加上“*”来赋值给引用类型变量。同样的,delete运算符的操作数必须是一个指针,所以在前面需要加上取地址运算符“&”。

综合一下,C++里面的“&”一共有三种含义:按位与运算符,它是一个二元运算符;取地址运算符,它是一个一元运算符;引用运算符,用于定义引用类型变量。

17、引用与函数

在C++中引入引用类型的主要目的是为了在函数的参数传递时提供方便。引用类型主要用作函数的参数或者用作函数的返回值类型。

当把函数的参数说明为引用类型时,把引用类型的实参称为引用传递。引用传递与地址传递类同,可作为函数的输入参数,也可作为函数的输出参数。使用引用类型的参数比使用指针类型的参数跟跟那个增加程序的可读性和编程的方便性。

例如交换两个数据:

#include <iostream>
using namespace std;

void swap1(int* p1, int* p2) {
	int temp;
	temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}

void swap2(int &p1, int &p2) {
	int temp;
	temp = p1;
	p1 = p2;
	p2 = temp;
}

int main()
{
	int a = 10, b = 20;

	cout << a << '\t' << b << endl;
	swap1(&a, &b);
	cout << a << '\t' << b << endl;
	swap2(a, b);
	cout << a << '\t' << b << endl;

	system("pause");
	return 0;
}

该程序的运行结果为:

10      20
20      10
10      20
请按任意键继续. . .

可见swap1()和swap2()函数的功能完全相同,都是实现两个数的交换。

  • 对于函数使用引用类型的形参,实参直接使用变量名。而对于函数使用指针类型的实参,实参必须是变量的地址;
  • 对于函数使用引用类型的形参,函数内直接使用变量名计算。而对于函数使用指针类型的实参,函数内需要使用“*”来取指针指向的数据。

对比起来,使用引用类型的参数比使用指针类型的参数更方便。

当把函数的返回值定义为引用类型时,根据引用类型的定义,它所返回的值一定是某一个变量的别名。因此它相当于返回了一个变量,所以可对其返回值进行赋值。比如:

#include <iostream>
using namespace std;

int& fun(void) {
	static int count;
	return ++count;
}

int main()
{
	cout << fun() << endl;
	fun() = 100;
	cout << fun() << endl;

	system("pause");
	return 0;
}

该程序的运行结果为:

1
101
请按任意键继续. . .

所以,一般情况下,当函数的返回值为引用类型的时候,该引用类型所引用的对象是静态变量,或者是全局变量。原因:对于自动存储类型或者寄存器类型的局部变量,函数不能返回这种变量的引用。因这种类型的变量在函数执行结束就不存在了,所以对它的引用是无效的。

18、在C++中当指定函数的类型为void时,表示其没有返回值,或者说返回值的值无效。当把指针定义为void类型时,表示可以指向任意类型的数据。void型指针也称为无类型指针,可以把任意类型的指针赋值给它。但若将void型的指针赋值给其他类型的指针变量时,则必须进行强制类型转换。

19、const类型变量

当用const限制说明标识符时,表示所说明的数据类型为常量类型。可分为const型常量和const型指针。

定义const型常量

用const定义的标识符常量时,一定要对其进行初始化。在说明时进行初始化是对这种常量置值的唯一方法,不能用赋值运算符对这种常量进行赋值。

定义const型指针

有三种不同的方法来说明const型指针,其作用和含义都是不同的。第一种形式将const放在指针变量的类型之前,比如:

int a, b;
const int* p = &a;

这种形式定义的指针变量表示指针变量指向的数据是一个常量。因此不能改变指针变量指向的数据值,但可以改变指针变量的值。这种形式定义的指针变量,在定义时可以赋初值,也可以不赋初值。比如:

*p = 10;            //错误
a = 20;            //正确
p = &b;            //正确

这种形式下,以下两种方式的表述是一样的:

const int* p = &a;
int const* p = &a;

第二种形式是将const放在指针变量“*”之后。比如:

int a, b;
int* const p = &a;

这种形式定义的指针变量,表示指针变量的值是一个常数。因此不能改变这种指针变量的值,但可以改变指针变量所指向的数据值。这种形式定义的指针变量,在定义时必须赋初值。比如:

*p = 20;                //正确
p = &b;                //错误

第三种形式是把一个const放在指针变量的类型前面,再把另一个const放在指针变量的“*”之后。比如:

int a, b;
const int* const p = &a;

这种形式定义的指针变量,表示指针变量的值是一个常量,指针变量所指向的数据也是一个常量。因此不能改变指针变量的值,也不能改变指针变量所指向的数据值。这种形式定义的指针变量,在定义时必须赋初值。比如:

*p = 20;                //错误
p = &b;                //错误

20、为了增强程序的可读性和可移植型,C++提供了产生新的类型标识符的功能。定义新类型标识符的一般格式为:

typedof 数据类型 标识符;
经类型定义后,该标识符可以作为类型说明符或者作为强制类型转换的类型标识符来使用。

猜你喜欢

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