[Relearning C/Cpp] Statements and Preprocessor
突然发现两个不错的网址
https://en.cppreference.com/w/cpp/language
https://en.cppreference.com/w/c/language
书籍千千万,不如这个定义来得直接啊
基本语句
if…else,switch…case,while,for,do…while,break,continue,goto,return,空语句(一个分号),try…catch
一些比较简单的就不说了,着重说一些没啥概念的
switch…case
1 | switch(expression){ |
这里就有一个问题了,if…else和switch…case谁效率更高呢?我发现比较有意思的事情是,没那么绝对,得看编译器给不给你优化
1 | int main() { |
汇编出来的代码是这样子的
1 | main: |
但是这种情况肯定是不常用的,神经病啊..写个没用的代码?我们让代码变得有用一点
1 | int main() { |
再汇编一下瞅瞅
1 | main: |
但是…https://blog.csdn.net/jeremyjone/article/details/103428009 网上都总结好了,我还折腾啥呢
这都是基本操作,就不说了,我们再看try…catch,这个东西只有C++有,叫做异常处理,有仨关键字:try,catch,throw,看看代码实例
1 |
|
标准C++是没有类似finally这样的语句结构的。C#/Java 中保证无论是否出现异常,finally block 的代码均会得到执行;而 C++ 中,不论是否出现异常,局部变量的析构函数是会保证执行的,所以相对应与 finally block,C++ 的解决办法是 RAII,即Resource Aquisition Is Initialization。
基本的思路是,通过一个局部对象来表现资源,于是局部对象的析构函数将会释放资源。这样,程序员就不会忘记释放资源了。但是写析构函数是个技术活啊
C++11开始支持”Range-based for loop”,就是Java中的增强for循环
1 | void test1() { |
还有个类似的函数std::for_each()
2 宏
首先最常见的#include
,导入头文件,一般分为#include <filename>
和#include "filename"
一般来说,用<>
表示搜索标准库里面的;用""
表示搜索自己写的,如果自己写的没找到,再去搜标准库。所以我们直接用""
是没问题的
其次常见的就是#define
了,定义常量,比如说我们会防止头文件重复导入,在头文件上定义一个常量来标识
1 |
|
这里又认识了#ifndef
和#endif
(结束判断),意思就是if not define,当然也会有ifdef
,这样就可以防止头文件被多次导入引出的麻烦了
还有我们常说的宏开关#if
,这其实就是if的功能,看起来高大上一点而已,还有#else
,和#elif
(就是else if)
1 |
还有#pragma
,这个宏在visual studio中常见,这个宏用起来比较复杂,其一般格式是#pragma para
,其中para表示参数,瞅瞅常见参数
#pragma message
常常这么用#pragma message("some msg..")
,当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来,编译时才会打印哦
1 | E:\Workspace\CCpp\test>g++ -std=c++17 main.cpp -o x |
同时,**还有#error
和#warning
**,也是针对编译时的,其中warning只是发出警告,error会停止编译
#pragma code_seg
#pragma code_seg( ["section-name"[,"section-class"] ] )
,它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它
#pragma once
只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,和上面提到的宏定义道理是一样的,这个宏在VS经常见到,但不是标准的,所以可能有的不兼容
#pragma pack
使用指令#pragma pack (n)
,编译器将按照n个字节对齐。使用指令#pragma pack ()
,编译器将取消自定义字节对齐方式。关于字节对齐,其实就是性能和空间之间的平衡
1 | struct TestStruct1 |
按照常理说,如果c1的内存位置是0,那么s就是1-2,c2就是3,i就是4-7;实际上呢,地址开头是0,2,4,8。意思就是默认四字节对齐
首先,每个成员分别按自己的方式对齐,并能最小化长度。
其次,复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,可以最小化长度。
然后,对齐后的长度必须是成员中最大的对齐参数的整数倍,这样在处理数组时可以保证每一项都边界对齐。
还有其他的,不了解了..
下一个是**#line
**,说到这个,就不得不说__LINE__
和__FILE__
,一般来说这LINE和FILE就是单纯的行数和文件名,然而这个LINE和FILE就是固定的吗?显然不是,这个line就可以重新定义这两个宏
语法:#line lineNum fileName
,其中lineNum是数字;fileName是字符串,可省略。这个宏定义了lineNum,那下一行的数字就是lineNum
再说可变参数宏,源码里面常见的打log的工具
#define ERROR(format, ...) fprintf(stderr, format, __VA_ARGS__)
这__VA_ARGS__
就是可变参数宏,嗯,可变参数..
再说源码里面常用的**#,##,#@
**
#
: 对应变量字符串化
##
: 连接符
#@
: 将单字符的标记转成单字符 举个栗子
1 | int i = 1; |