Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/60.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/variables/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
char的va_arg故障_C_Variables_Parameter Passing - Fatal编程技术网

char的va_arg故障

char的va_arg故障,c,variables,parameter-passing,C,Variables,Parameter Passing,我尝试使用va_arg检索函数中的下一个参数。它适用于所有类型,包括char*但char: void test1(char t,...) { va_list args; va_start(args, t); if(t=='c') Serial.println(va_arg(args, char)); else if(t=='n') Serial.println(va_arg(args, int)); va_end(args); } 测试: 你能证实/解释一下吗?代码在9

我尝试使用va_arg检索函数中的下一个参数。它适用于所有类型,包括char*但char:

void test1(char t,...) {
  va_list args;
  va_start(args, t);
  if(t=='c') Serial.println(va_arg(args, char));
  else if(t=='n') Serial.println(va_arg(args, int));
  va_end(args);  
}
测试:


你能证实/解释一下吗?代码在9600波特的Arduino Uno上运行。

小于int的参数在传递给变量函数之前被提升为int,因此此类函数应以int类型检索参数。

小于int的参数在传递给变量函数之前被提升为int,因此,此类函数应检索int类型的参数。

变量函数附带了一个用于隐式类型提升的特殊规则,称为默认参数提升

C17 6.5.2.2/7

函数原型声明器中的省略号表示法导致 参数类型转换在最后声明的参数之后停止。默认参数 对尾部参数执行升级

省略号是

默认参数提升通常仅在使用旧式非原型函数时使用。因此,它们被定义为:

C17 6.5.2.2/6

如果表示被调用函数的表达式具有不包含原型的类型,则对每个参数执行整数提升,并将具有float类型的参数提升为double。这些被称为默认参数

在您的例子中,char计数为整数类型,上面的意思是它将在传递给变量函数时得到

二进制这意味着ASCII?=0x3F升级为int。AVR使用16位小尾端,因此它在内存中存储为0x3F 0x00。问题不在这里

相反,当您尝试在错误的类型上使用va_arg时,会调用未定义的行为。这在va_arg的文档中有说明:

C17.16.1.1/2

如果没有实际的下一个参数,或者 类型与实际下一个参数的类型不兼容,根据默认参数升级,行为未定义

因此,唯一可能的解决方案是将代码更改为ift=='c'Serial.printlnva_args,int

与您的问题无关,在8位MCU上使用可变函数和stdio.h是非常糟糕的做法。这些功能不仅危险,还会消耗大量闪存和RAM


此外,对于嵌入式系统,您应该始终使用stdint.h而不是默认类型的C。

变量函数附带了一个奇怪的特殊规则,用于隐式类型升级,称为默认参数升级

C17 6.5.2.2/7

函数原型声明器中的省略号表示法导致 参数类型转换在最后声明的参数之后停止。默认参数 对尾部参数执行升级

省略号是

默认参数提升通常仅在使用旧式非原型函数时使用。因此,它们被定义为:

C17 6.5.2.2/6

如果表示被调用函数的表达式具有不包含原型的类型,则对每个参数执行整数提升,并将具有float类型的参数提升为double。这些被称为默认参数

在您的例子中,char计数为整数类型,上面的意思是它将在传递给变量函数时得到

二进制这意味着ASCII?=0x3F升级为int。AVR使用16位小尾端,因此它在内存中存储为0x3F 0x00。问题不在这里

相反,当您尝试在错误的类型上使用va_arg时,会调用未定义的行为。这在va_arg的文档中有说明:

C17.16.1.1/2

如果没有实际的下一个参数,或者 类型与实际下一个参数的类型不兼容,根据默认参数升级,行为未定义

因此,唯一可能的解决方案是将代码更改为ift=='c'Serial.printlnva_args,int

与您的问题无关,在8位MCU上使用可变函数和stdio.h是非常糟糕的做法。这些功能不仅危险,还会消耗大量闪存和RAM


此外,对于嵌入式系统,您应该始终使用STNDTE.H代替C.< /P>的默认类型。VAYARG是C++中从未使用过的东西之一。这样做的目的是什么。看起来更像VAYARG是C++中从未使用过的东西之一。这样做的目的是什么。看起来你是对的,我找到了参考7.16.1.1,正确的代码是ift='c'Serial.printlncharva_args,int;-似乎AVR使用的是大端整数表示,不像x86,所以“c”被编码为0x00 0x63,所以在我的原始代码中,第一个字节被提取到结果中,零有效地截断了字符串,这正是我观察到的。@JanTuroň我相信它应该使用小端,所以这不是原因。@JanTuroň我发布了一个更详细的answe



r解释代码不起作用的原因。这只是未定义的行为-它与AVR Endianness无关。+1。一个智能设计的va_arg可以在引擎盖下进行这项推广,使用户不必知道这一点。只是说/咆哮|@PSkocik在标准中有许多UB案例,委员会可以很容易地确定并做出明确定义。虽然对于va_arg&friends来说,他们占据了UB摘要附件的重要部分,所以在这个特定的案例中,我倾向于损坏无法修复。你是对的,我找到了参考7.16.1.1,正确的代码是ift=='c'Serial.printlncharva_Args,int;-与x86不同,AVR似乎使用大端整数表示,因此“c”编码为0x00 0x63,因此在我的原始代码中,第一个字节被提取到结果中,零有效地截断了字符串,这正是我观察到的。@JanTuroň我相信它应该使用小端,所以这不是原因。@JanTuroň我发布了一个更详细的答案,解释了为什么代码不起作用。这只是未定义的行为-它与AVR Endianness无关。+1。一个智能设计的va_arg可以在引擎盖下进行这项推广,使用户不必知道这一点。只是说/咆哮|@PSkocik在标准中有许多UB案例,委员会可以很容易地确定并做出明确定义。虽然对于va_arg&friends来说,它们占据了UB摘要附件的重要部分,所以在这个具体案例中,我倾向于使用无法修复的损坏。我发现可变格式输出函数在嵌入式系统上很有用,尽管我更喜欢使用我自己的而不是printf系列。对于许多应用程序,我的首选函数使用字节值0x80-0xFF作为格式说明符,它比printf参数更紧凑、更易于解析,还包括插入小数点之类的选项,这使得在用户界面中显示小数点成为可能,而只需进行整数运算。@supercat有点像用炸药捕鱼是多么有用,只是不太安全:MISRA-C和我所知道的所有其他编码标准都禁止可变函数。如果不是希望在实际情况下对可变函数和非类型函数使用相同的调用约定,可变函数的实现方式可能会允许ABI不可知论者获得指向下一个参数的指针,如果有,并报告其类型[所有结构和所有联合都将集中在一起]函数,同时使大多数调用站点的代码比现有方法更紧凑,从而允许安全地实现适当编写的可变函数。@supercat尽可能,底线是嵌入式系统应该是确定性的,而不是可变的。为什么函数不能同时是可变的和确定性的呢?调用方知道有多少个参数,将负责构建一个长度前缀的数据结构并向其传递一个指针。构建过程的大部分可以在编译器提供的子例程中处理,以避免客户机代码膨胀。这样的设计会破坏任何试图在没有原型的情况下调用可变函数的代码,这就是实现没有这样做的原因,但它本来可以既安全又节省代码空间。我发现可变格式输出函数在嵌入式系统上很有用,虽然我更喜欢使用我自己的而不是printf家族。对于许多应用程序,我的首选函数使用字节值0x80-0xFF作为格式说明符,它比printf参数更紧凑、更易于解析,还包括插入小数点之类的选项,这使得在用户界面中显示小数点成为可能,而只需进行整数运算。@supercat有点像用炸药捕鱼是多么有用,只是不太安全:MISRA-C和我所知道的所有其他编码标准都禁止可变函数。如果不是希望在实际情况下对可变函数和非类型函数使用相同的调用约定,可变函数的实现方式可能会允许ABI不可知论者获得指向下一个参数的指针,如果有,并报告其类型[所有结构和所有联合都将集中在一起]函数,同时使大多数调用站点的代码比现有方法更紧凑,从而允许安全地实现适当编写的可变函数。@supercat尽可能,底线是嵌入式系统应该是确定性的,而不是可变的。为什么函数不能同时是可变的和确定性的呢?调用方知道有多少个参数,将负责构建一个长度前缀的数据结构并向其传递一个指针。构建过程的大部分可以在编译器提供的子例程中处理,以避免客户机代码膨胀。这样的设计会破坏任何试图在没有原型的情况下调用可变函数的代码,这就是实现没有这样做的原因 但如果不是这样,它可能既安全又节省代码空间。
int n = 42;
char c = '?';

test1('n', n); // prints 42
test1('c', c); // prints nothing!