C++的一些经典问题分析(一)
来源:互联网 发布:nginx和libevent的区别 编辑:程序博客网 时间:2024/06/05 16:02
一、const和引用的疑惑
示例代码1:
#include <stdio.h>int main(){ const int x = 1; const int& rx = x; int& nrx = const_cast<int&>(rx); nrx = 5; printf("x = %d\n", x); printf("rx = %d\n", rx); printf("nrx = %d\n", nrx); printf("x = %p\n", &x); printf("rx = %p\n", &rx); printf("nrx = %p\n", &nrx); printf("\n");/*****************************************************/ volatile const int y = 2; int* p = NULL; p = const_cast<int*>(&y); *p = 6; printf("y = %d\n", y); printf("*p = %d\n", *p); printf("\n");/*****************************************************/ const int z = y; p = const_cast<int*>(&z); *p = 7; printf("z = %d\n", z); printf("*p = %d\n", *p); const int yy = 3; const int zz = yy; p = const_cast<int*>(&z); *p = 7; printf("zz = %d\n", zz); printf("*p = %d\n", *p); printf("\n");/*****************************************************/ char c = 'c'; char& rc = c; const int& trc = c; rc = 'a'; printf("c = %c\n", c); printf("rc = %c\n", rc); printf("trc = %c\n", trc); return 0;}
编译运行结果:
x = 1rx = 5nrx = 5&x = 0x7ffe97b5e228&rx = 0x7ffe97b5e228&nrx = 0x7ffe97b5e228y = 6*p = 6z = 7*p = 7zz = 3*p = 7c = arc = atrc = c
将代码分成四段来分析。
第一段:对const常量进行引用会导致编译器为其分配空间,并且引用名所对应的是分配出来的空间,而不是符号表中的常量;虽然const常量被分配了空间,但是在使用原来的常量名时这个空间中的值不会被使用;
第二段:被volatile修饰的const常量不会进入符号表,它退化为只读变量,每次访问都从内存中取值;
第三段:在编译过程中,y的值需要访问内存后才能知道,而yy的值是在符号表中的,在编译期间就能知道它的值。所以在编译期间不能直接确定初始值的const量,都被作为只读变量处理;能直接确定的进入符号表;
第四段:const引用的类型与初始化变量的类型相同时,使初始化变量成为只读变量;不同时,生成一个新的只读变量,其初始值与初始化变量相同。
二、引用与指针的疑惑
示例代码:
#include <stdio.h>struct SV{ int x; int y; int z;};struct SR{ int& x; int& y; int& z;};int main(){ SV sv = {1, 2, 3}; SR sr = {sv.x, sv.y, sv.z}; printf("&sv = %p\n", &sv); printf("&sv.x = %p\n", &sv.x); printf("&sv.y = %p\n", &sv.y); printf("&sv.z = %p\n", &sv.z); printf("&sr = %p\n", &sr); printf("&sr.x = %p\n", &sr.x); printf("&sr.y = %p\n", &sr.y); printf("&sr.z = %p\n", &sr.z); SV& rsv = sv; rsv.x = 4; rsv.y = 5; rsv.z = 6; printf("sv.x = %d\n", sv.x); printf("sv.y = %d\n", sv.y); printf("sv.z = %d\n", sv.z); return 0;}
编译运行结果:
&sv = 0x7fff21bcfee0&sv.x = 0x7fff21bcfee0&sv.y = 0x7fff21bcfee4&sv.z = 0x7fff21bcfee8&sr = 0x7fff21bcfef0&sr.x = 0x7fff21bcfee0&sr.y = 0x7fff21bcfee4&sr.z = 0x7fff21bcfee8sv.x = 4sv.y = 5sv.z = 6
1、指针与引用的区别:
指针是一个变量,其值为一个内存地址,通过指针可以访问对应内存地址中的值,而引用只是一个变量的新名字,所有对引用的操作(赋值,取地址等)都会传递到其引用的变量上;指针可以被const修饰成为常量或者只读变量,而const引用使其引用的变量具有只读属性;指针就是变量,不需要初始化,也可以指向不同的地址,而引用天生就必须在定义时初始化,之后无法在引用其它变量。
2、如何理解“引用的本质就是指针常量”?
从使用C++语言的角度来看:引用与指针常量没有任何的关系,引用是变量的新名字,操作引用就是操作对应的变量;
从C++编译器的角度来看:为了支持新概念“引用”必须要一个有效的解决方案,在编译器内部,使用指针常量来实现“引用”,因此“引用”在定义时必须初始化。
当进行C++编程时,直接站在使用的角度看待引用,与指针毫无关系!当对C++程序中的一些涉及引用的bug或者“奇怪行为”进行分析时,可以考虑站在C++编译器的角度看待引用!
三、重载的疑惑
示例代码1:
#include <stdio.h>int main(){ printf("sizeof(\'1\') = %d\n", sizeof('1')); printf("sizeof(2) = %d\n", sizeof(2)); printf("sizeof(3.0) = %d\n", sizeof(3.0)); char c = '1'; short s = '1'; int i = '1'; long l = '1'; long long ll = '1'; c = 2; c = 1000; s = 2; i = 2; l = 2; ll = 2; float f = 0; double d = 0; f = 3.0; d = 3.0; return 0;}
编译:
example6-3.cpp:16:7: warning: overflow in implicit constant conversion [-Woverflow] c = 1000;
运行:
sizeof('1') = 1sizeof(2) = 4sizeof(3.0) = 8
C++编译器对字面量的处理方式:
1、整数型字面量的默认类型为int,占用4个字节
2、浮点型字面量的默认类型为double,占用8个字节
3、字符型字面量的默认类型为char,占用1个字节
4、字符串型字面量的默认类型为const char*,占用4个字节
当使用字面量对变量进行初始化或赋值时,
无溢出产生:编译器对字面量进行默认类型转换;
产生溢出:编译器会做截断操作,并产生警告;
示例代码2:
#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); func(1, 2); func(1 '2'); func('1', 2); func('1', '2'); return 0;}
编译运行:
void func(int a,int b)void func(int a,char b)void func(char a,int b)void func(char a,char b)void func(int a,int b)void func(int a,char b)void func(char a,int b)void func(char a,char b)
深入理解重载规则:精确匹配实参、通过默认类型转换匹配实参和通过默认参数匹配实参。三条规则会同时对已存在的重载函数进行挑选,当实参为变量并能够精确匹配形参时,不再进行默认类型转换的尝试;当实参为字面量时,编译器会同时进行精确匹配和默认类型转换的尝试。
四、C方式编译的疑惑
示例代码:
extern "C"{ void func(int x) { 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);}void func(int a, int b){ printf("%d\n",a+b);}int main(){ func(1); func("chuckie_chen"); func(1, 2); return 0;}
编译成功,运行:
i = 1ri = 5chuckie_chen3
深入理解extern “C”:
extern “C”告诉编C++译器将其中的代码进行C方式的编译,C方式的编译主要指按照C语言的规则对函数名进行编译,函数名经过编译后可能与源码中的名字有所不同,C++编译器为了支持重载,函数名经过编译后会加上参数信息,因而编译后的函数名与源码中完全不同,而C编译器不会在编译后的函数名中加上参数信息,extern “C”中的重载函数经过C方式编译后将得到相同的函数名。因此extern “C”中不允许重载函数,但extern “C”中的函数可以与extern “C”之外的函数进行重载。在C++编译器中,C方式和C++方式主要的不同就是函数名编译方式的不同。
- C++的一些经典问题分析(一)
- 关于嵌入式C&&C++的一些编程问题(一)
- 【分享】一些经典的C/C++语言基础算法及代码(一)
- java一一些经典技术问题(不定期更新)
- 12-经典问题分析一-const和引用的疑问
- c一些经典的操作
- 异常的一些经典问题
- 一段经典的c 分析
- 视角来分析 腾讯 微信访客系统的一些问题 (一)
- 学习C的一些基本概念(一)
- 学习C的一些笔记(一)
- C的一些问题
- C的一些问题
- 用C/C++解决一些数学问题(一)
- 一些经典的C语言程序算法
- C 语言的一些经典题目
- C语言的一些经典案例
- 网上的一些经典C语言题目
- mt19937 随机数
- 欢迎使用CSDN-markdown编辑器
- eclipse将现存的项目导入新的Repository目录
- cache原理介绍
- tensorflow serving for centos 7.2
- C++的一些经典问题分析(一)
- maven项目依赖,找不到依赖jar包的配置文件
- Hive基础概念之Metadata和MetaStore
- relative定位下要使用top、left,而不是margin-top、margin-left
- 《送给初学者的五个调试小技巧》
- ESP8266模块个人使用体会
- javaScript之函数,分支结构 (一)
- (3)Storm实时日志分析实战--编码实现
- 内存区域的划分和分配