const 和 #define 的区别 ?
const 和指针一块出现的时候,到底谁不能修改 ?
const 和 volatile 能同时修饰一个变量吗 ?
const 在 c++ 中的作用 ?
1 const 和 #define 的区别
const 和 #define 的相同点:
(1) 常数
const 和 #define 定义的对象,我们都可以当做一个常数来使用。
const 和 #define 的区别:
(1) 生命周期
#define 在编译预处理的时候会做字符串替换,替换之后宏就不存在了,也就是说宏的声明周期只存在于源码和预处理阶段。在目标文件中,程序运行的时候,宏是不存在的。
const 修饰的变量,在编译阶段会做语法检查,如果有显式修改,会报编译错误。const 常量在运行时也是实际存在的,存在于栈或者数据段中。
(2) 作用域
#define 定义的宏,只要包含了这个头文件,都可以使用这个宏;在源文件中,宏定义之后的代码也都可以访问这个宏;即使宏定义函数内部,那么这个宏定义后边的其它函数也是可以使用这个宏的。
const 修饰的局部变量只能在函数内访问,const 修饰的全局变量和普通全局变量的作用域是相同的。
(3) #define 修饰的宏可以 redifine
如下代码,在函数 test2() 内部对HOURS_PER_DAY 进行了重定义。重定义虽然是在函数内部,但是对后边的函数都是生效的。在 test2() 中重定义,在函数 test3() 中也是生效的。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define HOURS_PER_DAY 24
void test1() {
printf("1, hours per day = %d\n", HOURS_PER_DAY);
}
void test2() {
#undef HOURS_PER_DAT
#define HOURS_PER_DAY 20
printf("2, hours per day = %d\n", HOURS_PER_DAY);
}
void test3() {
printf("3, hours per day = %d\n", HOURS_PER_DAY);
}
int main() {
test1();
test2();
test3();
return 0;
}
在 c++ 编码规范中,推荐使用 const 常量,而不是 #define。
从上边的分析也可以看出来,#define 是可以重定义的,并且 #define 定义的宏没有作用域限制。这就可能给代码带来不安全性。
而对于 const 来说,局部变量的作用域只是函数内;全局 const 变量也不能重新修改。 const 的作用域限制更加严格,所以使用 const 安全性会更高。
2 const 和指针一块出现的时候,到底谁不能修改
const 修饰的是谁,那么谁就不能修改。
int a = 10;
int * const pa1 = &a; // const 修饰的是 int * 类型,是指针,所以 pa1 不能修改,*pa1 可以修改
const int *pa2 = &a; // const 修饰的是 int 类型,所以 pa2 可以修改,*pa2 不能修改
const int * const pa3 = &a; // pa3 和 *pa3 均不能修改
如下是代码和编译结果。
a 是 const 常量,所以不能修改。
p1 中 const 修饰的是 int 常量,所以 *p1 不能修改,p1 可以修改。
p2 中 const 修饰的是指针,所以 p2 不能修改,*p2 可以修改。
p3 const 修饰了指针和 int 值,所以 p3 不能修改,*p3 也不能修改。
3 const 和 volatile 能不能修饰同一个变量
可以。
这两个关键字,从字面意思上是有一些矛盾的。const 是说这个变量是常数,不能修改;volatile 又说这个变量是易变的。
但是两者可以修饰同一个变量,侧重点并不矛盾。const 是提醒编译器,这个变量是常数,不能在代码中显式修改,我们使用它的目的就是这样的,不让在代码中显式修改;volatile 说变量是易变的,告诉编译器,不要做访问优化,每次访问的时候都要到内存中去取值。
在嵌入式开发中,常常用到 volatile 来修饰寄存器变量,寄存器变量不允许软件显式修改,但是硬件可以修改。这个时候就可以使用 const 和 volatile 进行修饰。
4 const 在 c++ 中的作用
(1) 左值引用指向右值
如下代码 ra2 是左值引用,左值引用不能直接指向一个右值。
ra3 是一个 const 左值引用,可以指向一个右值。
从运行结果可以看出来,const 左值引用可以指向右值,右值也是保存在栈里的, ra3 的地址和 a 的地址挨着。
从运行结果可以看出来,const 引用 ra3,通过地址间接修改也是可以修改的。
#include <iostream>
#include <string>
int main() {
int a = 10;
int &ra1 = a;
// int &ra2 = 10; // error: cannot bind non-const lvalue reference of type ‘int&’ to an rvalue of type ‘int’
const int &ra3 = 10;
std::cout << "a addr = " << &a << ", ra3 addr = " << &ra3 <<std::endl;
int *p = (int *)&ra3;
std::cout << "ra3 = " << ra3 << std::endl;
*p = 100;
std::cout << "ra3 = " <<ra3 << std::endl;
return 0;
}
(2) 修饰成员函数
用 const 修饰的成员函数,在函数中不能修改类的成员变量。
#include <iostream>
#include <string>
class Test {
public:
Test() {
a = 10;
std::cout << "Test()" << std::endl;
}
~Test() {
std::cout << "~Test()" << std::endl;
}
void Do() const {
a = 100;
}
void Do1(int data) {
a = 100;
data = 200;
}
int a;
};
int main() {
Test t;
t.Do();
return 0;
}
5 参考
linux 下 c 语言中的局部 const 常量可以修改
[c 语言] 实例观察 c 语言中 volatile 的作用