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]
", 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
"

#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
"

#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...
");
    printf("server start to work...
");
    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 ");
#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函数定义,尤其对大函数来说。

原文地址:https://www.cnblogs.com/unixshell/p/3453749.html