受到这个问题及其答案的启发,我做了一些测试。一个答案表明,数字太大(超过 32 位整数)并且它们被截断,但这并不能解释结果。同样显然它不会将双方作为字符串进行比较(正如我所期望的那样)。似乎if感到困惑并认为“好吧,我不知道 - 给它一个真实的”。(使用neq, gtr,lss而不是equ, geq,leq总是给出 FALSE)。
如果任何一个a和/或b在 32 位整数的边界内或包含 [0-9] 之外的任何字符,则代码按预期工作。
@echo off
set a=333333333333
set b=444444444444
call :compare
set b=222222222222
call :compare
goto :eof
:compare
echo comparing %a% with %b%
if %a% geq %b% (echo a ^>= b) else (echo -)
if %b% geq %a% (echo b ^>= a) else (echo -)
if %a% leq %b% (echo a ^<= b) else (echo -)
if %b% leq %a% (echo b ^<= a) else (echo -)
if %a% equ %b% (echo a = b) else (echo -)
if %a% == %b% (echo a == b) else (echo -)
Run Code Online (Sandbox Code Playgroud)
对此是否有任何合乎逻辑的解释,或者这只是我们不得不不假思索地接受的东西?
这个结果的原因可以在函数strtol 的文档中找到,它首先用于使用比较运算符EQU, NEQ, LSS, LEQ, GTR,GEQ如我在 Windows 批处理文件中对等价于 NEQ、LSS、GTR 等符号的回答中所述。
返回值
成功时,该函数将转换后的整数作为 long int 值返回。
如果无法执行有效转换,则返回零值 (0L)。
如果读取的值超出 long int 可表示值的范围,则函数返回 LONG_MAX 或 LONG_MIN(在<climits> 中定义),并将errno设置为 ERANGE。
最后一句话在这里最重要。
看起来IF incmd.exe的编码类似于此 C 代码:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char* argv[])
{
const char csNo[] = "no";
const char csYes[] = "yes";
char* pcEndValue1;
char* pcEndValue2;
int iExitCode = 2;
int iErrorNumber1;
int iErrorNumber2;
int iStringResult1;
int iStringResult2;
long lIntegerValue1;
long lIntegerValue2;
if(argc > 2)
{
/* Convert the two arguments to 32-bit signed integers. */
lIntegerValue1 = strtol(argv[1],&pcEndValue1,0);
iErrorNumber1 = errno;
lIntegerValue2 = strtol(argv[2],&pcEndValue2,0);
iErrorNumber2 = errno;
/* Failed the conversion for any of the two arguments? */
if(((lIntegerValue1 == 0) && (*pcEndValue1 != '\0')) ||
((lIntegerValue2 == 0) && (*pcEndValue2 != '\0')))
{
/* Compare case-sensitive the two arguments as strings. */
iStringResult1 = strcmp(argv[1],argv[2]);
iStringResult2 = strcmp(argv[2],argv[1]);
printf("String comparing %s (a) with %s (b):\n\n",argv[1],argv[2]);
printf("a GEQ b: %s\n",(iStringResult1 >= 0) ? csYes : csNo);
printf("b GEQ a: %s\n",(iStringResult2 >= 0) ? csYes : csNo);
printf("a LEQ b: %s\n",(iStringResult1 <= 0) ? csYes : csNo);
printf("b LEQ a: %s\n",(iStringResult2 <= 0) ? csYes : csNo);
printf("a EQU b: %s\n",(iStringResult2 == 0) ? csYes : csNo);
iExitCode = 1;
}
else
{
/* Compare the values. */
printf("Value comparing %s/%ld (a) with %s/%ld (b):\n\n",argv[1],lIntegerValue1,argv[2],lIntegerValue2);
printf("a GEQ b: %s\n",(lIntegerValue1 >= lIntegerValue2) ? csYes : csNo);
printf("b GEQ a: %s\n",(lIntegerValue2 >= lIntegerValue1) ? csYes : csNo);
printf("a LEQ b: %s\n",(lIntegerValue1 <= lIntegerValue2) ? csYes : csNo);
printf("b LEQ a: %s\n",(lIntegerValue2 <= lIntegerValue1) ? csYes : csNo);
printf("a EQU b: %s\n",(lIntegerValue2 == lIntegerValue1) ? csYes : csNo);
iExitCode = 0;
}
printf("\nError number a: %d ... %s\n",iErrorNumber1,strerror(iErrorNumber1));
printf("Error number b: %d ... %s\n",iErrorNumber2,strerror(iErrorNumber2));
}
return iExitCode;
}
Run Code Online (Sandbox Code Playgroud)
将此 C 代码编译为控制台应用程序并运行带有参数的可执行文件,333333333333 444444444444例如输出:
Value comparing 333333333333/2147483647 (a) with 444444444444/2147483647 (b):
a GEQ b: yes
b GEQ a: yes
a LEQ b: yes
b LEQ a: yes
a EQU b: yes
Error number a: 2 ... Output of function out of range (ERANGE)
Error number b: 2 ... Output of function out of range (ERANGE)
Run Code Online (Sandbox Code Playgroud)
并使用参数运行可执行文件会333333333333 222222222222导致例如输出:
Value comparing 333333333333/2147483647 (a) with 222222222222/2147483647 (b):
a GEQ b: yes
b GEQ a: yes
a LEQ b: yes
b LEQ a: yes
a EQU b: yes
Error number a: 2 ... Output of function out of range (ERANGE)
Error number b: 2 ... Output of function out of range (ERANGE)
Run Code Online (Sandbox Code Playgroud)
注意:错误编号和相应的错误字符串可能因使用的 C 编译器和标准库而异。
在这两个测试用例中,两个参数都导致在从 string 转换为 long int 时出现 32 位有符号整数溢出。因此strtol返回所有四个值LONG_MAX并设置errno为ERANGE. 但是溢出条件不是由IF in的代码评估的cmd.exe。它只是检查转换结果以及结束指针指向两个参数的哪个字符,就像上面的 C 代码一样。
换句话说,IF处理比较运算符EQU, NEQ, LSS, LEQ, 的使用GTR,GEQ总是整数比较,只要从字符串到整数的转换不会因为参数字符串中的无效字符而导致两个参数中的任何一个失败。超出范围条件不是IF不进行整数比较的理由。
仅当两个参数字符串之一包含整数的无效字符时,才进行字符串比较。