va_list的问题

gcc多平台va_list重载问题 刚刚解决了一个自认为十分诡异的BUG,其根本原因是自己的技术深度不够,没有考虑不同平台下gcc中内置变量的定义不同所造成的。 现象为Koala LINUX客户端在64位平台工作正常,在32位平台会出现core dump,对core文件进行分析,调用堆栈如下所示: #0 0x00322701 in vfprintf () from /lib/libc.so.6 #1 0x003475f0 阅读详情

1. 概述

由于在C语言中没有函数重载,解决不定数目函数参数问题变得比较麻烦;即使采用C++,如果参数个数不能确定,也很难采用函数重载.对这种情况,有些人采用指针参数来解决问题.下面就c语言中处理不定参数数目的问题进行讨论.

2. 定义

大家先看几宏.

在VC++6.0的include有一个stdarg.h头文件,有如下几个宏定义:

#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )            //第一个可选参数地址
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) //下一个参数地址
#define va_end(ap) ( ap = (va_list)0 )                                // 将指针置为无效

如果对以上几个宏定义不理解,可以略过,接这看后面的内容.

3. 参数在堆栈中分布,位置

在进程中,堆栈地址是从高到低分配的.当执行一个函数的时候,将参数列表入栈,压入堆栈的高地址部分,然后入栈函数的返回地址,接着入栈函数的执行代码,这个入栈过程,堆栈地址不断递减,一些黑客就是在堆栈中修改函数返回地址,执行自己的代码来达到执行自己插入的代码段的目的.

总之,函数在堆栈中的分布情况是:地址从高到低,依次是:函数参数列表,函数返回地址,函数执行代码段.

堆栈中,各个函数的分布情况是倒序的.即最后一个参数在列表中地址最高部分,第一个参数在列表地址的最低部分.参数在堆栈中的分布情况如下:

最后一个参数

倒数第二个参数

...

第一个参数

函数返回地址

函数代码段

4. 示例代码

void arg_test(int i, ...);

int main(int argc,char *argv[])
{
  int int_size = _INTSIZEOF(int);
  printf("int_size=%d\n", int_size);
  arg_test(0, 4);
  //arg_cnt(4,1,2,3,4);
  return 0;
}

void arg_test(int i, ...)
{
  int j=0;
  va_list arg_ptr; 
  va_start(arg_ptr, i); 
  printf("&i = %p\n", &i);//打印参数i在堆栈中的地址
  printf("arg_ptr = %p\n", arg_ptr);//打印va_start之后arg_ptr地址,应该比参数i的地址高sizeof(int)个字节,这时arg_ptr指向下一个参数的地址
  j=*((int *)arg_ptr);
  printf("%d %d\n", i, j); 
  j=va_arg(arg_ptr, int);
  printf("arg_ptr = %p\n", arg_ptr);//打印va_arg后arg_ptr的地址,应该比调用va_arg前高sizeof(int)个字节,这时arg_ptr指向下一个参数的地址
  va_end(arg_ptr);
  printf("%d %d\n", i, j);
}

5. 代码说明:

int int_size = _INTSIZEOF(int);//得到int类型所占字节数
va_start(arg_ptr, i); //得到第一个可变参数地址,

根据定义:#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) ,(va_list)&v得到起始参数的地址, 再加上_INTSIZEOF(v) ,就是起始参数下一个参数的地址,即第一个可变参数地址.

j=va_arg(arg_ptr, int); //得到第一个参数的值,并且arg_ptr指针上移一个_INTSIZEOF(int),即指向下一个可变参数的地址.
va_end(arg_ptr);        //置空arg_ptr,即arg_ptr=0;

总结:读取可变参数的过程其实就是堆栈中,使用指针,遍历堆栈段中的参数列表,从低地址到高地址一个一个地把参数内容读出来的过程.

6. 在编程中应该注意的问题和解决办法

虽然可以通过在堆栈中遍历参数列表来读出所有的可变参数,但是由于不知道可变参数有多少个,什么时候应该结束遍历,如果在堆栈中遍历太多,那么很可能读取一些无效的数据.

解决办法:a.可以在第一个起始参数中指定参数个数,那么就可以在循环还中读取所有的可变参数;b.定义一个结束标记,在调用函数的时候,在最后一个参数中传递这个标记,这样在遍历可变参数的时候,可以根据这个标记结束可变参数的遍历;

下面是一段示例代码:

//第一个参数定义可选参数个数,用于循环取初参数内容
void arg_cnt(int cnt, ...);
int main(int argc,char *argv[])
{
  int int_size = _INTSIZEOF(int);
  printf("int_size=%d\n", int_size);
  arg_cnt(4,1,2,3,4);
  return 0;
}

void arg_cnt(int cnt, ...)
{
  int value=0;
  int i=0;
  int arg_cnt=cnt;
  va_list arg_ptr; 
  va_start(arg_ptr, cnt); 
  for(i = 0; i < cnt; i++)
  {
   value = va_arg(arg_ptr,int);
   printf("value%d=%d\n", i+1, value);
  }
}

编译运行结果为:

虽然可以根据上面两个办法解决读取参数个数的问题,但是如果参数类型都是不定的,该怎么办,如果不知道参数的类型,即使读到了参数也没有办法进行处理.

解决办法:可以自定义一些可能出现的参数类型,这样在可变参数列表中,可以可变参数列表中的那类型,然后根据类型,读取可变参数值,并进行准确地转换.传递参数的时候可以这样传递:参数数目,可变参数类型1,可变参数值1,可变参数类型2,可变参数值2,....

这里给出一个完整的例子:

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

const int INT_TYPE = 100000;
const int STR_TYPE = 100001;
const int CHAR_TYPE = 100002;
const int LONG_TYPE = 100003;
const int FLOAT_TYPE = 100004;
const int DOUBLE_TYPE = 100005;
/*第一个参数定义可选参数个数,用于循环取初参数内容,可变参数采用arg_type,arg_value...的形式传递,以处理不同的可变参数类型*/
void arg_type(int cnt, ...);
/*第一个参数定义可选参数个数,用于循环取初参数内容*/
void arg_cnt(int cnt, ...);
/*测试va_start,va_arg的使用方法,函数参数在堆栈中的地址分布情况*/
void arg_test(int i, ...);
int main(int argc,char *argv[])
{
  int int_size = _INTSIZEOF(int);
  printf("int_size=%d\n", int_size);
  arg_test(0, 4);
  arg_cnt(4,1,2,3,4);
  arg_type(2, INT_TYPE, 222, STR_TYPE, "ok,hello world!");
  return 0;
}

void arg_test(int i, ...)//测试va_start,va_
{
  int j=0;
  va_list arg_ptr; 
  va_start(arg_ptr, i); 
  printf("&i = %p\n", &i);//打印参数i在堆栈中的地址
  printf("arg_ptr = %p\n", arg_ptr);//打印va_start之后arg_ptr地址,应该比参数i的地址高sizeof(int)个字节,这时arg_ptr指向下一个参数的地址
  j=*((int *)arg_ptr);
  printf("%d %d\n", i, j); 
  j=va_arg(arg_ptr, int);
  printf("arg_ptr = %p\n", arg_ptr);//打印va_arg后arg_ptr的地址,应该比调用va_arg前高sizeof(int)个字节,这时arg_ptr指向下一个参数的地址
  va_end(arg_ptr);
  printf("%d %d\n", i, j);
}

void arg_cnt(int cnt, ...)
{
  int value=0;
  int i=0;
  int arg_cnt=cnt;
  va_list arg_ptr; 
  va_start(arg_ptr, cnt); 
  for(i = 0; i < cnt; i++)
  {
    value = va_arg(arg_ptr,int);
    printf("value%d=%d\n", i+1, value);
  }
}

void arg_type(int cnt, ...)
{
  int arg_type = 0;
  int int_value=0;
  int i=0;
  int arg_cnt=cnt; 
  char *str_value = NULL;
  va_list arg_ptr; 
  va_start(arg_ptr, cnt);
  for(i = 0; i < cnt; i++)
  {
    arg_type = va_arg(arg_ptr,int);
    switch(arg_type)
   {
    case INT_TYPE:
       int_value = va_arg(arg_ptr,int);
       printf("value%d=%d\n", i+1, int_value);
       break;
    case STR_TYPE:
       str_value = va_arg(arg_ptr,char*);
       printf("value%d=%d\n", i+1, str_value);
       break;
    default:
       break;
   }
  }
}

有关VA_LIST的用法:

VA_LIST 是在C语言中解决变参问题的一组宏

VA_LIST的用法:     

     (1)首先在函数里定义一具VA_LIST型的变量,这个变量是指向参数的指针

       (2)然后用VA_START宏初始化变量刚定义的VA_LIST变量,这个宏的第二个参数是第一个可变参数的前一个参数,是一个固定的参数。

       (3)然后用VA_ARG返回可变的参数,VA_ARG的第二个参数是你要返回的参数的类型。

       (4)最后用VA_END宏结束可变参数的获取。然后你就可以在函数里使用第二个参数了。如果函数有多个可变参数的,依次调用VA_ARG获取各个参数。

 VA_LIST在编译器中的处理:

       (1)在运行VA_START(ap,v)以后,ap指向第一个可变参数在堆栈的地址。

     (2)VA_ARG()取得类型t的可变参数值,在这步操作中首先apt = sizeof(t类型),让ap指向下一个参数的地址。然后返回ap-sizeof(t类型)的t类型*指针,这正是第一个可变参数在堆栈里的地址。然后用*取得这个地址的内容。

     (3)VA_END(),X86平台定义为ap = ((char*)0),使ap不再指向堆栈,而是跟NULL一样,有些直接定义为((void*)0),这样编译器不会为VA_END产生代码,例如gcc在Linux的X86平台就是这样定义的。

要注意的是:由于参数的地址用于VA_START宏,所以参数不能声明为寄存器变量,或作为函数或数组类型。

使用VA_LIST应该注意的问题:

   (1)因为va_start, va_arg, va_end等定义成宏,所以它显得很愚蠢,可变参数的类型和个数完全在该函数中由程序代码控制,它并不能智能地识别不同参数的个数和类型. 也就是说,你想实现智能识别可变参数的话是要通过在自己的程序里作判断来实现的.

    (2)另外有一个问题,因为编译器对可变参数的函数的原型检查不够严格,对编程查错不利.不利于我们写出高质量的代码。

小结:可变参数的函数原理其实很简单,而VA系列是以宏定义来定义的,实现跟堆栈相关。我们写一个可变函数的C函数时,有利也有弊,所以在不必要的场合,我们无需用到可变参数,如果在C++里,我们应该利用C++多态性来实现可变参数的功能,尽量避免用C语言的方式来实现。

 

请问:va_list(),va_start()是何意?

 

(一)写一个简单的可变参数的C函数

 

下面我们来探讨如何写一个简单的可变参数的C函数.写可变参数的

C函数要在程序中用到以下这些宏:

void va_start( va_list arg_ptr, prev_param );

 

type va_arg( va_list arg_ptr, type );

 

void va_end( va_list arg_ptr );

va在这里是variable-argument(可变参数)的意思.

这些宏定义在stdarg.h中,所以用到可变参数的程序应该包含这个

头文件.下面我们写一个简单的可变参数的函数,改函数至少有一个整数

参数,第二个参数也是整数,是可选的.函数只是打印这两个参数的值.

void simple_va_fun(int i, ...)

{

va_list arg_ptr;

int j=0;

 

va_start(arg_ptr, i);

j=va_arg(arg_ptr, int);

va_end(arg_ptr);

printf("%d %d\n", i, j);

return;

}

我们可以在我们的头文件中这样声明我们的函数:

extern void simple_va_fun(int i, ...);

我们在程序中可以这样调用:

simple_va_fun(100);

simple_va_fun(100,200);

从这个函数的实现可以看到,我们使用可变参数应该有以下步骤:

1)首先在函数里定义一个va_list型的变量,这里是arg_ptr,这个变

量是指向参数的指针.

2)然后用va_start宏初始化变量arg_ptr,这个宏的第二个参数是第

一个可变参数的前一个参数,是一个固定的参数.

3)然后用va_arg返回可变的参数,并赋值给整数j. va_arg的第二个

参数是你要返回的参数的类型,这里是int型.

4)最后用va_end宏结束可变参数的获取.然后你就可以在函数里使

用第二个参数了.如果函数有多个可变参数的,依次调用va_arg获

取各个参数.

如果我们用下面三种方法调用的话,都是合法的,但结果却不一样:

1)simple_va_fun(100);

结果是:100 -123456789(会变的值)

2)simple_va_fun(100,200);

结果是:100 200

3)simple_va_fun(100,200,300);

结果是:100 200

我们看到第一种调用有错误,第二种调用正确,第三种调用尽管结果

正确,但和我们函数最初的设计有冲突.下面一节我们探讨出现这些结果

的原因和可变参数在编译器中是如何处理的.

 

(二)可变参数在编译器中的处理

 

我们知道va_start,va_arg,va_end是在stdarg.h中被定义成宏的,

由于1)硬件平台的不同 2)编译器的不同,所以定义的宏也有所不同,下

面以VC++中stdarg.h里x86平台的宏定义摘录如下(’\’号表示折行):

 

typedef char * va_list;

 

#define _INTSIZEOF(n) \

((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )

 

#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )

 

#define va_arg(ap,t) \

( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

 

#define va_end(ap) ( ap = (va_list)0 )

 

定义_INTSIZEOF(n)主要是为了某些需要内存的对齐的系统.C语言的函

数是从右向左压入堆栈的,图(1)是函数的参数在堆栈中的分布位置.我

们看到va_list被定义成char*,有一些平台或操作系统定义为void*.再

看va_start的定义,定义为&v+_INTSIZEOF(v),而&v是固定参数在堆栈的

地址,所以我们运行va_start(ap, v)以后,ap指向第一个可变参数在堆

栈的地址,如图:

 

高地址|-----------------------------|

|函数返回地址 |

|-----------------------------|

|....... |

|-----------------------------|

|第n个参数(第一个可变参数) |

|-----------------------------|<--va_start后ap指向

|第n-1个参数(最后一个固定参数)|

低地址|-----------------------------|<-- &v

图( 1 )

 

然后,我们用va_arg()取得类型t的可变参数值,以上例为int型为例,我

们看一下va_arg取int型的返回值:

j= ( *(int*)((ap += _INTSIZEOF(int))-_INTSIZEOF(int)) );

首先ap+=sizeof(int),已经指向下一个参数的地址了.然后返回

ap-sizeof(int)的int*指针,这正是第一个可变参数在堆栈里的地址

(图2).然后用*取得这个地址的内容(参数值)赋给j.

 

高地址|-----------------------------|

|函数返回地址 |

|-----------------------------|

|....... |

|-----------------------------|<--va_arg后ap指向

|第n个参数(第一个可变参数) |

|-----------------------------|<--va_start后ap指向

|第n-1个参数(最后一个固定参数)|

低地址|-----------------------------|<-- &v

图( 2 )

 

最后要说的是va_end宏的意思,x86平台定义为ap=(char*)0;使ap不再

指向堆栈,而是跟NULL一样.有些直接定义为((void*)0),这样编译器不

会为va_end产生代码,例如gcc在linux的x86平台就是这样定义的.

在这里大家要注意一个问题:由于参数的地址用于va_start宏,所

以参数不能声明为寄存器变量或作为函数或数组类型.

关于va_start, va_arg, va_end的描述就是这些了,我们要注意的

是不同的操作系统和硬件平台的定义有些不同,但原理却是相似的.

 

(三)可变参数在编程中要注意的问题

 

因为va_start, va_arg, va_end等定义成宏,所以它显得很愚蠢,

可变参数的类型和个数完全在该函数中由程序代码控制,它并不能智能

地识别不同参数的个数和类型.

有人会问:那么printf中不是实现了智能识别参数吗?那是因为函数

printf是从固定参数format字符串来分析出参数的类型,再调用va_arg

的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通

过在自己的程序里作判断来实现的.

另外有一个问题,因为编译器对可变参数的函数的原型检查不够严

格,对编程查错不利.如果simple_va_fun()改为:

void simple_va_fun(int i, ...)

{

va_list arg_ptr;

char *s=NULL;

 

va_start(arg_ptr, i);

s=va_arg(arg_ptr, char*);

va_end(arg_ptr);

printf("%d %s\n", i, s);

return;

}

可变参数为char*型,当我们忘记用两个参数来调用该函数时,就会出现

core dump(Unix) 或者页面非法的错误(window平台).但也有可能不出

错,但错误却是难以发现,不利于我们写出高质量的程序.

以下提一下va系列宏的兼容性.

System V Unix把va_start定义为只有一个参数的宏:

va_start(va_list arg_ptr);

而ANSI C则定义为:

va_start(va_list arg_ptr, prev_param);

如果我们要用system V的定义,应该用vararg.h头文件中所定义的

宏,ANSI C的宏跟system V的宏是不兼容的,我们一般都用ANSI C,所以

 

 

原文载于:http://www.cppblog.com/xmoss/archive/2009/07/20/90680.aspx

 

VA_LIST 是在C语言中解决变参问题的一组宏

 

他有这么几个成员:

 

1) va_list型变量:

 

#ifdef  _M_ALPHA

typedef struct {

        char *a0;       /* pointer to first homed integer argument */

        int offset;     /* byte offset of next parameter */

} va_list;

#else

typedef char *  va_list;

#endif

 

2)_INTSIZEOF 宏,获取类型占用的空间长度,最小占用长度为int的整数倍:

 

#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

 

3)VA_START宏,获取可变参数列表的第一个参数的地址(ap是类型为va_list的指针,v是可变参数最左边的参数):

 

#define va_start(ap,v)  ( ap = (va_list)&v + _INTSIZEOF(v) )

 

4)VA_ARG宏,获取可变参数的当前参数,返回指定类型并将指针指向下一参数(t参数描述了当前参数的类型):

 

#define va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

 

5)VA_END宏,清空va_list可变参数列表:

 

#define va_end(ap)      ( ap = (va_list)0 )

 

VA_LIST的用法:     

       (1)首先在函数里定义一具VA_LIST型的变量,这个变量是指向参数的指针;

       (2)然后用VA_START宏初始化变量刚定义的VA_LIST变量;

       (3)然后用VA_ARG返回可变的参数,VA_ARG的第二个参数是你要返回的参数的类型(如果函数有多个可变参数的,依次调用VA_ARG获取各个参数);

       (4)最后用VA_END宏结束可变参数的获取。

使用VA_LIST应该注意的问题:

   (1)可变参数的类型和个数完全由程序代码控制,它并不能智能地识别不同参数的个数和类型;

   (2)如果我们不需要一一详解每个参数,只需要将可变列表拷贝至某个缓冲,可用vsprintf函数;

   (3)因为编译器对可变参数的函数的原型检查不够严格,对编程查错不利.不利于我们写出高质量的代码;

 

小结:可变参数的函数原理其实很简单,而VA系列是以宏定义来定义的,实现跟堆栈相关。我们写一个可变参数的C函数时,有利也有弊,所以在不必要的场合,我们无需用到可变参数,如果在C++里,我们应该利用C++多态性来实现可变参数的功能,尽量避免用C语言的方式来实现。

 

 

示例代码:

de>#include<stdio.h>

#include<stdarg.h>

void simple_va_fun(int start,...)

{

     va_list arg_ptr;

     int nArgValue = start;

     int nArgCount = 0;

     va_start(arg_ptr,start);

     do

     {

     ++nArgCount;

      printf("the %d the arg:%d\n",nArgCount,nArgValue);

       nArgValue=va_arg(arg_ptr,int);

    }while(nArgValue != -1);

    return;

     }

main()

{

simple_va_fun(100,-1);

simple_va_fun(100,200,-1);

getchar();

      }de>

 

de>///////////////////de>

 

int writeMultiString(void *c, ...)

{

    va_list ap;

    const char *str;

 

    va_start(ap, c);

    str = va_arg(ap, const char *);

 

    while (str != NULL)

    {

        fputs(str, (FILE *)c);

        str = va_arg(ap, const char *);

    }

 

    va_end(ap);

    return 0;

}

 

保姆级教程:用Anaconda+PyCharm搞定YOLOv8本地部署(附避坑指南) 本文提供了一份详细的YOLOv8本地部署指南,涵盖Anaconda环境配置、PyCharm集成到模型部署的全过程。特别针对国内开发者需求,整合了镜像加速和常见报错解决方案,帮助快速完成目标检测模型的本地部署与实战应用。 阅读详情

相关推荐

原生H5如何优雅拦截浏览器返回事件:全面屏侧滑退出的解决方案

本文深入探讨了在原生H5中如何优雅地处理浏览器返回事件,特别是针对全面屏手机侧滑返回手势的解决方案。核心是利用HTML5 History API,通过`history.pushState`方法在历史栈中插入记录,并监听`popstate`事件,从而实现在用户侧滑时平滑关闭全屏预览层而非直接退出页面,有效提升了移动端H5应用的原生交互体验。

i0j1k2l3m的博客 270

有关va_list和vsnprintf输出函数的问题

 va_list ap; //声明一个变量来转换参数列表 va_start(ap,fmt); //初始化变量 va_end(ap); //结束变量列表,和va_start成对使用 可以根据va_arg(ap,type)取出参数 已经经过调试成功的输出程序

郭耀今 -- 蓝天白云 1681

Carla学习笔记(一)服务器跑carla本地显示窗口

其中host后替换为你的服务器IP,-carla-rpc-port和-p后为端口号,需要一致。python3 manual_control.py也可以替换为其他python脚本。因为电脑配置不够,所以在服务器跑carla,本地运行PythonAPI里的样例。把carla仓库的PythonAPI拷贝到本地,或者直接去官方里下载一下。

做题记录 4978

linux list 添加失败,c – Linux上的va_list错误行为

我有一些代码将variadic参数转换为va_list,然后将列表传递给一个函数,然后调用vsnprintf.这在Windows和OS X上运行良好,但在Linux上却出现了奇怪的结果.在以下代码示例中:#include #include #include #include char *myPrintfInner(const char *message, va_list params){va_li...

weixin_42628573的博客 310

从Gotham到Foundry:一文读懂Palantir三大产品线(Gotham/Foundry/Apollo)的核心差异与适用场景

本文深度解析Palantir三大产品线Gotham、Foundry和Apollo的核心差异与适用场景。Gotham专为国防与情报设计,提供军事级安全分析;Foundry是企业数字化转型的通用平台,支持低代码开发;Apollo则是云原生基石,确保系统可靠运行。通过对比产品架构与典型应用,帮助技术决策者根据需求做出明智选择。

weixin_42524499的博客 842

Triton server的部署、构建、backend插件机制代码梳理、模型加载、自定义C++backend流程

Triton inference server的部署、构建、backend机制代码详细梳理、模型加载过程、自定义C++backend流程.

C/C++、嵌入式开发、深度学习算法、模型部署与推理优化 473

关于va和字节对齐

这是一个使用可变函数参数的示例程序,如下: #include #include #include //#include typedef char *  va_list; //#define _INTSIZEOF(n)  ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int)

wl_haanel的专栏 966

Python爬虫实战教程:如何高效爬取税务局通知通告

税务局发布的通知通告包含政策法规、办税指南、公告信息等,是纳税人、企业合规经营的重要参考。官网发布频率高,手动查看不便,自动爬取并保存,方便信息及时掌握及历史数据归档。本文以合法合规的方式示范,建议实际应用前确认目标网站允许爬取的范围和方式。建议使用pandas进行数据清洗,如去重、空值处理等。对于研究税务政策、进行大数据分析、搭建行业信息平台,爬取税务局通知通告信息尤为关键。公告列表以分页形式展现,包含标题、发布时间和详情页链接。进行政策趋势图、发布频率统计等可视化。爬取公开数据时,务必遵守网站的。

2201_76125261的博客 1781

[C++]遍历可变参数 (va_list)

前言   在C++中使用可变参数可没C#中那么方便,使用起来也很麻烦,想尽办法搜了很多资料仍然无果,但是确有一些心得。     文章     1.  http://www.programmer-club.com.tw/ShowSameTitleN/vc/13915.html   2.  va_list、va_start、va_arg、va_end的原理与使用   正文   一....

weixin_34221073的博客 438

【文献阅读】使用深度语音后验改进独立于说话者的构音障碍可懂度分类

原文名称:本文探讨了利用DeepSpeech后验概率改进说话人无关的构音障碍可理解性分类方法。作者提出了一个基于DeepSpeech(一种端到端的语音转文本引擎)输出的新特征集,用于评估构音障碍患者的语音可理解性。这些新特征在说话人无关的情况下表现良好,能够更好地处理不同说话人的变异性。背景介绍:数据集:特征提取:实验设置:结果:本文提出的新特征集在说话人无关的构音障碍可理解性分类任务中表现出色,特别是在低可理解性和高可理解性类别上。未来的工作将集中在减少低可理解性和中可理解性类别之间的混淆,以进一步提高系

帅小柏的博客 452

图形推理2-四大类六提示

图形推理笔记2

小y的学习笔记 6746

C/C++(6)va_list、va_start、va_arg、va_end实现可变参数列表函数

经典的可变参数表printf函数的实现原理:在C/C++中,对函数参数的扫描是从后向前的。 函数参数是通过压入堆栈的方式来给函数传参数的,最先压入的参数最后出来,而栈是从内存的高地址向低地址存放的,控制存放位置的是堆栈指针,最先压入的参数是在最上面,最后压入的参数在最下面,结构上看起来是第一个,所以最后压入的参数总是能够被函数找到,因为它就在堆栈指针的上方。printf的第一个被找到的参数就是那个

kღïξs√√ 1478

在 Linux 64 位上遭遇 Signal 7 与 ST22 SYSTEM_CORE_DUMPED:从 es/implementation=std 到 /dev/shm 的一套落地排查法

摘要: Linux 64位SAP系统出现SYSTEM_CORE_DUMPED和Signal 7(对应SIGBUS)错误时,核心问题是进程因共享内存资源异常被操作系统强制终止。主要诱因包括:1)/dev/shm的tmpfs默认容量不足(通常仅占物理内存50%);2)SAP内存管理参数es/implementation=map在64位场景下的敏感性。解决方案应双管齐下:在实例profile设置es/implementation=std以降低对tmpfs的依赖,同时按(RAM+Swap)*75%重新配置/dev/

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 164

【C语言】C中解决变参问题—VA_LIST

VA_LIST是C语言中解决变参问题的一组宏。头文件来自stdarg.h。查看linux系统源码方式我一般用locate stdarg.h,然后找到提示目录vi进去。源文件定义是:va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。在调用参数表之前,应该定义一个 va_list 类型的变量 args;然后通过va_start对args 进行初始化,让它指向可变参数表里面的第一个参数,第一个参数是 args 本身,第二个参数是在变参表前面紧挨着的一个变量;然后是获取参数

688

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

编程参考 - va_list的定义问题

会发现,这个类型的定义虽然由头文件stdarg.h提供,是C标准,但实际定义却是根据编译器、操作系统和ABI所决定的。但当使用mips-linux toolchain时,第三个就会调用到No.2函数,这种情况仅发生在format后面只有一个参数,且是char*变量时。编译器在根据参数列表选择要调用的函数时,第一个参数两个函数都匹配,而第二个的话,char*就匹配到了va_list,优先于省略参数。gcc中的arm.cc和aarch64.cc里面有各自的va_list的定义。

guoqx的专栏 1364

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

C语言可变参数问题(va_list,va_start,va_arg,va_end用法及定义)

#include #include<stdarg.h> using namespace std; void Outputinfo(const char pchdata=“One world,one dream!”){ cout<<pchdata<<endl; } int GetMax(int x,int z,int y=10){//默认值只能在所有非默认值的右边,否则会发生错误。 if(x<y) x=y; if(x<z) x=z; return x; } vo

qq_40422174的博客 666

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

va_list va_start va_end va_arg 解决变参问题

解决参数个数不确定的问题。 头文件 #include<stdarg.h> VA_LIST 是在C语言中解决变参问题的一组宏,用于获取不确定个数的参数。 #ifdef _M_ALPHA typedef struct { char *a0; /* pointer to first homed integer argument */ int offset; /* byte o...

weixin_30498921的博客 141

C++中利用 va_list实现可变参数函数需要注意的问题

本文介绍了C++语言中利用va_list实现可变参数传递可能遇到的问题,并给出的解决办法。

suiyueruge2019的博客 1080

VA_LIST的用法和问题——解决未知参数个数问题

C++定义的函数是可以支持函数参数个数不确定的。VA_LIST是在C++语言中解决变参问题的一组宏,所在头文件:#include <stdarg.h>,用于获取不确定个数的参数同时使用"…"代替多个参数,调用时只需要根据需要传入多个参数。 VA_LIST的用法: [转载] 首先在函数里定义一具VA_LIST型的变量,这个变量是指向参数的指针; 然后用VA_START宏初始化刚定义的VA_LIST变量; 然后用VA_ARG返回可变的参数,VA_ARG的第二个参数是你要返回的参数的类型(如果函数有

D_daytime的博客 2159

va_list

VA_LIST 是在C语言中解决变参问题的一组宏,变参问题是指参数的个数不定,可以是传入一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同. va_list 用法: 在函数里定义一具VA_LIST型的变量,这个变量是指向参数的指针; 然后用VA_START宏初始化变量刚定义的VA_LIST变量; 然后用VA_ARG返回可变的参数,VA_ARG的第二个参数是你要返回的参数...

MJ_Lee的博客 3306

va_list-关于方法变参的问题

由于网上太多关于此题目的优秀资源,所以下面的都是些摘自网络资源的片段。 下面片段摘自:http://baike.baidu.com/link?url=VUkCVXA6Q2lgVB7q9D9A84H3_JixWri0YNiO4Gey8MXaKPBfRae4-rAfufjKpUWN-rbIsqdqRg0gbHrtgGXXF_ 概述:         VA_LIST 是在C语言中解决变参问题的一组

afei198409的专栏 1212

C语言中: va_list 解决变参问题

VA_LIST 是在C语言中解决变参问题的一组宏,所在头文件:#include <stdarg.h>,用于获取不确定个数的参数 ——摘自百度百科 va_list 是一个字符指针,在代码中可以理解为指向当前参数的一个指针,这里有几个与 va_list 相关的常见的函数void va_start ( va_list ap, param ); //对va_list变量进行初始化,将ap指针指向参数列表

代码改变世界 4237

va_list解决c中变参问题

(1) va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。 (2)在调用参数表之前,应该定义一个 va_list 类型的变量 args; (3)然后通过va_start对 args 进行初始化,让它指向可变参数表里面的第一个参数,第一个参数是args 本身,第二个参数是在变参表前面紧挨着的一个变量;然后是获取参数, (4)调用va_arg,它的第一个...

金溪的博客 914

解决变参数问题:va_list、va_start、vsnprintf、va_end的使用方法和实例

1、va_list、va_start、vsnprintf、va_end的使用方法和实例 #include void test_char(const char* format, ...){ char buf[512]; va_list args; va_start(args, format); vsnprintf(buf, sizeof(buf), form

寒江蓑笠的专栏 1755

编译原理实验1中ast.c文件va_list初始化报错问题

编译原理实验1中ast.c文件va_list初始化报错问题 实验内容 编译原理的实验一中语法分析器parser的构造。 遇到的问题 当执行完以下两步 1.flex lex.l 生成lex.yy.c 2.bison -d parser.y 生成parser.tab.c和 parser.tab.h 都成功了,但是执行第三步 gcc -o parser lex.yy.c parser.tab.c ast.c 来生成 parser.exe时

m0_52934091的博客 496
上一篇: 基于Video4Linux的视频采集模块开发
下一篇: C语言函数调用的规则
wangrunmin
博客等级 码龄17年 265粉丝 74原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值