1.c/c++中的const
1 const概述
const 修饰的对象为一个常量,不能被改变
2 c/c++中const的区别
c语言中的const1 const修饰的局部变量,存在栈区,虽然不能通过const修饰的变量去修改栈区内容,但是可以 通过地址去修改
const修饰的全局变量是保存在常量区,不能通过变量名去修改.也不能通过地址去修改。
const修饰的全局变量,如果其他文件想使用,直接extern声明外部可用即可。
c中的:
在main.c中
#include <stdio.h> const int b = 10;//const修饰的全局变量保存在常量区 void test03() { extern const int num;//声明num是外部可用的 printf("num=%d\n",num); } //const修饰的全局变量 void test02() { //b = 100; int *p = &b; *p = 100;//错误的 不能修改常量区的内容 printf("b=%d\n",b); } //const修饰的局部变量 void test01() { //在c语言中const修饰的变量保存在栈区 const int a = 10; //a = 100; int *p = &a; *p = 100; printf("a=%d\n",a); } int main() { test03(); return 0; }
onst int num = 1;
c++中的const:
1 const修饰的局部变量赋值常量时,局部变量保存在符号表中,修改不了,是一个常量
2 const修饰的全局变量保存在常量区,不能被修改
3 const修饰的全局变量默认是内部链接属性,加上extern修饰变成外部链接属性
mian.cpp
#include <iostream> using namespace std; const int b = 1; void test03() { extern const int num; cout << num << endl; } void test02() { //const修饰的全局变量存在常量区 int *p = (int *)&b; *p = 100;//错误的 cout << b<< endl; } //c++中const修饰的局部变量 void test01() { //c++中const修饰的局部变量存在符号表中 const int a = 10; //a = 100; //对const修饰的局部变量取地址 编译器会产生一个临时变量来保存a的地址 // int tmp = a; int *p = &tmp int *p = (int *)&a; cout << a << endl; } int main() { test03(); return 0; }
extern const int num = 1;// const修饰的全局变量默认是内部链接属性.
c/c++中的const异同
相同的点:
c和c++中的const修饰的全局变量都是保存在常量区,不能被修改
不同的点:
c语言中const修饰的局部变量赋值为常量时,,局部变量保存在栈区,可以被指针修
改
c++中,const修饰的局部变量赋值为常量时,局部变量保存符号表中,不能被修改
c语言中const修饰的全局变量默认是外部链接属性
c++语言中const修饰的全局变量默认是内部链接属性
c++中const修饰的变量,分配内存情况
const修饰的全局变量在常量区分配了内存
对const修饰的局部变量赋值为常量时,对其取地址,会在栈区分配临时的内存空间
const修饰的局部变量赋值为变量时,局部变量保存在栈区
const修饰的局部变量时一个自定义变量,也是在栈区分配内存
只有一种情况,const'修饰的局部变量被赋值为常量时,这个局部变量保存在符号表中,
符号表后中的变量,编译器直接访问,不会为其开辟空间。
#include <iostream> using namespace std; const int a = 1;//const修饰的全局变量在常量区分配了内存 void test01() { const int a = 10;//const修饰的局部变量赋值为常量没有分配内存,存在符号化表中 int *p = (int *)&a;//对const修饰的局部变量赋值为常量的变量取地址 会分配一个临 时的空间 int tmp =a *p =&tmp } void test02() { int b = 2; //const修饰的局部变量赋值变量时 局部变量存在栈区 const int a = b; int *p = (int *)&a; *p = 100; cout << a << endl; } struct stu { int a; int b; }; void test03() { //const修饰的变量为自定义变量时,保存在栈区 const struct stu obj = {1,2}; struct stu *p = (struct stu *)&obj; p‐>a = 3; p‐>b = 4; cout << obj.a << " " << obj.b << endl; } int main() { test03(); return 0; }
尽量以const替换define
有两点原因:
1. const修饰的全局变量或const修饰的局部变量赋值为常量,是有类型的,而define的
宏没有 类型
2. const修饰的全局变量或const修饰的局部变量赋值为常量有作用域的,而define的
宏没有作用域
#include <iostream> using namespace std; namespace A { const int max = 1024; const short max1 = 1024; #define MAX 1024 } // 宏没有作用域 宏没有类型(int) void fun(int a) { } void fun(short a) { } void test01() { cout << A::max << endl; cout << MAX << endl; fun(MAX);//void fun(int a) fun(A::max);//void fun(int a) fun(A::max1);//void fun(short a) } int main() 30 { 31 32 return 0; 33 }
宏定义的变量是做替换的,并且是全局的没有作用限制,但是const修饰的变量为常量是有范围的,范围即为原来变量的范围。
2.引用
引用的作用为给变量取别名。
引用的基本用法 :
原类型 &别名 = 旧名 这里的&不是取地址符,而是引用。
在这里取别名后,两者的地址是一样的,且对a操作,b也会跟着操作。
注意事项:
引用一旦初始化,不能更改引用的指向
引用定义时必须初始化
不能引用NULL
引用可以引用任意类型包括数组
&在等号的左边是引用,在等号的右边是取地址
#include <iostream> using namespace std; void test01() { int a = 10; //引用一旦初始化之后不能改变引用的标识 int &b = a; b = 100; cout << a << endl; int c = 1; //b = c; 代表把c的值赋值给b 不是给c取别名为b //int &d; 引用定义时必须初始化 } void test02() { int a[5] = { 1,2,3,4,5 }; //int(&arr)[5] = a; typedef int ARR[5]; //type & 别名 = 旧名 ARR & arr = a; for (int i = 0; i < 5; i++) { cout << arr[i] << " "; } cout << endl; } int main() { test02(); return 0; }
变量的引用并不会为他开辟空间,只想相当于多了个名字。
函数的引用:
注意:
引用可以作为函数的形参
函数调用时 就是 &实参=形参 实参的改变影响形参,我们不用指针也可以做到这一点。
不能返回局部变量的引用
//指针方式交换 void swap(int *x, int *y) { int tmp = *x; *x = *y; *y = tmp; } void test01() { int a = 10; int b = 20; swap(&a,&b); cout << a << " " << b << endl; } //引用交换 void swap_ref(int &x, int &y)// int &x =a, int &y =b) { int tmp = x; x = y; y = tmp; } int main() { int a = 10; int b = 20; swap_ref(a, b); swap(&a,&b); }
引用的本质
引用的本质是一个指针常量
type &b = a; 编译器底层这么实现的:
type *const b = &a;
#include <iostream> using namespace std; void test01() { int a = 10; int &b = a;//编译器优化 int* const b = &a //指针常量 不能改变指针变量的指向 // b =0x100; b = 1000;// *b =1000 } void fun(int *&q)//int *&q = p ==> 编译器 int * const q =&p { } void test02() { int *p = NULL; fun(p); }
指针的引用
套用引用公式: type &q = p
假设:
type为指针类型
void fun ( int * & q ) // int* &q = p
{
}
void test ()
{
int * p = NULL ;
fun ( p );
}
5 常量引用
const type &p = q;
常量引用代表不能通过引用去修改引用标识的那块空间
#include <iostream> using namespace std; void test01() { int a = 10; // const修饰的是引用& 不能通过引用去修改引用的这块空间的内容 const int &b = a; //b = 1000;//err } void test02() { //int &b = 100;//不能引用常量 const int &b = 1;//int tmp =1 ,const int &b= tmp } int main() { return 0; }
内联函数
内联函数的基本概念
在 c++ 中,预定义宏的概念是用内联函数来实现的,而内联函数本身也是一个真正的函数。
内联函数具有普通函数的所有行为。唯一不同之处在于 内联函数会在适当的地方像预定义宏
一样展开,所以不需要函数调用的开销。 因此应该不使用宏,使用内联函数。 在普通函数 ( 非成员函数 ) 函数前面加上 inline 关键字使之成为内联函数。
但是必须注意必须
函数体和声明结合在一起,否则编译器将它作为普通函数来对待。
inline void func(int a);
以上写法没有任何效果,仅仅是声明函数,应该如下方式来做 :
inline int func(int a){return ++;}
注意 : 编译器将会检查函数参数列表使用是否正确,并返回值 ( 进行必要的转换 ) 。这些事预
处理器无法完成的。
但是函数调用就会占用空间:,但是内联函数相对于普通函数的优势只是省去了函数调用时候的压
栈,跳转,返回的开销。我们可以理解为内联函数是以空间换时间。
内联函数就是继承了宏函数的高效,并且不会出错,还可以当成类的成员函数用
2 宏函数和内联函数的区别
宏函数的替换是发生在 预处理阶段
内联函数的替换是发生在 编译阶段
宏函数容易出错,内联函数不会
内联函数和宏函数一样,都省去了调用函数的开销。
举个例子
//宏函数 #define MYCOMPARE(a,b) (a)<(b)?(a):(b) //内联函数 inline int mycpmpare(int a, int b) { return a < b ? a : b; } void test02() { int a = 1; int b = 5; //int c = MYCOMPARE(++a, b);// ++a<b?++a:b int c = mycpmpare(++a, b); cout << c << endl; }
类的成员函数默认编译器会将它做成内联函数
class Person{ public: Person(){ cout << "构造函数!" << endl; } void PrintPerson(){ cout << "输出Person!" << endl; } }
类里的成员函数默认为内联函数。
内联仅仅只是给编译器一个建议,编译器不一定会接受这种建议 ,如果你没有将函数声明为
内联函数,那么编译器也可能将此函数做内联编译。一个好的编译器将会内联小的、简单的
函数