• 可变参数与日志demo


     在c语言中,我们常用的printf()函数就使用到了可变参数。那他是什么呢?

     #include <stdio.h>

           int printf(const char *format, ...);
           int fprintf(FILE *stream, const char *format, ...);
           int sprintf(char *str, const char *format, ...);
           int snprintf(char *str, size_t size, const char *format, ...);

     可以看到printf函数家族的最后一个参数都是“...”。那这个“...”参数是什么意思呢?

    看一下我们平时使用printf函数的格式

    1. const char* str="hello world";
    2. int a=1;
    3. printf("%s %d\n", str,a);

    const char *format参数就是前面的“%s %d\n”这样的字符串,...参数就是可变参数也就是说你可以指定任意参数任意类型。

    这意味着printf函数与其他函数都有所不同,普通函数都指明了参数个数以及参数类型,但是printf函数不同,他会根据你输入的参数个数以及参数类型打印出结果。那么他是如何做到的呢?


    1.printf函数会根据format中的%s或者%d,%x%p这样的字符来判断后面的参数是什么类型,从而准确打印。

    2. printf函数会使用 va_list p指明一个参数指针p,va_arg(p,int)根据类型提取参数,va_start(p,format)让p指向可变参数部分的起始地址,va_end(p)这样的函数来把参数指针置空,这样就保证了可变参数的完全打印,不会漏掉某个参数; 
     

    简单日志demo

    主要是为了理解日志的思想,利用了可变参数,使得程序的测试结果标准化,可视化,简易化,减轻了程序员负担。

    下面的日志格式: 日志等级 时间 进程pid 消息体。

    1. #pragma once
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. // 日志是有日志等级的
    11. const std::string filename = "log/tcpserver.log";
    12. enum
    13. {
    14. Debug = 0,
    15. Info,
    16. Warning,
    17. Error,
    18. Fatal,
    19. Uknown
    20. };
    21. static std::string toLevelString(int level)
    22. {
    23. switch (level)
    24. {
    25. case Debug:
    26. return "Debug";
    27. case Info:
    28. return "Info";
    29. case Warning:
    30. return "Warning";
    31. case Error:
    32. return "Error";
    33. case Fatal:
    34. return "Fatal";
    35. default:
    36. return "Uknown";
    37. }
    38. }
    39. static std::string getTime()
    40. {
    41. time_t curr = time(nullptr);
    42. struct tm *tmp = localtime(&curr);
    43. char buffer[128];
    44. //snprintf()函数会自动把整型转成字符串,然后储存到buffer中。
    45. snprintf(buffer, sizeof(buffer), "%d-%d-%d %d:%d:%d", tmp->tm_year + 1900, tmp->tm_mon+1, tmp->tm_mday,
    46. tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
    47. return buffer;
    48. }
    49. // 日志格式: 日志等级 时间 pid 消息体
    50. // logMessage(DEBUG, "hello: %d, %s", 12, s.c_str()); // DEBUG hello:12, world
    51. void logMessage(int level, const char *format, ...)
    52. {
    53. char logLeft[1024];
    54. std::string level_string = toLevelString(level);
    55. std::string curr_time = getTime();
    56. snprintf(logLeft, sizeof(logLeft), "[%s] [%s] [%d] ", level_string.c_str(), curr_time.c_str(), getpid());
    57. char logRight[1024];
    58. va_list p;
    59. va_start(p, format);
    60. vsnprintf(logRight, sizeof(logRight), format, p);
    61. va_end(p);
    62. // 打印
    63. // printf("%s%s\n", logLeft, logRight);
    64. // 保存到文件中
    65. FILE *fp = fopen(filename.c_str(), "a");
    66. if(fp == nullptr)return;
    67. fprintf(fp,"%s%s\n", logLeft, logRight);
    68. fflush(fp); //可写也可以不写
    69. fclose(fp);
    70. // 预备
    71. // va_list p; // char *
    72. // int a = va_arg(p, int); // 根据类型提取参数
    73. // va_start(p, format); //p指向可变参数部分的起始地址
    74. // va_end(p); // p = NULL;
    75. }

  • 相关阅读:
    什么牌子的蓝牙耳机耐用?类似airpods pro的降噪耳机推荐
    【C语言必知必会 | 第二篇】编译器的安装与使用
    elasticsearch实现聚合后两个字段相除相加相减相乘运算
    深拷贝浅拷贝
    MySQL 锁
    第二十二章 Ajax
    学信息系统项目管理师第4版系列07_项目管理知识体系
    [De1CTF 2019]SSRF Me
    【Docker】2、Docker安装MySQL5.7服务
    [开源]Web端的P2P文件传输工具,简单安全高效的P2P文件传输服务
  • 原文地址:https://blog.csdn.net/zzxz8/article/details/132910066