下列程序编译是否会报错:
// 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
/* 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(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