【八】疑难问题小结(一 -- 七篇)
来源:互联网 发布:人工智能三大定律 编辑:程序博客网 时间:2024/05/02 03:06
目录
- 目录
- 1const和引用的疑惑
- 什么是符号表符号表存储在程序中的哪个地方
- 怎样定义const常量才会使用符号表
- 2引用与指针的疑惑
- 指针与引用的区别
- 如何理解引用的本质就是指针常量
- 3重载与默认类型转换
- C编译器对字面量的处理方式
- 当使用字面量对变量进行初始化或赋值时
- 深入理解重载规则
- 4C方式编译的疑惑
- 深入理解extern C
- 1const和引用的疑惑
1、const和引用的疑惑
关于const和引用的疑难问题,在前面的 第三篇 和 第七篇 文章中已经解释过了,前面多次提到符号表,这里重点解释以下符号表!
什么是符号表?符号表存储在程序中的哪个地方?
- 符号表是编译器在编译过程中产生的关于源程序中语法符号的数据结构
- 如常量表、变量名表、数组名表、函数名表等等
- 符号表是编译器自用的内部数据结构
- 符号表不会进入最终产生的可执行程序中
怎样定义const常量才会使用符号表?
- 只有用字面量初始化的const常量才会进入符号表
- 对const常量进行引用会导致编译器为其分配空间
- 虽然const常量被分配了空间,但是这个空间中的值不会被使用
- 使用其它变量初始化的const常量仍然是只读变量
- 被volatile修饰的const常量不会进入符号表
- 会退化为只读变量,每次访问都从内存中取值
- const引用的类型与初始化变量的类型
- 相同:从该引用变量的角度来说,会使初始化变量成为只读变量,即无法通过该引用变量来改变初始化变量的值
- 不同:生成一个新的只读变量,其初始值与初始化变量相同
总之,在编译期间不能直接确定初始值的const量,都被作为只读变量处理。
2、引用与指针的疑惑
指针与引用的区别
- 指针是一个变量,其值为一个内存地址,通过指针可以访问对应内存地址中的值
- 引用只是一个变量的新名字,所有对引用的操作(赋值,取地址等)都会传递到其引用的变量上
- 指针可以被const修饰成为常量或者只读变量
- const引用使其引用的变量具有只读属性
- 指针就是变量,不需要初始化,也可以指向不同的地址
- 引用天生就必须在定义时初始化,之后无法在引用其它变量
如何理解“引用的本质就是指针常量”?
- 从使用C++语言的角度来看
- 引用与指针常量没有任何的关系
- 引用是变量的新名字,操作引用就是操作对应的变量
- 从C++编译器的角度来看
- 为了支持新概念“引用”必须要一个有效的解决方案
- 在编译器内部,使用指针常量来实现“引用”,因此“引用”在定义时必须初始化
所以:
- 当进行C++编程时,直接站在使用的角度看待引用,与指针毫无关系!
- 当对C++程序中的一些涉及引用的bug或者“奇怪行为”进行分析时,可以考虑站在C++编译器的角度看待引用!
3、重载与默认类型转换
先看示例:
exp-1.cpp
#include <stdio.h>void func(int a, int b){ printf("void func(int a, int b)\n");}void func(int a, char b){ printf("void func(int a, char b)\n");}void func(char a, int b){ printf("void func(char a, int b)\n");}void func(char a, char b){ printf("void func(char a, char b)\n");}int main(){ int a = 1; char b = '2'; func(a, a); func(a, b); func(b, a); func(b, b); printf("*************************************\n"); func(1, 2); func(1, '2'); func('1', 2); func('1', '2'); return 0;}
运行结果:
从运行结果来看,每个函数都按照我们想要的方式进行了匹配,但是我们通过字面值常量来调用函数时,编译器实际上做了默认的类型转换,这才使得函数匹配成功,但也正是由于默认的类型转换,可能就会导致匹配不像我们想象的那样进行!
C++编译器对字面量的处理方式
- 整数型字面量的默认类型为int,占用4个字节
- 浮点型字面量的默认类型为double,占用8个字节
- 字符型字面量的默认类型为char,占用1个字节
- 字符串型字面量的默认类型为const char*,占用4个字节
当使用字面量对变量进行初始化或赋值时
- 无溢出产生:编译器对字面量进行默认类型转换
- 产生溢出:编译器会做截断操作,并产生警告
深入理解重载规则
- 精确匹配实参
- 通过默认类型转换匹配实参
- 通过默认参数匹配实参
三条规则会同时对已存在的重载函数进行挑选
- 当实参为变量并能够精确匹配形参时,不再进行默认类型转换的尝试。
- 当实参为字面量时,编译器会同时进行精确匹配和默认类型转换的尝试。
4、C方式编译的疑惑
深入理解extern “C”
- extern “C”告诉编C++译器将其中的代码进行C方式的编译
- C方式的编译主要指按照C语言的规则对函数名进行编译
- 函数名经过编译后可能与源码中的名字有所不同
- C++编译器为了支持重载,函数名经过编译后会加上参数信息等附加信息,因而编译后的函数名与源码中完全不同,且每个重载函数的函数名都不一样
- C编译器不会在编译后的函数名中加上参数信息,与源代码中函数名一样
- C方式的编译主要指按照C语言的规则对函数名进行编译
示例:
exp-2.cpp
#include <stdio.h>extern "C"{ void func() { const int i = 1; int& ri = const_cast<int&>(i); ri = 5; printf("i = %d\n", i); printf("ri = %d\n", ri); }}void func(const char* s){ printf("%s\n", s);}int func(int a, int b){ return a + b;}int main(){ func(); func("Hello Linux!"); func(1, 2); return 0;}
这里有三个重载函数,无参数的func函数被指定用C语言方式编译,现在编译两个版本:
第一版:无参数的func函数采用C语言方式编译;
第二版:无参数的func函数采用C++方式编译,即去掉extern “C”
编译命令:
g++ -S main.cpp -o main.sg++ -S main.cpp -o main_cpp.s
对比两次生成的汇编代码:
差别截图上已经标明,这其实就是为什么C和C++互相调用时,提示找不到函数的本质原因,因为一个编译后函数名变了,而一个却没变!
Tip:
extern “C” 中的重载函数经过C方式编译后将得到相同的函数名,因此 extern “C” 中不允许重载函数,但 extern “C” 中的函数可以与extern “C”之外的函数进行重载。
其次,再看C++重载函数编译出的汇编代码:
.file "main.cpp" .section .rodata.LC0: .string "i = %d\n".LC1: .string "ri = %d\n" .text .globl _Z4funcv .type _Z4funcv, @function_Z4funcv:.LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp movl $1, -12(%rbp) leaq -12(%rbp), %rax movq %rax, -8(%rbp) movq -8(%rbp), %rax movl $5, (%rax) movl $1, %esi movl $.LC0, %edi movl $0, %eax call printf movq -8(%rbp), %rax movl (%rax), %eax movl %eax, %esi movl $.LC1, %edi movl $0, %eax call printf leave .cfi_def_cfa 7, 8 ret .cfi_endproc.LFE0: .size _Z4funcv, .-_Z4funcv .globl _Z4funcPKc .type _Z4funcPKc, @function_Z4funcPKc:.LFB1: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp movq %rdi, -8(%rbp) movq -8(%rbp), %rax movq %rax, %rdi call puts leave .cfi_def_cfa 7, 8 ret .cfi_endproc.LFE1: .size _Z4funcPKc, .-_Z4funcPKc .globl _Z4funcii .type _Z4funcii, @function_Z4funcii:.LFB2: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl %edi, -4(%rbp) movl %esi, -8(%rbp) movl -8(%rbp), %eax movl -4(%rbp), %edx addl %edx, %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc.LFE2: .size _Z4funcii, .-_Z4funcii .section .rodata.LC2: .string "Hello Linux\357\274\201" .text .globl main .type main, @functionmain:.LFB3: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 call _Z4funcv movl $.LC2, %edi call _Z4funcPKc movl $2, %esi movl $1, %edi call _Z4funcii movl $0, %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc.LFE3: .size main, .-main .ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4" .section .note.GNU-stack,"",@progbits
可以发现,虽然源代码中函数名都一样,但是在编译阶段就被编译器处理了,生成的汇编代码中每个重载函数的名字都不一样!所以重载是源代码级别的,汇编级别的没有重载一说,都是不同的函数!
0 0
- 【八】疑难问题小结(一 -- 七篇)
- 实例解析 C/C++ 疑难问题(一)
- (八)学习小结
- 疑难问题
- 疑难问题
- 排序算法(八):小结
- 数据结构小结(八)图
- Python每日小结(八)
- Java基本功练习七(一维数组强化[豆机问题、八皇后问题])
- 数据结构小结(七)查找
- Python每日小结(七)
- 忆疑难问题定位及解决一
- 侧滑删除进阶(七、八)
- linux基础练习(七,八)
- WebGIS小结之八(鹰眼实现)
- 数据结构小结(八)图的使用
- Linux系统知识小结(八)
- 步步为营(七)贪心(6)小结
- 11203-rac升级到11204遇到的INS-06006和无法发现node的问题
- 欢迎使用CSDN-markdown编辑器
- [易飞]信息传递-多表(含外表)关联取值
- 九度oj 1063
- 快速排序法 quickSort---java
- 【八】疑难问题小结(一 -- 七篇)
- UVA 10123 No Tipping
- [Android]GreenDao(1)--项目配置
- 博弈-Green Hackenbush(无向图删边)
- Linux常用命令
- 我们不仅要当管理者还要当CEO!-程序员
- protocol buffer安装及使用(非常详细)
- 返乡休假结束
- 设计模式在ssh中的典型应用