Antkillerfarm Hacking V7.0

C/C++编程心得(一)

2015-01-05

参考资料

https://en.cppreference.com/

这个网站可以查到C++的标准库的用法。

https://isocpp.org

这个是C++标准组织的网站,可以查看C++的新特性。

http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines

这个是C++新特性的代码规范指南

https://en.cppreference.com/w/cpp/compiler_support

这里可以查看各个编译器对于新特性的支持程度。

总结下来就是(2019.6):

  • C++ 11/14基本都支持了。

  • 硬件公司如Cray/Nvidia/Oracle(Sun)/IBM基本不支持C++ 17。

  • 能支持C++2a的就只有gcc、Clang、MSVC和EDG eccp了,其中MSVC支持的特性较少,而其他三者相对更新的比较勤。

https://www.edg.com/

这是EDG的官网。它是一家专门做C++前端的公司。因此,他们的更新和开源项目gcc/Clang一样勤,也就不足为奇了。事实上它的商业模式就是:如果硬件公司没有实力或财力做编译器,那么就购买我的前端好了。


Compiler Explorer是Matt Godbolt创建的一个在线编译网站。支持不同版本的gcc生成不同平台的汇编代码。

官网:

https://godbolt.org/

代码:

https://github.com/compiler-explorer/compiler-explorer


https://lgtm.com/

这是一个代码缺陷检测工具


https://www.fluentcpp.com/

一个教你cpp技巧的网站

历史

https://mp.weixin.qq.com/s/cfBT0wsqzZRwedMO7Qz_vA

“任性”的C语言之父:因拒付论文装订费错失博士学位,论文52年后重见天日(Dennis Ritchie)

https://zhuanlan.zhihu.com/p/257089138

C语言发展史的点点滴滴

https://mp.weixin.qq.com/s/_HG19EqrEQiiRRx2SxB19g

图灵老友写下1000条指令程序,锤炼70年,化身350万行代码飞向火星(Christopher Strachey)

头文件被多个源文件引用

在头文件中定义字符串时,如果该头文件会被多个源文件引用的话,字符串必须被定义为const,否则会有重定义错。当然最好在头文件中只放声明,不要放定义。

使用诸如

#ifndef _COMMON_STRING_
#define _COMMON_STRING_
...
...
...
#endif

解决不了这个问题。因为这段代码解决的是同一个源文件重复包含某个头文件的问题。而这里的问题是不同的源文件包含同一头文件时,产生的重复定义的问题。const关键字保证了同一标识符只会定义一次。

指针数组与数组指针

int (*p)[10] 先是 ` *p`起作用 ,声明一个指针(p肯定是指针了),然后是[10](数组说明),则p是指向10个整形元素组成的数组的指针。——数组指针。

若是int *p[10] 则首先是[ ]起用,它是一个数组了。然后是*,所以这个数组元素是指针型的。——指针数组。

引申一下

    int a[10];
    printf("%x\n",&a);
    printf("%x\n",a);

可以看到&a和a的值是相等的,但含义不同。a相当于int *p,而&a相当于int (*p)[10]

类似的

int (*p[])(int)函数指针数组。

int (*p())[]返回数组指针的函数。

int *p()[]字面上可以解释为返回指针数组的函数,不过函数是不能返回数组的。

int *(*a())()这是一个函数,它没有参数,它的返回值是一个函数指针,这个指针指向的函数,也没有参数,且返回值是int型的指针。

sizeof

http://blog.csdn.net/van150/archive/2005/12/05/544454.aspx

sizeof进行结构体大小的判断

VC的默认规则基本如上所示,gcc的默认规则就是4字节对齐。

可以比较一下

struct s
{
  char a;
  double b;
  int c;
}

struct s
{
  char a;
  int b;
  double c;
}

在各个平台上的sizeof值。

附带说一下如果看到以下的代码片段,也就不要觉得惊奇了。

typedef enum _XXX
{
    XXX_0        = 0,
    XXX_1        = 1,
    XXX_FORCE_DWORD    = 0x7FFFFFFF/* 编译器对齐 */
}XXX;

字节对齐的设定

1)编译器选项指定

VC:/Zp

gcc:-fpack-struct

2)代码指定

VC和gcc都可以用#pragma pack(4)

gcc还可以用__attribute__((packed))

宏展开函数的小技巧

“伪”函数

有的时候,会使用宏展开的方式定义“伪”函数。

这些“伪”函数有以下特征:

1.不是真正的函数,而是接受参数的宏。

2.形式上与普通函数相同。

为了实现这个功能,教科书上给出的做法是这样的:

#define st(x)      do { x } while (0)
#define HAL_DMA_SET_SOURCE( pDesc, src ) \
  st( \
    pDesc->srcAddrH = (uint8)((uint16)(src) >> 8); \
    pDesc->srcAddrL = (uint8)( (uint16)(src) & 0xFF ); \
  )

但是do { x } while (0)在有些编译器上会报warning。最近看Ti的代码的时候,看到了一种更好的办法:

#define st(x)      do { x } while (__LINE__ == -1)

operator ‘#’

#define TO_STRING(expr) # expr
// TO_STRING(hello_world)

#define PRINT_VARIABLE_NAME(var)  printf("%s", TO_STRING(var));

int main() {
    int number = 10;
    PRINT_VARIABLE_NAME(number);
    printf(" = %d\n", number);
    return 0;
}

operator ‘##’

#define IMPL_1(str) printf("call impl 1 print:%s\n", str);
#define IMPL_2(str) printf("call impl 2 print:%s\n", str);

#define IMPL(which, str) IMPL_ ## which(str)

int main() {
    IMPL(1, "aaaaaa");
    IMPL(2, "bbbbbb");
    return 0;
}

Macro overload

#define ADD_2(a, b) ((a) + (b))
#define ADD_3(a, b, c) ((a) + (b) + (c))
#define ADD_4(a, b, c, d) ((a) + (b) + (c) + (d))

#define GET_OVERLOAD(_1, _2, _3, _4, MACRO, ...) MACRO

// __VA_ARGS__ since C99
// ##__VA_ARGS__ to handle empty variadic
// __VA_OPT__ since C++20
#define ADD(...) GET_OVERLOAD(__VA_ARGS__, ADD_4, ADD_3, ADD_2)(__VA_ARGS__)

int main() {
    printf("%d\n", ADD(1, 2));
    printf("%d\n", ADD(1, 2, 3));
    printf("%d\n", ADD(1, 2, 3, 4));
    return 0;
}

FUNCTION and PRETTY_FUNCTION

template<typename T>
T add(T x, T y) {
    printf("__FUNCTION__ is: %s\n", __FUNCTION__);
    printf("__PRETTY_FUNCTION__ is: %s\n", __PRETTY_FUNCTION__);
    return x + y;
}

int main() {
    add<int>(1, 2);
    return 0;
}

Boost preprocessor library

虽然这个库很强,但是preprocessor并不是图灵完备的,而C++的模板库/CMake则是图灵完备的。

https://www.boost.org/doc/libs/1_75_0/libs/preprocessor/doc/index.html

The Boost Library Preprocessor Subset for C/C++

strtok

strtok函数多用于分割字符串,但它会改变被分割字符串的值。因此,如果该字符串以后还有用的话,需要首先复制该字符串,然后对复制的字符串执行strtok函数。

C++ 11/14/17/20的新特性

我是在2003年左右学习C++的,后来直到2009年以前,C++都是我的主要工作语言。但由于本人先学的C语言,所以编程的思想一直是函数式的。完成一项任务,无论用C、C++、Java、C#,还是Matlab、Python,风格都是差不多的。这也是后来我对GTK情有独钟的一个重要的原因。

总的来说,我对C++用的多,但理解的却不深。只对单继承、成员函数的封装、访问之类的概念有一定的认识和使用。多重继承、模板会看不会写。更不必提C++ 03和C++ 11的新特性了。最近因为研究Cocos2d-x,而接触到这些新特性,颇有些感觉到自己已经是老古董了。

不过好在Java语言用的还可以,大部分的C++新特性,学起来倒也难不倒我。


2019.6

本来一直做嵌入式开发,基本都是C语言。奈何现在的高性能并行计算,基本都是C++的天下了。而且Google、ARM之流,其使用策略颇为激进,C++ 11是保底,C++ 14才是主流。

以后真的不敢在简历里写会C++了。。。囧


C++语言为了保持与C语言的兼容性,在语法层面上曾经尽力保持C-like。但随着越来越多内容被加入到C++中,这一要求也越来越难以实现。最终,C++11标准的发布彻底改变了C++语言的面貌,其改变之大甚至让C++11看起来像一门全新的语言。因此,多把C++11后的C++称为“Modern C++”。


C++ 03标准到C++ 11标准之间长达8年的间隙,给了其他语言赶超并诟病C++的机会。一些厂商为了解决C++的问题,也加了不少私货,从而导致了版本兼容性的分裂。

为了解决这个问题,C++标准委员会提出了新的版本发布策略:不再等待某项重要特性的成熟,再集体打包发布。而是成熟一项,发布一项。

所以,可以预见的是,即使没有什么重要新特性,也一定会有C++ 20标准。

当然了,从另一个角度来说,C++ 11有改天换地之效,而之后的升级则有些无足轻重了。


https://mp.weixin.qq.com/s/lTu8OCecwe9iHIn82bWQaA

现代C++教程:高速上手C++11/14/17/20,82页pdf

https://zhuanlan.zhihu.com/p/139515439

c++11新特性,所有知识点都在这了!

https://zhuanlan.zhihu.com/p/165389083

C++14新特性的所有知识点全在这儿啦!

https://zhuanlan.zhihu.com/p/165640868

C++17新特性

https://www.zhihu.com/answer/1813428455

C++20有哪些值得注意的新特性?


1.auto

C++11中引入的auto主要有两种用途:自动类型推断和返回值占位。auto在C++98中,标识临时变量的语义,由于使用极少且多余,在C++11中已被删除。前后两个标准中的auto,完全是两个概念,这点尤其需要注意。

http://blog.csdn.net/huang_xw/article/details/8760403

auto的使用

2.nullprt

http://blog.csdn.net/huang_xw/article/details/8764346

引入nullprt

3.仿函数、lambda表达式和闭包

http://www.cnblogs.com/npbool/p/3434757.html

C++11初探:lambda表达式和闭包

4.C++、Java和C#的lambda表达式的格式

C++

auto func = [=](int x, int y)->int {return x * y;};

Java

Runnable r1 = (int x, int y) -> { return x * y; }

C#

Func<int, int, int> towParams = (x, y) => x * y

// plus sign before lambda
auto lambda = +[] (int a, int b) { return a + b; };
// +[] means func pointer: int (func*)(int a, int b);

5.列表初始化

struct Student
{
    char* name;
    int age;
};
Student s={"dablelv",18};

std::vector<int> nVec(10, 1); // 包含10个元素,且值为1 
std::vector<int> nVec{10, 1}; // 包含2个元素,值分别为10,1

https://blog.csdn.net/sevenjoin/article/details/81779221

C++11列表初始化-花括号的使用

Fork me on GitHub