c语言技巧--长期更新
1. #define LOWER(c) (unsigned char)(c | 0x20)
换成小写
2. gcc -Wall -Werror //告警当成 错误来处理
3. gcc -Ox //优化等级,一般debug搞成0, release搞成3
4. 一种处理错误码的方法
不说什么,贴代码
#include <stdio.h> #define HTC_ERROR_MAP(xx) \ xx(OK, "SUCCESS")\ xx(INVALID_VERSION, "invalid protocal version")\ xx(INVALID_STATUS, "invalid protocal status")\ #define HTC_ERRNO_GEN(n,s) HTC_##n, #define HTC_ERRSTR_GEN(n,s) {"HTC_"#n,s}, enum HTC_ERRNO { HTC_ERROR_MAP(HTC_ERRNO_GEN) }; static struct { const char* name; const char* desc; } protocal_err[] = {HTC_ERROR_MAP(HTC_ERRSTR_GEN)}; int main() { enum HTC_ERRNO errno=HTC_OK; printf("name = [%s], desc = [%s]\n", protocal_err[errno].name, protocal_err[errno].desc); return 0; }
这里,使用了宏来巧妙的处理了错误码的枚举和错误码详细信息
使用的技巧:## 宏连接两个宏定义, #连接字符串
5. gcc -DXXXXX
用来定义宏,一般用来写DEBUG宏
CPPFLAGS_DEBUG = $(CPPFLAGS) -DHTTP_PARSER_DEBUG=1
6. 封装数据结构,只有外部可见的数据结构才放在头文件中声明,定义仍然放在.c文件中
7. Makefile如果想取得文件夹下全部文件
$(wildcard $(PATH)/*.c)
8..c转成.o文件
$(xxx:%.c=%.o)
即可
例子:
$(ALLFILES:%.c=%.o)
9. 使用void* 来适配任何类型数据
void* data = (void*) 128;
10. #define ULLONG_MAX (uint64_t-1)
uint64_t 为 typedef unsigned long int
11. struct in_addr ip地址的数据结构
保存一个属性,s_addr,一般是转换成网络序格式的32位ip地址
可以使用inet_aton()函数来进行转换,
12. printf 打印16进制的方法
%X
%08X 长度为8的16进制
13. #error
该语法报用户自定义的错误信息
14. socket(AF_INET, SOCK_STREAM, 0)
15. bind
bind需要注意的是:struct sockaddr_in中的sin_port sin_addr都是网络序的
bind第二个参数为了兼容各种sockaddr数据结构,使用struct sockaddr定义参数,需要转换格式
所以 saddr.sin_family = AF_INET
saddr.sin_port=htons(xxxx)
struct addr_in addr;
bzero(&addr, sizeof(addr));
inet_pton(AF_INET, "xx,xx,xx,xx", &addr);
saddr.sin_addr= addr;
bind(AF_INET, (struct sockaddr*)(&saddr), INET_ADDRSTRLEN);(IPV4的长度)
16. 在Accept 函数ok后,应该关掉socket返回的fd。结束accept,需要关闭accept函数返回的fd
17. server socket -> bind->listen-> accept->fork child -> close(sockfd)->>read--->close
parent ->close(connectfd) ->>accet->>
/** **/ #include <stdio.h> #include <sys/stat.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <strings.h> #define USAGE "exec method: ./server\n" #define IP_ADDR "127.0.0.1" #define PORT 2013 #define IPV4 AF_INET #define BACKLOG 50 //#define IPV4_ADDR_LEN INET_ADDRSTR_LEN #define EXIT_MSG "bye\n" #define handle_error(xx)\ do{perror(xx); exit(EXIT_FAILURE);}while(0)\ static void usage() { printf(USAGE); } static void proc_msg(int sockfd, int connectfd, pid_t name) { char buf[1024]; int readlen=0; bzero(buf, sizeof(buf)); printf("accept success...\n"); printf("server start to work...\n"); close(sockfd); for(;;) { bzero(buf, sizeof(buf)); readlen = recv(connectfd, buf, 1024, 0); if(readlen >0) { printf("[%d] receive msg : %s", name,buf); if(strncmp(EXIT_MSG, buf) == 0) break; } if(readlen <= 0) { perror("fail read ..."); break; } } } int main(int argc , char* argv[]) { int sockfd = 0; int connectfd =0; struct in_addr inaddr; struct sockaddr_in skaddr; socklen_t len; pid_t child = 0; char buf[1024]; int readlen = 0; if(argc >1) { usage(); return 1; } bzero(&inaddr, sizeof(struct in_addr)); bzero(&skaddr, sizeof(struct sockaddr_in)); skaddr.sin_family = IPV4; if(inet_pton(IPV4, IP_ADDR, &(skaddr.sin_addr)) == 0) handle_error("IP address is not avialabel..."); skaddr.sin_port = htons(PORT); //scoket sockfd = socket(IPV4, SOCK_STREAM, 0); //bind if( bind(sockfd, (struct sockaddr*)(&skaddr), sizeof(struct sockaddr_in)) != 0) handle_error("bind fd failed..."); //listen if(listen(sockfd, BACKLOG) != 0) handle_error("listen fd failed..."); len = sizeof(struct sockaddr_in); for(;;) { if((connectfd =accept(sockfd, (struct sockaddr*)(&skaddr), &len)) >0) { if((child = fork()) == 0) { proc_msg(sockfd, connectfd, child); close(connectfd); } close(connectfd); } } return 0; }
client socket->connect->
#include <stdio.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <strings.h> #include <unistd.h> #include <string.h> int main() { int sockfd = 0; struct sockaddr_in skaddr; char buf[1024]; bzero(&skaddr, sizeof(skaddr)); skaddr.sin_family = AF_INET; inet_pton(AF_INET, "127.0.0.1", &(skaddr.sin_addr)); skaddr.sin_port = htons(2013); //socket sockfd = socket(AF_INET, SOCK_STREAM, 0); //connect connect(sockfd, (struct sockaddr*)(&skaddr), sizeof(skaddr)); //send for(;;) { bzero(buf, sizeof(buf)); if(fgets(buf, 1024, stdin) != NULL) { send(sockfd, buf, strnlen(buf, 1024), 0); } } sleep(3); close(sockfd); }
18. killall -HUP xxxx 杀掉进程,进程名
19. 僵死进程
子进程已终 止,父进程尚未对其执行wait操作,子进程会转入“僵死”状态。内核为“僵死”状态的进程保留最少的信息量(进程标识,终止状态,资源使用信息),过后 父进程执行wait时可以获取子进程信息。只要僵死的进程不通过wait从系统中移去,它将会占据内核进程表中的一个栏位。如果进程表被填满,内核将不能 再产生新进程。如果父进程已终止,它的僵死子进程将由init进程收养,并自动执行wait将它们移去。
请对子进程进程wait操作。
20.
信号集是一个位向量,其中每一位对应着linux系统的一个信号。可使用如下函数对信号集进行处理:
#include <signal.h>
int sigemptyset(sigset_t * set);
int sigfillset(sigset_t * set);
int sigaddset(sigset_t * set);
int sigdelset(sigset_t * set);
sigemptyset将一个信号集清空;sigfillset将信号集的所有位置位;sigaddset函数将参数signo指定的信号所对应的位设置为1;sigdelset将signo的对应位设置为0。
21. EINTR
注意处理EINTR错误,errno返回时EINTR的时候,说明被信号中断导致的错误
22. signal是软中断
23. sigaction.sa_handler= func;
24. 在处理SIGCHLD信号的函数中,while(waitpid(-1, &status, WNOHANG) > 0)来处理子进程的退出
25. I/O复用的概念,想象一下这种场景,进程阻塞在读取I/O上,但是另一个I/O也在等待操作,那么这怎么办呢,这时候就需要用到I/O复用的概念了。
26.
select 函数,用来等待多个I/O的读写状态,不再需要的等待一个I/O而阻塞了
27. socket连接握手
client --> seq k ---> server
client<---- seq J ack k+1 <-----server
client ----> ack j+1---->server
三次的消息flag分别是: seq, seq ack, ack 02, 12 , 10
28. close 和shutdown的区别,
共享套接字的情况下,close只是共享个数减 1, 只有到个数为零的时候,才会关闭套接字。
而shutdown是等缓存中数据都取完后,直接关闭套接字,并不关心有多少个共享存在。但是套接字还是存在的,还需要用close来关闭
29. TIME_WAIT状态,是socket已经关闭后,保持的状态,一般是2MSL的时长
30. CLOSE_WAIT是被动关闭的一端,收到close后,没有close的状态
应该处理这种情况
if(recv(clientArray[i], recvBuf, 1024, 0) == 0)
{
#if __DEBUG__
printf("process CLOSE_WAIT status\n");
#endif
close(clientArray[i]);
close(sockfd);
index--;
}
31. linux kernel经常看到宏
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
#define container_of(ptr, type, member) \
({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr);\
(type *)( (char *)__mptr - offsetof(type,member) ); \
})
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
offsetof宏是用来找到结构中,某个成员的偏移量
container_of 宏是根据成员的指针找到容器的指针
整个宏就是根据成员的指针找到容器的指针,ptr入参为成员的指针
32. inline使用
inline函数不要超过10行代码,且不能包含循环、switch、if语句。
在一个c文件中定义的inline函数是不能在其它c文件中直接使用,google推荐把inline函数定义在**-inl.h头文件中。
不要过度使用inline函数定义,尤其对大函数来说。