关注、星标公众号,直达精彩内容
const是constant的简写,用来定义常量,如果一个变量被const修饰,那么它的值就不能再被改变。
1、define是预编译指令,定义的宏是在预处理阶段展开的,而const是普通变量的定义,是只读变量,且是在编译运行阶段使用的。
2、define定义的是常量,define定义的宏在编译后消失了,它不占用内存,而const定义的常变量本质上仍然是一个变量,具有变量的基本属性,有类型、占用存储单元,除了不能作为数组的长度,用const定义的常变量具有宏的优点,而且使用更方便。
3、define定义的对象没有数据类型,编译器只能机械地进行字符替换,没有类型安全检查,即会出现“边际问题”或者是“括号问题”。而const定义的是变量,有数据类型。
1、可以用来修饰变量,修饰函数参数,修饰函数返回值,且被const修饰的东西,都受到强制保护,可以预防其它代码无意识的进行修改,从而提高了程序的健壮性(是指系统对于规范要求以外的输入能够判断这个输入不符合规范要求,并能有合理的处理方式。ps:即所谓高手写的程序不容易死);
2、使编译器保护那些不希望被修改的参数,防止无意代码的修改,减少bug;
3、增强代码的可读性,给读代码的人传递有用的信息,声明一个参数,是为了告诉用户这个参数的应用目的。
示例代码:
#include
#include
#include
const int a = 10; //const修饰的全局变量放在常量区
//1.const修饰的全局变量,即使语法通过,但是运行的时候会受到常量区的保护,段错误,运行失败
void test01()
{
//a = 100; //直接修改语法不通过
int *p = &a;
*p = 100; //间接修改语法通过,运行时产生段错误
printf("a %d\n",a);
}
//2.const修饰的局部变量
void test02()
{
const int b = 10; //分配到栈上
//b = 100; //直接修改语法不通过
//c语言下称为伪常量
int *p = &b;
*p = 100;
printf("b %d\n",*p); //间接修改成功
//int a[b]; 伪常量是不可以初始化数组的
}
//3.字符串常量
void test03()
{
char *p1 = "hello world";
char *p2 = "hello world";
char *p3 = "hello world";
printf("%s\n",p1);
printf("%s\n", p2);
printf("%s\n", p3);
printf("%s\n",&"hello world"); //四个输出的结果一样
//p1[0] = 'z'; //不允许修改字符串常量
printf("p1[0] %c\n",p1[0]); //可以输出
}
int main()
{
// test01();
// test02();
test03();
return 0;
}
const修饰的普通变量:定义的时候就要给它赋初值,之后哪怕是赋相同值都不行。const修饰的局部变量还是变量,直接修改编译器报错,可以间接修改,存放在栈区,代码块结束时释放。
const修饰全局变量:直接修改编译器报错,间接修改编译器也许会通过,但运行时会报错(段错误)。const修饰的全局变量存放在全局(静态)存储区,编译期最初将其保存在符号表中,第一次使用时为其分配内存,在程序结束时释放。
const修饰字符串常量:字符串常量位于文字常量区(也有文章归类于代码区),本身就不允许被修改,如果没有const的修饰,我们可能会在后面有意无意的修改字符串常量,这样会导致对只读内存区域的赋值,然后程序会立刻异常终止。有了const,这个错误就能在程序被编译的时候就立即检查出来,这就是const的好处。让逻辑错误在编译期被发现。
常量指针和指针常量傻傻分不清楚,以下方法帮助你来区分二者:区分常量指针和指针常量的关键就在于星号的位置,我们以星号为分界线。
如果我们将星号读作‘指针’,将const读作‘常量’的话,内容正好符合。
代码示例:
//第一种,常量指针
const int *p1; //p本身不是const的,而p指向的变量是const
int const *p2; //p本身不是const的,而p指向的变量是const
#include
int main()
{
int a = 5;
int b = 20;
const int *p = &a;
// *p = 100; //编译器报错
p = &b; //完全可以
printf("%d\n",*p); //间接修改成功
return 0;
}
需要注意的是以下两点:
1、常量指针说的是不能通过这个指针改变变量的值,但是还是可以通过其他的方式来改变变量的值的。
2、常量指针指向的值不能改变,但是这并不是意味着指针本身不能改变,常量指针可以指向其他的地址。
//第二种,指针常量
int* const p3; //p本身是const的,而p指向的变量不是const
#include
int main()
{
int a = 5;
int b = 20;
int *p = &a;
int* const n = &a;
// n = &b; //error: assignment of read-only variable ‘n’
*p = 8;
printf("%d\n",a);
return 0;
}
指针常量是指指针本身是个常量,不能在指向其他的地址,需要注意的是,指针常量指向的地址不能改变,但是地址中保存的数值是可以改变的,可以通过其他指向该地址的指针来修改。
//第三种
const int* const p4; //p本身是const的,而p指向的变量也是const
是以上两种的结合,指针指向的位置不能改变并且也不能通过这个指针改变变量的值,但是依然可以通过其他的普通指针改变变量的值。
const修饰参数是为了防止函数体内可能会修改参数原始对象。因此,有三种情况可讨论:
1、函数参数为值传递:
值传递(pass-by-value)是传递一份参数的拷贝给函数,因此不论函数体代码如何运行,也只会修改拷贝而无法修改原始对象,这种情况不需要将参数声明为const。例如:void func(int x)不用写成void func(const int x)
2、函数参数为指针:
指针传递(pass-by-pointer)只会进行浅拷贝,拷贝一份指针给函数,而不会拷贝一份原始对象。根据上面对指针常量、常量指针等讨论,同样分为三种情况:
2.1 防止修改指针指向的内容
典型C库函数:char *strcpy(char *dest, const char *src);
2.2 防止修改指针指向的地址
void swap ( int * const p1 , int * const p2 );指针p1和指针p2指向的地址都不能修改。
2.3 防止修改指针指向的内容和地址
1、如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const 修饰没有任何价值。例如把函数int GetInt(void) 写成const int GetInt(void)是没有意义的。
2、如果给以“指针传递”方式的函数返回值加 const 修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const 修饰的同类型指针。
const char * GetString(void);
char *str = GetString(); //error: conflicting types for ‘str’
const char *str = GetString(); //这种用法才是正确的