C 语言

C++ 字面量

C++中的字典字面量

You can actually do this:

std::map<std::string, int> mymap = {{"one", 1}, {"two", 2}, {"three", 3}};

What is actually happening here is that std::map stores an std::pair of the key value types, in this case std::pair<const std::string,int>. This is only possible because of c++11’s new uniform initialization syntax which in this case calls a constructor overload of std::pair<const std::string,int>. In this case std::map has a constructor with an std::intializer_list which is responsible for the outside braces.

So unlike python’s any class you create can use this syntax to initialize itself as long as you create a constructor that takes an initializer list (or uniform initialization syntax is applicable)

https://stackoverflow.com/a/20230177/1061155

make 和 premake

Rule

target: dependencies(sepreated by spaces)
    command(s) to run to build the target from dependencies

Basic usage

in most projects, we have a .h file for functiont interfaces, and the whole program depends on it.
typically, all .o files are compiled from corresponding .c source files

cc = gcc              # marco
prom = calc
deps = calc.h         # the one .h to rule them all
obj = main.o getch.o getop.o stack.o
$(prom): $(obj)
    $(CC) -o $(prom) $(obj)
%.o: %.c $(deps)      #pattern rule, which means all .o depends on all .c and $(deps)
    $(CC) -c $< -o $@ # $< means the depender and $@ means the dependee

Functions

we can make the file even smarter by using makefile funcions
Makefile function syntax $(func params)

cc = gcc
prom = calc
deps = $(shell find ./ -name "*.h") # find all header files using the builtin shell function
src = $(shell find ./ -name "*.c")
obj = $(src:%.c=%.o) 
$(prom): $(obj)
    $(CC) -o $(prom) $(obj)
%.o: %.c $(deps)
    $(CC) -c $< -o $@
clean:                              # empty target to run a commnad
    rm -rf $(obj) $(prom)

Reference

http://www.epubit.com.cn/article/546

Autotools

Autotools is a collection of three tools:

  1. autoconf — This is used to generate the “configure” shell script. As I mentioned earlier, this is the script that analyzes your system at compile-time. For example, does your system use “cc” or “gcc” as the C compiler?
  2. automake — This is used to generate Makefiles. It uses information provided by Autoconf. For example, if your system has “gcc”, it will use “gcc” in the Makefile. Or, if it finds “cc” instead, will use “cc” in the Makefile.
  3. libtool — This is used to create shared libraries, platform-independently.
$ autoscan                    #--> creates `autoscan.scan` file
$ mv autoscan.scan to `autoscan.ac` file
$ autoconf                    # --> use autoconf.ac to create `configure` file
# we need a `makefile.in` as the template for configure file to use
$ automake                    # --> use `makefile.in` to create `makefile`
$ autoheader                  # --> generate `config.h.in`
$ ./configure                 # --> generate the makefile and config.h
$ make && make install        # horry!

To be continued at:
http://markuskimius.wikidot.com/programming:tut:autotools:5

premake 基本用法

premake 可以生成makefile
premake gmake
生成的 makefile 支持
make 默认构建
make help 查看帮助文件
make config=release 按照 release 构建
make clean 清除构建
make config=release clean 清除 release 构建

premake5脚本的名字是 premake5.lua, 本质上就是一个 lua 脚本, 每一行都是一个函数调用, 因为参数恰好是字符串或者 table, 所以可以省略括号

-- premake5.lua
workspace "HelloWorld"
   configurations { "Debug", "Release" }
project "HelloWorld"
   kind "ConsoleApp"
   language "C"
   targetdir "bin/%{cfg.buildcfg}"
files { "**.h", "**.c" }
filter "configurations:Debug"
      defines { "DEBUG" }
      flags { "Symbols" }
filter "configurations:Release"
      defines { "NDEBUG" }
      optimize "On"

常用函数

workspace   相当于 vs 的 solution   
project project 
kind     指定编译目标类型   ConsoleApp WindowedApp    SharedLib StaticLib     
location     指定编译目标目录   
define   定义常量   
files   添加文件    文件名 *.ext **.ext
removefiles  屏蔽文件   
links   链接库 
libdirs 添加库目录   
configurations  指定不同的编译选项   需要通过 filter {"configurations:<name>"} 指定具体选项
platforms    指定不同的平台    和 vs 的 platform 类似, 但是也需要使用 filter 定义
includedirs 添加 include 目录   
optimize     设置优化选项  Off On
buildoptions    编译选项    比如-std=c99

作用范围

作用范围会发生继承, 使用 workspce '*' 或者 project '*'代表选中了所有workspace 或者 project

premake install

newaction {
   trigger     = "install",
   description = "Install the software",
   execute = function ()
      -- copy files, etc. here
   end
}

C的编译、调试与静态检查

# 使用Clang/gcc 常用的选项

“`
-std=c11 设定标准为 c11
-Wall 显示所有警告
-O2 二级优化, 通常够用了
-march=native 释放本地CPU所有指令
-g 如果需要使用 gdb 调试的话

-Dmarco=value 定义宏
-Umarco undef 宏
-Ipath 添加到 include
-llibrary 链接到 liblibbrary.a 文件
-Lpath 添加到链接

-c 只编译而不链接
-S 生成汇编代码, 但是不生成机器代码
-E 只预处理

-fopenmp 打开 OpenMP 支持
-pthread 添加 pthread 支持
-Werror 把所有 warning 显示为 error
“`

# 如何生成静态库, 动态库

see:

1. http://www.adp-gmbh.ch/cpp/gcc/create_lib.html
2. http://stackoverflow.com/questions/2734719/how-to-compile-a-static-library-in-linux

## 静态库

静态库的创建原理是把不同的目标文件打包在一起, 所以分两步

1. gcc -c -o mean.o mean.c
2. ar rcs libmean.a mean.o
生成的库的名字多了 lib 和. a

使用 `gcc -static main.c -L. -lmean -o a.out`

## 动态库

动态库需要生成 PIC(地址无关代码),

-Wl 后面的命令会传递给链接器

“`
gcc -c -fPIC calc_mean.c -o calc_mean.o # 大写的-fPIC 比- fpic 更通用, 虽然在 x86平台上没有区别
gcc -shared -Wl,-soname,libmean.so.1 -o libmean.so.1.0.1 calc_mean.o
“`

使用 `gcc main.c -o a.out -L. -lmean`

“`
LD_LIBRARY_PATH=.
./dynamically_linked
“`

## 最常用的指令

cc -Wall -std=c11 source.c -o executable
g++ -Wall -std=c++11 source.cc -o executable

# tips

处理二进制数据时尽量使用uint8_t,而不要使用char

函数的参数类型(接口)尽量使用 `void*`
不要这么做:

“`
void processAddBytesOverflow(uint8_t *bytes, uint32_t len) {
for (uint32_t i = 0; i < len; i++) { bytes[0] += bytes[i]; } } ``` 这么做: ``` void processAddBytesOverflow(void *input, uint32_t len) { uint8_t *bytes = input; for (uint32_t i = 0; i < len; i++) { bytes[0] += bytes[i]; } } ``` 来自

提交仓库前,统一格式,而不应该在编写过程中注意

不要使用malloc,总是使用calloc,因为清零的性能损失太小了,但是却经常忘记. 这一点存疑

尽量保证在编写内存获取代码的时候就写好释放代码

## 内存泄漏的排查

核心思想,malloc/free不配对

“`
windows,使用
#define _CRTDBG_MAP_MALLOC
#include

_CrtDumpMemoryLeaks();
“`
linux,使用mtrace实现动态检查
使用valgrind实现静态检查
valgrind –leak-check=full ./a.out
注意查看definitely lost和possible lost

## 调试的工具

– valgrind 排查内存问题
– strace/ltrace 查看系统调用和库调用
– pmap 查看内存使用情况

## 测试

## rr

mozilla’s rr is a promising tool to replace gdb. it can replay the recored execution of a program, so you can replay it until you find out the bug.

http://rr-project.org/

pthread

pthreads online reference

https://computing.llnl.gov/tutorials/pthreads/
http://pages.cs.wisc.edu/~travitch/pthreads_primer.html

pthread does not define semphore

http://blog.jobbole.com/59676/ ACTOR vs PROACTOR

基础 socket 编程

创建与使用 socket, 一个 echo server 和 client

socket 客户端的四个步骤:

1. 使用 socket 函数创建连接
2. 使用 connect 连接到服务器
3. 使用 send 和 recv 接收和发送消息
4. 使用 close 关闭连接

第一步 创建一个 TCP socket
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
返回的 sock 可以看做一个 handle, 本质上是一个文件描述符( file descriptor) , 小于0表示错误

表示 socket 地址的结构 sockaddr_in, 其中 in 表示 internet, 不是input

“`
struct sockaddr_in {
__uint8_t sin_len;
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
“`

其中 sin_family和 sin_port和 sin_addr.s_addr 字段必须填写

使用 inet_pton把 IP 字符串转化为32位整形
端口号需要使用 htons 转化

下一步就是连接了, 注意 connect 和好多函数都要求把参数从 sockaddr_in 转化到 sockaddr, 然后再跟一个参数表示实际的数据结构的长度. 这是因为 C 不支持多态.

第三步就是发送和接收数据, 我们使用 send 函数发送, send成功的话会返回发送成功的数据的长度, 可能和指定的数据不等, 因此需要判断是否发送成功. recv 也可能返回任意长度的数据, 因此需要在一个循环里接受. send 和 recv 都是阻塞的

“`
int send(int socket, const void* buffer, size_t length, int flags);
int recv(int socket, const void* buffer, size_t length, int flags);
“`

最后关闭连接

需要特别注意的是, 永远不要把来自其他主机的消息使用 pritnf 等不安全的函数打印出来

socket 服务器的四个流程

1. 使用 socket 函数创建链接
2. 使用 bind 函数绑定端口号
3. 使用 listen打开监听模式
4. 使用 accept 接收新的客户, 并为之服务
a. accept 会为每个客户创建新的 socket
b. 通过这个 socket 与客户端之间 send/recv
c. 关闭该 socket
5. 关闭监听的 socket

第一步,创建 socket, 和客户端完全一样
注意, 服务端的地址有所不同, 为了接受任何一个客户, 我们需要监听服务器的所有 IP (服务器可能有多个网卡, 也就有多个 IP).

第二步, 使用 bind 绑定到对应的 ip 和端口号, listen 把 socket 设定为监听状态. 注意 listen 还有一个参数代表

第三步, 使用 accept 开始阻塞地等待客户端. 注意 accept 实际上有两个返回值,一个通过函数返回的形式返回一个客户端的 socket, 还有一个通过指针的形式返回一个 sockaddr_in

第四步,接下来, 把每一个获得的 client socket 都对应地处理

关闭主 socket

最后需要注意的是,

1. 用到的一些转化函数

inet_pton/inetntop htons, ntohs, htonl, ntohl

2. socket 为地址定义了通用的数据结构 sockaddr, 也就是 sockaddr_in 等结构应该看作是 sockaddr 的子类. 在 connect 和 bind 等函数中的参数都是 sockaddr* 以及实际数据类型的 size.

# 使用 UDP socket

相比来说, UDP 就非常简单了, 相对于 TCP 在 IP 层之上提供个各种服务, UDP 只添加了两项, 端口号和校验, 而且只是简单地把校验出错的包丢掉.

C/C++ 中的 RAII

# RAII 的好处

* 异常安全
* 保证匹配
* 防止内存泄露

# RAII in C

This is inherent implementation dependent, since the Standard doesn’t include such a possibility. For GCC, the cleanup attribute runs a function when a variable goes out of scope:

“`
#include
void scoped(int * pvariable) {
printf(“variable (%d) goes out of scope\n”, *pvariable);
}
int main(void) {
printf(“before scope\n”);
{
int watched __attribute__((cleanup (scoped)));
watched = 42;
}
printf(“after scope\n”);
}
“`

Prints:
“`
before scope
variable (42) goes out of scope
after scope
“`

C语言中的 vargs

“`
int max(int n, …) {
va_list arg_pointer;
int result = INT_MIN;

va_start(arg_pointer, n);
for (int i = 0; i < n; i++) { int arg = va_arg(arg_pointer, int); if (arg > result)
result = arg;
}
va_end(arg_pointer);

return result;
}
“`

Reference
——

1. http://www.cnblogs.com/chinazhangjie/archive/2012/08/18/2645475.html

2. http://wiki.jikexueyuan.com/project/c-advance/other.html

C语言中已经废弃的函数

由于安全性原因,C 标准库中的不少函数已经被废弃了,同时增加了对应的替换函数。

“`
sprintf –> snprintf
gets –> fgets
strcat –> strncat
strcpy –> strncpy
“`

`strtok`函数用于分解字符串,需要一个变量保存内部分解进度
`strtok_r`是它的可重入版本

C语言中的 setjmp/longjmp

首先吐槽一下这个缩写, 好好地 jump 单词才四个字母不用, 非要缩写成 jmp 三个字母, 每次都打错, 蛋疼

在 C 中,goto 语句是不能跨越函数的,而执行这类跳转功能的是 `setjmp` 和 `longjmp` 宏。这两个宏对于处理发生在深层嵌套函数调用中的出错情况是非常有用的。

此即为:非局部跳转。非局部指的是,这不是由普通 C 语言 goto 语句在一个函数内实施的跳转,而是在栈上跳过若干调用帧,返回到当前函数调用路径的某个函数中。

#include
int setjmp (jmp_buf env) ; /*设置调转点*/
void longjmp (jmp_buf env, int val) ; /*跳转*/

`setjmp` 参数 env 的类型是一个特殊类型 `jmp_buf`。这一数据类型是某种形式的数组,其中存放 在调用 `longjmp` 时能用来恢复栈状态的所有信息。因为需在另一个函数中引用 env 变量,所以应该将 env 变量定义为全局变量。

`longjmp` 参数 val,它将成为从 setjmp 处返回的值。

#include
#include
static jmp_buf buf;
void second(void){
printf(“second\n”);
longjmp(buf,1);
// 跳回setjmp的调用处使得setjmp返回值为1
}
void first(void) {
second();
printf(“first\n”);
// 不可能执行到此行
}
int main(){
if (!setjmp(buf)) {
// 进入此行前,setjmp返回0
first();
} else {
// 当longjmp跳转回,setjmp返回1,因此进入此行
printf(“main\n”);
}
return 0;
}

直接调用 setjmp 时,返回值为 0,这一般用于初始化(设置跳转点时)。以后再调用 longjmp 宏时用 env 变量进行跳转。程序会自动跳转到 setjmp 宏的返回语句处,此时 setjmp 的返回值为非 0,由 longjmp 的第二个参数指定。
一般地,宏 `setjmp` 和 `longjmp` 是成对使用的,这样程序流程可以从一个深层嵌套的函数中返回。