前言
以下问题以Q&A形式记录,基本上都是笔者在初学一轮后,掌握不牢或者频繁忘记的点
Q&A的形式有助于学习过程中时刻关注自己的输入与输出关系,也适合做查漏补缺和复盘。
本文对读者可以用作自查,答案在后面,需要时自行对照。
--
问题集
Q1:结构化编程?C++的核心:OOP和泛型编程?
Q2:关于头文件命名,#include 合规,#include 语法没有错误,为什么却编译报错?
Q3:using namespace std; 中,using语法是属于编译指令吗?namespace的作用域的设计目的?
Q4:cout << 中的 “<<” 运算符叫什么名字?stream/流 是什么?控制符(manipulator)是什么?
Q5:const在C和C++的区别?const安全的底层原理?
Q6:在函数运行中定义变量合适吗?推荐做法是什么?a的值会随着循环每次变化吗?eg :
int main() {
while(1) {
int a = 0; print(a++); // 这里的 定义int a变量是合理的吗?
}
}
Q7:C风格中,原来需要预处理的 xxx.h 头文件在C++的新方式?
Q8:int在不同系统上可能有不同的位数表现(16bits/32bits),那么有办法设置int位宽吗?
Q9:以下两条语句的含义?
int a{5};
int b = {5};
Q10:unsigned类型有浮点数吗?
Q11:如果提前知道变量可能表示的int值大于16位,则推荐使用 long 而不是 int(大于20e则使用 long long),why?
Q12:伪代码 int a + bool b 的执行结果如何?其背后原理?进一步地,两个float数 f1+f2 内部运算是怎么样的?
Q13:语句 short i; cout << int(i); 的含义是什么?为什么要这样设计?这里的int()是一个函数吗?
Q14:关于auto关键字原初的设计意图?
Q15:char a[n] 和 string的本质区别?
Q16:关于连续使用cin,我们已知的问题是第一个cin随着回车会导致遗留\0到下一个cin被读到
标准的解决方式是什么?
Q17:输出结果?
cout << R"(Jim "King" Tutt uses "\n" instead of endl.)" <<'\n';
Q18:C++允许在声明结构变量时省略关键字struct?
Q19:bit field 是什么?(也叫位域,From 小盖伦er)
Q21:关于枚举型变量对象band,下面几个表达式的正确性?(思考:enum-整型之间的提升)
Q22:指针的危险?(与之关联:Q27)
Q23:指针解引用之前的金科玉律?(这个学过c都会,跟Q22一样看看即可)
Q24:动态数组?动态联编?如何创建?如何释放?
Q25:对于C++, int *psome = new int[x]; 这个语句中,sizeof(psome)代表什么?sizeof(*psome)又代表什么?
Q26:p是一个指针,那么 p = p+1 之后,p指向哪?(以C++11标准-巩固)
Q27:假设有一个字符串 str1,那么下面的表达式语法合理吗?
str1 [-1]
*(str1 - 1)
Q28:除了vector和 xx[ ],还有什么方式可以表示数组?
Q29:关于vector和array,vector的底层实现是什么?
Q30:二维数组在内存的空间地址是连续的么?
Q31:关于RAII,说说RAII的简单理念是什么?
Q32:比起 cin >> sr.arr 说说这里为什么用 cin.getline() ?
参考解答
Q1:结构化编程?C++的核心:OOP和泛型编程?
A1:结构化编程简单来说就是 if-else 等结构的使用,属于早期设计。OOP+泛型编程是C++核心
Q2:关于头文件命名,#include 合规,#include 语法没有错误,为什么却编译报错?为什么这样设计?
A2:其实这两种都是支持的。但是
1)<iostream.h> 是一个过时的包含方式,编译器所选定的标准可能不支持了
2)<iostream> 将库的内容包含在 std 命名空间中,这有助于避免命名冲突,是现代C++编程中推荐的做法。
Q3:using namespace std; 中,using语法是属于编译指令吗?namespace的意义是什么?
A3:namespace 的作用:一个潜在的问题是,可能使用两个已封装好的空间,它们都包含一个名为 xxx() 的函数。
这样,使用 xxx() 函数时,编译器将不知道指的是哪个版本。命名空间是为了避免这个问题。
eg:
Microflop::wanda("go dancing?");
Piscine::wanda("a fish named Desire");
using namespace std; 的作用域:affects all function definitions in this file;也就是一次using会影响整个.cpp
Q4:cout << 中的 “<<” 运算符叫什么名字?stream/流 是什么?控制符(manipulator)是什么?
A4:
“<<” 运算符是重载之后,根据实际功能的定义,在cout语境下为“插入”。(形同左移,但不是左移)
endl 是一个典型的控制符,由iostream中定义,且位于std中,在输出流中插入endl将导致屏幕光标移到下一行开头。
Q5:const在C和C++的区别?为什么const在C++中相对安全一些?
A5:const在C++中不能被通过&的方式进行改写。主要的操作是在编译阶段把这个const修饰的变量偷换了,让程序员没法找到他。
Q6:在函数运行中定义变量合适吗?推荐做法是什么?a的值会每次变化吗?eg :
int main() {
while(1) {
int a = 0; print(a++);
}
}
A6:在C中,所有的变量声明通常都位于函数或过程的开始位置,但C++没有这种限制。
不过,对于声明变量,C++的做法是尽可能在首次使用变量前声明它,以便于清楚的看出哪些在一起被定义了。
这个程序打印的结果是000000,说明 int a 在每次循环都会刷新。这里引用我和kimi的问答解释:
Q:C++中的这段代码,变量a会不断地定义吗?还是说只是被赋值?
A:在您提供的C++代码片段中,变量 a 会在每次 while 循环的开始时重新定义,并初始化为 0。
这意味着每次循环都会创建一个新的局部变量 a,并且每次都会从 0 开始。
Q:这个a变量的生命周期是仅在这次循环中?所以被创建在栈空间,在循环结束的时候被释放对吗?
A:是的,您的理解是正确的。在C++中,局部变量(如在 while 循环中定义的变量 a)的生命周期仅限于它们被定义的代码块。一旦代码块执行完毕,局部变量就会被销毁,其占用的内存(在栈上)也会被释放。
Q7:math.h头文件在C++的新版本?
A7:cmath
Q8:int在不同系统上可能有不同的位数表现(16bits/32bits),那么有办法设置int位宽吗?
A8:标准C++并没有提供直接设置,但可以查。查 int 位宽的方法:在<climits>头文件中,通过宏 INT_MAX
Q9:以下两条语句的含义?
int a{5};
int b = {5};
A9:一种不是很常见,但实际是为新手考虑的变量赋值方法——列表初始化。
其内在设计思路是:将大括号 { } 作为一种通用的初始化器,对所有不同类型的变量进行统一化赋值
对于列表初始化有一定要求,就是不允许“缩窄”,即不能用越界赋值。这里考虑安全也不允许使用变量,避免运行时不安全:
Q10:unsigned类型有浮点数吗?
A10:原文:注意, unsigned 本身是unsigned int 的缩写。
Q11:如果知道变量可能表示的int值大于16位,则使用long(大于20e则使用 long long),why?
A11:这样,将程序移植到16位系统时,就不会因为int范围改变导致突然无法正常工作。
所以即使系统上 int 为32位,也应这样做。
Q12:伪代码 int a + bool b 的执行结果如何?
其背后的原理是什么?进一步地,两个float数 f1+f2 其内部运算是怎么样的?
int a = 1;
bool b = true;
cout << a+b;
A12:int a + bool b 会整形提升为 int a + int b,f1+f2 会提升为 double a + double b
Q13:语句 short i; cout << int(i); 的含义是什么?为什么要这样设计?int是一个函数吗?
A13:这里是C++的强制转换语法,但只是看起来像是函数,实则不然
Q14:关于auto关键字的设计意图?
A14:自动推导那些不容易由程序员设计所产生变化的复杂类型,尤其是STL的iterator类型
Q15:char a[n] 和 string的本质区别?
A15:
Q16:关于连续使用cin
我们已知的问题是第一个cin随着回车会导致遗留\0到下一个cin被读到,标准的解决方式是什么?
A16:从cin后面补充一个 cin.get()
cin >> year;
cin.get();
也可以利用表达式 cin>>year 返回 cin 对象,将调用拼接起来:
(cin >> year).get();
Q17:输出结果?
cout << R"(Jim "King" Tutt uses "\n" instead of endl.)" <<'\n';
A17:前缀R:输出原始字符串
上述代码将显示如下内容:
Jim "King" Tutt uses \n instead of endl //特别注意:这里的 "\n" 变成了 \n,但不是粗暴的去除""
Q18:C++允许在声明结构变量时省略关键字struct?
A18:yes:
Q19:bit field 是什么?(位域)
A19:
经过代码验证,位域的内存占用并不是简单地取4字节对齐(或者说存储字长对齐),而是根据以下因素决定的:
1)位域的位数 2)结构体的内存对齐
举个例子:假设一个结构体中有一个位域和一个整数成员,位域占用了23位,整数成员占用32位。那么,位域会占用3个字节(23位 / 8位/字节 = 2.875字节,向上取整为3字节),整数成员会占用4字节。整个结构体的总大小可能会因为整数成员的对齐而增加到 8字节。
示例代码如下:
struct Example {
bool flag : 1; // 占用1位
unsigned int value : 23; // 占用23位
int data; // 占用4字节
};
但要注意,有的编译器会把不同类型的数据分成不同位域,上面的代码会输出示例 Example e 的大小为12字节。
具体是8字节还是12字节,以编译器自己的对齐和打包规则为准!
Q20:union主要用来省内存。目前作用不大了。等于一个可选类型变量
Q21:关于枚举型变量对象band,下面几个表达式的正确性?(思考:enum-整型之间的提升)
A21:枚举可以被看作是整型的扩展,看起来好像 “整型是基类,枚举是整型的派生”。
这里扩充两个小知识:
1)枚举不能++操作 2)更有迷惑性的 enum = int 型赋值错误
虽然尝试直接将整型变量 i 赋值给枚举变量 c 是不允许的,但是,通过使用 static_cast<>,可以显式地将整型转换为枚举类型。
Q22:指针的危险
A22:
Q23:指针解引用之前所谓的“金科玉律”?
A23:
Q24:动态数组?动态联编?如何创建?如何释放?
A24:
创建:int *psome = new int[10];
释放:delete [ ] psome;
使用 new 时,如果在运行阶段需要数组,则创建它,如果不需要则不创建。
关于释放:如果使用 new[] 为数组分配内存,则应使用delete [] 来释放。
Q25:对于C++, int *psome = new int[x]; 这个语句
sizeof(psome)代表什么?
sizeof(*psome)代表什么?
A25:这段代码是创建了一个动态数组,其中,sizeof(*psome)表示的是psome指针所指向的对象的类型 int 的大小。
它只表示单个int类型的大小,与x无关,也就是 sizeof(int)
由于psome是一个指向int的指针,*psome表示的是psome所指向的int变量的值。
那么是否能查找到动态数组的大小呢?答案是:程序跟踪了由 new int[] 创造的数组大小,用于delete [],但由于信息不公开,所以不能通过sizeof() 的方式来对整个动态数组的大小进行查询。
Q26:p是一个指针,那么 p = p+1 之后,p指向哪?
A26:将指针变量加1后,增加的量等于它指向的类型的字节数。
Q27:假设有一个字符串 str1,那么下面的语法合理吗?
str1 [-1]
*(str1 - 1)
A27:至少合法。与C语言一样,C++默认也不检查这种超界错误,无论是数组,vector还是array
Q28:除了vector和xx[ ],还有什么方式可以表示数组?
A28:在这些如array的模板类中,有很多设计是为了防止越界而进行设计的。
Q29:关于vector和array,vector的底层实现是什么?
A29:vector的底层实现是array,大小可变,注意添加删除操作实际上就是array重建,然后动态地管理len大小
Q30:二维数组在内存的空间地址是连续的么?
A30:不同编程语言的内存管理是不一样的,以C++为例,在C++中二维数组是连续分布的。
Q31:关于RAII,说说RAII的简单理念是什么?
A31:RAII(Resource Acquisition Is Initialization) 是 C++ 中一种重要的编程技术。
它通过对象的生命周期来管理资源,确保资源在使用完毕后能够被正确释放。
说白了也很简单,就是让设计好的构造和析构函数对申请和释放进行封装,避免遗忘的同时,便于找到内存问题的根源:
#include<cstdio>
#include<iostream>
using namespace std;
class safeResource{
public:
int name;
char *arr; // 这里维护一个动态数组
safeResource(){
arr = new char[5]; // 构造申请
}
~safeResource(){
delete [] arr; // 析构释放
}
};
int main(){
safeResource sr;
if(!cin.getline(sr.arr, 5)){
cout << "input error" << endl;
}
cout << sr.arr << " " << endl;
return 0;
}
Q32:比起 cin >> sr.arr 说说这里为什么用 cin.getline() ?
// sr.arr 是一个动态数组的起始地址
if(!cin.getline(sr.arr, 5)){
cout << "input error" << endl;
}
A32:虽然用 cin >> sr.arr 也能通过,但是 cin 的方法不会自带一个 '\0'
getline的方法就可以为 sr.arr 附带一个可靠的结束符号,经测试,即使是输入多了,最后一个字符仍然是\0,其他则被丢弃。
并且 cin.getline 的方法更易于异常处理。
Q33:在大多数现代系统上,指针的大小通常是 4 或 8 字节(取决于_____)
A33:系统是32位还是64位