C++中的bool类型变量
1.在C语言中并没有真正的bool类型变量,当在C中需要使用到bool类型变量的时候将其退化成为int类型来进行使用。
2.C++中的
bool类型变量是C++中的一个基本类型,C++中的bool类型变量只能取true和false,这里的
true和false也是C++的关键字。
3.
C++中的bool类型变量的存储空间理论上只占用一个字节,同时也有特殊情况的村子,假如我们在程序中连续定义多个bool类型的变量的时候,这个时候C++编译器可能会产生优化。
主要的优化方式是:在一个字节中的第一位表示第一个bool类型的变量,第二位表示第二个bool类型的变量,以此类推。这种优化也只是可能。
4.
在C++编译器的内部,关于bool类型的变量,true的值默认为1,false值默认为0
。例程如下:
#include <stdio.h>
int main()
{
bool a = true;
printf ("a = %d, sizeof(a) = %d", a, sizeof(a));
}
上述程序的打印结果为全1,4
5.在给bool类型的变量赋值过程中,
0代表false,其他的一切非0值代表true.
例程如下:
#include <stdio.h>
int main()
{
bool a = true;
int b = 0;
a = 7;
b = a;
printf ("a = %d, b = %d", a, b);
}
上述程序的打印结果是 a = 1, b = 1
#include <stdio.h>
int main()
{
bool a = true;
int b = 0;
a = 0;
b = a;
printf ("a = %d, b = %d", a, b);
}
上述程序的运行结果是a = 0, b = 0
#include <stdio.h>
int main()
{
bool a = true;
int b = 0;
a = -9;
b = a;
printf ("a = %d, b = %d", a, b);
}
上述程序的运行结果是:a = 1, b = 1
6在程序中定义一个bool类型的变量有两种对bool类型变量进行赋值的方法
①使用数字进行赋值,0代表false,在编译器内部退化为真正的数值0,非0代表true,在编译器内部退化成为1
②使用关键字true和false进行赋值,true代表真,在编译器内部退化为数值1,false代表假,在编译器内部退化为0
②使用关键字true和false进行赋值,true代表真,在编译器内部退化为数值1,false代表假,在编译器内部退化为0
例程:
#include <stdio.h>
int main()
{
bool a = true;
bool b = 9;
printf ("a = %d, b = %d", a, b);
}
#include <stdio.h>
int main()
{
bool a = false;
a++;
a = a - 1;
printf ("a = %d", a);
}
程序的打印结果是a = 0
C++中的三目运算符
1.在C中三目运算符返回的是一个确切的值,也就是说C中三目运算符只能作为右值使用,因为无论如何一个确切的值都不能够出现在等号的左面。
2.
在C++中三目运算符返回的是一个变量的本身,也就是可以在程序的任何地方出现,无论左值还是右值
。例程如下:
int main()
{
int a = 1;
int b = 2;
(a < b ? a : b) = 3;
//(a < b ? a : 2) = 3;
printf ("a = %d , b = %d", a, b);
}
这个程序在C中不能够编译通过,在C++中,编译运行的结果是3,2
3.在C++中三目运算符作为左值是有条件限制的,三目运算符返回的必须是两个变量的值,有一个不是变量都不能够运行通过。例程如下:
#include <stdio.h>
int main()
{
int a = 1;
int b = 2;
//(a < b ? a : b) = 3;
(a < b ? a : 2) = 3;
printf ("a = %d , b = %d", a, b);
}
C++中的引用
1.变量是一段存储空间的别名,类型代表一段固定大小的存储空间。程序都是通过变量来说申请一段存储空间的。那么一个变量的名字就代表一段存储空间,这段存储空间除了变量名还可以有一个别名。这个别名就被称为引用。
2.在C++中引用的定义形式如下:
Type& a = b;
int a = 1;
int& b = a;
这里b就是a的别名,C++一种类型要求十分严谨的语言,所以这里的b的别名的定义,一定要和a这个变量的类型是一致的。
普通的引用必须用其他的变量进行初始化。
#include <stdio.h>
int main()
{
//int a = 1;
char a = 'a';
int& b = a;
b = 6;
printf ("a = %d , b = %d", a, b);
}
程序报错。
#include <stdio.h>
int main()
{
int a = 1;
int& b = a;
b = 6;
printf ("a = %d , b = %d", a, b);
}
程序的打印结果是a = 6, b = 6 这里的仅仅是a的一个别名,所以
随着b变量对应的内存地址的内容的改变a的内存对应的内容也发生相应的改变。
#include <stdio.h>
int main()
{
int a = 1;
int& b = a;
b = 6;
printf ("&a = %d , &b = %d", &a, &b);
}
#include <stdio.h>
int main()
{
//int& b ;
int& b = 5;
b = 6;
printf ("a = %d , b = %d", a,&b);
}
以上两种情况都报错。
#include <stdio.h>
void swap(int& a, int &b)
{
int t = a;
a = b;
b = t;
printf ("a = %d, b = %d",a, b);
}
int main()
{
int a = 3;
int b = 4;
swap (a, b);
}
上面的程序是普通引用的一个应用,交换两个变量的值,这里不适用指针而是用指针来进行操作,也同样成功的完成了两个值得交换。
注意:这里的两个引用int& a, int& b分别代表a,b这两段存储空间的别名,但是这里的这两个引用并没有提前定义。这是C++的语法规则,当引用作为函数的参数的时候不用进行定义。
3.const引用
const引用让一个引用成为只读引用,但是其实这里它修饰的是一个只读变量。直白的说,const引用让一个变量成为了只读变量。
#include <stdio.h>
int main()
{
int a = 3;
const int& b = a;
int *p = (int *)&b;
*p = 9;
printf ("a = %d, b = %d",a, b);
}
打印结果是a=9,b=9
当const用来修饰一个引用的时候,这个引用变成了常引用,这个时候C++会为这段常量值分配内存空间,同时也将引用名作为这段空间的别名。
#include <stdio.h>
int main()
{
const int& b = 1;
printf ("b = %d", b);
}
这里的变量b能够顺利的打印出来,因为const修饰的引用分配了内存空间,所以可以直接对引用进行初始化。同样的,如果要改变这个引用的值,因为这里的引用变量b是一个只读变量,所以我们只能通过指针的方式进行改变。例程如下:
#include <stdio.h>
int main()
{
const int& b = 1;
//b = 7;
int *p = (int*)&b;
*p = 9;
printf ("b = %d", b);
}
这个程序中,单纯的令b = 7将会报错,最后该程序的打印结果是b = 9
4.引用的实质
每一个引用都有自己的存储空间,引用实质上是一个常指针。
Type& a = Type* const a;
实际上每一个引用都有自己的存储空间,而这个存储空间根据操作系统的位数来决定。也就是根本的不同的操作系统上一个指针的大小事不相同的。
实际上每一个引用都有自己的存储空间,而这个存储空间根据操作系统的位数来决定。也就是根本的不同的操作系统上一个指针的大小事不相同的。
#include <stdio.h>
struct Student
{
char& a;
char& b;
};
int main()
{
printf ("sizeof(Student) = %d", sizeof(Student));
}
程序的打印结果是sizeof(Student) = 8
进一步证明引用占据存储空间的程序如下:
#include <stdio.h>
struct Student
{
int& a;
int& b;
int& c;
};
int main()
{
int a = 1;
int b = 2;
int c = 3;
Student d = {a, b, c};
printf ("&a = %p\n", &a);
printf ("&b = %p\n", &b);
printf ("&c = %p\n", &c);
printf ("&d = %p\n", &d);
}
5.函数返回值和引用
当函数的返回值为引用的时候,若返回值是栈变量,不能成为其他引用的初始值,也不能够作为左值使用。
当函数的返回值为静态变量或者全局变量的时候,可以作为其他引用的初始值,同时也能够作为左值进行使用。
#include <stdio.h>
int& f()
{
static int a = 0;
return a;
}
int& g()
{
int a = 0;
return a;
}
int main()
{
int a = g();
int& b = g();
f() = 10;
printf("a = %d\n", a);
printf("b = %d\n", b);
printf("f() = %d\n", f());
printf("Press enter to continue ...");
getchar();
return 0;
}
6.课后问题
①问题: 在 C++ 中不允许定义引用数组 Type&a[N] ,为什么?
前面已经说过,引用的实质是常指针:
Type&name
ç
è
Type*
const
name,假如我们按照上述的方式定义一个引用数组,代码如下:
int b[5] = {1,2,3,4,5};
int& a[5] = b[5];
如果按照上述方式进行定义,那么我们所定义的int& a[5] = b[5];那么这样转化成常指针以后的形式是:int* const a[5] = b[5];很明显等号的左面是一个int*类型,而等号的右面是int类型,这个时候无论如何也不能够将int类型的常量赋值给int*类型,更何况是C++这种强类型的语言。
换个角度来说,
如果引用数组那么这个数组中的每一元素都应该是一个引用,然而最后还都要落在数组上,也就是说这些引用需要占据一片连续的内存空间,再向上推,引用的每一个变量的地址也就是引用对应的每一个变量的地址,这样引用和不引用其实没有差别。但是在C语言中,当我们向函数传递数组参数的时候都知道最后会退化为指针,这样我们其实只是根据数组的地址在使用数组,但是很容易忽略掉数组的大小,容易造成错误,
但是C++中的引用数组其实并不是引用数组中的每一元素,而是对指向这个数组的指针进行引用,这样同时可以考虑数组的个数问题
。
②问题:
如何定义一个数组的引用?
通过问题①可以知道,数组的引用其实只要引用数组名或者说是引用指向这个数组的指针。数组的应用形式定义如下:
#include <stdio.h>
int main()
{
int a[3] = {1,2,3};
int (&c)[3] = a;
for (int i = 0; i < 3; i++)
{
printf ("c[i] = %d\n", c[i]);
}
}
在这里,我们实际上引用的数组名或者说是引用的指向一个个为3的数组的地址。
③数组引用和数组指针有什么区别?
通过②和③可以看出,数组的指针,是指向一个数组的指针,而我们数组的引用也是引用一个指向固定大小的数组的地址或者数组名,联系前面的C语言,可以发现,这两个概念没有差别。
④如何定义一个函数引用?
函数的引用就是函数指针的引用,因为函数的实现是在程序的运行过程中才开始实行内存分配的,所以其他函数的引用和数组的引用由类似的地方,所以对函数的引用其实也就是对函数指针的引用。具体实现如下:
#include <stdio.h>
int func(void)
{
printf ("helllo!\n");
return 0;
}
int main()
{
int (*p)(void) = func;//定义函数指针
int (* &q)(void) = p;
}
由于是C++的菜鸟,没有写过大的C++程序,更没接触过真正的项目,所以这里的函数引用和数组的引用的应用场景不太了解,暂时只是把老师的课后题完成了。