本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 )
以上操作一但出现符合
就会结束程序,跳出循环……
但是倒回去仔细看错误的写法,看似正确实则暗流涌动,一句话就可以直击要害
的时候怎么办?!! 减完后变成
此时是判断不出来的,就会一直死循环 下去!
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
...
解剖死因
如果
和
是这么操作,那么运行就会很正常
满足
条件就执行
的循环
否则执行
的循环
if( ... )
for( int i = 1;i <= n;i ++ )
...
else
for( int j = 1;j <= n;j ++ )
...
但是当我们在循环里面再次嵌套条件语句的时候,此时就问题大大滴了!!
我们要了解
的运行原则,
在不加
强制区分的时候,是默认否定离它最近的
情况
也就是说,在错误写法中, 否定的是 循环里面的 条件
是算一条语句的,嵌套在循环里面时,就可以不用打括号
for( int i = 1;i <= n;i ++ )
if( ... ) ...
else ...
//上面写法等价于下面的写法
//如果if,else里面有多条语句的时候
//注意要打括号,不然if-else会被中断,出现CE
for( int i = 1;i <= n;i ++ ) {
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
程序并不会按照我们的缩进,智能分类匹配
PS:此问题出自蒟蒻博主,例题“zamjena”,当时调了好几天,自己也发现了加括号和不加括号会有答案的区别,但是当时没有意识到本质的原因是什么
博主自我反思:
有的时候觉得if-else很简洁,而且我自己不太喜欢打大括号,从而可以缩减代码行数
现在细想来觉得还是应该从严谨逻辑的角度出发,还是打个大括号,避免这种分歧
fuck 3:int范围边界的1ll使用
与位运算结合
正确/错误写法
long long n = 1ll << 31 - 1;
//运行结果:2147483647
long long n = 1 << 31 - 1;
操刀手术
有符号整型 ,占用4字节,32bit
然而取值范围
无符号整数 取值范围就可以达到
C++计算的时候是默认 类型,也就是说如果我们不强调 , 就只申请了 类型
我们可能会以为:我左边储存答案的变量开了
啊,这不存的下嘛
其实不然,可以理解为是右边每个计算部分存好后再统一进行操作
然后放到左边变量里面,并不是直接在左边变量里面操作
请看接下来的两种操作写法
int A, B;
long long n = A + B;
long long A;
int B;
long long n = A + B;
将
赋值
会发现第一种写法溢出了,而第二种写法算出了正确答案
这是因为
暂时将结果存在了
里面,然后再像赋值一样赋给
可以试试
也是一样的
而第一种写法
是
类型的,显然存不下,所以就爆出去了
解决方法有两种
第一个就是像第二种写法一样直接把变量开成
第二种就是计算时乘以
,一般习惯在第一个变量前面加,这样右边整个答案只要在
范围内都可以存储
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板题
还不知道为什么错了,要是
三个加一起就爆
了呀,我看了一眼就发现了哈哈哈哈哈
fuck 4:内存计算
做
州区划分,给爷调崩了,一直
,我都以为是被针对了!!
AC/RE写法
long long dp[23][1 << 22], g[23][1 << 22], inv[1 << 22];
long long dp[23][1 << 21], g[23][1 << 21], inv[1 << 21];
死因报告
在 C++ 中
类型每个空间是 个字节
是 个字节
类型是 个字节
数组占用内存的计算
①: 的空间大小 (数组大小) (转化为 ) (转化为 ) (转化为 )
②:直接用 ,这样算出来的空间占存的单位是byte
看到这里的时候就已经明白自己是如何死得死翘翘的了
就以
的数组大小来举栗计算
按照
的字节数来计算就已经如此之大,更何况开的是
,内存算出来就要
我认为学习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遗失的答案掉的坑,凸(艹皿艹 )
局部变量只在局部内有值
全局变量适用于全局
局部内可以正确调用局部变量
局部外的其他版块一旦涉及到该变量,默认调用全局
一言以蔽之:局部内优先调用局部内的变量,次调用全局变量
所以错误写法中
版块的
其实是等于0的,该
循环根本未被执行
报告完毕!
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++的时候就曾经犯过这个问题,没想到时隔多年再次煞笔
由 连接的顺序结构
一连串的条件限制,程序默认从左往右顺次判定
所以在错误写法中,极有可能
就已经炸出
的范围了,看都没看后面的条件限制
最终导致程序死亡