1.1 三目运算符 和bool类型
1.1.1 bool类型
- C++中的bool类型
- C++在C的基本类型上增加了bool
- C++ 中的bool 可取的指 只有true 和false
- 理论上bool只占用一个字节
注意 :true 代表真值,编译器内部用1表示
注意 :false 代表假值,编译器内部用0表示
1.1.2 bool类型 实例
- bool类型只有true(非0 ) 和false(0)两个值
- C++编译器将非0值转换为true ,0值转换为false;
- 实例1
只需要记住一点:bool类型的取值就是0 或1
int main()
{
bool b =false;
int a = b;
printf("sizeof(b)=%d\n",sizeof(b)); //1
printf("a =%d,b=%d\n",a,b); //0 0
b =10 ;
a =b;
printf("a =%d,b=%d\n",a,b); // 1 1
b =-5 ;
a =b ;
printf("a =%d,b=%d\n",a,b); // 1 1
a =10;
b =a;
printf("a =%d,b=%d\n",a,b); // 10 1
return 0;
}
1.1.3 bool类型是C++中的基本数据类型
- 可以定义bool类型的全局变量
- 可以定义bool类型的常量
- 可以定义bool类型的指针
- 可以定义bool类型数组
1.1.4 三目运算符
int main()
{
int a =1;
int b =2;
(a < b ? a:b) =3; //在c++中是成立的
printf("a= %d\n",a);
printf("b= %d\n",b);
return 0;
}
1.1.5三目运算符在C语言和C++中的比较
- 在C语言中三目运算符只能作为右值使用
- 在C++中三目运算符既可以作为左值,又可以作为右值。
注意:三目运算符可能返回值中如果有一个是常量值,则不能作为左值使用
1.1.6 C++ 对三目运算符中升级
C++对三目运算符做了怎样的升级,这种升级的意义是什么?
- 当三目运算符的可能返回都是变量时,返回的是变量引用
- 当三目运算符的可能取值中有常量时,返回的是值。
int a = 1;
int b =2;
(a<b ? a:b) = 3; //返回a或b的引用,可以作为左值
(a<b ? 1:b)=4; //error ,返回1或b不能做左值
1.2 引用
1.2.1 问题引出
变量是一段连续空间的别名,那么变量只能有一个别名吗?
1.2.2 引用
C++中提出了引用的概念。
- 引用可以看做已经定义了变量的别名
- 引用语法: Type & name =var;
int a=10;
int & b= a;
b=5; // 操作b就是操作a
注意: 普通引用定义时必须用同类型的变量进行初始化
1.2.3 引用的错误示例
//error引用1 使用不同类型的引用
int a =10;
float &b =a;
//error引用2 定义引用,但是不初始化
int a =10;
int &b ;
//error引用3 对常用进行引用
int & b =10 ;
1.3引用的本质和意义
1.3.1 引用的意义
引用的意义:
(1)引用作为一个变量的别名,在一些场合可以替代指针
(2)引用提高了代码的可读性和实用性
(3)注意:引用作为函数的参数不需要初始化,在调用函数的时候才进行初始化。
注意:普通引用是对变量的引用,如果调用这个函数的时候参数是常数,就会出错。错误案例代码如下
void swap1(int & a,int &b)
{
int c =a;
a=b;
b =c;
}
int main()
{
swap1(1,2) ; //error
// 不能直接将参数赋值为常数。这样就不能进行引用了。
return 0;
}
1.3.2 特殊的引用
(1)const 对变量的引用
const 对变量引用,那么这个变量就拥有只读属性,不能直接修改变量的值,可以间接修改变量的值。
示例程序如下:
int main()
{
int a = 10;
const int &b =a;
// b =3; //error
int *p =(int*) &b;
*p= 3;
printf("a= %d\n",a) ; //3
printf("b= %d\n",b); //3
return 0;
}
(2)const 对常量的引用
const 对常量引用,const int &b =1; C++编译器会对这个常量值 分配内存,并将引用名作为这段空间的别名。
const int & b =1;
int * p =(int *) &b;
b =5; //error ,只读变量
*p =5; //ok 修改变量 a 的值
结论: 使用常量对const 引用初始化化后将生成一个只读变量。
在C++中要对一个变量定义只读属性,只需要给这个变量定义const引用。
1.3.3 引用有自己的存储空间吗?
示例代码:
struct student
{
char & a;
char & b;
};
int main()
{
printf("student :%d\n",sizeof(student)); //8
printf("sizeof(char &) :%d\n",sizeof(char&)); //1
return 0;
}
1.3.4 引用本质
注意
- c++ 编译器在编译过程中用 指针常量作为引用的内部实现,因此引用所占的内存空间与指针相同。
- 从使用角度看,引用只是一个别名,c++为了实用性而隐藏了引用的存储空间这一细节
struct TRef
{
char* before;
char& ref;
char* after;
};
int main(int argc, char* argv[])
{
char a = 'a';
char& b = a;
char c = 'c';
TRef r = {&a, b, &c};
printf("sizeof(r) = %d\n", sizeof(r)); //12
printf("sizeof(r.before) = %d\n", sizeof(r.before)); // 4
printf("sizeof(r.after) = %d\n", sizeof(r.after)); //4
printf("&r.before = %p\n", &r.before); //
printf("&r.after = %p\n", &r.after); //
return 0;
}
- 反汇编代码
1.3.5 函数返回值是引用
不能返回局部变量的引用,但可以返回静态的局部变量。
注:利用引用可以避免重新改变变量的值。(在函数参数中经常用到)
1.3.6 引用 的意义
C++中的引用旨在大多数情况下代替指针
- 功能性 : 可以满足多数需要使用指针的场合
- 安全性 : 可以避免由于制作操作不当带来的内存错误
- 操作性 : 简单易用,有不失功能强大
//error
int& func1() //实际是为了返回指针 int * const ,不要返回局部变量的引用
{
int d =0;
printf("func1: d =%d\n",d);
return d;
}
//ok
int& func2()
{
static int s =0;
printf("func2: s =%d\n",s);
return s;
}
int main()
{
int & md =func1();
int & ms = func2();
printf("\n");
printf("main: md =%d\n",md);
printf("main: ms =%d\n",ms);
printf("\n");
md =10;
ms =11;
func1();
func2();
printf("\n");
printf("main: md =%d\n",md);
printf("main: ms =%d\n",ms);
printf("\n");
}
结果:
1.4
1.4.1const 什么时候是常量,什么时候是只读变量?
- 只有用字面值常量初始化后的const常量才是真正意义上的常量
- 被变量初始化后的const常量是只读的变量,不会进入符号表中
- 被volatile修饰的const 常量也是只读变量,const在这里仅仅表示是只读的。
注意:在编译期间不能确定初始值的const常量都是只读变量
const 引用只不过是让变量拥有了只读属性
1.4.2
为了兼容C语言语法,在C++中,定义const 常量的时候,也会分配空间,只不过我们不使用这个内存空间。当遇到&
的时候,才会使用这个 空间。
代码如下:
#include <stdio.h>
int main()
{
const int x = 1;
const int& rx = x; //分配内存
int& nrx = const_cast<int&>(rx); //
nrx = 5;
printf("x = %d\n", x); // 1 从符号表里取值
printf("rx = %d\n", rx); // 5
printf("nrx = %d\n", nrx); // 5
printf("&x = %p\n", &x); // 三个地址相同
printf("&rx = %p\n", &rx); //
printf("&nrx = %p\n", &nrx); //
cout<<"volatile const int y = 2;"<<endl;
volatile const int y = 2;
int* p = const_cast<int*>(&y);
*p = 6;
printf("y = %d\n", y); // 6
printf("p = %p\n", p); //
const int z = y;
p = const_cast<int*>(&z);
*p = 7;
printf("z = %d\n", z); //7
printf("p = %p\n", p);
cout<<"============================"<<endl;
char c = 'c';
char& rc = c;
const int& trc = c;
rc = 'a';
printf("c = %c\n", c); //a
printf("rc = %c\n", rc); // a
printf("trc = %c\n", trc); //c
return 0;
}
1.4.3 引用和指针有什么关系?
引用和指针有什么关系,如何理解“引用的本质就是指针常量”。
-
指针是一个变量
(1)值为一个内存地址,不需要初始化,可以保存不同的地址
(2)通过指针可以访问对应内存地址中的值
(3)指针可以被const 修饰为常量或者只读变量 -
引用只是一个变量的新名字
(1) 对引用的操作(赋值,取地址)都会传到代表的变量上
(2) const引用使其代表的变量具有只读属性
(3) 引用必须在定义时初始化,之后无法代表其他变量 -
从使用C++ 语言角度看
(1)引用和指针没有任何区别
(2)引用是变量的新名字,操作引用就是操作对应的变量 -
从 C++编译器角度看
(1)为了支持新概念“引用”必须要一个有效的解决方案
(2)在编译器内部,使用指针常量实现引用
(3)因此,引用在定义时候必须初始化
。 -
工程开发中:
(1)进行C++编程时,直接站在使用的角度看待引用,引用和指针毫无关系,引用就是变量的别名
(2)对C++代码进行调试分析时,一些特殊情况,可以考虑站在C++编译器的角度看到问题。
1.4.4 c++中不支持数组的引用(重要的事情说三遍)
//c++ 天生就要支持C语言的特性,而C语言中数组是一段连续的内存空间,C++需要兼容这个特性,而数据的引用就破坏了这个特性所以C++中不存在数组的引用。
#include <stdio.h>
int a = 1;
struct SV
{
int& x;
int& y;
int& z;
};
int main()
{
int b = 2;
int* pc = new int(3);
SV sv = {a, b, *pc};
int& array[] = {a, b, *pc}; // &array[1] - &array[0] = ? Expected ==> 4 error
printf("&sv.x = %p\n", &sv.x);
printf("&sv.y = %p\n", &sv.y);
printf("&sv.z = %p\n", &sv.z);
delete pc;
return 0;
}
1.5 引用的各种情况总结
1.5.1 普通引用
普通引用分为三种情况:
- 普通对变量引用
int main(int argc, char* argv[])
{
int a= 10;
int &b =a;
int * p = &b;
*p =20;
printf("a = %d\n",a); //20
printf("b = %d\n",b); //20
return 0;
}
- 普通引用做函数参数
void swap(int &a ,int &b)
{
int c =a;
a= b;
b =c;
}
int main(int argc, char* argv[])
{
int a= 10;
int b =20;
swap(a,b);
printf("a = %d\n",a); //20
printf("b = %d\n",b); //10
return 0;
}
- 普通引用做返回值
#include <stdio.h>
int& getNum()
{
static int a =10; // 这里必须是 static, 必须是具有全局作用周期的变量,如果去掉static就会报错
return a;
}
int main(int argc, char* argv[])
{
int a = getNum();
printf("a = %d\n",a); //10
return 0;
}
- 普通引用:对指针的引用
#include <stdio.h>
#include <stdlib.h>
void test(int * &p )
{
p =(int*)malloc(sizeof(int));
*p =10;
}
int main(int argc, char* argv[])
{
int * p =NULL;
test(p);
printf("*p = %d\n",*p);
printf("p = %p\n",p);
return 0;
}
结果:
*p = 10
p = 0x1423010
1.5.2 const 引用
const引用分为以下几种情况:
- const 引用对变量引用
int main(int argc, char* argv[])
{
const int a =5;
const int & b =a;
int * p =const_cast<int*>(&b);
*p =10;
printf("a = %d\n",a);
printf("b = %d\n",b);
printf("*p = %d\n",*p);
printf("&a = %p\n",&a);
printf("&b = %p\n",&b);
printf("p = %p\n", p);
return 0;
}
结果如下:
a = 10
b = 10
*p = 10
&a = 0x7ffc9838e2d4
&b = 0x7ffc9838e2d4
p = 0x7ffc9838e2d4
- const 引用对常量引用
int main(int argc, char* argv[])
{
const int a =5;
const int & b =a;
int * p =const_cast<int*>(&b);
*p =10;
printf("a = %d\n",a);
printf("b = %d\n",b);
printf("*p = %d\n",*p);
printf("&a = %p\n",&a);
printf("&b = %p\n",&b);
printf("p = %p\n", p);
return 0;
}
结果如下:
a = 5
b = 10
*p = 10
&a = 0x7ffee63bbb74
&b = 0x7ffee63bbb74
p = 0x7ffee63bbb74
- const 引用做函数参数
void add(const int& a,const int & b)
{
printf("a+b = %d\n", a+b);
}
// const 引用做函数参数,传值的时候可以是字面值常量
// 如果这里是普通引用,那么传入字面值常量就会报错,因为普通引用的对变量的引用。
int main(int argc, char* argv[])
{
add(1,2);
return 0;
}
- const 引用做返回值
注意下面这个程序是错的,因为返回了局部变量的const引用
const int & test()
{
int a =10;
return a;
}
int main(int argc, char* argv[])
{
const int a =test();
printf("a =%d\n",a);
return 0;
}
1.5.3 遗忘的知识点
const 引用的类型和初始化变量的 类型
- 相同 初始化变量为只读变量
- 不同 形成一个新的只读变量
int main(int argc, char* argv[])
{
char c ='c';
const char &b = c;
const int & a =c;
printf("a =%c\n",a);
printf("b =%c\n",b);
printf("c =%c\n",c);
int *p =const_cast<int*>(&a);
*p ='A';
printf("a =%c\n",a);
printf("b =%c\n",b);
printf("c =%c\n",c);
return 0;
}
结果
a =c
b =c
c =c
a =A
b =c
c =c
1.5.4补充1
const指针的引用没有搞懂失败原因
int main()
{
int * p = (int*)malloc(sizeof(int));
const int *& tmp1 = p; //error
int *& tmp2 = p;
int a =10;
const int & b =a;
return 0;
}
1.5.5 补充2 结构体引用和const 结构体引用
- 结构体引用
struct Teacher
{
char name[64];
int age;
};
void func1(Teacher &t)
{
strcpy(t.name,"zhangsan");
t.age = 32;
}
int main()
{
Teacher t1;
t1.age =20;
strcpy(t1.name,"lili");
printf("================before func1==========================\n");
printf("main t1.name =%s\n",t1.name);
printf("main t1.age=%d\n",t1.age);
printf("================after func1==========================\n");
func1(t1);
printf("main t1.name =%s\n",t1.name);
printf("main t1.age=%d\n",t1.age);
return 0;
}
- const 结构体引用
//error程序
//不能改变只读Teacher的值
void func2(const Teacher &t)
{
strcpy(t.name,"zhangsan");
t.age = 32;
}
void func2(const Teacher &t) const引用做函数参数在后面的类和对象中经常用到
参考一: 狄泰软件学院课程。