- UID
- 852665
|
最近一直在参加各种校招,可算现在闲下来了,又有时间学学习、看看书,继续写我的C++学习笔记。
本节知识点:1.c++中的bool类型:
其实c语言中也有bool类型,如果是遵守c90标准的编译器(其实现在大量编译器都是c90标准的),对于bool类型的使用除了要使用头文件stdbool.h外,与c++中的bool类型完全相同。如果不是c90标准,则此时的bool其实就是int类型。详细见下stdbool.h文件的源码:
[cpp] view plaincopy
- #ifndef _STDBOOL_H_
- #define _STDBOOL_H_
- #define __bool_true_false_are_defined 1
- #ifndef __cplusplus
- #define false 0
- #define true 1
- #define bool _Bool
- #if __STDC_VERSION__ < 199901L && __GNUC__ < 3 && !defined(__INTEL_COMPILER)
- typedef
int _Bool; - #endif
- #endif /** !__cplusplus */
- #endif /** !_STDBOOL_H_ */
c++中bool类型有几个特性:
a.理论上bool变量只占用一个字节,但是如果多个bool变量定义在一起,可能会各占一个bit,这个取决于编译器的实现。
b.bool类型是一个很严格的类型,只有true和false两个值,一切非零的值都会变成true。测试代码如下:
[cpp] view plaincopy
- #include <stdio.h>
- int main(int argc, char *argv[])
- {
- int a;
- bool b = true;
- printf("b = %d, sizeof(b) = %d\n", b, sizeof(b));
- b = 3;
- a = b;
- printf("a = %d, b = %d\n", a, b);
- b = -5;
- a = b;
- printf("a = %d, b = %d\n", a, b);
- a = 10;
- b = a;
- printf("a = %d, b = %d\n", a, b);
- b = 0;
- printf("b = %d\n", b);
- printf("Press enter to continue ...");
- getchar();
- return 0;
- }
2.c++中引用的概念:
1.引用的定义:
引用是c到c++升级中提出的一个新概念。引用可以看作一个已定义变量的别名。普通引用在声明的时候必须要用其他变量进行初始化,一旦初始化这个引用就不能再是其它变量的别名了。
[cpp] view plaincopy
- <span style="white-space:pre"> </span>Type& name = var;
2.引用的作用:引用作为变量的别名,在某些情况下可以替代指针,相对指针来说具有更好的可读性和实用性。
例子:swap函数的对比:
[cpp] view plaincopy
- void swap(int& a,int& b) //使用引用作为函数参数,进行两个数交换
- {
- int t = a;
- a = b;
- b = t;
- }
- swap(a,b);
[cpp] view plaincopy
- void swap(int* a,int* b) //使用地址传递,进行两个数交换
- {
- int t = *a;
- *a = *b;
- *b = t;
- }
- swap(&a,&b);
可见两个程序,第二个容易被误会成为是a与b地址的交换,可读性不高。切记:引用作为函数参数的时候不用进行初始化,当调用函数的时候才进行初始化。
3.常引用(const引用):const Type& name = var; const引用是使name引用具有只读属性,依然可以通过指针的方式改变name的值与常量有本质区别,其实常引用就是一个只读变量,有内存空间的(所以说在这个点上与下面的用法是异曲同工的),但是这个内存空间是与var的内存空间共用的(不是单独开辟的 在这一点上是与下面的用法有区别的地方)。原因就是因为它是一个引用,是不可能存在符号表中的,它实质是一个指针!
常引用的另一个用法:当使用常量,或者类型不一致的变量对const引用进行初始化时,c++编译器会为常量,或者类型不一致的变量的值分配空间,并将引用名作为这段空间的别名。也就是说使用常量对const引用初始化后将生成一个只读变量(与c语言中的只读变量意义相同)。
代码如下:
[cpp] view plaincopy
- int main()
- {
- char a = 10;
- const
int& b = a; - //const int& b = 9;
- printf("a %x\n",&a);
- printf("b %x\n",&b);
- return 0;
- }
4.引用的实质:
引用在c++中的内部实现其实就是一个常指针,Type& name <> Type * const name(是地址不能变的一个指针),其实每次给引用初始化的时候Type& name1= var1,就是在给Type * const name = &var的过程,也就是说这个地址不能变的特性决定了引用一旦初始化就不能再成为其他变量的别名。当你每次使用引用的时候,无论是作为左值还是右值的时候,其实var1就是*var。
注意:引用实质是指针,所有引用有自己的存储空间,所占的空间大小与指针相同。
5.返回值为引用:
a.引用为函数返回值:
若返回栈变量:不能成为其它引用的初始化值(int& b = fun();)、不能成为左值(即不能被赋值fun() = 10;)、但是可以当做右值使用(int a = fun(); 即把这个引用的值赋值给a变量)。
若返回静态变量或全局变量:可以成为其它引用的初始化值、即可以作为右值使用,也可以作为左值使用。代码如下:
[cpp] view plaincopy
- #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;
- }
b.c++中三目运算符的升级:c语言中的三目运算符返回的是变量的值,不能作为左值使用。c++中的三目运算符返回的是变量的引用,因此可以即当做右值也当做左值。
注意:三目运算符可能返回的值中如果有一个是常量,则不能作为左值使用,如:(a<b?1:b) = 10; 是会编译出错的。因为此时的三目运算符返回的是值,而不再是引用了。
课后思考:1.c++中为什么不允许定义引用数组,Type& name[num]: 引用数组理论上是一个数组,每一个元素都是一个引用。但是数组是一段连续内存空间,所有说这些引用的地址也应该是连续的。但是引用的地址也就是变量的地址,所以说引用数组是没有意义的,直接用原来的变量数组就可以了。换个角度再说,如果引用数组成了,那想访问这个数组,对引用取地址&&name,不就产生二义性了吗!所以c++里面没有引用数组。
2.如何定义一个数组的引用:其实数组的引用,就是一个数组元素类型的指针的引用,如:int* &d = p;
3.数组的引用与数组指针有什么区别:数组的引用,是一个数组元素类型的指针的引用。数组指针,是一个指向数组的指针。两者没有什么关系!!!
4.如何定义一个函数的引用:函数的引用,就是函数指针的引用,如:void (* &m)(void) = q;
5.函数的引用与函数的指针有什么区别:没什么区别,函数的引用就是函数指针的引用,两者是一个东西!!!
这里有一个小例子(请仔细查看备注):
[cpp] view plaincopy
- #include <stdio.h>
- void fun(void)
- {
- printf("hello fun\n");
- }
- int main(int argc, char *argv[])
- {
- int a[10]={1,2,3,4,5};
- int* p = a;
- printf("%d\n",p[2]);
- int* &d = p; //这里定义了一个数组的引用,即int*类型指针的引用
- printf("%d\n",d[2]); //此时d就是p
- void (*q)(void) = fun; //这里定义了一个函数指针
- void (* &m)(void) = q; //这里定义了一个void (* )(void)类型的函数指针的引用
- m();
- //q();
- int qq = 12;
- int (*nl)[10] = &a;
- int (*na)[10] = (int (*)[10])qq; //这里要注意这个强制类型转换的方式 (int (*)[10])为一个整体类型
- //要注意给一般引用初始化的时候 一定要是一个变量 而不能是一个常量或者只读变量或者类型不匹配的变量 这里&a是一个只读变量 所以应该用常引用
- int (* const &n)[10] = &a;//注意a是变量 &a是只读变量 也可以像上面的方式一样 用一个指针过度下
- int (* &pn2)[10] = (int (* &)[10])n;//同一个原理的强制类型转换 有const与没有const 完全是两个类型
- int (* &pn)[10] = nl; //这里定义了一个int(* )[10]类型的数组指针的引用
- int (* &pn1)[10] = pn; //因为 pn就是nl
- printf("%d\n",**n);
- //printf("%d\n",**pn);
- printf("Press enter to continue ...");
- getchar();
- return 0;
- }
对于这段代码,有几个要注意的问题:
1.int (*na)[10] = (int (*)[10])qq; //这里要注意这个强制类型转换的方式 (int (*)[10])为一个整体类型
2.c++是严格检查数据类型的,有const与无const完全是不同的两个类型
3.int (* const &n)[10] = &a; //要注意,给一般引用初始化的时候,一定要是一个变量而不能是一个常量或者只读变量或者类型不匹配的变量,这里&a是一个只读变量,所以应该用常引用 。
4.有数组引用(变量指针的引用 int* &a = b;),有函数引用(函数指针的引用 void (* &m)(void) = q;),有数组指针的引用(int (* &pn)[10] = nl;),有引用函数(一个函数的返回值是一个引用 int&fun(void)),但是切记 没有引用数组!!!
|
|