前言
本文主要讲述变量名冲突的问题。
\\color{orange}免责声明
由于版本问题,您遇到的情况可能与我所讲述的不符,一切以本机版本为准,如果您遇到了其他问题,欢迎在评论区留下您的观点或遇到的问题。
即使现在变量名问题已经得到解决,我仍不建议大家在 NOI 系列比赛中使用这些变量名。
本人所用配置是:
- Microsoft:
Windows 10
- Dev C++:
5.11- MingW64:
4.9.2- Linux stu:
20.04
- Genay:
1.36- MingW64:
Ubuntu 9.4.0- Luogu IDE:
C++14(GCC 9)
正文
引入
在机房颓的时候突然发现自己之前写的一道学校改编的入门考试题(原题在这里),
我这样写:
cpp
double len(double x1, double y1, double x2, double y2){
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
按照现在的一般说法,这应该会 CE,因为 x1 y1 不能用作变量名。
但是它诡异地 AC 了。
?不是说不行吗?
于是 cao2333 开始了查资料 -> 验证 -> 不对 -> 查资料的循环......
前置知识:
#ifdef Value:和日常写代码时的if()很像,如果Value值为 1 则会执行下面的语句。#ifndef Value:与#ifdef恰好相反,当后面的Value条件不成立时才会执行。#endif:表示终止条件判断,与#ifdef和#ifndef连用。#include<filename>导入头文件#define A B宏定义,告诉编译器遇到A就当成B。#undef A表示取消#define的宏定义。
cmath
cmath文件是标准的数学库。在这个库中,包括了abs(绝对值)sin cos tan(三角函数)ceil floor(向上/向下取整)等等十分好用的函数的指向。
有很多说法指出,x1 等变量名被占用是 cmath 头文件的锅。那么我们扒一下他的代码:
P.S. 省略 1600 多行代码。
......算了,还是用搜索吧。
搜索结果:未找到"x1"。
很好看来这个问题并没有出在 cmath 文件。
那么为什么很多人加了 cmath 会有问题、不加就没问题呢?
我们仔细审视一下 cmath 文件,发现这里的很多函数用几个模糊的大写字母来计算,大部分函数只有不到十行,这显然不足以完成仅仅推导式就要十几行的数学函数。
实际上,cmath 文件并不提供 部分 数学函数的解决办法,它的性质与 bits/stdc++.h 相似,都是将一些零散的头文件合并,并将深层头文件内容 、本文件中的额外附加 以及执行条件一并添加到你编写的程序中。
因此如果深层的头文件出现问题,依赖该文件的其他文件也会报错。
在 cmath 的开头部分中,我们找到以下代码:
cpp
#pragma GCC system_header
#include <bits/c++config.h>
#include <bits/cpp_type_traits.h>
#include <ext/type_traits.h>
#include <math.h>
看来问题就出在这几个头文件里了。
math.h
经过搜索大法,问题出在 C:\Program Files (x86)\Dev-Cpp\MinGW64\x86_64-w64-mingw32\include\math.h 上。
math.h文件中,存储了部分函数的定义、运算方式 和 大部分数学函数的指向 。包括但不限于abssincos等。
然后观察到:
cpp
_CRTIMP double __cdecl j0 (double) __MINGW_ATTRIB_DEPRECATED_MSVC2005;
_CRTIMP double __cdecl j1 (double) __MINGW_ATTRIB_DEPRECATED_MSVC2005;
_CRTIMP double __cdecl jn (int, double) __MINGW_ATTRIB_DEPRECATED_MSVC2005;
_CRTIMP double __cdecl y0 (double) __MINGW_ATTRIB_DEPRECATED_MSVC2005;
_CRTIMP double __cdecl y1 (double) __MINGW_ATTRIB_DEPRECATED_MSVC2005;
_CRTIMP double __cdecl yn (int, double) __MINGW_ATTRIB_DEPRECATED_MSVC2005;
这是现在的代码。如果你是报错的话,这一段大概率长这样:
cpp
_CRTIMP double __cdecl j0 (double);
_CRTIMP double __cdecl j1 (double);
_CRTIMP double __cdecl jn (int, double);
_CRTIMP double __cdecl y0 (double);
_CRTIMP double __cdecl y1 (double);
_CRTIMP double __cdecl yn (int, double);
并不知道两段有什么区别
总之,可能是 MinGW 不知道何时更新了,现在定义变量名使用 x1 y1 x0 y0 都没有问题了。
拓展
虽然现在可以用这些变量名了,但是我还是有一些问题和有趣的发现:
这几个函数是干什么用的?
这个我已经没有时间和精力去递归式查找了,于是在这里贴出 Bing 搜索的结果:
\(j0,j1,jn\) 用于返回第一类贝塞尔函数解;
\(y0,y1,yn\) 用于返回第二类贝塞尔函数解;
似乎在计算机学中还没有广泛使用?如果感兴趣的话可以去看看偶,没准你是下一个新算法的发明者。
为什么现在没事了?
是啊为什么?
由于机房的网是断开的,只能上几个限定网站,因此我也不知道 Bing 到的这个结果 中的解决办法是什么......
源代码打表
在 math.h 函数中:
cpp
#define M_E 2.7182818284590452354
#define M_LOG2E 1.4426950408889634074
#define M_LOG10E 0.43429448190325182765
#define M_LN2 0.69314718055994530942
#define M_LN10 2.30258509299404568402
#define M_PI 3.14159265358979323846
#define M_PI_2 1.57079632679489661923
#define M_PI_4 0.78539816339744830962
#define M_1_PI 0.31830988618379067154
#define M_2_PI 0.63661977236758134308
#define M_2_SQRTPI 1.12837916709551257390
#define M_SQRT2 1.41421356237309504880
#define M_SQRT1_2 0.70710678118654752440
呵,想必大家都知道这片源代码在干什么吧......
赤裸裸的打表啊!
两个 math.h
在搜索时,C:\Program Files (x86)\Dev-Cpp\MinGW64\lib\gcc\x86_64-w64-mingw32\4.9.2\include\c++\tr1 路径下还有一份 math.h 文件。这份文件中有大量的 using tr1::...:: 代码。应该是 tr1 的实现代码。不知道是做什么的,今天按下不表。
变量名通缉单
在 cmath 文件中,断断续续出现了下列代码:
\\color{blue}过长,已折叠
cpp
#undef abs
#undef div
#undef acos
#undef asin
#undef atan
#undef atan2
#undef ceil
#undef cos
#undef cosh
#undef exp
#undef fabs
#undef floor
#undef fmod
#undef frexp
#undef ldexp
#undef log
#undef log10
#undef modf
#undef pow
#undef sin
#undef sinh
#undef sqrt
#undef tan
#undef tanh
#undef fpclassify
#undef isfinite
#undef isinf
#undef isnan
#undef isnormal
#undef signbit
#undef isgreater
#undef isgreaterequal
#undef isless
#undef islessequal
#undef islessgreater
#undef isunordered
#undef acosh
#undef acoshf
#undef acoshl
#undef asinh
#undef asinhf
#undef asinhl
#undef atanh
#undef atanhf
#undef atanhl
#undef cbrt
#undef cbrtf
#undef cbrtl
#undef copysign
#undef copysignf
#undef copysignl
#undef erf
#undef erff
#undef erfl
#undef erfc
#undef erfcf
#undef erfcl
#undef exp2
#undef exp2f
#undef exp2l
#undef expm1
#undef expm1f
#undef expm1l
#undef fdim
#undef fdimf
#undef fdiml
#undef fma
#undef fmaf
#undef fmal
#undef fmax
#undef fmaxf
#undef fmaxl
#undef fmin
#undef fminf
#undef fminl
#undef hypot
#undef hypotf
#undef hypotl
#undef ilogb
#undef ilogbf
#undef ilogbl
#undef lgamma
#undef lgammaf
#undef lgammal
#undef llrint
#undef llrintf
#undef llrintl
#undef llround
#undef llroundf
#undef llroundl
#undef log1p
#undef log1pf
#undef log1pl
#undef log2
#undef log2f
#undef log2l
#undef logb
#undef logbf
#undef logbl
#undef lrint
#undef lrintf
#undef lrintl
#undef lround
#undef lroundf
#undef lroundl
#undef nan
#undef nanf
#undef nanl
#undef nearbyint
#undef nearbyintf
#undef nearbyintl
#undef nextafter
#undef nextafterf
#undef nextafterl
#undef nexttoward
#undef nexttowardf
#undef nexttowardl
#undef remainder
#undef remainderf
#undef remainderl
#undef remquo
#undef remquof
#undef remquol
#undef rint
#undef rintf
#undef rintl
#undef round
#undef roundf
#undef roundl
#undef scalbln
#undef scalblnf
#undef scalblnl
#undef scalbn
#undef scalbnf
#undef scalbnl
#undef tgamma
#undef tgammaf
#undef tgammal
#undef trunc
#undef truncf
#undef truncl
实测发现,在这张列表里涉及到的所有 #undef 后面的命名 全部有对应函数 ,都不能作为变量名。
后记
其实也算是颓了一个下午。有一种探案的感觉呢。不过在查资料的过程中,我发现了这样一个问题:
大家要么是查到资料解决问题就好,要么是发现问题避开问题,很少的巨佬会身先士卒追根究底、不找到根因不罢休。
我也不是什么巨佬啦,我的调查就到这里了,如果有很多人支持的话,我应该会出第二期 QwQ,如果有巨佬收到启发,希望诸位巨佬也要坚持到底呀!
\\\color{green}The\\;\\;End \\