C Basic Notes
编程习惯
Macro(宏)
括号
尽量添加足够的括号,减少宏定义的二义性
特殊用法
#
: 字符串化##
: 强制连接符do { ... } while (0)
: 防止语法错误
头文件
缺少标准库头文件
缺少函数原型
链接成功 - 链接器自动装载库函数,不影响程序执行 只警告,不报错
覆盖标准库函数原型
- 定义过多参数原型,调用时传入过多参数,函数正确执行(无视多余参数)
- 定义缺少参数原型,调用时传入不完整参数,函数错误执行,误把 0xc(%ebp),0x10(%ebp),…等更多内存单元当作函数参数
缺少宏定义
链接失败 - 宏定义会被识别为函数,但链接器查找不到相应库函数
防止重复包括头文件
#ifndef _FILENAME_H_
#define _FILENAME_H_
...
...
...
#endif
头文件不申请内存单元
除了全局共享静态变量外, 头文件中的定义不允许申请实际的内存单元
检查
边界检查
- 空/满栈检查
- 参数合法性检查 e.g elemSize > 0 检查
指针检查
- Alloctor 失败,需添加 NULL 检查:
- assert
- exit
类型转换
机器码转换
- 有符号类型转换: 进行符号扩展
- 无符号类型转换: 进行零扩展
Pointer Tips and Best Practice
Error Prone Pointers
int i = 37;
float f = *(float *)&i;
float f = 7.0;
short s = *(short *)&f;
- 悬挂指针
- 未初始化
- 改写未知区域
- 下标越界
- 内存上溢 e.g
gets(string);
- 指针相关运算符优先级与结合性
- 返回局部变量的地址
- 重复释放内存空间
- 内存泄漏 e.g 未释放空间/未释放部分深度空间(多维数组)
- 不能引用 void 指针指向的内存单元
Debugging Malloc
处理 void 指针
Tips: 中途运用强制类型转换,使得 void 指针可以执行指针加减运算
void *target = (char *)void_pointer + ...;
利用 void 指针实现 Generic
通用型 Swap 函数
void swap(void *vp1, void *vp2, int size) {
char buffer[size];
memcpy(buffer, vp1, size);
memcpy(vp1, vp2, size);
memcpy(vp2, buffer, size);
}
通用型 Search Function
实现
void *lsearch(void *key, void *base, int n, int elemSize,
int (*cmp_fn)(void *, void *)) {
for (int i = 0;i < n;i++) {
void * elemAddr = (char *)base + i * elemSize;
if (cmp_fn(key, elemAddr) == 0) {
return elemAddr;
}
}
return NULL;
}
int 实例
int IntCmp(void *elem1, void *elem2) {
int *ip1 = (int *)elem1;
int *ip2 = (int *)elem2;
return *ip1 - *ip2;
}
int array[] = {4, 2, 3, 7, 11, 6},
size = 6,
target = 7;
// 应进行强制类型转换
int * found = (int *)lsearch(&target, array, 6, sizeof(int), IntCmp);
if (found == NULL) {
printf("Not Found");
} else {
printf("Found");
}
string 实例
int StrCmp(void *vp1, void *vp2) {
// 必须进行强制类型转换
char *s1 = *(char **)vp1;
char *s2 = *(char **)vp2;
return strcmp(s1, s2);
}
char *notes[] = {"Ab", "F#", "B", "Gb", "D"},
*target = "Eb";
char ** found = lsearch(&target, notes, 5, sizeof(char *), StrCmp);
泛型数据结构
通用型栈
typedef struct {
void *elements;
int elemSize;
int logLen;
int allocLen;
} stack;
void StackNew(stack *s, int elemSize);
void StackDispose(stack *s);
void StackPush(stack *s, void *elemAddr);
void StackPop(stack *s, void *elemAddr);
void StackNew(stack *s, int elemSize) {
// 参数合法性检查
if (s->elemSize <= 0) {
perror("ElemSize <= 0");
return;
}
s->elemSize = elemSize;
s->logLen = 0;
s->allocLen = 4;
s->elements = (int *)malloc(s->allocLen * elemSize);
// NULL检查
if (s->elements == NULL) {
perror("No Mem");
exit(0);
}
}
void StackPush(stack *s, void *elemAddr) {
// 满栈检查
if (s->logLen == s->allocLen) {
s->allocLen *= 2;
s->elements = (int *)malloc(s->elements, s->allocLen * s->elemSize);
}
void *target = (char *)s->elements + s->logLen * s->elemSize;
memcpy(target, elemAddr, s->elemSize);
s->logLen++;
}
void StackPop(stack *s, void *elemAddr) {
// 空栈检查
if (s->logLen == 0) {
perror("Empty Stack");
return;
}
s->logLen--;
void *source = (char *)s->elements + s->logLen * s->elemSize;
memcpy(elemAddr, source, s->elemSize);
}
Tools
Valgrind - GitHub Repo
Useful Functions
memset
free
free 函数会回退 4/8 字节,取出 heap 块的长度/信息,根据此信息进行 heap 块的释放.
Strings
strdup
string duplicate - char *strdup(string)
封装 allocator 细节
strchr and strstr
返回字符/串在字符串中出现的位置(地址)
strtok
strcasecmp
getopt
解析命令行参数, 轻松地提取以 - 或 / 开头的参数
I/O
String Scanf
可以用作简易匹配读取函数
// 提取除 http:// 外的字符串
sscanf(buf, "http://%s", url_part);
Exceptions
perror(string) - 用来将上一个函数发生错误的原因输出到标准设备(stderr)
Process
Fork and Execve
- fork(): 创建当前进程的拷贝
- execve(): 用另一程序的代码代替当前进程的代码
int execve(char *filename, char *argv[], char *env_p[])
void fork_exec(char *path, char *argv[]) {
pid_t pid = fork();
if (0 != pid) {
printf("Parent: created a child %d\n", pid);
} else {
printf("Child: exec-ing new program now\n");
execv(path, argv);
}
printf("This line printed by parent only!\n");
}
Other
getpid()
.wait(int *child_status)
/waitpid(pid)
.exit()
.
Threads
PThread
typedef unsigned long int pthread_t;
/**
* create thread
* @param {指向线程标识符的指针} pthread_t *__thread
* @param {设置线程属性} __const pthread_attr_t *__attr
* @param {线程运行函数的起始地址} void *(*__start_routine) (void *)
* @param {运行函数的参数} void *__arg
*/
extern int pthread_create __P ((
pthread_t *__thread,
__const pthread_attr_t *__attr,
void *(*__start_routine) (void *), void *__arg)
);
/**
* 等待线程
* @param {被等待的线程标识符} pthread_t __th
* @param {一个用户定义的指针,它可以用来存储被等待线程的返回值} void **__thread_return
*/
extern int pthread_join __P ((pthread_t __th, void **__thread_return));
/**
* 退出线程
* @param {函数的返回代码} (void *__retval)) __attribute__ ((__noreturn__)
*/
extern void pthread_exit __P ((void *__retval)) __attribute__ ((__noreturn__));
// 一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用pthread_join的线程则返回错误代码 ESRCH
// 以下为互斥锁相关函数
pthread_mutex_init
pthread_mutexattr_init
/**
* 设置属性 pshared
* PTHREAD_PROCESS_PRIVATE
* PTHREAD_PROCESS_SHARED
*/
pthread_mutexattr_setpshared
/**
* 设置互斥锁类型
* PTHREAD_MUTEX_NORMAL
* PTHREAD_MUTEX_ERRORCHECK
* PTHREAD_MUTEX_RECURSIVE
* PTHREAD_MUTEX_DEFAULT
*/
pthread_mutexattr_settype
pthread_mutex_lock
pthread_mutex_unlock
pthread_delay_np
InitThreadPackage;
ThreadNew;
ThreadSleep;
RunAllThreads;
SemaphoreNew(int > 0);
SemaphoreWait(lock);
SemaphoreSignal(lock);
Semaphore
- 哲学家就餐问题
- 将 Semaphore 变量的值在允许范围内(不至于使得线程锁失效)使得其取最大值,减少线程阻塞
- EmptyBuf 8, FullBuf 0
- 双向通信,互相唤醒 -
Writer:sw(empty),ss(full);
Reader:sw(full),ss(empty);
void SellTickets(int agent, int *ticketsNum, Semaphore lock) {
while (true) {
// 当 lock == 0 时,当前进程阻塞, 等待 lock > 0
// 当 lock > 0 时, 当前进程继续进行, 并且 lock--
SemaphoreWait(lock);
if (*ticketsNum == 0) break; // 票已售磬
(*ticketsNum)--; // 售出一张票
printf("Sell One Ticket.\n");
// lock++ 使得 lock > 0
// 若有其他进程调用了SemaphoreWait, 且因之前 lock == 0 而被阻塞, 则此时其他进程可继续进行
SemaphoreSignal(lock);
}
// break to here
// 作用同循环内的 Signal 函数
SemaphoreSignal(lock);
}
联合体
- 机器码 e.g 理解 IEEE 754 标准
- 区分大/小端模式
Naming Conventions
常用缩写词
原词 | 缩写 |
---|---|
addition | add |
answer | ans |
array | arr |
average | avg |
buffer | buf 或 buff |
capture | cap 或 capt |
check | chk |
count | cnt |
column | col |
control | ctrl |
decode | dec |
define | def |
delete | del |
destination | dst 或 dest |
display | disp |
division | div |
encode | enc |
environment | env |
error | err |
float | flt |
frequency | freq |
header | hdr |
index | idx |
image | img |
increment | inc |
initialize | init |
iteration | itr |
length | len |
memory | mem |
middle | mid |
make | mk |
message | msg |
multiplication | mul |
number | num |
operand | opnd |
optimization | opt |
operator | optr |
packet | pkt |
position | pos |
previous | pre/prev |
payload | type |
pointer | ptr/pt |
return | code |
record | rcd/rc |
receive | recv |
result | res |
return | ret |
source | src |
stack | stk |
string | str |
subtraction | sub |
table | tab |
temporary | tmp 或 temp |
total | tot |
time | stamp |
value | val |
Header File
防止其他文件重复#include 本文件
#ifndef MONGOOSE_HEADER_INCLUDED
#define MONGOOSE_HEADER_INCLUDED
/*.................................
* do something here
*.................................
*/
#endif /* MONGOOSE_HEADER_INCLUDED */
C Standard Library
Assert
- 关闭断言
#define NDEBUG
#include <assert.h>
- 开启断言
#undef NDEBUG
#include <assert.h>
Types
- 可检测字符
getc、fgetc、getchar 函数可返回值(EOF 值/unsigned char 类型)
- 不可检测字符
非 EOF 值/非 unsigned char 类型(会引发严重错误)
Errno
- errno 的值在程序启动时为零,但是不会被任何库函数设为零
errno = 0;
y = sqrt(x);
if (errno != 0) {
printf("invalid x : %e\n", x);
}
#ifndef _I386_ERRNO_H
#define _I386_ERRNO_H
#define EPERM 1 /* Operation not permitted */
#define ENOENT 2 /* No such file or directory */
#define ESRCH 3 /* No such process */
#define EINTR 4 /* Interrupted system call */
#define EIO 5 /* I/O error */
#define ENXIO 6 /* No such device or address */
#define E2BIG 7 /* Arg list too long */
#define ENOEXEC 8 /* Exec format error */
#define EBADF 9 /* Bad file number */
#define ECHILD 10 /* No child processes */
#define EAGAIN 11 /* Try again */
#define ENOMEM 12 /* Out of memory */
#define EACCES 13 /* Permission denied */
#define EFAULT 14 /* Bad address */
#define ENOTBLK 15 /* Block device required */
#define EBUSY 16 /* Device or resource busy */
#define EEXIST 17 /* File exists */
#define EXDEV 18 /* Cross-device link */
#define ENODEV 19 /* No such device */
#define ENOTDIR 20 /* Not a directory */
#define EISDIR 21 /* Is a directory */
#define EINVAL 22 /* Invalid argument */
#define ENFILE 23 /* File table overflow */
#define EMFILE 24 /* Too many open files */
#define ENOTTY 25 /* Not a typewriter */
#define ETXTBSY 26 /* Text file busy */
#define EFBIG 27 /* File too large */
#define ENOSPC 28 /* No space left on device */
#define ESPIPE 29 /* Illegal seek */
#define EROFS 30 /* Read-only file system */
#define EMLINK 31 /* Too many links */
#define EPIPE 32 /* Broken pipe */
#define EDOM 33 /* Math argument out of domain of func */
#define ERANGE 34 /* Math result not representable */
#define EDEADLK 35 /* Resource deadlock would occur */
#define ENAMETOOLONG 36 /* File name too long */
#define ENOLCK 37 /* No record locks available */
#define ENOSYS 38 /* Function not implemented */
#define ENOTEMPTY 39 /* Directory not empty */
#define ELOOP 40 /* Too many symbolic links encountered */
#define EWOULDBLOCK EAGAIN /* Operation would block */
#define ENOMSG 42 /* No message of desired type */
#define EIDRM 43 /* Identifier removed */
#define ECHRNG 44 /* Channel number out of range */
#define EL2NSYNC 45 /* Level 2 not synchronized */
#define EL3HLT 46 /* Level 3 halted */
#define EL3RST 47 /* Level 3 reset */
#define ELNRNG 48 /* Link number out of range */
#define EUNATCH 49 /* Protocol driver not attached */
#define ENOCSI 50 /* No CSI structure available */
#define EL2HLT 51 /* Level 2 halted */
#define EBADE 52 /* Invalid exchange */
#define EBADR 53 /* Invalid request descriptor */
#define EXFULL 54 /* Exchange full */
#define ENOANO 55 /* No anode */
#define EBADRQC 56 /* Invalid request code */
#define EBADSLT 57 /* Invalid slot */
#define EDEADLOCK EDEADLK
#define EBFONT 59 /* Bad font file format */
#define ENOSTR 60 /* Device not a stream */
#define ENODATA 61 /* No data available */
#define ETIME 62 /* Timer expired */
#define ENOSR 63 /* Out of streams resources */
#define ENONET 64 /* Machine is not on the network */
#define ENOPKG 65 /* Package not installed */
#define EREMOTE 66 /* Object is remote */
#define ENOLINK 67 /* Link has been severed */
#define EADV 68 /* Advertise error */
#define ESRMNT 69 /* Srmount error */
#define ECOMM 70 /* Communication error on send */
#define EPROTO 71 /* Protocol error */
#define EMULTIHOP 72 /* Multi hop attempted */
#define EDOTDOT 73 /* RFS specific error */
#define EBADMSG 74 /* Not a data message */
#define EOVERFLOW 75 /* Value too large for defined data type */
#define ENOTUNIQ 76 /* Name not unique on network */
#define EBADFD 77 /* File descriptor in bad state */
#define EREMCHG 78 /* Remote address changed */
#define ELIBACC 79 /* Can not access a needed shared library */
#define ELIBBAD 80 /* Accessing a corrupted shared library */
#define ELIBSCN 81 /* .lib section in a.out corrupted */
#define ELIBMAX 82 /* Attempting to link in too many shared libraries */
#define ELIBEXEC 83 /* Cannot exec a shared library directly */
#define EILSEQ 84 /* Illegal byte sequence */
#define ERESTART 85 /* Interrupted system call should be restarted */
#define ESTRPIPE 86 /* Streams pipe error */
#define EUSERS 87 /* Too many users */
#define ENOTSOCK 88 /* Socket operation on non-socket */
#define EDESTADDRREQ 89 /* Destination address required */
#define EMSGSIZE 90 /* Message too long */
#define EPROTOTYPE 91 /* Protocol wrong type for socket */
#define ENOPROTOOPT 92 /* Protocol not available */
#define EPROTONOSUPPORT 93 /* Protocol not supported */
#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
#define EPFNOSUPPORT 96 /* Protocol family not supported */
#define EAFNOSUPPORT 97 /* Address family not supported by protocol */
#define EADDRINUSE 98 /* Address already in use */
#define EADDRNOTAVAIL 99 /* Cannot assign requested address */
#define ENETDOWN 100 /* Network is down */
#define ENETUNREACH 101 /* Network is unreachable */
#define ENETRESET 102 /* Network dropped connection because of reset */
#define ECONNABORTED 103 /* Software caused connection abort */
#define ECONNRESET 104 /* Connection reset by peer */
#define ENOBUFS 105 /* No buffer space available */
#define EISCONN 106 /* Transport endpoint is already connected */
#define ENOTCONN 107 /* Transport endpoint is not connected */
#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */
#define ETOOMANYREFS 109 /* Too many references: cannot splice */
#define ETIMEDOUT 110 /* Connection timed out */
#define ECONNREFUSED 111 /* Connection refused */
#define EHOSTDOWN 112 /* Host is down */
#define EHOSTUNREACH 113 /* No route to host */
#define EALREADY 114 /* Operation already in progress */
#define EINPROGRESS 115 /* Operation now in progress */
#define ESTALE 116 /* Stale NFS file handle */
#define EUCLEAN 117 /* Structure needs cleaning */
#define ENOTNAM 118 /* Not a XENIX named type file */
#define ENAVAIL 119 /* No XENIX semaphores available */
#define EISNAM 120 /* Is a named type file */
#define EREMOTEIO 121 /* Remote I/O error */
#define EDQUOT 122 /* Quota exceeded */
#define ENOMEDIUM 123 /* No medium found */
#define EMEDIUMTYPE 124 /* Wrong medium type */
#endif
Float and Limits
- 宏定义:
CHAR/UCHAR/SCHAR/SHRT/USHRT/INT/UINT/LONG/ULONG/FLT/DBL/LDBL
有关的MIN/MAX/EPSILON
Locale
- 实现时间/单位/货币等一系列的国际化
- 常用函数
_CRTIMP char * __cdecl setlocale(int, const char *);
_CRTIMP struct lconv * __cdecl localeconv(void);
- int 值
#define LC_ALL 0
#define LC_COLLATE 1
#define LC_CTYPE 2
#define LC_MONETARY 3
#define LC_NUMERIC 4
#define LC_TIME 5
Math
数学函数库(包括后缀 f(float)/l(long double))
SetJMP
- 常用函数
int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);
- 使用:用于 if/else、loop、switch 语句
- 直接调用 setjmp 函数时,返回值为 0;
- 调用 longjmp 函数时,若 val 值不为 0,则跳转至上次 setjmp 返回值为 0 处,继续向后执行语句
- 功能
- 实现非本地(局部)跳转(跨越多层函数调用栈进行跳转)
- 实现类 Java 异常机制(异常抛出及捕获)
Signal
信号处理程序中所有数据应为 volatile 类型
_CRTIMP int __cdecl raise(int);
_CRTIMP void (__cdecl * __cdecl signal(int, void (__cdecl *)(int)))(int);
Stdarg
用于编写可变参数函数
void printargs(int arg1, ...) /* 输出所有int类型的参数,直到-1结束 */
{
va_list ap;
int i;
va_start(ap, arg1);
for (i = arg1; i != -1; i = va_arg(ap, int))
printf("%d ", i);
va_end(ap);
putchar('\n');
}
Stddef
- 宏
- NULL Null 指针常量
offsetof(type, member-designator)
获得字段在结构体中的偏移量
- 类型
ptrdiff_t
带符号的整数类型, 用来表示指针相减的结果类型wchar_t
宽字符类型size_t
无符号整数类型, 用来表示 sizeof 操作符的结果类型
String
- men 系函数:操作任意字符序列
- strn 系函数:操作非空字符序列
- str 系函数:操作字符串序列('\0')
GDB Tutorial
Basic Command
- r(run)
- l(list)
- b(break)
- b line_num
- b filename:line_num
- display/format address
e.g display $pc
/i $pc
.
- t 按二进制格式显示变量.
- d 按十进制格式显示变量.
- o 按八进制格式显示变量.
- u 按十六进制格式显示无符号整型.
- x 按十六进制格式显示变量.
- a 按十六进制格式显示变量.
- f 按浮点数格式显示变量.
- c 按字符格式显示变量.
- s 按字符串格式显示变量.
- disas 显示汇编代码.
- x /num-size-format
$pc
/rsp/rbp.
e.g size:w(2 字节) format:x/d/s(十六进制/十进制/字符串) 2wx
- examine - 查看内存
n、f、u 是可选的参数
- n 是一个正整数,表示显示内存的长度,也就是说从当前地址向后显示几个地址的内容。
- f 表示显示的格式,参见上面。如果地址所指的是字符串,那么格式可以是 s,如果地十是指令地址,那么格式可以是 i。
- u 表示从当前地址往后请求的字节数,如果不指定的话,GDB 默认是 4 个 bytes. u 参数可以用下面的字符来代替,b 表示单字节,h 表示双字节,w 表示四字节,g 表示八字节. 当我们指定了字节长度后,GDB 会从指内存定的内存地址开始,读写指定字节,并把其当作一个值取出来. 表示一个内存地址.
命令:x/3uh 0x54320 表示,从内存地址 0x54320 读取内容,h 表示以双字节为一个单位,3 表示三个单位,u 表示按十六进制显示。
- s step into
- step1 下一条汇编指令
- n next line
- p print
- q quit
- up last stack
- bt(back trace) function stack 显示堆栈回溯信息
- info breakpoints/register
GDB Set Command
- set disassembly
- set variable
GDB Shell
shell command
GDB Assemble
GDB Core Dump
ulimit -c unlimited
gdb -c core_file_path target_exe_path
GDB Disasm
- CS Segment
(gdb) disass
(gdb) x/i
(gdb) x/5i $pc
(gdb) ni/si
- Registers
(gdb) i r
(gdb) i r a
(gdb) i r ds
- DS Segment
- SS Segment
GDB Stack Frame
(gdb) bt
(gdb) frame n
(gdb) info locals
GCC
- -E: cpp(c preprocessor) 预处理 => .i
- -S: cll 编译 => .s
- -c: as(assemble) 汇编 => .o
- -time Time the execution of each subprocess
-std=<standard>
Assume that the input sources are for<standard>
-B <directory>
Add<directory>
to the compiler's search paths- -v Display the programs invoked by the compiler
-o <file>
Place the output into<file>
- -shared Create a shared library
-Wall
-v --help
Awesome Tools
- 用于创建代码文档资料的 NDoc
- 用于生成解决方案的 NAnt
- 用于生成代码的 CodeSmith
- 用于监视代码的 FxCop
- 用于编译少量代码的 Snippet Compiler
- 两种不同的转换器工具:ASP.NET 版本转换器和 Visual Studio .NET 项目转换器
- 用于生成正则表达式的 Regulator
- 用于分析程序集的 .NET Reflector
- 用于单元测试的 NUnit