Table of Contents

1 变量和基本类型

1.1 类型

  • 是用 char 时,需要明确指明 unsigned char 还是 signed char ,否则根据系统的不同而不同。
  • long long >= long >= int >= short

1.1.1 如何选择类型

  1. 若不为负,选unsigned
  2. intlong long ,而不用 long ,因为 long 一般和 int 一样。
  3. 明确指定 char 的类型
  4. 一般用 double 而不用 float

1.1.2 Static

  • static member function: can use ClassName::function() directly
  • static member variable: only one object for all instance of the class
  • static variable: A static variable inside a function keeps its value between invocations. A static global variable or a function is "seen" only in the file it's declared in
  • static functions: Static functions are not visible outside of the C file they are defined in.

1.1.3 转义

代码 含义
\n 换行
\r 回车
\v 纵向制表
\b 退格

1.1.4 字面值类型


符号 含义 类型
u Unicode16字符 char16_t
U Unicode32 char32_t
L 宽字符 wchar_t L'a'
u8 utf8字符串 char u8"hi"


符号 类型 适用于
U Unsigned 整型
L long 整型
LL long long 整型
F float 浮点型
L long double 浮点型


1.1.5 初始化

int a=0;
int a={0};
int a{0}; //C++11
int a(0);

定义于任何函数体之 内置类型变量 被初始化为0. 定义于任何函数体之 内置类型变量 不初始化。


extern int i; // 声明
extern int i=1; // 定义,不可在函数体内部

2 Language Specification

2.1 varargs functions (variadic functions)

a function to take a variable number or type of arguments

Receiving of arguments: You actually need both the number and type of the arguments to retrieve.

  1. create va_list
  2. initialize using va_start
  3. access using multiple va_arg. The first gives you the first arg, and so on.
  4. call va_end

The macro prototypes (defined in stdarg.h). Note these are macros, not functions.

  • void va_start (va_list ap, last-required)
  • type va_arg (va_list ap, type)
  • void va_end (va_list ap)

An real world example:

#include <stdarg.h>
#include <stdio.h>

add_em_up (int count,...)
  va_list ap;
  int i, sum;
  va_start (ap, count);         /* Initialize the argument list. */
  sum = 0;
  for (i = 0; i < count; i++)
    sum += va_arg (ap, int);    /* Get the next argument value. */
  va_end (ap);                  /* Clean up. */
  return sum;
main (void)
  /* This call prints 16. */
  printf ("%d\n", add_em_up (3, 5, 5, 6));
  /* This call prints 55. */
  printf ("%d\n", add_em_up (10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
  return 0;

2.2 Variadic Macros

#define eprintf(format, ...) fprintf (stderr, format, __VA_ARGS__)

The __VA_ARGS__ will be replaced with whatever in .... The variable argument is completely macro-expanded before it is inserted into the macro expansion, just like an ordinary argument.

3 idioms

typedef enum { false, true } bool;

4 Cast

4.1 In a word

  • static_cast: ordinary type conversions.
  • dynamic_cast: converting pointers/references within an inheritance hierarchy.
  • reinterpret_cast: low-level reinterpreting of bit patterns. Use with extreme caution.
  • const_cast: casting away const/volatile. Avoid this unless you are stuck using a const-incorrect API.

4.2 C style cast: DO NOT USE

4.3 Static Cast

static_cast is the first cast you should attempt to use. It does things like implicit conversions between types (such as int to float, or pointer to void*), and it can also call explicit conversion functions (or implicit ones). In many cases, explicitly stating static_cast isn't necessary, but it's important to note that the T(something) syntax is equivalent to (T)something and should be avoided (more on that later). A T(something, something_else) is safe, however, and guaranteed to call the constructor.

static_cast can also cast through inheritance hierarchies. It is unnecessary when casting upwards (towards a base class), but when casting downwards it can be used as long as it doesn't cast through virtual inheritance. It does not do checking, however, and it is undefined behavior to static_cast down a hierarchy to a type that isn't actually the type of the object.

4.4 Const Cast

const_cast can be used to remove or add const to a variable; no other C++ cast is capable of removing it (not even reinterpret_cast). It is important to note that modifying a formerly const value is only undefined if the original variable is const; if you use it to take the const off a reference to something that wasn't declared with const, it is safe. This can be useful when overloading member functions based on const, for instance. It can also be used to add const to an object, such as to call a member function overload.

const_cast also works similarly on volatile, though that's less common.

4.5 Dynamic Cast

dynamic_cast is almost exclusively used for handling polymorphism. You can cast a pointer or reference to any polymorphic type to any other class type (a polymorphic type has at least one virtual function, declared or inherited). You can use it for more than just casting downwards – you can cast sideways or even up another chain. The dynamic_cast will seek out the desired object and return it if possible. If it can't, it will return NULL in the case of a pointer, or throw std::bad_cast in the case of a reference.

dynamic_cast has some limitations, though. It doesn't work if there are multiple objects of the same type in the inheritance hierarchy (the so-called 'dreaded diamond') and you aren't using virtual inheritance. It also can only go through public inheritance - it will always fail to travel through protected or private inheritance. This is rarely an issue, however, as such forms of inheritance are rare.

4.6 Reinterpret Cast

reinterpret_cast is the most dangerous cast, and should be used very sparingly. It turns one type directly into another - such as casting the value from one pointer to another, or storing a pointer in an int, or all sorts of other nasty things. Largely, the only guarantee you get with reinterpret_cast is that normally if you cast the result back to the original type, you will get the exact same value (but not if the intermediate type is smaller than the original type). There are a number of conversions that reinterpret_cast cannot do, too. It's used primarily for particularly weird conversions and bit manipulations, like turning a raw data stream into actual data, or storing data in the low bits of an aligned pointer.

4.7 C style cast

C casts are casts using (type)object or type(object). A C-style cast is defined as the first of the following which succeeds:

  • const_cast
  • static_cast (though ignoring access restrictions)
  • static_cast (see above), then const_cast
  • reinterpret_cast
  • reinterpret_cast, then const_cast

It can therefore be used as a replacement for other casts in some instances, but can be extremely dangerous because of the ability to devolve into a reinterpret_cast, and the latter should be preferred when explicit casting is needed, unless you are sure static_cast will succeed or reinterpret_cast will fail. Even then, consider the longer, more explicit option.

C-style casts also ignore access control when performing a static_cast, which means that they have the ability to perform an operation that no other cast can. This is mostly a kludge, though, and in my mind is just another reason to avoid C-style casts.

5 Compound Literals

A compound literal looks like a cast containing an initializer. Its value is an object of the type specified in the cast, containing the elements specified in the initializer; it is an lvalue.

5.1 Example

struct foo {int a; char b[2];} structure;

The constructing:

structure = ((struct foo) {x + y, 'a', 0});

5.2 more examples

char **foo = (char *[]) { "x", "y", "z" };

5.3 static

Value in the compound literals must be constant.

static struct foo x = (struct foo) {1, 'a', 'b'};
static int y[] = (int []) {1, 2, 3};
static int z[] = (int [3]) {1};

6 extern

  • extern means extend the visibility of a variable or function.
  • Declaration can be many times, but definition can only appear once.
  • Definition will allocate memory, but declaration will never allocate memory.

6.1 Function

For function declare and define, `extern` is added by compiler by default. So use or not use `extern` for functions are equivalent.

6.2 Variable

define a variable

int a;

declare a variable

extern int a;

This can be used so that in this file, a refer to the variable actually defined and allocated in another file. The definition of the variable in the other file does not have extern, but it is still available by this file …

An exception: extern a variable with initialization

extern int a = 8;

This will be treated as definition.

6.3 extern "C"

extern "C" makes a function-name in C++ have 'C' linkage (compiler does not mangle the name) so that client C code can link to (i.e use) your function using a 'C' compatible header file that contains just the declaration of your function.

  1. Since C++ has overloading of function names and C does not
  2. C++ compiler cannot just use the function name as a unique id to link to, so it mangles the name by adding information about the arguments
  3. A C compiler does not need to mangle the name since you can not overload function names in C

When you state that a function has extern "C" linkage in C++, the C++ compiler does not add argument/parameter type information to the name used for linkage.

6.4 syntax

  • can specify "C" linkage to each individual declaration/definition explicitly
  • use a block to group a sequence of declarations/definitions to have a certain linkage:
extern "C" void foo(int);
extern "C"
   void g(char);
   int i;

7 restrict

The restrict keyword is a declaration of intent given by the programmer to the compiler.

It says that for the lifetime of the pointer, only it or a value directly derived from it (such as pointer + 1) will be used to access the object to which it points.

This limits the effects of pointer aliasing, aiding optimizations.

If the declaration of intent is not followed and the object is accessed by an independent pointer, this will result in undefined behavior.

8 volatile

When your code works without compiler optimization, but fails when you turn optimization on, perhaps it is because of `volatile`.

If compiler found that around a variable, no one change it, it will do some optimization based on this. Maybe remove unnecessary code which it thinks will never execute.

The keyword tells the compiler that the value of the variable may change at any time. It may change unexpectedly, so DO NOT optimize the code when you compiler think it would not change.

8.1 syntax

declare a variable(both are equalvalent)

volatile int foo;
int volatile foo;

declare pointers to volatile varialbes(common usage)

volatile uint8_t *pReg;
uint8_t volatile *pReg;

volatile pointers to non-volatile data(very rare)

int * volatile p;

volatile pointer to volatile variable(also rare)

int volatile * volatile p;

8.2 When to use it

8.2.1 Memory-mapped peripheral registers

The register's value may change by hardware. But in the code, compiler cannot see it, so it may assume it is constant, and do some optimization.

uint8_t *pReg = (uint8_t) 0x1234;
while (*pReg==0) {}

Since no `volatile`, the assembly looks like:

  mov ptr, #0x1234
  mov a, @ptr
  bz loop

To fix it, use volatile to declare it:

uint8_t volatile *pReg = (uint8_t volatile *)0x1234

The assembly will be:

```asm mov ptr, #0x1234 loop: mov a, @ptr bz loop ```

8.2.2 Global variables modified by an ISR(Interrupt Service Routine)

Compiler will of course not know about interrupt. So when the global file can be modified by interrupt, we must tell it.

int volatile etx_rcvd = FALSE;
void main() {
  while(!ext_rcvd) {}
interrupt void rx_isr(void) {
  if (ETX == rx_char) {
    etx_rcvd = TRUE;

If no volatile, compiler will think the while condition always be true, thus never go out of the loop.

8.2.3 Global variables accessed by multiple tasks within a multi-threaded application

Compiler doesn't find the variable change near the code it is defined, so it may assume it is unchanged. While another task in the same time may change it, it is just like the interrupt.

9 Operator Precedence

Precedence Operator Description Associativity
0 :: scope resolution L to R
1 ++ -- Suffix increment and decrement  
  () Function call  
  [] Array subscripting  
  . Structure and union member access  
  -> Structure and union member access through pointer  
  (type){list} Compound literal(C99)  
2 ++ -- Prefix increment and decrement R to L
  + - Unary plus and minus  
  ! ~ Logical NOT and bitwise NOT  
  (type) Type cast  
  * dereference  
  & Address-of  
  _Alignof Alignment requirement(C11)  
  new, new[] Dynamic memory allocation  
  delete, delete[] Dynamic memory deallocation  
3 * / %   L to R
4 + - Addition and subtraction  
5 << >> Bitwise left shift and right shift  
6 < <= Compare  
  > >=    
7 = !    
8 & Bitwise AND  
9 ^ Bitwise XOR (exclusive or)  
10 l Bitwise OR (inclusive or)  
11 && Logical AND  
12 ll Logical OR  
13 ?: Ternary conditional R to L
14 throw    
  += -=    
  *= /= %=    
  <<= >>= Assignment by bitwise left shift and right shift  
  &= ^= l= Assignment by bitwise AND, XOR, and OR  
15 , Comma L to R

9.1 notes

9.1.1 For ?:

the middle of the conditional operator (between ? and :) is parsed as if parenthesized: its precedence relative to ?: is ignored

9.1.2 For C++

The operand of sizeof can't be a C-style type cast: the expression sizeof (int) * p is unambiguously interpreted as (sizeof(int)) * p, but not sizeof((int)*p).

9.1.3 In c++ table, the ?: is also in 14 cell

10 Unix Library


#include <unistd.h>
unsigned int sleep(unsigned int seconds); // seconds
int usleep(useconds_t useconds); // microseconds
int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);

There's no implementation of clock_gettime as in Unix, the following serves as an portable solution.

#ifdef __MACH__
#include <sys/time.h>
//clock_gettime is not implemented on OSX
int clock_gettime(int /*clk_id*/, struct timespec* t) {
    struct timeval now;
    int rv = gettimeofday(&now, NULL);
    if (rv) return rv;
    t->tv_sec  = now.tv_sec;
    t->tv_nsec = now.tv_usec * 1000;
    return 0;

use it like this

double get_time() {
  struct timespec ts;
  clock_gettime(CLOCK_REALTIME, &ts);
  double d = (double)ts.tv_sec + 1.0e-9*ts.tv_nsec;
  return d;

Author: Hebi Li

Created: 2017-03-27 Mon 14:36