【转载】[新手向] C八股及常见问题 by JuicyMio
萌新在问问题之前,建议认真读完下文
转自:C八股及常见问题 - JuicyMio's blog
C/C++杂项知识点
用于记录一些比较奇怪/少见的c语法/易错易忘点/常见问题, 以备考试等用途
杂项中的杂项
- 编译没过先看报错信息, 不认识英文请善用翻译
- 如何提问: 先使用搜索引擎. 如果一定要问人, 附上全部 全部 全部 代码, 以及报错信息 报错信息 报错信息. 和一组出错了的输入输出, 以及注释或你对代码的解释. 因为阅读别人的代码是一件很烧脑的事.
- 错误示范: 代码截图, 我错哪了?
- 不要拍屏
- 查错技巧: 学习使用gdb, 设置断点并查看相关变量的值. 一个低配但很快的方法是用printf输出关键变量
- 关于CSDN: 虽然内容质量感人被StackOverflow等英文网站甩了十条街, 但还是有一些值得一看的文章的. 而且哪怕用百度查到csdn文章看了也比啥也不查直接问强.
- 输入数据之后卡住了怎么办: 查看你的代码里是否有死循环, 或者你的输入格式是否合法.
- 输入输出对不上: 看看格式控制符跟变量类型能否对应, 常见错误是用
%f
读入double
类型变量. - 报错信息里有
cannot open output file .../*.exe: permission denied
或者"无法打开xxx程序进行写入"之类的东西: 你之前的程序没关掉 - 输出了奇怪的数字: 大概率数组越界
- Segmentation fault: 访问了不可访问的内存. 常见原因是数组越界, 指针漂移, 缓冲区溢出, 数组开太大爆栈...
- 带缺省值的参数需要放在后面, 这样在调用函数的时候就可以不写那个参数.
放在前面编译会报错.
int f(int a = 0, int b); // wrong
int f(int b, int a = 0); // right
-
字符串占用的空间要算上 '\0'
-
关于锟斤拷烫烫烫等成因:
手持两把锟斤拷,口中疾呼烫烫烫,脚踏千朵屯屯屯,笑看万物锘锘锘- 锟斤拷
源于GBK字符集和Unicode字符集之间的转换问题。Unicode和老编码体系的转化过程中,肯定有一些字,用Unicode是没法表示的,Unicode官方用了一个占位符来表示这些文字,这就是:U+FFFD REPLACEMENT CHARACTER。那么U+FFFD的UTF-8编码出来,恰好是 '\xef\xbf\xbd'。如果这个'\xef\xbf\xbd',重复多次,例如 '\xef\xbf\xbd\xef\xbf\xbd',然后放到GBK/CP936/GB2312/GB18030的环境中显示的话,一个汉字2个字节,最终的结果就是:锟斤拷——锟(0xEFBF),斤(0xBDEF),拷(0xBFBD)。
- 烫烫烫
VS/VC debug模式下栈内未初始化的内存会全部被设置成0xcc, 这个0xcc是INT3中断指令的机器码, 所以执行这块内存就会中断程序.
VS调试器默认字符集是MBCS, 其中0xcccc是'烫'字, 所以......
INT3断点是断点的一种,在诸如Ollydbg中的快捷键是F2,是一种很常用的断点类型。INT3指令的机器码为CC,所以通常也称之为CC指令。当被调试进程执行INT3指令导致一个异常时,调试器就会捕捉这个异常从而停在断点处,然后将断点处的指令恢复成原来的指令。当然,如果自己写调试器,也可以用其他一些指令代替INT3指令来触发异常。
注: 关于INT3指令的问题可以查阅内中断相关知识, 简单来讲执行INT n指令会调用编号n对应的中断处理程序, 而INT 3是x86系列cpu专门用于调试的指令, 它的中断处理程序会寻找调试器并将控制转交给调试器, 这些知识在汇编语言课里就会学到.
- 屯屯屯
由于相似的原因, 堆中空间的缺省值是0xcd, 也是一个中断指令的机器码, 0xcdcd是屯 - 锘锘锘
BOM 是 Byte Order Mark 的缩写。是UTF编码方案里用于标识编码的标准标记,在UTF-16里本来是FF FE,变成UTF-8就成了EF BB BF。这个标记是可选的,因为UTF8字节没有顺序,所以它可以被用来检测一个字节流是否是UTF-8编码的。
常见原因是在记事本里保存了代码, 因为记事本用utf-8编码, 而且保存自动加BOM
锘EFBB
匡BFEF
豢BBBF - 葺葺葺
虽然诗里没有, 但堆上申请后释放空间后缺省值是0xdd, 0xdddd是葺
虽然这个问题在中文环境里令人尤其迷惑, 但英文使用者同样对未初始化的内存为何是这些值感到好奇.
When and why will a compiler initialise memory to 0xCD, 0xDD, etc. on malloc/free/new/delete?
-
<
优先级比=
高, 这使
if(x = f() < 0)
并不会像看起来那样工作, 它会首先执行f()<0
, 最后被赋值给x的是这个关系运算符的真(1)假(0).
- 宏/函数/内联函数
- 参数: 宏的参数在展开时不经任何处理直接进行字符串替换, 函数的参数会进行类型检查, 计算后由实参传值到形参. 形参是函数内的局部变量, 占用栈空间.
- 宏的展开由预处理器处理, 在预编译(编译之前)时进行, 直接用宏体替换宏名, 占用预编译时间. 函数在执行时才会被调用. 调用时需要保护现场(保存调用者保存的寄存器), 进入函数, 返回主调函数, 恢复现场(恢复寄存器).
- 宏会有各种副作用, 这使它难于调试
- 由于简单的展开, ++ --运算符可能会执行多次
- 同样由于简单的展开, 如果不加足够多的括号, 会出很多优先级的问题.
- 但宏也有很多好处
- 可以在并不直接允许重载的C语言里实现简单的重载(不过用指针等方法也可以实现)
- 对于简单而重复多次的功能, 可以大大减小开销, 提升速度
- 实现其他奇技淫巧
- 内联函数(inline)
- 由编译器处理
- 用类似宏的方法, 将函数代码直接嵌入到调用处, 不能有循环, 选择等任何复杂结构, 可以节约开销的同时防止宏的一些副作用.
- 如果函数不够简单, 编译器会拒绝内联. 在开了某些优化时编译器会自动将一些简单的函数内联.
- 形参出现在函数定义中用来占位,只有在调用时分配内存. 实参出现在函数调用时.在函数调用时实参的值被传递给形参.
C/C++的'\n'和std::endl是否刷新缓冲区的问题
如果cout输出到一个交互设备(interactive device), 如终端(terminal), 它们通常是行缓冲而非完全缓冲的, 所以'\n'会刷新缓冲区, 但如文件这种完全缓冲的目标, '\n'不会刷新缓冲区.
我提出这个问题并空想一番之后被M4tsuri丢了两个StackOverflow链接, 我去面壁了...
Does printf always flush the buffer on encountering a newline
does new line character also flush the buffer