下列程序编译是否会报错:

// directive_1.c
#include <stdio.h>

#ifndef MIN
  #define MIN(x, y) ((x) > (y) ? (y) : (x))
#endif /**/x

int main()
{
    printf("min val = %d\n", MIN(100, -1));

    return 0;
}

讲道理程序endif后面有个多余的x应该会编译失败,可是程序编译仅仅有个告警,运行正常。

[root c++]#gcc -g -Wall -o gcc gcc.c
gcc.c:6:12: warning: extra tokens at end of #endif directive [-Wendif-labels]
 #endif /**/x
            ^
[root c++]#./gcc
min val = -1

这和学习cpulimit源码发现的问题如出一辙:

cc -o cpulimit cpulimit.c list.o process_iterator.o process_group.o -Wall -g -D_GNU_SOURCE
cpulimit.c:46:18: warning: extra tokens at end of #ifdef directive
 #ifdef __APPLE__ || __FREEBSD__
                  ^~
make[1]: Leaving directory '/root/cpulimit/src'

原作者这里使用了错误的ifdef,应该使用if defined,详细介绍:

#ifdef 和#if defined区别,#ifdef应该只能有单个条件,如果使用了多个条件gcc编译会有相应告警,而#if defined则可以使用复合条件,类似中的例子:
#if defined(__APPLE__) && defined(__GNUC__)
#  define Q_OS_MACX

ifdef和if defined的区别

https://stackoverflow.com/questions/9229601/what-is-in-c-code

/* Force a compilation error if condition is true, but also produce a
   result (of value 0 and type size_t), so the expression can be used
   e.g. in a structure initializer (or where-ever else comma expressions
   aren't permitted). */
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
#define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); }))

详细解释:

This is, in effect, a way to check whether the expression e can be evaluated to be 0, and if not, to fail the build.

The macro is somewhat misnamed; it should be something more like BUILD_BUG_OR_ZERO, rather than ...ON_ZERO. (There have been occasional discussions about whether this is a confusing name.)

You should read the expression like this:

sizeof(struct { int: -!!(e); }))
(e): Compute expression e.

!!(e): Logically negate twice: 0 if e == 0; otherwise 1.

-!!(e): Numerically negate the expression from step 2: 0 if it was 0; otherwise -1.

struct{int: -!!(0);} --> struct{int: 0;}: If it was zero, then we declare a struct with an anonymous integer bitfield that has width zero. Everything is fine and we proceed as normal.

struct{int: -!!(1);} --> struct{int: -1;}: On the other hand, if it isn't zero, then it will be some negative number. Declaring any bitfield with negative width is a compilation error.

So we'll either wind up with a bitfield that has width 0 in a struct, which is fine, or a bitfield with negative width, which is a compilation error. Then we take sizeof that field, so we get a size_t with the appropriate width (which will be zero in the case where e is zero).

Some people have asked: Why not just use an assert?

keithmo's answer here has a good response:

These macros implement a compile-time test, while assert() is a run-time test.

Exactly right. You don't want to detect problems in your kernel at runtime that could have been caught earlier! It's a critical piece of the operating system. To whatever extent problems can be detected at compile time, so much the better.
gcc命令
[root c++]#g++ -E -dM - < /dev/null
#define __SSP_STRONG__ 3
#define __DBL_MIN_EXP__ (-1021)
#define __FLT32X_MAX_EXP__ 1024
#define __UINT_LEAST16_MAX__ 0xffff
#define __ATOMIC_ACQUIRE 2
#define __FLT128_MAX_10_EXP__ 4932
#define __FLT_MIN__ 1.17549435082228750796873653722224568e-38F
#define __GCC_IEC_559_COMPLEX 2
#define __UINT_LEAST8_TYPE__ unsigned char
#define __SIZEOF_FLOAT80__ 16

g++ -E -dM - < /dev/null这个命令有点奇怪,为什么把/dev/null作为标准输入才能正确执行呢?

assert断言

assert
学到一个用法:

assert(length >= 0 && "Whoops, length can't possibly be negative! (didn't we just check 10 lines ago?) Tell jsmith");

这样在断言出错的时候,提示会更明显,提示会完整的打印在终端:

[root c++]#./a.out
a.out: assert.c:13: main: Assertion `x <= 0 && "Whoops, length can't possibly be negative! (didn't we just check 10 lines ago?) Tell jsmith"' failed.
Aborted (core dumped)

assert 虽然是一个宏,但在预处理阶段不生效,而是在运行阶段才起作用,所以又叫“动态断言”。
C++ 11中引入了static_assert,用法如下:

#include <iostream>
int main(void)
{
    static_assert(2 + 2 == 4, "2+2 isn't 4");      // well-formed
    static_assert(sizeof(int) < sizeof(char),
                 "this program requires that int is less than char"); // compile-time error
}

编译报错:

[root c++]#g++ static_assert.cpp
static_assert.cpp: In function ‘int main()’:
static_assert.cpp:5:5: error: static assertion failed: this program requires that int is less than char
     static_assert(sizeof(int) < sizeof(char),
     ^~~~~~~~~~~~~
模板特化
#include <iostream>
using namespace std;

template<int N>
struct fib                   // 递归计算斐波那契数列
{
    static const int value =
        fib<N - 1>::value + fib<N - 2>::value;
};

template<>
struct fib<0>                // 模板特化计算fib<0>
{
    static const int value = 1;
};

template<>
struct fib<1>               // 模板特化计算fib<1>
{
    static const int value = 1;
};
int main()
{
    // 调用后输出2,3,5,8
    cout << fib<2>::value << endl;
    cout << fib<3>::value << endl;
    cout << fib<4>::value << endl;
    cout << fib<5>::value << endl;
    return 0;
}

运行结果:

[root c++]#./a.out
2
3
5
8
shared_ptr循环引用

一个完整例子:

#include <memory>
#include <assert.h>

using namespace std;

class Node final
{
public:
    using this_type     = Node;
    using shared_type   = std::shared_ptr<this_type>;
public:
    shared_type     next;      // 使用智能指针来指向下一个节点
};

int main()
{
    auto n1 = make_shared<Node>();   // 工厂函数创建智能指针
    auto n2 = make_shared<Node>();   // 工厂函数创建智能指针

    assert(n1.use_count() == 1);    // 引用计数为1
    assert(n2.use_count() == 1);

    n1->next = n2;                 // 两个节点互指,形成了循环引用
    n2->next = n1;

    assert(n1.use_count() == 2);    // 引用计数为2
    assert(n2.use_count() == 2);    // 无法减到0,无法销毁,导致内存泄漏

    return 0;
}
lambda表达式

例子一:

#include <iostream>

int main()
{
    auto func = [](int x)          // 定义一个lambda表达式
    {
        std::cout << x*x << std::endl;      // lambda表达式的具体内容
    };

    func(3);                      // 调用lambda表达式
    return 0;
}

运行结果:

[root c++]#./a.out
9

匿名lambda表达式例子:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main()
{
    vector<int> v = {3, 1, 8, 5, 0};     // 标准容器
    cout << *find_if(begin(v), end(v),   // 标准库里的查找算法
                  [](int x)                             // 匿名lambda表达式,不需要auto赋值
                  {
                      return x >= 5;        // 用做算法的谓词判断条件
                  }                               // lambda表达式结束
    )
    << endl;                        // 语句执行完,lambda表达式就不存在了
    return 0;
}

find_if返回范围 [first, last) 中满足特定判别标准的首个元素,返回值是指向首个满足条件的迭代器,或若找不到这种元素则为 last 。
程序运行结果:

[root c++]#./a.out
8

如果没有符合条件的元素则返回0

[root c++]#./a.out
0