浅析C++const

1.值替代

在使用宏#define进行常量替换时,仅仅只是文本替代,不进行类型检查。

#define  BUFFSIZE  100
int  buf[BUFFSIZE]
进行预处理

kernel@Ubuntu:~/Desktop/const$ cpp const.cpp const.i
kernel@Ubuntu:~/Desktop/const$ cat const.i
# 1 "const.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "const.cpp"


int buf[100];

BUFFSIZE只是一个名字,它在预处理的时候存在,在编译的时候不对该符号进行维护,也就是BUFFSIZE符号消失了。因此它不占用存储空间,仅仅在预处理期间就进行值替代,目的是为使用它的所有编译单元提供一个值。但是由于在编译的时候就是一个单纯的值,而且没有类型,如果出现不可思议的数,并不清楚这个数来自哪里。因此使用C++ const把值替代带到编译器阶段来消除这个问题,并不在预处理器阶段就进行值替代。

const in buffsize = 100;

这样编译的时候,编译器需要知道这个值在何处使用,同时编译器还可以执行常量折叠,把一个复杂的表达式进行简单化处理。在编译器看作为一个常量,把buffsize当作一个符号,并存放在符号表中,不给buffsize分配空间。

常量折叠

int main(void) {
    const int buffsize = 100;
    int i = buffsize;
    int j = 99;
    int k = j;
}<strong>
</strong>

对上面程序const.cpp进行编译链接生成可执行目标文件const,对const进行反汇编。

00000000004004ed <main>:
int main(void) {
  4004ed:	55                   	push   %rbp
  4004ee:	48 89 e5             	mov    %rsp,%rbp

    const int buffsize = 100;
  4004f1:	c7 45 f0 64 00 00 00 	movl   $0x64,-0x10(%rbp)
    int i = buffsize;
  4004f8:	c7 45 f4 64 00 00 00 	movl   $0x64,-0xc(%rbp)
    int j = 99;
  4004ff:	c7 45 f8 63 00 00 00 	movl   $0x63,-0x8(%rbp)
    int k = j;
  400506:	8b 45 f8             	mov    -0x8(%rbp),%eax
  400509:	89 45 fc             	mov    %eax,-0x4(%rbp)
}

const运行时,在对变量 i 进行赋值操作直接进行常量折叠,相当于直接把一个常量放入到一个i的栈空间。而非const的变量则需要进行从栈中取j的值,再放入寄存器eax中,从eax寄存器取出其值放入到i所在的栈空间。

2 头文件中的const

如果const为全局变量,则在编译后,将const修饰的符号的存放在.symtab的符号表中,但是const默认为内部链接,该符号为本地符号,不能被其他目标模块所引用,不会造成链接时的冲突。如需要引用加上extern进行引用。

不使用extern进行外部链接,buffsize的符号条目如下

6: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    4 _ZL8buffsize

可见buffsize是一个本地符号,不可以被其他模块引用。

使用extern进行外部链接

extern const int buffsize = 100;
9: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    4 buffsize

此时buffsize是一个全局符号,可以被其他模块引用。

C++ 编译器一般不为const成员分配存储空间,但是当使用了extern修饰const成员,其他模块会引用这个成员,所以会强制分配存储空间,但用extern声明const会阻止常量折叠。在对const成员进行取地址的时候,也会在分配空间。

3. C与C++ const的区别

(1)在C++中可以有如下定义:

const int buffsize = 100;
int buf[buffsize] = {0};

允许将一个定义为常量的变量当作数组的大小。C++中const在编译阶段被看作为一个常量,并不分配空间,并将其值存放在符号表中,在适当的时候进行常量折叠。

但是在C中,如果按照上述的定义会出现如下错误:

const.c:7:5: error: variable-sized object may not be initialized
     int buf[buffsize] = {0};

因为在C中,const修饰的是一个不可改变的变量,会在运行时分配内存。所以编译器不知道编译时buffsize的值。

(2)C中可以有有如下定义:

const int buffsize;

C编译器把它看做为一个声明,这样在C++的做法是不对的。在C中const为外部链接,声明上面的形式,如果外部有一个定义,则该模块可以引用。

而C++默认为内部链接,如果要进行如上声明,需要加上extern。

extern const int buffsize;

(3)C++中,编译器会把const看成一个符号,放入到符号表,通常不会给const分配空间,执行常量折叠。如果定义为全局变量也不会分配空间。而C中,一个const总是要占用内存空间。当const修饰的为全局符号且外部模块需要引用本地的const时,const默认为内部链接,需要加上extern,并且此时编译器强制为const分配空间,由于是const定义,可以进行常量折叠。对const进行&操作,也会为const分配空间。

#include <stdio.h>
int main(void) {
    const int i = 100;
    int * p = (int *)&i;
    *p = 99;
    printf("%p, %p, %d, %d\n", &i, p, i, *p);
}

分别使用gcc和g++进行编译。

kernel@Ubuntu:~/Desktop/const$ g++ -g const.c -o const
kernel@Ubuntu:~/Desktop/const$ ./const 
0x7ffd5b36fa74, 0x7ffd5b36fa74, 100, 99
kernel@Ubuntu:~/Desktop/const$ gcc -g const.c -o const
kernel@Ubuntu:~/Desktop/const$ ./const 
0x7ffd74dd4154, 0x7ffd74dd4154, 99, 99

当使用g++编译的时候,虽然p和i的地址相同,但是i和*p的值不一样。因为在编译阶段,没有对const分配空间,对const进行了常量折叠,将i用100来代替,也就是说i自身变为了100,并不是其空间存放了100。虽然用&i分配了一个内存空间,使用指针对其指向的内容进行了赋值。但是i的值不会变,内存空间里的值会改变。

因为C总是要为const分配一个空间,所以指针p指向的空间,就是存储i值的空间,因此在指针更改其指向空间的值的时候,i的值也被改变。

注:在用const int *对int * p进行赋值时,要将const int *强制转化为int *,否则c++编译会报错,而c会报警告。

kernel@Ubuntu:~/Desktop/const$ gcc -g const.c -o const
const.c: In function ‘main’:
const.c:7:15: warning: initialization discards ‘const’ qualifier from pointer target type [enabled by default]
     int * p = &i;	
kernel@Ubuntu:~/Desktop/const$ g++ -g const.c -o const
const.c: In function ‘int main()’:
const.c:7:16: error: invalid conversion from ‘const int*’ to ‘int*’ [-fpermissive]
     int * p = &i;

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值