Linux c/c++ do{...}while(0)的妙用

前言

最近用man手册查看API函数时,常见例子中含有do{…}while(0)奇怪的用法。为什么要用do{…}while(0)呢?就是能够实现复杂的定义,通用性强,任何情况下都适用。

do{…}while(0)的妙用

下面do{…}while(0)是Linux内核中常见用在错误的处理上:

#define handle_error_en(en, msg) \
       do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)

#define handle_error(msg) \
       do { perror(msg); exit(EXIT_FAILURE); } while (0)

使用do{….}while(0) 把它包裹起来,成为一个独立的语法单元,从而不会与上下文发生混淆。同时因为绝大多数的编译器都能够识别do{…}while(0)这种无用的循环并进行优化,所以使用这种方法也不会导致程序的性能降低。另外,do…while一定会执行一次,不论while中的语句是什么。

完整代码:

#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>

#define handle_error_en(en, msg) \
       do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)

#define handle_error(msg) \
       do { perror(msg); exit(EXIT_FAILURE); } while (0)

struct thread_info {   
   pthread_t thread_id;        /* ID returned by pthread_create() */
   int       thread_num;      
   char     *argv_string;      
};

static void *thread_start(void *arg)
{
       struct thread_info *tinfo = arg;
       char *uargv, *p;

       printf("Thread %d: top of stack near %p; argv_string=%s\n",
               tinfo->thread_num, &p, tinfo->argv_string);

       uargv = strdup(tinfo->argv_string);
       if (uargv == NULL)
           handle_error("strdup");

       for (p = uargv; *p != '\0'; p++)
           *p = toupper(*p);

       return uargv;
}

int main(int argc, char *argv[])
{
       int s, tnum, opt, num_threads;
       struct thread_info *tinfo;
       pthread_attr_t attr;
       int stack_size;
       void *res;

       /* “-s”选项指定线程的堆栈大小 */

       stack_size = -1;
       while ((opt = getopt(argc, argv, "s:")) != -1) 
       {
           switch (opt) {
           case 's':
               stack_size = strtoul(optarg, NULL, 0);
               break;

           default:
               fprintf(stderr, "Usage: %s [-s stack-size] arg...\n",
                       argv[0]);
               exit(EXIT_FAILURE);
           }
       }

       num_threads = argc - optind;

       /* 初始化线程创建属性 */

       s = pthread_attr_init(&attr);
       if (s != 0)
           handle_error_en(s, "pthread_attr_init");

       if (stack_size > 0) {
           s = pthread_attr_setstacksize(&attr, stack_size);
           if (s != 0)
               handle_error_en(s, "pthread_attr_setstacksize");
       }

       /* 为pthread_create()参数分配内存 */

       tinfo = calloc(num_threads, sizeof(struct thread_info));
       if (tinfo == NULL)
           handle_error("calloc");

       /* 为每个命令行参数创建一个线程 */

       for (tnum = 0; tnum < num_threads; tnum++) {
           tinfo[tnum].thread_num = tnum + 1;
           tinfo[tnum].argv_string = argv[optind + tnum];

           /* 线程的创建,pthread_create()调用将线程ID存储到tinfo[]对应元素 */

           s = pthread_create(&tinfo[tnum].thread_id, &attr,
                              &thread_start, &tinfo[tnum]);
           if (s != 0)
               handle_error_en(s, "pthread_create");
       }

   /* 销毁线程属性对象,因为它不是不再需要 */

   s = pthread_attr_destroy(&attr);
   if (s != 0)
       handle_error_en(s, "pthread_attr_destroy");

   /* 线程等待, 并显示其返回值 */

   for (tnum = 0; tnum < num_threads; tnum++) {
       s = pthread_join(tinfo[tnum].thread_id, &res);
       if (s != 0)
           handle_error_en(s, "pthread_join");

       printf("Joined with thread %d; returned value was %s\n",
               tinfo[tnum].thread_num, (char *) res);
       free(res);     
   }

   free(tinfo);
   exit(EXIT_SUCCESS);
}

输出结果:

在这里插入图片描述
也就是说,为什么Linux内核代码要这样来做,这是因为内核代码采用do{}while(0);这种结构可以保证无论在什么地方都可以正确的执行一次 ,这就是它用得最妙的地方。

发布了71 篇原创文章 · 获赞 42 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/chen1415886044/article/details/103705276