

























因为180°=π弧度,那么1°=π/180弧度。所以想输出sin(90°)应该cout << sin(90*π/180) << endl;,只不过没有C++库里定义π (豆包误人子弟,md后来在知乎直答里知道,cmath或者math.c库函数里有“派π”这玩意M_PI )(M_PI不是C++标准的一部分,是编译器or操作系统的扩展定义)
cos90°,横坐标x=r*cosθ,所以cos90°是0,cos180°是-1,可以用acos输出圆周率精确数值,代码如下,acos0是90°,计算机里三角函数都是用弧度制表示
#include <iostream> #include <cmath> #include<iomanip> using namespace std; int main() { // 使用 acos 函数计算 pi const double pi = 2 * acos(0.0); // const double pi = acos(-1);两个都可以,这个更直接,只不过一开始没想到acos-1直接就是π cout<<setprecision(19)<< pi << endl; }
#include <iostream> #include <iomanip> using namespace std; int main() { double num = 3.14159265358979323846; // 设置输出精度为 4 位小数 cout << setprecision(4) <<"#" <<num << endl; cout << fixed << setprecision(4) <<"@"<< num << endl; // 取消 fixed 格式 cout.unsetf(ios::fixed); cout.precision(4); cout << num << endl; }
简单来说,伪随机数是由种子(seed)和伪随机数生成器(PRNG, Pseudo-Random Number Generator)的算法共同产生的。生成器的公式根据所使用的算法而不同,但一个经典例子是线性同余生成器(LCG, Linear Congruential Generator),其公式大致如下:
即生成10个随机数,如果是时间戳的形式,则这10个随机数满足上述同余公式,只不过a、c、m都是计算机设定的(编程语言的标准库会使用预定义的设置或内部算法来初始化随机数生成器),如果自己设定可以这么写

1 #include <iostream> 2 3 // 自定义线性同余法生成伪随机数 4 unsigned long long lcg_rand(unsigned long long previous, unsigned long long a, unsigned long long c, unsigned long long m) { 5 return (previous * a + c) % m; 6 } 7 8 int main() { 9 // 自定义参数 10 unsigned long long seed = 1; // 初始种子值 11 unsigned long long a = 1103515245; // 乘数 12 unsigned long long c = 12345; // 增量 13 unsigned long long m = (1ull << 31) - 1; // 模数,通常使用2的31次方减1 14 15 std::cout << "Random number sequence using custom LCG:\n"; 16 17 // 生成并打印前5个随机数 18 for(int i = 0; i < 5; ++i) { 19 seed = lcg_rand(seed, a, c, m); 20 std::cout << seed << std::endl; 21 } 22 23 return 0; 24 }
View Code
这个定理探讨的是形如 ax ≡ b (mod m) 的方程,其中 a, b, 和 m 是整数,m 不为零。
线性同余定理关注的是这样的方程有多少解,以及如何找到这些解。
在算法和计算机科学中,线性同余算法利用了线性同余的概念来生成伪随机数序列
之前听过,回顾一下,bing,知乎回答(没看这个),OI_Wiki(没看这个),看到这个 PKU内部讲解(里面只看了第3页,仅仅是弄懂了这个PPT,ACM数论的玩意先搁置吧)
先说下逆元:
逆元是指在特定运算下,与一个元素相结合能产生单位元的元素。在乘法运算中,一个数a的逆元是b,满足a乘以b等于1。在模运算环境下,如果考虑模n的乘法,数a的逆元x需要满足ax ≡ 1 (mod n)。但只有当a和n互质(即最大公约数为1)时,逆元才存在且唯一。逆元的概念广泛应用于数论、密码学以及计算机科学中的算法设计。
3的乘法逆元是2(在模5条件下)这句话:
在模5的运算中,寻找一个数,使得这个数与3相乘后,结果除以5的余数是1。这里所说的“3的乘法逆元是2”,是因为当我们将3乘以2时,得到6,而6除以5的余数是1(即6 ≡ 1 (mod 5))。这意味着在模5的世界里,2就是3的逆元,因为3乘以2的结果模5等于1,满足乘法逆元的定义。
开始解释:
如果3k模5等于4,我们可以通过等式来找到k模5的值,即k除以5的余数。
为了找到k模5的值,我们可以将等式两边同时除以3(假设k和m都是整数,且这样的操作在模运算的语境下意味着寻找一个等价的k值,使得该值除以5的余数不变),但是直接除以3在模运算中不直接适用,我们可以直接考虑3的逆元在模5下的情况,3的乘法逆元是2,将原方程两边同时乘以3的逆元2,得到: 2∗(3∗k)≡2∗4(mod5),已经说过在mod5的情况下,2*3是1,这转换为: k≡8(mod5)
简化后,k模5等于3,因为8除以5的余数是3。所以,k模5是3
继续解释:
15𝑟 + 11 ≡ 6 (𝑚𝑜𝑑 7),即15𝑟 ≡ 2 (𝑚𝑜𝑑 7)。
知乎直答
在模7的环境下,-5等价于2,因为-5加上7等于2
继续解释:
它有唯一解𝑟≡ 2 (𝑚𝑜𝑑 7).
知乎直答:
这个问题涉及模线性同余方程的解法。给定的同余方程是 15r≡2mod7。要找到这个方程的解,我们可以通过简化方程来开始,利用模运算的性质。
首先,观察到15和7的最大公约数是1,这意味着方程有唯一解。根据模运算的性质,我们可以将15除以7的余数作为新的系数,因为15mod7=1。因此,方程可以简化为 r≡2mod7。
简化后的方程直接告诉我们,r除以7的余数必须是2,这意味着r的值可以表示为7k+2的形式,其中k是任意整数。但是,当我们特别询问是否有唯一解时,答案是指在模7意义下的特定解,即r本身在模7下的等价类就是2,这表明在0到6(因为是模7)的范围内,解是唯一的,即r=2。
更广泛地,根据同余方程的理论,如果一个线性同余方程形如ax≡b mod m,且a与m互质(即gcd(a,m)=1),那么方程一定有且只有一个解,这个解可以通过扩展欧几里得算法或者直接的简化方法找到。在这个特定例子中,由于15和7互质,且通过简化直接得到了解,所以我们可以确定r≡2mod7是唯一的解。
当系数和模数互质时,根据同余方程的性质,方程ax ≡ b mod m 有唯一解。这是因为乘法逆元的存在保证了可以通过乘以某个数来“取消”a的影响,找到使得等式成立的唯一x值。
那对于这里a的逆元,也就是15在mod7下的逆元:
在模7的意义下,要找到15的逆元,即找到一个数x,使得15x≡1mod7。根据扩展欧几里得算法,我们知道15和7互质(gcd(15,7)=1),所以这样的x存在且唯一。
通过计算或直接观察,我们可以发现15≡1mod7,因为15除以7的余数是1,但这不是我们要找的逆元的形式。正确的做法是寻找满足条件的x。实际上,我们寻找的是一个数,使得15x与1同余于模7下。
直接计算或使用扩展欧几里得算法,我们可以得到15的逆元。由于15等价于1(在模7下),我们可以简化问题为求解x使得x≡15-1mod7。直观地,因为15≡1mod7,其逆元就是1本身。因此,在模7下,15的逆元是1。
互质的重要性:在方程ax ≡ b mod m 中,如果a和m互质(即它们的最大公约数为1),根据贝祖定理,存在整数x和y使得ax+my=1。这意味着a在模m下有乘法逆元,可以找到一个数让等式两边同时乘以它来解出x。
数论好有趣好费时间啊 (韩信点兵问题最早出现在南北朝的《孙子算经》中,这个问题因此也被称为“孙子问题” 此类问题在现代数学中叫做“一次同余问题”,其解法称为“中国剩余定理”或“孙子定理”。 宋代数学家秦九韶在《数书九章》中对这个问题作出了完整系统的解答,称为“大衍求一术”。可不是那个九章算法啊哈哈)。
进一步研究一下好神奇啊(这博主也是个死妈玩意,讲的根本就是在糊弄傻子,跟窜稀一样完全不想评价),发现tm是小学五年级题目,给我干自闭了,数论这个先搁置吧(文中的“三人同行七十稀”咋来的,问知乎直答,解释的还行,但没时间去钻研了,先搁置)(md小学就开始有中国剩余定理了吗我靠)(找了个视频给我都整的不好意思在图书馆看这玩意,小学数学~~~~(>_<)~~~~)(记得之前C语言心魔有过这玩意,穷举法笑死我了)(全网满片子像这种的狗逼博主)
至此全网没有找到讲解正确的,全都是糊弄傻子的讲解方法,直接搬出答案来自圆其说验证人家是正确的。
另外好神奇的博客里,
先找到一个除以7余2,除以11余4的数(可以在4上不断加11尝试,4除以7不余2,15除以7不余2,26除以7不余2,37除以7余2),是37。但是37除以13不余5,那就只能在37上加7和11的最小公倍数77了。
37÷13=2……11
77÷13=2……12
根据余数的加法定理,要加6个才行(11+12×6=83,83除以13余5)。
问了知乎直答,解释的不错但真没心情没时间去研究这玩意了,如果不是在学C++,而是在刷题,一定会钻研清楚,先搁置。
备注:
如果看到“b|a”,这通常意味着“b能整除a”,即a是b的倍数。即存在整数c,使得a = bc。
找到最小的值后,每次增加他们的最小公倍数,余数不变
说完了生成器,接着说上面的随机数算法,我一直听到的说法是rand()是伪随机数发生器,由,初始值决定,我一直以为加了时间戳就是真随机数了,但其实不是,我们计算机里学到的关于生成随机数的东西,全叫“伪”,啥是真?自然界不可预测的
真正的随机数不是通过算法生成的,而是基于物理过程,如放射性衰变、大气噪声等不可预测的自然现象。这些数字序列是不可预测的,因为它们的生成过程不依赖于任何确定性的计算。在需要高度安全性或不可预测性的应用中,如加密,真随机数是必要的
显式地设置种子(即调用 srand(seed) 来设定起始点)
如果没显示设置种子,就会将相同的固定值作为默认种子(默认种子值取决于各个编译器的具体实现,或者计算机的预设值)。这就导致每次运行程序时,如果没有改变种子,那么产生的序列都是完全一样的。即
#include <iostream> #include <ctime> #include <cstdlib> using namespace std; int main() { int i; for(int j=0;j<10;j++){ i= rand(); cout<<i<<endl; } }
未指定种子的情况:

未指定种子的情况: 如果你不调用srand(seed),rand()函数仍然可以正常工作,但它会使用某个默认值进行初始化。 ①在早期的标准(如C++98/03) 有些编译器确实会在这种情况下使用固定的默认种子(例如1),这会导致每次运行程序时生成相同的随机数序列。 ②现代标准 (C++11及以后): 根据C++11及其后续版本的规定,如果rand()在调用前没有被srand(seed)初始化,其行为仍然是未定义的。也就是说,编译器可以选择任何方式处理这种情况,包括但不限于使用一个固定的默认种子或使用其他机制。 实际上,许多现代编译器和实现倾向于在这种情况下使用当前时间或其他动态值作为种子,以便于获得更好的随机性。
View Code
rand()和srand()是C语言中用于生成伪随机数的一对函数,它们有不同的用途和功能。
rand()
作用:生成一个伪随机数。
返回值:返回一个介于0到RAND_MAX(32767)之间的整数(包括0,不包括RAND_MAX)
关于32767 32767是一个在计算机科学和编程中具有特定意义的数字,它代表了16位(两个字节)有符号整数的最大值。在二进制表示中,32767是15个1后面跟着一个0(即01111111 11111111),转换为十进制就是32767。这个数值常用于表示特定的数据类型限制,比如在C语言和其他许多编程语言中,基本的短整型(short int或signed short)的范围通常是-32768到32767。
此外,32767在不同的上下文中也有特别的意义,例如在游戏《我的世界》中,附魔等级的最大值就是32767,因为游戏内部使用了16位的整数来存储这个信息。在PLC(可编程逻辑控制器)编程中,32767可能作为某些数据溢出的标志或最大计数值。
在计算机领域,这个数值是由于二进制补码系统决定的,其中最高位用作符号位,0表示正数,1表示负数,因此16位整数的正数范围是从0到32767,而负数范围是从-32768到-1。当一个正数达到32767再加1时,会因为溢出而得到一个负数,通常是-32768,这体现了有符号整数的算术溢出现象。
srand()
作用:设置随机数生成器的种子。
srand((unsigned int) time(NULL));srand((unsigned)time( NULL ));: 这行代码使用当前时间作为随机数生成器的种子,确保每次程序运行时都能得到不同的随机序列。这是因为time(NULL)返回自纪元(1970年1月1日00:00:00 UTC)以来的秒数,确保了种子的唯一性。
Q:哪怕有了实时的秒数也不是伪随机是吗?
A:使用实时秒数作为种子可以增加伪随机数生成的不可预测性,因为每次调用时种子值会随时间变化。但这并不改变生成的数是伪随机的本质,因为尽管种子随时间变化看似随机,但只要生成器算法是确定性的,从同一秒数开始,用相同的生成器,得到的随机数序列将是一样的。所以,即使是基于实时秒数,产生的仍然是伪随机数,而非真随机数。
Q:与long int有啥区别?
A:
time_t和long int的主要区别在于它们的用途和标准定义的灵活性:
time_t:专门用于存储时间值,即自纪元(通常是1970年1月1日)以来的秒数。它的具体数据类型(如long int、long long int)取决于编译器和操作系统,以确保能够表示足够长的时间跨度。在32位系统中,它可能与long int相似,但在64位系统中,它可能是long long int,以解决2038年问题。
long int:是一个更通用的整数类型,其大小和范围由编译器和目标平台决定,但不特定于时间存储。它可以用于任何需要大整数的场合,并不局限于时间表示。
总结来说,尽管在某些系统上time_t可能实际上被实现为long int,但它的语义和使用场景专注于时间处理,而long int则是一个更通用的数值类型。time_t通常是long int型
不写NULL行吗?只time() 可以的,直接写time()是等价于time(NULL)的。在C语言中,函数time()默认期待一个可选的指针参数,用来存放结果。如果不提供(即写为time()),编译器会默认认为你传递了NULL,函数会直接返回当前时间的time_t值而不是存储到某个变量。(更新:C编译器可以不写,即直接写 time() 是等价于 time(NULL) 的,因为 C 语言允许函数调用时参数缺失,并将缺失的参数视为 NULL。而C++ 对类型安全有更严格的要求,不允许像 C 语言那样自动将缺失的参数视为 NULL。当你调用 time() 函数时,必须显式提供一个 time_t* 类型的参数,以满足函数的参数要求。这是 C 和 C++ 在函数调用和类型安全方面的一个重要区别。我codeblock是GNU GCC complier,大多数扩展名.c和.cpp会给编译器一个暗示,分别使用C和C++编译器,但我弄完.c文件发现还是报错,查到说可以强制C编译器,输入你想要使用的 C 语言标准,如 -std=c11 或 -std=c99 或 -std=c17。但我输入完又提示
我怕把其他设置给我自动搞乱了,就不管了,不继续测试time()了,以后带参数就是了
时间戳 时间戳是一个表示时间的长整数,它代表自1970年1月1日(UTC/GMT的午夜)以来的秒数,不考虑闰秒。这个系统广泛应用于计算机科学中,用于记录和计算时间,被称为Unix时间戳。
time_t是啥? time_t是C语言标准库中定义的一种数据类型,用于存储时间值。它通常是一个可以表示日期和时间的整数类型,具体大小依赖于实现,但目的是能够表示从某个固定时间点(通常是1970年1月1日)以来的秒数。使用time_t,程序员可以方便地处理时间,包括获取当前时间、进行时间运算等。
NULL 在C和C++等编程语言中,是一个特殊的值,通常用于表示空指针或不存在的链接。在time(NULL)这个函数调用中,NULL是传递给函数的一个参数,意味着“没有特定的时间指针”。函数会忽略这个参数,并直接返回当前时间(从1970年1月1日00:00:00 UTC开始的秒数),这样设计是为了方便,你不需要预先创建一个time_t变量来接收结果。
输出time值 的示例代码(以C语言为例):
#include <stdio.h>
#include <time.h>
int main() {
// 获取当前时间戳
time_t currentTime = time(NULL);
// 输出时间戳
printf("当前时间戳(秒): %ld\n", (long)currentTime);
return 0;
}
Q:去掉unsigned
A:
srand((unsigned int) time(NULL)); 中的 unsigned int 可以去掉,因为 time() 返回的是一个长整型 (long) 值,并且可以直接传递给 srand() 函数。虽然去掉了 unsigned int 强制转换,效果通常也是相同的。
然而,进行这种类型的显式转换是有益的,因为它明确地表明您希望将 time_t 类型的结果转换为 unsigned int 类型,这有助于提高代码的清晰性和可读性。此外,在某些平台上,time_t 和 unsigned int 的大小可能不同,因此为了确保兼容性和避免潜在的问题,建议保留该转换。
以下是两种方式的效果对比:
// 方式一:包含 unsigned int 转换 srand((unsigned int) time(NULL)); // 方式二:不包含 unsigned int 转换 srand(time(NULL));
这两种方法都可以用来初始化随机数生成器并获得不同的随机数序列。不过推荐第一种做法,以便更好地表达意图和提升代码质量。
---------------------------------------------------------------------------------------------------------------
math.c是C的:默认接受double
cmath是C++的
Q:数学函数都在std里,为何还要cmath
A:<cmath> 是 C++ 标准库的一部分,它提供了许多数学运算所需的函数和常量。尽管 <cmath> 中的所有实体都在 std 命名空间内,仍然需要包含这个头文件的原因在于,预处理器指令 #include <cmath> 明确地指示编译器去查找并加载 <cmath> 提供的功能。
调用原理:
当你在代码中写下:
这意味着你要求编译器读取 <cmath> 头文件的内容,并将其提供的所有符号(如 sin, cos, sqrt 等)放入当前翻译单元的作用域中,但这些符号位于 std 命名空间内部。
为了访问其中的任何函数或常量,你需要显式地指明其所在的命名空间,除非你在作用域级别上打开了整个 std 命名空间,但这通常被认为是不良实践,因为它可能导致命名冲突和其他潜在问题。
Q:简单来说加cmath的必要性就是指定所需要的区域,较少时间呗
A:理解得差不多了,但稍微调整一下会更加准确。以下是简单的解释:
加载 <cmath> 的必要性
明确功能来源:
避免命名冲突:
提高可维护性和清晰度:
性能优化:
总结起来,添加 <cmath> 主要是为了让编译器识别和正确处理你想要使用的数学函数,而不是直接节省运行时间。不过,正确的头文件管理可以帮助编写高效且易于维护的代码。
命名空间主要用于解决命名冲突和组织代码结构。
头文件主要用于共享声明和减少代码冗余。
关于未定义行为:
是指程序执行过程中出现的一种状态,在标准规范中没有明确规定该状态下会发生什么情况。这意味着在这种情况下,程序的行为可能是不可预测的,可能会产生以下几种后果之一:
#include <stdio.h>
#include <math.h>
int main(){
printf("%d\n", sqrt(4)); // 使用%d来打印double类型的结果
return 0;
}
结果表现
当您在 printf 中使用不匹配的格式说明符(例如使用 %d 打印 double 类型的值)时,这种做法确实是未定义行为。
为了避免未定义行为,应确保使用的格式说明符与实际传递的参数类型相匹配。对于 double 类型的值,应当使用 %f 或 %lf 格式说明符。修改如下:
#include <stdio.h>
#include <math.h>
int main(){
printf("%.2f\n", sqrt(4)); // 使用%f来打印double类型的结果
return 0;
}
继续说cmath和math.c:
1、关于头文件的区别:
<cmath> 是 C++ 标准库的一部分,它包含了所有的数学函数并且支持函数重载。这意味着你可以传递不同类型的数据给同一个函数名称(如 float 或者 double),而不必担心类型转换的问题。
#include <cmath>
#include<iostream>
using namespace std;
int main(){
cout << sqrt(4)<<endl;
cout << sin(3.1415926/2);
}
而 <math.h> 则是 C 语言的标准库,不支持函数重载。所有函数默认只接受并返回双精度浮点数 (double) 参数。如果你试图向这些函数传递其他类型的参数,则可能需要显式地进行类型转换以确保正确的行为。(printf头文件是stdio,但iostream也可能隐式的包涵了printf的头文件stdio)
#include <stdio.h>
#include <math.h>
int main(){
printf("%lf\n", sqrt((double)4)); // 必须将整数转成double才能正常工作
printf("%lf\n", sin(M_PI / 2));
}
关于这一点我自己的总结是,其实没啥差别,如果这样写也可以啊,主要是printf的事并不是math.h还是cmath的事,不过根据上面提到的未定义那个事,math.c默认double,虽说用cout可以完美解决,但编译器底层逻 辑还是一个“只默认接收double”的这个限制,项目大起来就会有其他问题了,先这样吧
#include <math.h>
#include<iostream>
using namespace std;
int main(){
cout << sqrt(4)<<endl;
}
2、math.h: 则将它的所有标识符同时放在全局命名空间和 std 命名空间中,可能导致潜在的名字冲突问题。
了解完这个开始看教程里的number(关于π,C++20之后可以用命名空间里的number),这玩意又折腾老半天,但感觉和深刻,自己追问GPT_AI比看视频掌握的透彻
小知识总结:
不是所有IDE都有编译器,比如codeblock就没有

IDE 与编译器的关系: 一般来说,集成开发环境(IDE)是一个包含了多个工具的软件套件,旨在帮助开发者更方便地进行软件开发,但并不一定总是包含编译器。 有些 IDE 包含编译器: 例如,Visual Studio 包含了 Microsoft Visual C++(MSVC)编译器,它是专门为 Windows 平台开发的,并且这个编译器与 IDE 高度集成,为开发 Windows 应用程序提供了完整的开发环境,包括代码编辑、编译、调试、性能分析、图形化界面设计等功能。IDE 内部的编译器通常是为了方便开发者使用,而且通常与 IDE 紧密结合,可能具有对 IDE 自身的某些特性的支持,比如对 IDE 项目文件的解析和编译优化。 有些 IDE 不包含编译器: Code::Blocks 就是这样的一个例子,它本身是一个开源的 IDE,为用户提供了代码编辑、项目管理、调试等功能,但并不包含编译器。它需要用户在系统上安装编译器(如 GCC 或 MinGW 等),并在 Code::Blocks 中配置相应的编译器路径和选项,以完成代码编译。这样做的好处是可以让用户灵活选择不同的编译器,适用于不同的操作系统和开发场景,如在 Linux 上使用系统自带的 GCC,在 Windows 上使用 MinGW 或其他编译器。 还有些 IDE 可能支持多种编译器: Eclipse 是另一个常见的 IDE,它主要用于 Java 开发,但也可以用于 C/C++ 开发。对于 C/C++ 开发,Eclipse 本身不包含编译器,而是通过 CDT(C/C++ Development Tools)插件来支持多种编译器,用户可以配置使用 GCC、Clang 或其他编译器。这体现了 IDE 的灵活性,它可以与不同的编译器进行集成,以满足不同用户的需求和开发环境。 IDE 的主要目的是为开发者提供一个集成的开发环境,而编译器只是其中的一部分,它负责将源代码转换成可执行程序。有些 IDE 为了方便用户或针对特定的开发场景,会将编译器包含在其软件包中,而另一些 IDE 则为了保持灵活性和可扩展性,允许用户选择不同的编译器。这样可以让用户根据自己的开发需求、操作系统、项目的特殊要求等选择最适合的编译器,而不是局限于 IDE 自带的编译器。 综上所述,IDE 是否包含编译器取决于 IDE 的设计和目标用户群体。有些 IDE 包含编译器是为了方便和特定平台的开发,而有些则不包含,以提供更大的灵活性。
View Code
codeblock不自带编译器,我之前是下载GCC然后在codeblock里选择上GCC所在的路径编译的
想看gcc版本直接cmd里gcc -v
而VS2022是自带编译器,VS里运行可查看
#include <iostream> int main() { std::cout << "MSVC 编译器版本: " << _MSC_VER << std::endl; return 0; }
搁置疑惑:豆包说VS是专门给windows用的,linux用的是其他IDE,但如果是这样为啥菜鸟教程开头说linux貌似也是安装VS???没介绍单独Linux的IDE。而且既然VS是自带MSVC,那还安啥GCC啊??好混乱,不想了越想越头疼,也没个专业解答的。这些教程真的好垃圾
number正题:
先放代码 (codeblock里可能编译器不严格,不需要number也可以)
#include <iostream> #include <numbers> int main() { std::cout << "pi: " << std::numbers::pi << std::endl; return 0; }
看了下教程里说的std::numbers::pi连他自己的编译器都过不了, std::numbers 库是在 C++20 标准中才引入的,2020年的东西,而菜鸟教程是2017年之前写的(下面笔记最早有2017年的)。
折腾半天以为是codeblock版本低了,我一直用的版本是20年的,但发现现在2025年最新版依旧停留在20.03,故不是codeblock的问题。
豆包说 std::numbers::pi是C++20 标准中才引入的,从GCC 8开始有一定程度的支持,并且随着版本的更新不断完善,即只支持C++20的一部分,而到GCC 11才开始全面支持所有C++20的东西,gcc -v发现是 8.1.0,所以升级GCC,这里我还纠结大半天,环境变量这些最头疼了,知乎直答说半天误人子弟,要什么TDM_GCC或者MSYS2来搞,烦,直接问豆包说官网直接下(MinGW是windows的开源软件集,除了包括GCC还有其他调试器啥的,各种编译环境)
Downloads,下拉找到w64devkit点击,再点击GitHub博客园。.zip安装包。名字中间有i686的是 32 位版,没有的是 64 位版博客园。Win + R键,输入cmd打开命令行,输入gcc --version。若出现版本信息,则说明安装更新成功博客园。其实直接就是这个。
装完之后是一个w64devkit的文件夹,里面的bin下有gcc.exe啥的,这个是windows上的开发工具包,包涵了GCC啥的,可以理解为简洁版的MinGW,多了我也懒得研究。开始配置环境变量:
搁置疑惑:cmd都是中文正常显示,但w64devkit一开始在一个“下载”的目录下,有包括用户GerJCS岛的中文,这个“岛”字却乱码了,导致gcc -v的时候bin目录是包含乱码“岛”字的。也是出错点。但其他中文都正常,猜测可能唯独bin目录不允许有中文吧
更换位置,索性放到codeblock下,即C:\Program Files\CodeBlocks\w64devkit跟MinGW同级(C:\Program Files\CodeBlocks\MinGW)。然后环境变量配置里,注意:如果想应用新的目录,需要把新的放到旧的前面,这path路径是按照前后顺序来的,(还好奇查了下,path变量误删怎么办),最后呈现的就是
C:\Program Files\CodeBlocks\w64devkit\bin
C:\Program Files\CodeBlocks
C:\Program Files\CodeBlocks\MinGW\bin
但其实只有第一个有用,后两个可以删掉。
cmd里gcc -v是14.2.0了。还需要把codeblock的这里改下,
然后还要把 Other compiler options下加一句 -std=c++20。本来是豆包说在这里有关于C++20ISO勾选的,我没找到,就手动加了 -std=c++20。
至此codeblock完美支持C++20了,问题解决。
VS里是MSVC,需要右键“解决方案”找属性
改成20,之前默认是14,VS也支持C++20了,完美解决。
cmd运行的话,g++ -std=c++20 your_source_file.cpp -o your_executable,cmd也可以了,至于MSVC咋弄懒得了解了,我也没安装MSVC(VS自带的),外部命令行执行的就用GCC
至此经过自己摸索,终于知道GCC、GCC8/11/、C++11/14/20这些东西都咋回事了
关于number查几个小知识
1、std::numbers::pi 不是 GCC 编译器独有的,它是 C++20 标准的一部分,任何符合 C++20 标准的编译器都应该支持。
2、std::stoi、std::stol、std::stoll等字符串到整数的转换是C++11的
之前博客里用过。从GCC 4.8以后貌似就开始支持stoi了
3、最新版是C++23,GCC15.0.1
GCC 4.8开始完整支持C++11
GCC 5开始支持C++14
GCC 7开始支持C++17
GCC 11开始完整支持C++20
GCC 13开始支持C++23
如今我是GCC14.2.0
收获:
不用边学边记录,这样先卯足劲攻克一个问题,比较快。总怕忘记过程。打牌你们是怎么记住的
这是因为 _USE_MATH_DEFINES 必须在任何标准库头文件被包含之前进行定义。
再看另一个代码(即C++20之前是怎么用π的)
#define _USE_MATH_DEFINES // for Visual Studio to define M_PI #include <iostream> #include <cmath> int main() { #ifdef __GNUC__ const double pi = acos(-1)+1; #else const double pi = M_PI; #endif std::cout << pi << std::endl; // 使用定义的pi变量 }
VS:
1、输出3.14159
2、注释掉cmath也可以,豆包、知乎直答、文心GPT都没有结束清楚为啥VS里去掉cmath也可以,大概是有了#define _USE_MATH_DEFINES就会自动有cmath或者math.c吧。反正用的是M_PI
3、而且要将#define _USE_MATH_DEFINES 移到所有 #include 语句的最前面,否则VS里报错。
在 C++ 的预处理阶段,预处理器会按照代码中
#include和#define的顺序依次处理这些指令。当预处理器遇到#include <cmath>时,它会将cmath头文件的内容包含进来。对于 Visual Studio 编译器,<cmath>头文件内部的M_PI宏定义是通过_USE_MATH_DEFINES宏来决定是否被包含的。如果
#define _USE_MATH_DEFINES放在#include <cmath>之后,当预处理器处理cmath时,它还没有看到_USE_MATH_DEFINES的定义,因此不会包含M_PI的定义。所以,为了确保在处理cmath时,预处理器已经知晓_USE_MATH_DEFINES的存在,需要将#define _USE_MATH_DEFINES
codeblock:
1、输出是4.14159,
2、注释掉#define _USE_MATH_DEFINES那一行也可以,因为只要用到就是acos
解释代码:以下就叫条件编译
#ifdef __GNUC__:这里__GNUC__ 是一个预定义的宏,当使用 GCC(GNU 编译器集合)编译器时会被定义。
如果是GCC编译器会返回版本号啥的各种信息,有个值,而非GCC编译器就没值,就跳过
:就可以简单理解为,节省空间,即每次上下班带回家基本物品,而你收到领导说“搬工位”指令的时候,才会带走自己所有物品,#define _USE_MATH_DEFINES
而不搬工位时,你每天上下班如果都搬走自己所有物品,是不是有点太冗余每天行动太慢了。md学C++语言表达能力下降了不会表达了

1. #define _USE_MATH_DEFINES 的解释: 在 Visual Studio 中,<cmath> 头文件包含了许多数学函数和常量。然而,出于一些与向后兼容性、性能或实现细节相关的原因,并非所有的常量都会默认被包含。 #define _USE_MATH_DEFINES 是一个预处理器指令。当你使用这个指令时,它会通知 Visual Studio 的预处理器去包含一些通常不被包含的额外的数学定义。其中一个额外的定义就是 M_PI。 M_PI 是表示圆周率 π 的常量(约等于 3.14159265358979323846)。如果不使用 #define _USE_MATH_DEFINES,Visual Studio 的预处理器在编译过程中不会包含 M_PI,如果你在代码中尝试使用 M_PI,编译器会报错,因为它不认识这个符号。这就好比告诉编译器 “嘿,我需要你把这些通常隐藏起来的额外数学定义暴露出来”。 2. 关于 Visual Studio 这种行为的解释: Visual Studio 有它自己的设计和实现选择。它可能选择不默认包含 M_PI 是为了避免命名空间污染。在某些情况下,默认包含太多的常量和函数可能会导致命名冲突,或者使编译过程变慢,尤其是在大型项目中,并非所有这些数学常量都会被使用。 另一个可能的原因可能与历史原因有关。随着时间的推移,C++ 标准和库的实现已经发展,不同的编译器对于如何处理这些常量做出了不同的决策。Visual Studio 可能选择了一种更保守的方法,像 M_PI 这样的某些常量只有在用户通过 #define _USE_MATH_DEFINES 明确请求时才会被包含。
View Code
#define _USE_MATH_DEFINES
#ifdef __GNUC__
const double pi = acos(-1);
#else
const double pi = M_PI;
#endif
再说下M_PI这玩意,知乎直答有点不对,实践发现
对于codeblock用的是GCC:
1、必须有#define _USE_MATH_DEFINES
2、必须要有math库函数
3、必须放到math库函数前面
对于VS用的是自带的MSVC:
1、必须有#define _USE_MATH_DEFINES
2、必须放到所有#include头文件的前面
3、可以没有math库函数
再说一下M_PI不是C++标准的一部分
关于M_PI和number的总结就是:
1、C++20之后用的是 std::numbers::pi来获得π,C++20之前就是通过#define _USE_MATH_DEFINES和math库函数来获取π
2、命名空间 vs 库函数:
好吧感觉还是有些奇奇怪怪的问题,先这样吧。
关于gcc -v和gcc --version,
程度上有所不同:
gcc -v:这个命令在显示版本号的同时,还会提供一个详细的编译器配置和编译链的版本信息。它会列出预处理器、编译器、汇编器以及链接器使用的具体版本,并且在执行时会展示出整个编译过程使用的各种库和工具的详细路径,这对于了解编译器工作环境和排查依赖问题非常有帮助。
gcc --version:相比之下,这个命令更加简洁,它仅仅输出GCC编译器的版本号,不包含额外的配置或路径信息。这对于快速确认编译器版本或者在脚本中检查兼容性时非常方便。
简而言之:
单杠 - 通常代表一个简短的选项,它后面跟着一个字符,用于快速调用命令的特定功能,-v 提供了“verbose”(详细)信息,当需要深入了解编译器配置或解决环境问题时,可以使用 -v
双杠 -- 代表一个长选项,后面跟着的是单词,提供更明确的命令参数, --version 则是更直接的“version”查询,初学者使用 --version 来快速了解版本
黄金比例:(植物的叶片排列、花瓣数量、海螺的螺旋线黄金比例)(有说叶子是黄金分割率比例和斐波那契而数列的,不研究这个了)
希腊字母 “Φ” 的英文读音为 “phi”,音标为 /faɪ/,在汉语中可近似读作 “斐” 或 “费”std::cout << "phi: " << std::numbers::phi << std::endl;
1.61803
在 C++ 中,std::float32_t 和 std::float64_t 是标准库中定义的两种浮点数据类型别名,它们的解释如下:
std::float32_t:
float 类型的别名,通常表示单精度浮点数。std::float64_t:
double 类型的别名,代表双精度浮点数。double 类型(即 std::float64_t)可以更准确地表示和计算数据,减少舍入误差。#include <iostream>
#include <cstdint> // 包含头文件以使用 std::float32_t 和 std::float64_t
int main() {
std::float32_t singlePrecision = 3.1415926535f; // 使用 f 后缀表示单精度浮点数
std::float64_t doublePrecision = 3.141592653589793;
std::cout << "Single Precision: " << singlePrecision << std::endl;
std::cout << "Double Precision: " << doublePrecision << std::endl;
// 输出两种类型的大小
std::cout << "Size of std::float32_t: " << sizeof(std::float32_t) << " bytes" << std::endl;
std::cout << "Size of std::float64_t: " << sizeof(std::float64_t) << " bytes" << std::endl;
return 0;
}
代码解释:
iostream 头文件用于输入输出操作,cstdint 头文件包含了 std::float32_t 和 std::float64_t 的定义。main 函数中,创建了 std::float32_t 类型的变量 singlePrecision,并使用 3.1415926535f 赋值,这里的 f 后缀表示该常量是单精度浮点数。std::float64_t 类型的变量 doublePrecision,并赋值为 3.141592653589793,由于 double 类型的精度更高,它可以存储更多的有效数字。std::cout 输出这两个变量的值。sizeof 操作符输出 std::float32_t 和 std::float64_t 的大小,以字节为单位,分别显示它们占用的存储空间。请注意,在使用浮点数时,由于其存储方式的特殊性,可能会出现精度损失的情况,尤其是对于一些无限循环小数,不同的精度可能会导致不同的计算结果,因此在对精度要求较高的应用中,需要特别注意选择合适的浮点数类型,并考虑使用适当的舍入和误差处理方法。 此外,在进行浮点数的比较时,由于精度误差,不应该直接使用 == 运算符,而应该考虑两个数的差值是否在一个允许的误差范围内。
对于初学者来说,float 是最基础的单精度浮点数类型,你可以直接使用它。而 float32_t 其实就是 float,只是用 float32_t 这个名字能更清楚地表明它是 32 位的单精度类型;float64_t 就是 double,使用 float64_t 这个名字能让你一眼看出它是 64 位的双精度浮点数类型,这样在编写代码时可以更准确地表示你使用的浮点数的精度和存储大小,方便你根据实际需求选择合适的数据类型,避免混淆和错误。例如,如果你需要高精度的计算,像科学计算或金融计算,就可以用 float64_t;如果对精度要求不高且希望节省内存,就可以考虑使用 float32_t。
Q:3.4*10^38和精度6~9位不矛盾吗?一句话回答
A:不矛盾,float(即 float32_t)虽然能表示约 到 的范围,但在这个范围内,其精度只能精确到 6 到 9 位有效数字,即对数字的精确表示能力有限,超出有效数字部分的信息会因存储和表示方式而产生误差。
Q:命名空间也是std,库也是表示为std::某某某,这有啥区别呢?
A:在 C++ 中,虽然很多标准库的内容都在std命名空间下,但 “std命名空间” 和 “std::某某某” 还是有一些区别的,主要体现在以下几个方面:
概念含义
std命名空间:是 C++ 标准库中定义的一个命名空间,它就像是一个 “容器”,把 C++ 标准库中的各种标识符(如类、函数、变量等)都封装在里面,目的是为了避免这些标识符与用户自定义的标识符以及其他库中的标识符发生命名冲突。例如,在std命名空间下定义了string类、vector类、cout对象、endl操纵符等大量的标准库组件。std::某某某:这是使用std命名空间中具体成员的语法表示。“::” 是作用域解析运算符,std::某某某表示引用std命名空间中的名为 “某某某” 的成员,这个成员可以是类、函数、变量等。例如std::string表示std命名空间中的string类,std::cout表示std命名空间中的标准输出流对象。作用和用途
std命名空间:主要作用是提供了一种组织和管理标准库标识符的方式,将标准库的所有内容都放在一个统一的命名空间下,使得 C++ 标准库的结构更加清晰,也方便了程序员在编写代码时区分哪些是标准库的内容,哪些是自己定义的内容。std::某某某:用于在代码中明确地指定要使用std命名空间中的某个具体成员。当需要使用标准库中的特定功能时,就通过这种方式来引用相应的成员。豆包AI真的烦,问了半天云里雾里说的假大空,太玄乎太啰嗦了
最后我自己总结了下,发给他,问他是不是这个意思,他说我总结的对,如下:
作为初学者,我可不可以这么,std只是C++中的一个比较常见的命名空间,简称为std,而std相当于一个大班级体,里面有同学负责类、有同学负责函数、有同学负责对象、常量。
假设想用
pi,就std::numbers::pi,即std这个 “班级” 里有个 “小组” 叫numbers,在这个 “小组” 里有个 “同学”pi。通过#include <numbers>引入这个 “小组” 所在的 “行列” ,就能使用std::numbers::pi来调用这个 “同学” 提供的圆周率数值 。而在 C++ 中,
M_PI所在 “小组” 类似<cmath>库相关的隐藏设定区域,需加#define _USE_MATH_DEFINES使隐藏设定生效,才能像调用小组中 “隐身人” 一样使用M_PI
关于控制台/终端/命令行/cmd/bash自己,学的时候发现总有这玩意,混乱不堪,理顺一下
在计算机上,
终端像是你和计算机聊天的窗口,你可以在里面输入指令让计算机做事,比如在 Linux 系统的终端里输入命令来查看文件。
控制台更像计算机的 “总控制室”,主要供管理员对计算机系统进行全面管理和监控,比如 Windows 系统的设备管理器,管理员能在这对硬件设备进行各种设置。
Windows 里是有终端的,常见的 Windows 终端包括:
假设你把计算机想象成一个大型的工厂,那么:
CMD是Windows系统中一个功能相对简单,用于执行基本系统命令的终端。
codeblock的控制台:
之前用codeblock刷题的时候说那个运行弹出来的黑色框框就是控制台,它主要用于程序的输入输出。用户可以在其中输入程序所需的数据,程序运行的结果、提示信息等也会在这个控制台中显示出来,方便用户查看程序运行状态和验证结果是否正确,它是程序与用户进行交互的一个重要界面。
但从更准确的角度来说也可以叫终端。因为它为用户提供了与程序进行交互的接口,用户可以输入信息,程序也会在其中输出结果等,符合终端作为用户与计算机系统进行交互接口的定义。通常人们叫它控制台,可能是强调它在这个特定环境下主要用于显示程序运行的输入输出,起到类似于控制和查看程序运行状态的作用,但从技术角度讲它具有终端的功能和属性。
简单来说,命令行就是你在控制台、终端、CMD 中输入命令的地方或这种交互方式。在 CodeBlocks 弹出的控制台里,你能通过命令行输入数据给程序。终端里,你通过命令行输入各种命令让系统执行操作。CMD 更是以命令行作为核心交互方式,你在其中输入命令来实现查看文件、测试网络等功能。
Bash 可以理解为一种在终端中使用的命令行解释器,就像 CMD 是 Windows 系统下的命令行工具一样,Bash 是 Linux 和 Unix 系统中非常常用的命令行工具。你可以在 Linux 系统的终端里通过 Bash 输入命令来操作计算机,比如查看文件、运行程序等,它能让你与系统进行交互,执行各种任务,就如同你在 Windows 的 CMD 中输入命令一样,只不过 Bash 在 Linux 和 Unix 系统环境下功能更强大、更灵活,有很多独特的命令和特性,而控制台可以看作是显示 Bash 命令执行结果和进行交互的界面。
更准确地说,CMD 是 Windows 系统下的一种终端应用程序,
而 Bash 是 Linux 系统中的一种命令行解释器,通常在 Linux 终端环境中使用,为用户提供与系统交互的接口,很多 Linux 终端软件都以 Bash 作为默认的命令解释工具。
CMD(命令提示符)在某种程度上可以被看作是一个解释器。
总之,CMD 在 Windows 系统的命令行操作环境中充当了将用户输入的系统命令转化为系统操作的解释器角色,为用户与 Windows 操作系统之间提供了一种基于文本命令的交互方式。
Windows 的控制台包含以下几个方面:
系统管理工具:
命令行工具:
dir 查看目录内容、ipconfig 查看网络配置、ping 测试网络连接等。控制面板:
总之,Windows 的控制台提供了从命令行和图形界面多方面对系统进行管理、监控、操作和配置的工具,方便用户和管理员对系统进行维护和管理。
懒得研究解释器 和 编译器啥区别了
总结发现:
刷算法题真的是太简单了,纯网瘾,学C++对我这个初学者最头疼的是
1、配置各种环境啥的,就比如上面提到的。这些工具真的难用,不像codeblock上刷算法题不用操心这些直接写代码。比如更新GCC
2、还有就是各种头文件宏啥的,刚接触看到一头雾水。#if
备注:
###:开始用知乎直答+编辑器TinyMCE5,真他么极品,发现TinyMCE是一坨狗屎,而TinyMCE5就是一坨人屎,没好哪去。
MCE5为啥折叠代码就总出问题,刷新重新编辑是默认MCE版本,会把MCE5的折叠代码取消。
而MCE版本不知道为啥总会在修改文本的时候,修改后的保留,然后莫名其妙的把修改前的垃圾文本和格式一起搞到文章最后面,唉~~~~(>_<)~~~~异常痛苦这个博客园的编译器。TinyMCE是用阳寿开发的东西吗
只能将就用TinyMCE5,涉及到用代码就用TinyMCE的插入代码
###:数学公式
###:知乎直答没豆包智能,相当鸡肋,回答的极其缓慢,4/5个问题就直接卡死,相当卡了,很无语,且编辑框很烂,不方便点击。但很专业,互动性哪里有问题还是问题豆包吧,界面太好了,如果豆包解释的前后矛盾的时候在用知乎直答。知乎直答比文心GPT回答的好,唯独文心GPT比知乎直答快一点
豆包+知乎直答 (VS的Fitten Code就是个智障直接Pass,文心GPT界面受不了,但解答很棒会有奇效,豆包和知乎直答能解决的情况下绝对不会用)(而一般情况也是用豆包,只有豆包说不清楚不专业的时候才会用知乎直答,知乎直答也不行才会用文心GPT)
###:操Ni妈逼的为啥VS的自动补全跟个傻逼脑残一样啊,跳来跳去的,想删东西都删不了,想写啥反倒自作主张给我写成其他代码去了,逼不得已绝不会用傻逼VS
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。