orangelop

【OI01-cpp】OI 相关 C++ 基础

2023-04-04
OI  c++
 

【OI01-cpp】OI相关C++基础:命名空间、注释、输入与输出、#define命令、变量、预处理命令、运算、读入/输出优化(部分)

MS Windows Error Codes

Windows 错误代码:[MS-ERREF]: Windows Error Codes

头文件#include

命名空间

申明空间

namespace A {
int cnt;

void f(int x) { cnt = x; }
}  // namespace A

注:命名空间的声明是可以嵌套

namespace A {
namespace B {
void f() { ... }
}  // namespace B

void f() {
  B::f();  // 实际访问的是 A::B::f(),由于当前位于命名空间 A
           // 内,所以可以省略前面的 A::
}
}  // namespace A

void f()  // 这里定义的是全局命名空间的 f 函数,与 A::f 和 A::B::f
          // 都不会产生冲突
{
  A::f();
  A::B::f();
}

using

using指令有如下两种形式:

using 命名空间::成员名;:这条指令可以让我们省略某个成员名前的命名空间,直接通过成员名访问成员,相当于将这个成员导入了当前的作用域。
using namespace 命名空间;:这条指令可以直接通过成员名访问命名空间中的 任何 成员,相当于将这个命名空间的所有成员导入了当前的作用域。

std标准命名空间

using namespace std:引用std(所有)
using std::endl:引用std中的endl

注释

输入与输出

cin与cout

cin>>等价于cin.operator>>(),同理cout等价于cout.operator>>()

#include <iostream>

int main() {
  int x, y;           // 声明变量
  std::cin >> x >> y;    // 读入 x 和 y
  std::cout << y << std::endl << x;  // 输出 y,换行,再输出 x
  return 0;
}

scanf与printf

大多数情况下,它们的速度比 cin 和 cout 更快,并且能够方便地控制输入输出格式。

#include <cstdio>

int main() {
  int x, y;
  scanf("%d%d", &x, &y);   // 读入 x 和 y
  printf("%d\n%d", y, x);  // 输出 y,换行,再输出 x
  return 0;
}

注:其中,%d表示读入/输出的变量是一个有符号整型 (int 型)的变量。

类似地:

  • %s表示字符串。
  • %c表示字符。
  • %lf表示双精度浮点数 (double)。
  • %lld表示长整型 (long long)。根据系统不同,也可能是%I64d
  • %u表示无符号整型 (unsigned int)。
  • %llu表示无符号长整型 (unsigned long long),也可能是%I64u

除了类型标识符以外,还有一些控制格式的方式。许多都不常用,选取两个常用的列举如下:

  • %1d表示长度为1的整型。在读入时,即使没有空格也可以逐位读入数字。在输出时,若指定的长度大于数字的位数,就会在数字前用空格填充。若指定的长度小于数字的位数,就没有效果。
  • %.6lf 用于输出,保留六位小数。

Why &, \n

  • &实际上是取址运算符,返回的是变量在内存中的地址
  • \n转义字符,表示换行
  • 搜索:cpp常见的转义字符

#define命令

#define是一种预处理命令,用于定义宏,本质上是文本替换。

#include <iostream>
#define n 233

// n 不是变量,而是编译器会将代码中所有 n 文本替换为 233,但是作为标识符一部分的
// n 的就不会被替换,如 fn 不会被替换成 f233,同样,字符串内的也不会被替换

int main() {
  std::cout << n;  // 输出 233
  return 0;
}

宏可以带参数,带参数的宏可以像函数一样使用:

#include <iostream>
#define sum(x, y) ((x) + (y))
#define square(x) ((x) * (x))

using namespace std;

int main() {
  cout << sum(1, 2) << ' ' << 2 * sum(3, 5) << endl;  // 输出 3 16
}

注:使用#define是有风险的(由于#define作用域是整个程序,因此可能导致文本被意外地替换,需要使用#undef及时取消定义),因此应谨慎使用。较为推荐的做法是:使用const限定符声明常量,使用函数代替宏。

但是,在 OI 中,#define 依然有用武之处(以下两种是不被推荐的用法,会降低代码的规范性):

  1. #define int long long+signed main()。通常用于避免忘记开 long long 导致的错误,或是调试时排除忘开 long long 导致错误的可能性。(也可能导致增大常数甚至 TLE,或者因为爆空间而 MLE)
  2. #define For(i, l, r) for (int i = (l); i <= (r); ++i)#define pb push_back#define mid ((l + r) / 2),用于减短代码长度。

#ifdef结合:

#ifdef LINUX
// code for linux
#else
// code for other OS
#endif

变量

变量名的合法性

数据类型

  • 基础类型:
    1. void:无类型
    2. std::nullptr_t:空指针
    3. int: 符号性:signed/unsigned
      大小:short、long、long long
      扩展整数类型:GCC(__int128_t/__uint128_t)
    4. bool:取值为true/false
    5. char:
      符号性:signed/unsigned
    6. float(单精度浮点类型)/double(双精度浮点类型)/long double(扩展精度浮点类型)
  • 复合类型
  • 定宽整数类型

注:

  • 梯度:char,short,int,long,long long
    1 == sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long)

  • 在不引发歧义的情况下,允许省略部分修饰关键字,或调整修饰关键字的顺序。这意味着同一类型会存在多种等价表述。

例如 intsignedint signedsigned int 表示同一类型,而 unsigned longunsigned long int 表示同一类型。

  • 浮点类型可以支持一些特殊值:
    1. 无穷(INFINITY)
    2. -0.0
    3. NaN

类型转换:

  • 数值提升
  • 整数提升
  • 浮点提升
  • 数值转换

定义变量

定义一个变量,需要包含类型说明符(指明变量的类型),以及要定义的变量名。

  • 局部变量与全局变量(作用域不同):
int g = 20;  // 定义全局变量

int main() {
  int g = 10;         // 定义局部变量
  printf("%d\n", g);  // 输出 g
  return 0;
}
  • 常量:
const int a = 2;
a = 3;

预处理命令

预处理命令就是预处理器所接受的命令,用于对代码进行初步的文本变换,比如 文件包含操作#include和 处理宏#define等,对 GCC 而言,默认不会保留预处理阶段的输出.i文件。可以用-E选项保留输出文件。

运算

自增/自减

i = 100;

op1 = i++;  // op1 = 100,先 op1 = i,然后 i = i + 1

i = 100;

op2 = ++i;  // op2 = 101,先 i = i + 1,然后赋值 op2

i = 100;

op3 = i--;  // op3 = 100,先赋值 op3,然后 i = i - 1

i = 100;

op4 = --i;  // op4 = 99,先 i = i - 1,然后赋值 op4

复合赋值 & 大小比较

与python相同:

  • 复合赋值:op = op + 2 可写为 op += 2,op = op - 2 可写为 op -= 2,op= op * 2 可写为 op *= 2

  • 大小比较:与python相同

注:

  • 赋值:=;等于号:==
  • if (op=1)if (op==1) 看起来类似,但实际功能却相差甚远。第一条语句是在对 op 进行赋值,若赋值为非 0 时为真值,表达式的条件始终是满足的,无法达到判断的作用;而第二条语句才是对 op 的值进行判断。

条件运算 & 逻辑运算

  • 条件运算:a ? b : c 中如果表达式 a 成立,那么这个条件表达式的结果是 b,否则条件表达式的结果是 c。
  • 逻辑运算: &&:与 ||:或 !:非

    Result = op1 && op2;  // 当 op1 与 op2 都为真时则 Result 为真
    
    Result = op1 || op2;  // 当 op1 或 op2 其中一个为真时则 Result 为真
    
    Result = !op1;  // 当 op1 为假时则 Result 为真
    

读入、输出优化

std::ios::sync_with_stdio(false)

这个函数是一个「是否兼容 stdio」的开关,C++ 为了兼容 C,保证程序在使用了 printf 和 std::cout 的时候不发生混乱,将输出流绑到了一起。同步的输出流是线程安全的。这其实是 C++ 为了兼容而采取的保守措施,也是使 cin/cout 速度较慢的主要原因。

注:我们可以在进行 IO 操作之前将 stdio 解除绑定,但是在这样做之后要注意不能同时使用 std::cin 和 scanf,也不能同时使用 std::cout 和 printf,但是可以同时使用 std::cin 和 printf,也可以同时使用 scanf 和 std::cout。



Content
Return Click here...