C/C++ 代码风格推荐
Contents
代码风格的重要性
你听说过大括号圣战吗?
没错,就是指大括号是否换行。
虽然到底换不换行双方各执一词,但其各自内部的风格统一性却不可否认。
一个风格混乱的代码,首先就会让阅读程序的人感到厌烦,而且也会造成理解上的困难(包括写程序的人)。
C/C++ 常见代码风格介绍
一、代码缩进
这个非常重要,因为缩进有助于理解代码的结构。
缩进主要是由大括号带来的代码块带来,但是对于 if
、for
、while
等只有一个语句但换行的情况,也应有对应缩进。
可以看一下示例(大括号换行版本):
#include <bits/stdc++.h>
using namespace std;
struct node
{
public: //这类标识符建议不缩进
int l, r; //结构体、类、命名空间的缩进
private:
int len;
};
int x, y; //全局变量定义不缩进
int get(int x)
{
//函数代码块缩进
}
int main()
{
int n, m; //局部变量定义与普通语句无差别
cin >> x >> y;
if (x > y)
{
for (int i = y; i <= x; i++)
get(i); //单个语句换行依然需要缩进
}
else //else 应该跟对应的 if 缩进相同
{
for (int i = x;i <= y; i++)
{
get(i); //for 语句代码块的缩进
//do something..
}
}
return 0;
}
特别地,对于缩进使用制表符还是空格,取决于个人喜好。(这又是另一个圣战了)
二、大括号问题
主要分为两派:
#include <bits/stdc++.h>
using namespace std;
struct node
{
int l, r;
};
int x, y;
int get(int x)
{
//do something..
}
int main()
{
cin >> x >> y;
if (x > y)
{
for (int i = y; i <= x; i++)
{
get(i);
//do something..
}
}
else
{
for (int i = x; i <= y; i++)
{
get(i);
//do something..
}
}
return 0;
}
这是典型的大括号换行写法。
#include <bits/stdc++.h>
using namespace std;
int x, y;
struct node {
int l, r;
};
int get(int x) {
//do something..
}
int main() {
cin >> x >> y;
if (x > y) {
for (int i = y; i <= x; i++) {
get(i);
//do something..
}
}
else {
for (int i = x; i <= y; i++) {
get(i);
//do something..
}
}
return 0;
}
这是典型的大括号不换行写法(建议大括号与前面的代码中隔开一个空格)。
两者优劣我们暂且不讨论(也不敢讨论),选择哪种都可以,但关键是要从一而终,也就是要换行就都换行,不能两种风格相互夹杂,不伦不类。
当然有时候当函数或者代码块过于简短,有可能整个放在一行,比如:
int min(int x, int y) { return x > y ? y : x; }
(但我从来不这么干)
(吐槽不忘自己,我好像都是 C++ 用前一种,JavaScript 跟 PHP 用后一种,所以好像我自己都不知道推荐哪种。)
三、多语句同行问题
很多人喜欢进行这么写代码:
c = a; a = b; b = c;
这种一行多个 ;
的写法十分不美观,纯粹是为了缩短代码,“充分”利用一行的空间。
所以我们提供两种方案:
c = a;
a = b;
b = c;
c = a, a = b, b = c;
因为 ,
在 C/C++ 中也是一种运算符,所以第二种可以被看作一个语句,我个人认为更美观。
四、变量定义方式问题
有很多人喜欢对于每个变量都在需要使用时才定义。
然而有些变量本应是全局变量,却定义在了 main
函数中,这一是有可能增加代码编写复杂度,二是可能导致出错。
所以我们建议要严格区分局部跟全局变量。
比如一道题输入里涉及的 $n,m$ 这种变量,一般定义为全局变量,因为往往在整个代码中都会被使用到,而循环变量 $i,j,k$ 以及一些只在 if
和 for
代码块中临时用到的变量(比如交换用变量,临时读入变量,较大较小变量)则适合在对应位置局部定义,毕竟在不同的地方可能会被重复用到,这样方便于内存的释放。
而对于以后可能接触的一些工程代码的编写,这主要体现在 class
和 struct
中 public
、private
、protected
这些不同的限定,可以用于防范一些变量的不合理调用。
五、空格问题
首先除了缩进以外,请不要出现连续的空格。
比如 int x;
正常情况下千万不要写成 int x;
。(此处应有两个空格,我相信大家也看得出来有多丑)
然后个人建议对于 if
、while
、for
等关键字和其之后的 (
之间尽量打一个空格。(纯属习惯)
并且 #include
跟后面的头文件之间最好也加一个空格,比如 #include <cmath>
,#include "stdio.h"
这样。
顺便讲一下常见的头文件的引用问题:
- 一般系统头文件应该用
#include <>
,并且引用先于自定义头文件。 - 自定义头文件应该用
#include ""
。 - 不需要的头文件尽量不引用。
六、常量定义、类型别名问题
一般常量定义建议使用(仅适用于 C++):
const int N = 100009;
另一种我个人选择尽量不用:
#define M 233
类型别名同理:
typedef long long ll;
一般不用:
#define pii pair<int, int>
七、冗余代码的减少
对于重复代码尽量使用循环或者函数的方法减少。
比如某位同学的作业里面的两个点坐标之间的判断就可以写个函数。
还有一些废话可以缩短,比如:
//1
if (F) c = a;
else c = b;
//2
if (F) return true;
else false;
//3
b += a;
a++;
//4
if (a > b) b = a;
分别可以缩短为:
//1
c = F ? a : b;
//2
return F;
//3
b += (a++);
//4
b = max(a, b);
还有很多种,具体情况需要你对代码有一个清晰的理解。
此处第二种默认其中是一个布尔表达式。
当然这些情况的缩减代码,也有可能降低可读性,更重要的是掌握好分寸。(只是 a = a + 1;
这种改成 ++a;
我觉得不需要犹豫)
八、变量定义问题
这个国际上有很多种标准。
对于做题(非工程),主要讲求一个能让自己和别人看懂。
比如 cnt
用于计数,Max
用于最大值,val
表示值,next
和 prev
表示前后一个(指针)。
当然对于工程,有更多的要求,这个主要要适应他人的命名规则。
比如说用下划线分别连接变量类型、变量从属范围、变量含义,i_List_length
可能就表示一个 List
类(结构体)的 int
型变量 length
(长度)。(当然我也很支持驼峰命名法)
总结
代码风格存在的必要性是为了可读性的增强。
很多时候并不是代码写对就是好的,只要自己看懂就是好的,更重要的是增强代码可理解性,可移植性,而这些都是优良的代码风格可以带来的。
所以千万不能轻视代码风格。
随便附上一份我最近的代码:
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
// Input optimization
inline int read()
{
int f = 1;
char ch;
while (ch = getchar(), ch < '0' || ch > '9')
if (ch == '-') f = -1;
int x = ch - '0';
while (ch = getchar(), ch >= '0' && ch <= '9')
x = x * 10 + ch - '0';
return x * f;
}
// Output optimization, dicussed with Zhouyan
void print_not_zero(int x)
{
if (x == 0) return;
if (x < 0)
{
putchar('-');
print_not_zero(-x);
return;
}
print_not_zero(x / 10);
putchar(x % 10 + '0');
}
inline void print(int x)
{
x == 0 ? putchar('0') : (print_not_zero(x), 0);
}
const int N = 501009;
const int M = 109;
// Use scrolling array to reduce space complexity
int n, m, K, dp[2][M << 1] = {};
char A[N], B[N];
int main()
{
n = read(), m = read(), K = read();
scanf("%s", A + 1);
scanf("%s", B + 1);
// Terminate when the length difference is too large
if (abs(n - m) > K)
{
puts("-1");
return 0;
}
// Initialize state array
int now = 1, pre = 0;
for (int i = 0; i < 100; i++)
dp[now][i] = K + 1;
for (int i = 100; i <= 200; i++)
dp[now][i] = i - 100;
for (int i = 1; i <= n; i++)
{
/*
* We define dp[i][j],which means the minimum cost to turn A[1..i] into B[1..j].
* When the difference between i and j is greater than K, the minimum cost must not be lower than k.
* So we just need the state when j falls in the interval [i - K, i + K].
* Since K is no larger than 100, we can use dp[now][j] to substitute the original dp[i][i + j - 100].
* I discussed this part of optimization with Zhouyan.
*/
pre = now, now ^= 1;
memset(dp[now], 0x3f, sizeof(dp[now]));
for (int j = 100 - K; j <= 100 + K; j++)
{
if (dp[pre][j] > K) continue;
if (i + j > 100 && i + j - 100 <= m && A[i] == B[i + j - 100])
dp[now][j] = min(dp[now][j], dp[pre][j]);
if (j)
dp[now][j - 1] = min(dp[now][j - 1], dp[pre][j] + 1);
}
for (int j = 100 - K; j < 100 + K; j++)
dp[now][j + 1] = min(dp[now][j + 1], dp[now][j] + 1);
}
print(dp[now][m - n + 100] <= K ? dp[now][m - n + 100] : -1);
putchar('\n');
return 0;
}
Comments: 5
海蜇棒棒!
bbbb!
王海我男神!!!!!!!!
int x;
和int x;
有什麼區別?好像是html的bug。。。