你看这个坑,不香吗???!!!

本blog只是记录C++学习以来掉过的一些坑,写下来防止自己下一次再犯,顺便分享

蒟蒻本人没什么梦想,只求dalao轻喷,Thanks♪(・ω・)ノ

Fuck 1:’~’ 的使用

vector.size()如何坑害

正确/错误写法

正确写法

for( int i = G[u].size() - 2;i >= 0;i -- )

错误写法

for( int i = G[u].size() - 2;~ i;i -- )

剖析错因

~:是用来判断是否为-1的简便写法

常见用法
前向星套用

memset( head, -1, sizeof( head) );
...
for( int i = head[u];~ i;i = nxt[i] )

还有无限输入的判断

此时与EOF产生的作用一样

while( ~ scanf( "%d", &n ) )
//也可写作
while( scanf( "%d", &n ) != EOF )

以上操作一但出现符合 = 1 =-1 就会结束程序,跳出循环……
但是倒回去仔细看错误的写法,看似正确实则暗流涌动,一句话就可以直击要害

G [ u ] . s i z e ( ) = = 0 G[u].size()==0 的时候怎么办?!! 减完后变成 2 -2
此时是判断不出来的,就会一直死循环 2 -2 下去!

PS:此问题来自哲学的微笑——老刘,疯狂嘲笑哈哈哈哈
在这里插入图片描述


Fuck 2:if-else的缩进

套用在for循环里面的条件语句

正确/错误写法

正确写法

if( ... ) {
	for( int i = 1;i <= n;i ++ )
		if( ... )
}
else {
	...
}

错误写法

if( ... )
	for( int i = 1;i <= n;i ++ )
		if( ... )
else
	...

解剖死因

如果 i f e l s e if-else f o r for 是这么操作,那么运行就会很正常
满足 i f if 条件就执行 i i 的循环
否则执行 j j 的循环

if( ... )
	for( int i = 1;i <= n;i ++ )
		...
else
	for( int j = 1;j <= n;j ++ )
		...	

在这里插入图片描述
但是当我们在循环里面再次嵌套条件语句的时候,此时就问题大大滴了!!
i f e l s e if-else用法
我们要了解 i f e l s e if-else 的运行原则, e l s e else 在不加 { } \{\} 强制区分的时候,是默认否定离它最近的 i f if 情况

也就是说,在错误写法中, e l s e else 否定的是 f o r for 循环里面的 i f if 条件

i f e l s e if-else 是算一条语句的,嵌套在循环里面时,就可以不用打括号

for( int i = 1;i <= n;i ++ )
	if( ... ) ...
	else ...
//上面写法等价于下面的写法
//如果if,else里面有多条语句的时候
//注意要打括号,不然if-else会被中断,出现CE 
for( int i = 1;i <= n;i ++ ) {
	if( ... ) ...
	else ...
} 

i f e l s e if-else 是一条语句,所以中间是不能被其它非 i f e l s e if-else 语句打断

for( int i = 1;i <= n;i ++ )
	if( ... ) ...
	n = n + 1;
	else ...
//这个时候会if-else编译报错,并且提醒我们缺少大括号

往往题目的情况不止简简单单的两种,这个时候我们一样处理

for( int i = 1;i <= n;i ++ ) 
	if( ... ) ...
	else if( ... ) ...
		else if( ... ) ...
			else ...
//两种写法是等价的
for( int i = 1;i <= n;i ++ )
	if( ... )
	else if( ... ) ...
	else if( ... ) ...
	else ... 

综上,总结一下:
我们的缩进只是为了代码可观,逻辑清晰易懂
但是程序运行有自己严格的规则,并不是智能AI
程序并不会按照我们的缩进,智能分类匹配 i f e l s e if-else

在这里插入图片描述
PS:此问题出自蒟蒻博主,例题“zamjena”,当时调了好几天,自己也发现了加括号和不加括号会有答案的区别,但是当时没有意识到本质的原因是什么
在这里插入图片描述

博主自我反思:
有的时候觉得if-else很简洁,而且我自己不太喜欢打大括号,从而可以缩减代码行数
现在细想来觉得还是应该从严谨逻辑的角度出发,还是打个大括号,避免这种分歧


fuck 3:int范围边界的1ll使用

与位运算结合

正确/错误写法

正确写法

long long n = 1ll << 31 - 1;
//运行结果:2147483647

错误写法

long long n = 1 << 31 - 1;

操刀手术

有符号整型 i n t 2 32 int:2^{32} ,占用4字节,32bit
然而取值范围 [ 2 31 , 2 31 1 ] [-2^{31},2^{31}-1]
无符号整数 u n s i g n e d _ i n t unsigned\_int 取值范围就可以达到 [ 0 , 2 32 1 ] [0,2^{32}-1]

C++计算的时候是默认 i n t int 类型,也就是说如果我们不强调 1 l l 1ll 1 1 就只申请了 i n t int 类型

我们可能会以为:我左边储存答案的变量开了 l o n g   l o n g long\ long 啊,这不存的下嘛
在这里插入图片描述
其实不然,可以理解为是右边每个计算部分存好后再统一进行操作
然后放到左边变量里面,并不是直接在左边变量里面操作
延伸拓展
请看接下来的两种操作写法

int A, B;
long long n = A + B;
long long A;
int B;
long long n = A + B;

A , B A,B 赋值 2147483647 2147483647
会发现第一种写法溢出了,而第二种写法算出了正确答案
这是因为 A + B A+B 暂时将结果存在了 A A 里面,然后再像赋值一样赋给 n n
可以试试 A : i n t , B : l o n g   l o n g A:int,B:long\ long 也是一样的
而第一种写法 A A i n t int 类型的,显然存不下,所以就爆出去了
在这里插入图片描述
解决方法有两种
第一个就是像第二种写法一样直接把变量开成 l o n g   l o n g long\ long
第二种就是计算时乘以 1 l l 1ll ,一般习惯在第一个变量前面加,这样右边整个答案只要在 l o n g   l o n g long\ long 范围内都可以存储

int A, B;
long long n = 1ll * A + B;

泼水不收

long long n = ( 1 << 30 ) + ( 1 << 30 ) - 1;
long long n = ( 1 << 30 ) + 3 - ( 1 << 30 ) - 4;
...
long long n = ( 1 << 30 ) + x - ( 1 << 30 ) - ( x + 1 );
//可以试试这种写法
//x不要带得太大
//发现这种写法一样可以

这是为什么呢??留给dalao们,蒟蒻不懂
在这里插入图片描述

蒟蒻bb:
初学C++,学了很多算法
但是对这种知识真的是了解很少,不懂计算机的运算法则
所以经常会在各种歪歪扭扭的角(ka)角(ka)落(guo)落(guo)到处爆掉
在这里插入图片描述
PS
Upd–>2020-06-11
老刘好SB哈哈哈哈,写FWT板题 ( f [ k ] + f [ k + p ] + m o d ) (f[k]+f[k+p]+mod)%mod
还不知道为什么错了,要是 f [ k ] = f [ k + p ] = m o d 1 f[k]=f[k+p]=mod-1
三个加一起就爆 i n t int 了呀,我看了一眼就发现了哈哈哈哈哈
在这里插入图片描述

fuck 4:内存计算

在这里插入图片描述
F M T FMT 州区划分,给爷调崩了,一直 R E RE ,我都以为是被针对了!!
在这里插入图片描述

AC/RE写法

A C AC写法

long long dp[23][1 << 22], g[23][1 << 22], inv[1 << 22];

R E RE写法

long long dp[23][1 << 21], g[23][1 << 21], inv[1 << 21];

死因报告

1 G B = 1024 M B = 1024 1024 K B = 1024 1024 1024 B ( b y t e ) 1GB=1024MB=1024*1024KB=1024*1024*1024B(byte 字节)

C++
i n t int 类型每个空间是 4 4 个字节
l o n g   l o n g long\ long 8 8 个字节
b o o l bool 类型是 1 1 个字节

数组占用内存的计算
①: a [ x ] [ y ] a[x][y] 的空间大小 = x y =x*y (数组大小) 4 *4 (转化为 B y t e Byte / 1024 /1024 (转化为 K B KB / 1024 /1024 (转化为 M B MB
②:直接用 s i z e o f ( a ) sizeof(a) ,这样算出来的空间占存的单位是byte

看到这里的时候就已经明白自己是如何死得死翘翘的了
就以 R E RE 的数组大小来举栗计算
23 ( 1 < < 22 ) = 96468992 4 ( B ) / 1024 = 376832 ( K B ) / 1024 = 368 M B 23*(1<<22)=96468992*4(B)/1024=376832(KB)/1024=368MB
按照 i n t int 的字节数来计算就已经如此之大,更何况开的是 l o n g   l o n g long\ long ,内存算出来就要 × 2 ×2
在这里插入图片描述
我认为学习C++还是有必要掌握这么基本的内存计算方法,因为编译器的不同对数组的最大忍受也不同,可能编译能过,但是交上去跑出来是RE,这个时候调起来就会很崩溃...

Fuck 5:全局变量和局部变量的重复

正确/错误写法

正确写法

#include <cstdio>
int n;

void calc() {
	for( int i = 1;i <= n;i ++ )
		...
}

int main() {
	n = ...
	return 0;
}

错误写法

#include <cstdio>
int n;

void calc() {
	for( int i = 1;i <= n;i ++ )
		...
}

int main() {
	int n = ...
	return 0;
}

死法之傻逼

太智障了,这个坑,一点都不想说fa
做的FMT遗失的答案掉的坑,凸(艹皿艹 )
在这里插入图片描述

局部变量只在局部内有值
全局变量适用于全局

局部内可以正确调用局部变量
局部外的其他版块一旦涉及到该变量,默认调用全局
一言以蔽之:局部内优先调用局部内的变量,次调用全局变量

所以错误写法中 c a l c calc 版块的 n n 其实是等于0的,该 f o r for 循环根本未被执行
报告完毕!
在这里插入图片描述

fuck 6:顺序结构&& ||前后顺序

正确写法

bool vis[n + 5];
while( x <= n && vis[x] ) x += y;

反复作死

bool vis[n + 5];
while( vis[x] && x <= n ) x += y;

诈尸释因

顺序结构大师上线
在我初学C++的时候就曾经犯过这个问题,没想到时隔多年再次煞笔
在这里插入图片描述

& &   \&\&\ || 连接的顺序结构
一连串的条件限制,程序默认从左往右顺次判定

所以在错误写法中,极有可能 v i s [ x ] vis[x] 就已经炸出 v i s vis 的范围了,看都没看后面的条件限制
最终导致程序死亡
在这里插入图片描述

Fuck 7:咕咕咕咕咕咕咕咕咕咕咕

猜你喜欢

转载自blog.csdn.net/Emm_Titan/article/details/106578669