2025.10.14 闲话

好久没写过博客了,今天写一篇
没写博客主要是因为前段时间很忙,现在集训了,时间多了,自然就有空写博客了

笑点解析:懒得弄虚拟机的中文输入法,因此写这篇博客时专门从虚拟机切回windows

省流:

  1. 要用 ulimit -s unlimited
  2. 对于占用内存较大的类型可在传入函数参数时将引用传入

注:本文代码均在 NOILinux 下的 VSCode 中编译与运行,编译选项为 -std=c++14 -O2 -Wall

Part.1 起因

在做矩阵求逆的时候写了这样一段代码:

完整代码
#include < bits/stdc++.h >
#define int long long
using namespace std;

const int M = 810 , mod = 1e9 + 7;
int n;
int qpow(int x , int y){
	int res = 1;
	while(y){
		if(y & 1) res = res * x % mod;
		x = x * x % mod , y >>= 1;
	}
	return res;
}
int inv(int x){ return qpow(x , mod - 2); }

struct mat{
	int num[M][M];
	int* operator [](int x){ return num[x]; }
	mat(){ memset(num , 0 , sizeof(num)); }
}a;
istream& operator >>(istream& is , mat& A){
	for(int i = 0; i < n; ++i) for(int j = 0; j < n; ++j) is >> A[i][j];
	return is;
}
ostream& operator <<(ostream& os , mat A){
	for(int i = 0; i < n; ++i){
		for(int j = 0; j < n; ++j) os << A[i][j] << ' ';
		os << '\n';
	}
	return os;
}
void inv(mat A){
	for(int i = 0; i < n; ++i) for(int j = 0; j < n; ++j) A[i][j + n] = i == j;
	for(int i = 0; i < n; ++i){
		for(int j = i; j < n; ++j) if(A[j][i]){ swap(A.num[j] , A.num[i]); break; }
		if(!A[i][i]){ cout<<"No Solution"; return; }
		for(int j = n * 2 - 1; j >= i; --j) A[i][j] = A[i][j] * inv(A[i][i]) % mod;
		for(int j = i + 1; j < n; ++j)
			for(int k = n * 2 - 1; k >= i; --k)
				(A[j][k] += mod - A[j][i] * A[i][k] % mod) %= mod;
	}
	for(int i = n - 1; i >= 0; --i)
		for(int j = 0; j < i; ++j)
			for(int k = n * 2 - 1; k >= i; --k)
				(A[j][k] += mod - A[j][i] * A[i][k] % mod) %= mod;
	for(int i = 0; i < n; ++i)
		for(int j = 0; j < n; ++j)
			A[i][j] = A[i][j + n] , A[i][j + n] = 0;
	cout<<A;
}

signed main(){
	freopen("data.in" , "r" , stdin) , freopen("data.out" , "w" , stdout);
	ios::sync_with_stdio(0) , cin.tie(0) , cout.tie(0);
	cin>>n>>a;
	inv(a);
	return 0;
}  

省流:

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int M = 810 , n = 5;
struct mat{
	int num[M][M];
}a;
ostream& operator <<(ostream& os , mat A){
	for(int i = 0; i < n; ++i){
		for(int j = 0; j < n; ++j) os << A.num[i][j] << ' ';
		os << '\n';
	}
	return os;
}
void f(mat A){
	cout<<A;
}

signed main(){
	f(a);
	return 0;
}

喜报:正常过编了!
悲报:段错误 (核心已转储)

Part.2 调试

🤔🤔🤔
先是把无关代码删掉,得到以上省流版代码
还是段错误

operator <<(ostream& os , mat A) 中的 A 变成引用(即 operator <<(ostream& os , mat& A)
我们惊奇地发现——居然能正常运行了!

但是为什么

仍然是先前的代码,注释掉 #define int long long,居然也不会发生段错误
于是我便naive地以为只是 #define 的问题

然而这时发生了三个状况:

  1. 若删去 #define int long long 而只将 num[M][M] 设为 long long,则仍然会发生段错误
  2. 在1.的基础上,将 num[M][M] 设为 int,则不会发生段错误
  3. 即使不删去 #define int long long,将 M = 810 改为 M = 10 同样不会发生段错误

从第三点入手,我找出了一个分界:
\(M\le723\) 时不会发生段错误,否则会发生段错误

然而这个 \(723\) 貌似并没有什么很直观的特点

那便输出一下 sizeof(a.num)
结果:当 \(M=723\) 时输出 \(4181832\);当 \(M=724\) 时输出 \(4193408\)

🤔🤔🤔
我们发现 \(2^{22}=4194304\) 与这个值非常接近,而 \(4194304B=4096KB=4MB\)
答案呼之欲出——

Part.3 结论

起初,在 f(mat A)operator<<(ostream& os , mat A) 中,传入的都是 mat 类型的对象,各占用 \(810\times810\times8=5248800B\),加在一起超出了 \(8192KB\),造成了段错误
f(mat& A)operator<<(ostream& os , mat& A) 传入的是引用,不会额外占用空间,也不会超出 \(8192KB\) 的限制

那么这个 \(8192KB\) 是什么呢
是我偷懒没用ulimit -s unlimited导致栈空间上限是默认的\(8192KB\)

总结:对于占用内存较大的类型可在传入函数参数时将引用传入


随便写写

我怎么这就上高一了
一个半月集训可以集训出什么东西
快CSP了,加把劲!
快NOIP了,加把劲!

posted @ 2025-10-14 20:01  bjt123  阅读(23)  评论(2)    收藏  举报