0
点赞
收藏
分享

微信扫一扫

C++之va_start、vasprintf、va_end应用总结(二百二十六)

孟佳 2023-09-24 阅读 49

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长!

优质专栏:Audio工程师进阶系列原创干货持续更新中……】🚀

人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.

更多原创,欢迎关注:Android系统攻城狮

欢迎关注Android系统攻城狮

1.前言

在C/C++语言中,va_startvasprintfva_end是与可变参数相关的函数。它们通常与stdarg.h头文件一起使用。

  1. va_start函数:

    • va_start函数用于初始化可变参数列表。它接受两个参数:第一个参数是一个va_list类型的变量,第二个参数是最后一个固定参数的前一个参数的名称。
    • 这个函数将va_list类型变量初始化为指向可变参数列表的起始位置。
  2. vasprintf函数:

    • vasprintf函数用于将格式化的可变参数输出到动态分配的字符串中。
    • 它接受两个参数:第一个参数是一个指向char指针的指针,第二个参数是一个格式化字符串。
    • 这个函数会根据格式化字符串和可变参数列表生成一个动态分配的字符串,并将指针保存在第一个参数所指向的位置。
  3. va_end函数:

    • va_end函数用于结束可变参数的获取。
    • 它接受一个参数,即一个va_list类型的变量。
    • 这个函数对可变参数列表进行清理,使其无法再被访问。

通常在需要处理可变参数的情况下,使用实现Printf风格的函数或者处理命令行参数时。

2.应用实例

<1>.v1.0

#include <stdio.h>
#include <stdarg.h>

void sum(int count, ...)
{
    va_list args;
    int total = 0;
    
    // 初始化可变参数列表
    va_start(args, count);
    
    // 遍历可变参数列表并求和
    for (int i = 0; i < count; i++)
    {
        int num = va_arg(args, int);
        total += num;
    }
    
    // 结束可变参数列表
    va_end(args);
    
    printf("Sum: %d\n", total);
}

int main()
{
    sum(3, 1, 2, 3);  // 输出 Sum: 6
    
    return 0;
}

va_start是一个宏,用于处理可变参数列表。它的原理是基于C语言的可变参数机制。

va_start宏的工作原理如下:

  1. 首先,通过传入的参数列表的类型信息和格式化字符串来确定参数列表的起始位置。

  2. 然后,通过指针运算和类型大小来计算出参数列表中各个参数的位置。

  3. 最后,将参数列表位置信息保存在一个va_list类型的变量中,供后续函数进行处理。

va_start宏的实现通常依赖于编译器和操作系统的底层支持。它使用了一些机制来获取参数列表的信息,如栈帧结构、类型信息、指针运算等。

具体实现的细节可能因编译器和操作系统而异,但通常会涉及以下步骤:

  1. 获取栈帧指针:va_start宏需要通过访问栈来获取参数列表的位置信息。它首先获取当前函数的栈帧指针,可以通过编译器提供的相关函数或底层汇编指令来实现。

  2. 计算参数列表位置:通过栈帧指针和参数类型大小,va_start宏可以计算出参数列表中各个参数的位置。这通常涉及指针运算,根据参数类型大小来移动指针位置。

  3. 保存参数列表信息:va_start宏将参数列表位置信息保存在一个va_list类型的变量中,以便后续的函数进行处理。可以将栈帧指针和计算得到的参数列表位置信息存储在va_list变量中。

va_start宏使用一些编译器和操作系统提供的底层机制来实现可变参数的处理。具体实现可能会因编译器和操作系统的不同而异,也可能受到编译器的优化策略的影响。

<2>.v2.0

#include <stdio.h>
#include <stdarg.h>

char* format_string(const char* format, ...)
{
    va_list args;
    char* str;
    
    // 初始化可变参数列表
    va_start(args, format);
    
    // 格式化字符串并存储到动态分配的字符串中
    int length = vasprintf(&str, format, args);
    
    // 结束可变参数列表
    va_end(args);
    
    printf("Formatted String: %s\n", str);
    printf("String Length: %d\n", length);
    
    return str;
}

int main()
{
    char* result = format_string("Hello %s, your age is %d.", "John", 25);
    // 输出 Formatted String: Hello John, your age is 25.
    // 输出 String Length: 30
    
    free(result);  // 释放动态分配的字符串
    
    return 0;
}

va_end是一个宏,用于结束对可变参数列表的访问。它的原理是基于C语言的可变参数机制。

va_end宏的工作原理如下:

  1. 首先,它接收一个va_list类型的变量作为参数,该变量保存了参数列表的位置信息。

  2. 接着,通过一系列机制(如编译器和操作系统的底层支持)实现将参数列表进行清理和释放的操作。

  3. 最后,将va_list类型的变量标记为无效,以确保之后对它的访问是无效的。

具体实现的细节可能因编译器和操作系统而异,但通常会涉及以下步骤:

  1. 清理参数列表:va_end宏根据参数列表的位置信息,使用特定的机制将参数列表进行清理。它可能涉及释放资源、清理栈上的参数值等操作。具体的清理操作由编译器和操作系统提供的底层支持来实现。

  2. 使va_list变量失效:为了确保在之后对va_list变量的访问是无效的,va_end宏将该变量标记为无效。这样可以避免误用已经结束的参数列表。

va_end宏的实现依赖于编译器和操作系统的底层支持。具体实现的细节可能会因编译器和操作系统的不同而异。但无论实现细节如何,va_end宏的目标都是确保正确清理和释放可变参数列表所占用的资源,并将va_list变量标记为无效。

<3>.v3.0

#include <iostream>
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <string>

using namespace std;

string AStringPrintf(const char *format, ...) {
  va_list ap;
  va_start(ap, format);

  char *buffer;
  int bufferSize = vasprintf(&buffer, format, ap);

  va_end(ap);

  if(bufferSize < 0) {
    return string();
  }

  string result(buffer);

  free(buffer);
  buffer = NULL;

  return result;
}

int main(){
  char str[64]={1,2,3,5,6,7,8,9};

 string buf =  AStringPrintf(str);
 printf("buf = %s\n",buf.c_str());
}

  1. vsnprintf函数接收一个格式化字符串和一个va_list类型的参数列表。利用格式化字符串,它将参数列表中的数据转换为相应的字符串格式。

  2. 首先,vsnprintf函数根据格式化字符串中的格式指示符,逐个读取参数列表的值,并根据指示符的要求对其进行格式化。

  3. 然后,vsnprintf函数根据格式化的结果,将其写入到指定的缓冲区中。它会确保写入的数据不超过指定的缓冲区大小。

  4. 最后,vsnprintf函数会在写入完成后,返回实际写入的字符数(不包括结尾的’\0’)。如果缓冲区大小不足以容纳格式化后的字符串,vsnprintf函数会截断字符串,但仍然会返回实际要写入的字符数。

需要注意的是,由于vsnprintf函数是经过格式化而得到的字符串的长度是不确定的,因此在使用之前需要分配足够的缓冲区来存储格式化后的字符串,以避免缓冲区溢出的情况发生。

vsnprintf函数是一个用于以固定格式将可变参数格式化为字符串的函数。它将格式化的结果写入指定的缓冲区,并返回实际写入的字符数。这个函数在C++中可用于处理可变参数,并将其转换为字符串格式。

<4>.v4.0

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>

void dynamic_print(const char* format, ...)
{
  va_list args;
  va_start(args, format);

  char* dyn_str = NULL;
  size_t str_length = 0;

  // 计算动态生成字符串所需的长度
  str_length = vsnprintf(NULL, 0, format, args);

  // 为字符串动态分配内存
  dyn_str = (char*)malloc(str_length + 1);

  // 动态生成字符串
  vsnprintf(dyn_str, str_length + 1, format, args);

  printf("%s", dyn_str);

  free(dyn_str);
  va_end(args);
}

int main(){
  int num1 = 10;
  int num2 = 20;

  dynamic_print("Sum: %d + %d = %d\n", num1, num2, num1 + num2);

  return 0;
}

举报

相关推荐

0 条评论