简单AIDS的源码学习 <utils.c>

本文深入解析logger()函数,介绍C语言中处理可变参数的方法,包括va_list, va_start(), va_end()的用法。同时探讨了struct timeval, struct tm结构体,以及gettimeofday(), localtime(), strftime()等时间处理函数的原理和应用。" 51005010,2821033,Unity播放带Alpha通道视频解决方案,"['Unity3D', '虚拟现实', '视频播放', 'C#', 'Shader']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

相关源码

    #include <stdio.h>
    #include <time.h>
    #include <stdarg.h>
    #include <sys/time.h>
    #include "utils.h"

    /**
     * Logger is used to write any kind of messages to file descriptors.
     * Useful with debugging etc.
     *
     * @param file file descriptor to which messages should be written
     * @param level level of message (defined in header: DEBUG, INFO, WARN, ERROR)
     * @param format exactly as in printf
     */
    void logger(FILE *file, int level, const char *format, ...)
    {
        va_list ap;
        char time[255], *level_str;
        struct timeval tp;
        struct tm *tms;

        if (level == ERROR)
            level_str = "ERROR";
        else if (level == WARN)
            level_str = "WARN";
        else if (level == INFO)
            level_str = "INFO";
        else if (level == DEBUG)
            level_str = "DEBUG";

        va_start(ap, format);
        gettimeofday(&tp, NULL);
        tms = localtime(&(tp.tv_sec));
        strftime(time, sizeof(time), "%H:%M:%S", tms);

        fprintf(file, "[%-5s][%s.%03ld] ", level_str, time, tp.tv_usec / 1000);
        vfprintf(file, format, ap);
        fprintf(file, "\n");
        va_end(ap);
    }

源码函数解析

utils.c定义logger()函数

函数名: logger
功 能: 将特定信息写入相应文件中
用 法:

void logger(FILE *file, int level, const char *format, ...)

logger()函数中的陌生函数或类型学

  • va_ist,va_start(),va_end()
  • struct timeval,struct tm
  • gettimeofday(),localtime()
  • strftime()
  • fprintf()
  • vfprintf()

va_list,va_start(),va_end()

用指针参数解决不定数目函数参数问题,
(1)va_list
定义了一个指针arg_ptr, 用于指示可选的参数.

(2)va_start(arg_ptr, argN)
使参数列表指针arg_ptr指向函数参数列表中的第一个可选参数,argN是位于第一个可选参数之前的固定参数, 或者说最后一个固定参数.如有一va函数的声明是

void va_test(char a, char b, char c, ...)

则它的固定参数依次是a,b,c, 最后一个固定参数argN为c, 因此就是

va_start(arg_ptr, c)

(3)va_arg(arg_ptr, type)
返回参数列表中指针arg_ptr所指的参数, 返回类型为type. 并使指针arg_ptr指向参数列表中下一个参数.返回的是可选参数, 不包括固定参数.

(4)va_end(arg_ptr)
清空参数列表, 并置参数指针arg_ptr无效.

(注:va在这里是variable-argument(可变参数)的意思. 这些宏定义在stdarg.h中,所以用到可变参数的程序应该包含这个头文件)

struct timeval,struct tm

struct timeval 结构体
该结构体是Linux系统中定义,struct timeval结构体在time.h中的定义为:

        struct timeval  
        {  
        __time_t tv_sec;        /* Seconds. */  
        __suseconds_t tv_usec;  /* Microseconds. */  
        }; 

其中,tv_sec为Epoch(1970年1月1日00:00:00 UTC)到创建struct timeval时的秒数,tv_usec为微秒数,即秒后面的零头。

struct tm 结构体
该结构体是Linux系统中定义,struct timeval结构体在time.h中的定义为:

    struct tm
    {
      int tm_sec;                   /* Seconds.     [0-60] (1 leap second) */
      int tm_min;                   /* Minutes.     [0-59] */
      int tm_hour;                  /* Hours.       [0-23] */
      int tm_mday;                  /* Day.         [1-31] */
      int tm_mon;                   /* Month.       [0-11] */
      int tm_year;                  /* Year - 1900.  */
      int tm_wday;                  /* Day of week. [0-6] */
      int tm_yday;                  /* Days in year.[0-365] */
      int tm_isdst;                 /* DST.         [-1/0/1]*/
    };

linux下存储时间常见的有两种存储方式,一个是从1970年到现在经过了多少秒,一个是用一个结构来分别存储年月日时分秒的。time_t 这种类型就是用来存储从1970年到现在经过了多少秒,更精确一点,可以用结构struct timeval,它精确到微妙。而直接存储年月日的是一个结构体struct tm。

gettimeofday();localtime()

下面介绍一下我们常用的时间函数:

**char *asctime(const struct tm* timeptr);**

将结构中的信息转换为真实世界的时间,以字符串的形式显示

**char *ctime(const time_t *timep);**

将timep转换为真实世界的时间,以字符串显示,它和asctime不同就在于传入的参数形式不一样

**double difftime(time_t time1, time_t time2);**

返回两个时间相差的秒数

**int gettimeofday(struct timeval *tv, struct timezone *tz);**

返回当前距离1970年的秒数和微妙数,后面的tz是时区,一般不用
gettimeofday()会把目前的时间用timeval结构体返回,当地时区的信息则放到tz所指的timezone结构体中。
timezone 结构定义为:

    struct  timezone{
            int tz_minuteswest;/*和greenwich 时间差了多少分钟*/
            int tz_dsttime;/*type of DST correction*/
    }

在gettimeofday()函数中tv或者tz都可以为空。如果为空则就不返回其对应的结构体。
函数执行成功后返回0,失败后返回-1,错误代码存于error中。

**struct tm* gmtime(const time_t *timep);**

将time_t表示的时间转换为没有经过时区转换的UTC时间,是一个struct tm结构指针

**stuct tm* localtime(const time_t *timep);**

函数说明:localtime()将参数timep 所指的time_t 结构中的信息转换成真实世界所使用的时间日期表示方法,然后将结果由结构tm 返回。此函数返回的时间日期已经转换成当地时区。和gmtime类似,但是它是经过时区转换的时间。

返回值:返回结构tm 代表目前的当地时间。

**time_t mktime(struct tm* timeptr);**

将struct tm 结构的时间转换为从1970年至今的秒数

**time_t time(time_t *t);**

取得从1970年1月1日至今的秒数。

strftime()

linux 时间格式化函数strftime:

函数是时间日期的格式控制函数。strftime将一个struct tm结构格式化为一个字符串。

**函数原型:

size_t strftime(char *s,size_t maxsize,char *format,const struct tm *timeptr)

strftime函数对timeptr指向的tm结构所代表的时间和日期进行格式编排,其结果放在字符串s中。最多向字符串s中存放maxsize个字符。该函数返回向s指向的字符串中放置的字符数。格式字符串format用来对写入字符串的字符进行控制,它包含着将被传送到字符串里去的普通字符以及编排时间和日期格式的转换控制符。

此外,strptime()函数与strftime()功能上刚好相反,则是将一个字符串格式化为一个struct tm结构。关于**strftime()和strptime()**d的详细介绍请看参考文章

fprintf()

fprintf()函数主要用于格式化信息输出到指定的文件流中返回值:成功则返回输出的字节数,失败返回eof;

    int fprintf( FILE *stream, const char *format, ... );

fprintf()函数根据指定的format(格式)发送信息(参数)到由stream(流)指定的文件.因此fprintf()可以使得信息输出到指定的文件.fprintf()的返回值是输出的字符数,发生错误时返回一个负值.

函数原型:

`/* Write formatted output to STREAM.This function is a possible cancellation point and therefore not marked with __THROW.  */ 
extern int fprintf (FILE *__restrict __stream,__const char *__restrict __format, ...);`

关于fprintf的详细介绍请参考文章

vfprintf()

函数名: vfprintf
功 能: 送格式化输出到一流中,vfprintf()会根据参数format字符串来转换并格式化va_list(可变参数的)数据,然后将结果输出到参数stream指定的文件中,直到出现字符串结束(‘\0’)为止。
用 法:

int vfprintf(FILE *stream, char *format,
va_list param);

返回值: 成功则返回实际输出的字符数,失败则返回-1,错误原因存于errno中。

函数原型:

`/* Write formatted output to S from argument list ARG.This function is a possible cancellation point and therefore not marked with __THROW.  */
extern int vfprintf (FILE *__restrict __s, __const char *__restrict __format, _G_va_list __arg);`

关于vfprintf的详细介绍请看参考文章

将非标准头文件替换为 C++ 标准库头文件时,需根据每个头文件的功能选择合适的标准库组件进行替代。以下是针对每个头文件的替换方案: ### 替换 `#include <utils/threads.h>` 该头文件通常用于 Android 平台的线程管理,如 `Mutex`、`Condition`、`Thread` 等类。C++11 及后续标准提供了 `<thread>`、`<mutex>`、`<condition_variable>` 等标准库头文件来实现跨平台的线程控制。 - 使用 `std::thread` 替代 `Thread` 类 - 使用 `std::mutex` 替代 `Mutex` - 使用 `std::condition_variable` 替代 `Condition` ```cpp #include <thread> #include <mutex> #include <condition_variable> std::mutex mtx; std::condition_variable cv; void thread_func() { std::unique_lock<std::mutex> lock(mtx); // 等待条件满足 cv.wait(lock); // 执行线程逻辑 } std::thread t(thread_func); // 通知线程继续执行 cv.notify_one(); t.join(); ``` ### 替换 `#include <cutils/atomic.h>` 该头文件提供原子操作接口,如 `android_atomic_inc`、`android_atomic_dec`、`android_atomic_cmpxchg` 等。C++11 引入了 `std::atomic`,可直接替代这些原子操作。 - 使用 `std::atomic<int>` 替代 `android_atomic_t` - 使用 `fetch_add`、`fetch_sub`、`compare_exchange_strong` 等方法替代原有函数 ```cpp #include <atomic> std::atomic<int> counter(0); counter.fetch_add(1, std::memory_order_seq_cst); int expected = 0; counter.compare_exchange_strong(expected, 1, std::memory_order_seq_cst); ``` ### 替换 `#include <cutils/properties.h>` 该头文件用于访问 Android 系统属性,如 `property_get` 和 `property_set`。C++ 标准库中没有直接替代系统属性的组件,但可以通过环境变量或配置文件模拟部分功能。 - 使用 `getenv` 或 `setenv` 操作环境变量(POSIX 系统) - 或者使用 `std::map<std::string, std::string>` 模拟属性存储 ```cpp #include <cstdlib> const char* value = getenv("MY_PROPERTY"); if (value == nullptr) { // 默认值 value = "default_value"; } ``` ### 替换 `#include <utils/std/Log.h>` 该头文件用于 Android 日志输出,如 `LOGD`、`LOGI`、`LOGE` 等宏。C++ 标准库中没有日志系统,但可以使用 `std::cout`、`std::cerr` 或第三方日志库如 `spdlog`、`glog`。 ```cpp #include <iostream> std::cout << "Debug message" << std::endl; std::cerr << "Error message" << std::endl; ``` ### 替换 `#include <utils/std/Trace.h>` 该头文件用于性能追踪(如 ATrace_beginSection)。C++ 标准库中没有等效的性能追踪机制,但可以使用 `std::chrono` 进行时间测量,或使用第三方性能分析工具如 `perf`、`Valgrind`、`Google Profiler`。 ```cpp #include <chrono> auto start = std::chrono::high_resolution_clock::now(); // 执行耗时操作 auto end = std::chrono::high_resolution_clock::now(); std::chrono::duration<double> diff = end - start; std::cout << "Execution time: " << diff.count() << " s" << std::endl; ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值