循环和关系表达式

  • 指定运行次数的循环
    • for loop
  • 指定条件的循环
    • while loop
    • do-while loop

for 循环

1
2
for(init_expr; test_expr; update_expr)
statement;
  • 每次循环计算测试表达式的值
  • 若测试表达式为真(非零),依次执行 statement 和 update_expr
  • 若测试表达式为假,结束循环

初始化部分允许声明变量,作用域在 for 循环内部


表达式和语句:

任何值或任何有效的值和运算符的组合都是表达式,每个表达式都有值

  • 10 是值为 10 的表达式, 28*20 是值为 560 的表达式
  • 赋值表达式 x = 20 的值为左侧成员的值 20, x = y = z = 0 将三个变量设为相同的值
  • `maids = (cooks=4) + 3 的值为 7
  • 表达式后加分号即成为语句,反之未必

递增运算符和递减运算符:

  • n++: 后置递增/递减,先用再改
  • ++n: 前置递增/递减,先改再用

只能作用于变量,如 (i+j)++ 是非法的

与指针的优先级比较:

  • 前置递增/递减运算符和解引用运算符优先级相同,从右到左结合
  • 后置递增/递减运算符的优先级比前缀版本高,自左向右结合
    • *p++ 先使指针偏移,再解引用
    • (*p)++ 对指针指向的变量执行递增操作

实例:遍历 string

1
2
3
4
5
6
7
8
9
10
11
int main(){
string word;
cin >> word;

//display letters in reverse order
for(int i = word.size() - 1;i >= 0; i--)
cout << word[i];

return 0;
}


复合语句(语句块)

  • 一对花括号括起来的多条语句称为语句块,语法上等同于一条语句
  • 复合语句中定义的变量仅在该语句块中有效,且会覆盖语句块外部的同名变量

逗号运算符

int i, j; 中,逗号是分隔符

实例:反转字符串

1
2
for(j = 0, i = word.size() - 1; j < i; --i, ++j)   // 逗号运算符
swap(word[i], word[j]);

关系表达式

共有六种:>, >=, <, <=, ==, !=,其值为逻辑值 true 或 false

可用于数值、字符、以及 string 对象,不能用于 C 风格字符串

优先级:算术 > 关系 > 逻辑(逻辑非除外)

C 风格字符串的比较用 strcmp 函数,string 比较用关系运算符

while 循环

1
2
while( test-condition )
body

for 循环和 while 循环的区别:

  • for 循环省略测试条件时,默认为 true
  • for 循环头部可以声明和初始化局部变量

do-while 循环

for 和 while 是入口条件 ( entry-condition ) 循环
do-while是出口条件 ( exit-condition ) 循环

  • 先执行循环,再判断条件
  • 循环至少执行一次

Range-based for loop

基于范围的for循环

1
2
3
for(declaration : expression){
statement;
}
  • expression 是要遍历的序列,如初始值列表,数组,vector 或 array 对象
  • declaration 定义一个循环控制变量,每次迭代,这个变量会被初始化为expression的下一个元素的值
  • 如果要对序列中的元素进行修改,循环变量须声明为引用类型

举例:字符串转大写

1
2
3
string str = "cpp";
for( auto& c : str)
c = toupper(c);

自定义类的实现条件:

有迭代器、begin(), end() 成员方法、重载 != 比较操作, ++ 前置递增,和 * 解引用操作

for_each loop

头文件 <algorithm>

参见: cplusplus.com

1
2
3
4
5
6
7
8
9
template <class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function fn)
{
while (first != last){
fn (*first);
++first;
}
return std::move(fn);
}

将函数 fn 作用于范围 [first, last) 内对应的每个元素

  • fn 可以是函数指针或者移动可构造函数对象
  • 如果要修改元素,fn 的参数须使用引用
  • fn 的返回值将被忽略

举例:字符串转小写

1
2
char str[] = "CPP";
for_each(str, str+3, [](char& c){c = tolower(c);});

循环和文本输入

  1. 使用 cin 进行输入,使用特殊的哨兵字符(如 #)结束输入

    例:统计从开始到 # 为止,非空白符的字符数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    char ch;
    cin >> ch;
    int count = 0;
    while(ch != '#'){
    cout << ch;
    ++count;
    cin >> ch;
    }
    cout << " " << count;

    输入:hello world#test
    输出:helloworld 10

  2. 使用 cin.get() 逐个读取字符

    例:统计字母、空白、数字字符和其它字符的个数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    int r[4] = {0};
    char c;

    while((c = cin.get()) != EOF){
    if(isalpha(c))r[0]++;
    else if(c == ' ')r[1]++;
    else if(isdigit(c))r[2]++;
    else r[3]++;
    }

文件尾条件:

  • EOF 是在 <iostream> 中定义的符号常量,一般为 -1
  • Unix Shell 中在行首按 Ctrl+D 输入 EOF
  • Windows 命令行在行首按 Ctrl+Z 并按回车输入 EOF
  • 检测到 EOF 后,cin 将输入流状态的 eofbit 和 failbit 设为 1,阻断后续读取操作
  • eofbit 被设置时,cin.eof() 返回 true; eofbit 或 failbit 被设置为 1 时,cin.fail() 返回 true
  • cin.clear() 可以重置输入流状态以恢复输入

检测输入是否结束( 遇到 EOF 等)

  • while(cin.eof() == false)
  • while(cin.fail() == false)
  • while(!cin.fail())
  • while(cin)
  • while(cin >> ch)
  • while(cin.get(ch))
  • while((ch = cin.get()) != EOF)

cin.get(ch) 遇到 EOF 时不会将非 char 值 EOF 赋给 ch

分支语句和逻辑运算符

if 语句

1
2
3
4
5
6
7
8
if( expression1 )
statements1;
else if( expression2 )
statements2;
else if( expression3 )
statements3;
else
statements4;

逻辑运算符

逻辑与 &&, 逻辑或 ||, 逻辑非 !

  • &&|| 自左向右求值,具有短路特性:&& 遇到假以及 || 遇到真就会立即停止,右侧的表达式不会再执行
  • 逻辑非 !:将非 0 值变为 false, 0 变为 true
  • 优先级:逻辑非 > 算术 > 关系 > 逻辑与 > 逻辑或
  • 保留字 and, or, not 提供了逻辑运算符的另一种方式,但它们不是关键字

例:判断 year 是否为闰年

1
2
if( year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
cout << "leap year" << endl;

条件运算符

expr1 ? expr2 : expr3

根据 expr1 的真假决定取哪个值,或者给哪个变量赋值

1
2
z = ( a > b ? a : b );  // z 取 a, b 中的较大值
(a > b ? a : b) = -1; // a, b 中的较大者修改为 -1

switch 语句

1
2
3
4
5
6
7
8
9
10
11
switch( expression ){
case constant-expression :
// statements;
break; // 可选
case constant-expression :
// statements;
break;
// ...
default :
// statements;
}
  • switch 的 expression 必须是整型 (包括字符型)、枚举类型或者 class,其中 class 有一个单一的转换函数将其转换为整型或枚举类型
  • case 的 constant-expression 必须与 switch 中的变量具有相同的数据类型,且必须是一个常量或字面量
  • break 是可选的,没有显式跳转时,会进入下一个 case 继续执行
  • 如果所有分支都不匹配,则执行 default 分支,该分支可选

break, continue 和 goto

  • break: 从 switch 或循环中跳出
    • 如果有多层循环,break 只能跳出当前所在的循环
  • continue: 跳过本次循环剩余的代码,开始下一次循环
  • goto 可以调到任意位置的标号行,常用于终止深度嵌套
1
2
3
4
5
6
7
8
9
   for( ... )
for ( ... ){
...
if ( disaster )
goto error;
}
...
error:
...

文件操作

通过文件可以将数据持久化

文件类型分为两种:

  1. 文本文件:文件以文本的ASCII码形式存储
  2. 二进制文件:文件以文本的二进制形式存储

一、文件输入输出流

头文件:<fstream>

操作文件的三大类:

  1. ifstream:读操作
  2. ofstream:写操作
  3. fstream:读写操作

文件打开方式:

打开方式解释
ios::in为读文件而打开文件
ios::out为写文件而打开文件
ios::ate初始位置:文件尾
ios::app以追加方式写文件
ios::trunc如果文件存在先删除,再创建
ios::binary二进制方式

文件打开方式可以配合使用,利用|操作符

例如:用二进制方式写文件 ios::binary | ios::out

文本文件 - 读

1
2
3
4
5
6
7
8
9
10
//1.包含头文件
#include <fstream>
//2.创建流对象
ifstream fin;
//3.打开文件并判断是否打开成功
fin.open("文件路径", 打开方式);
//4.读取数据
//四种方式读取;
//5.关闭文件
fin.close();

读取文件的四种方式:

  1. char buf[1024] = {0};
    while(fin >> buf){
        cout<<buf<<endl;
    }
    
    1
    2
    3
    4

    2. ```c++
    char buf[1024] = {0};
    while(fin.getline(buf, sizeof(buf))){...}
  2. string buf;
    while(getline(fin,buf)){...}
    
    1
    2
    3
    4

    4. ```c++
    char c;
    while( (c = fin.get()) != EOF ){cout<<c;}

文本文件 - 写

1
2
3
4
5
6
7
8
9
10
//1.包含头文件
#include <fstream>
//2.创建流对象
ofstream fout;
//3.打开文件
fout.open("文件路径", ios::out);
//4.写入数据
fout<<"写入的数据";
//5.关闭文件
fout.close();

二进制文件 - 写

以二进制方式对文件进行读写操作

打开方式要指定为 ios::binary

二进制方式写文件主要利用流对象调用成员函数 write

函数原型:ostream& write(const char* buffer, int len);

注:字符指针bufer指向内存中一段存储空间,len是读写的字节数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Person{
public:
char m_Name[64];
int m_Age;
};

void test01(){
ofstream fout("person.txt", ios::out | ios::binary);

Person p = {"张三", 18};
fout.write(reinterpret_cast<const char *>(&p), sizeof(Person));

fout.close();
};
int main(){test01();return 0;}

二进制文件 - 读

二进制方式读文件主要利用流对象调用成员函数 read

函数原型:istream& read(char* buffer, int len);

注:字符指针bufer指向内存中一段存储空间,len是读写的字节数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Person{
public:

char m_Name[64];
int m_Age;
};

void test02(){
ifstream fin;
fin.open("person.txt", ios::in | ios::binary);

if(!fin.is_open()){
cout<<"文件打开失败"<<endl;
return;
}

Person p;

fin.read(reinterpret_cast<char *>(&p), sizeof(Person));
cout<<p.m_Name<<endl;
cout<<p.m_Age<<endl;

fin.close();
}
int main(){test02();return 0;}

二、重定向版

头文件:<cstdio>

格式:FILE* freopen(const char* __filename, const char* __modes, FILE* __stream);

参数说明:

mode:文件打开的模式,和fopen中的模式(r/w)相同

stream:文件指针,通常使用标准流文件(stdin/stdout/stderr)

参数含义
stdin标准输入流,默认为键盘
stdout标准输出流,默认为屏幕
stderr标准错误流,一般默认为屏幕

通过调用 freopen ,就可以修改标准流文件的默认值,实现重定向

代码模板:

1
2
3
4
5
6
7
8
#include <cstdio>
int main(){
freopen("slyar.in", "r", stdin);
freopen("slyar.out", "w", stdout);
/*中间按原样写代码,什么都不用修改*/
fclose(stdin);fclose(stdout);
return 0;
}

三、fopen版

头文件:<cstdio>

实例:

1
2
3
4
5
6
7
8
9
10
FILE* fin,* fout;
fin=fopen("in.txt", "rb");
fout=fopen("out.txt", "wb");
int temp,sum(0);
while(fscanf(fin,"%d",&temp)==1){
sum+=temp;
}
fprintf(fout,"%d\n",sum);
fclose(fin);fclose(fout);
return 0;

打开文件

格式:FILE* fopen(const char*__filename,const char*__modes);

  • 读写方式(可组合):
modes含义
wwrite写,若文件存在会清空文件,若不存在则创建文件
rread读,不具备清空或创建功能
aappend追加
+可读可写
bbinary二进制

读写文件

  • 以字符方式读写

    fgetc(), fputc()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    FILE* fin, * fout;
    fout = fopen("char1.txt","w");
    //字符形式读写文件
    char str[] = "This is a string";
    for (int i = 0; i < strlen(str);i++){
    fputc(str[i],fout);
    }
    fclose(fout);

    fin = fopen("char1.txt", "r");
    char key = fgetc(fin);
    while(key != EOF){
    putchar(key);
    key = fgetc(fin);
    }
    fclose(fin);
  • 以字符串方式读写

    fgets(), fputs()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    FILE* fin, * fout;
    fout = fopen("char1.txt","w");
    char str[] = {"This is a string"};
    fputs(str, fout);
    fclose(fout);

    fin = fopen("char1.txt", "r");
    char buf[1024];
    fgets(buf, 1024, fin);
    puts(buf);
    fclose(fin);
  • 格式化读写

    fprintf(), fscanf()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    class Person{
    public:

    char m_Name[64];
    int m_Age;
    };

    void test04(){
    Person p[3]= {{"张三", 18}, {"李四", 20}, {"王五", 19}};

    FILE* fout = fopen("person2.txt","w");
    for (auto & i : p){
    fprintf(fout,"%s\t%d\n", i.m_Name,i.m_Age);
    }
    fclose(fout);

    FILE* fin = fopen("person2.txt", "r");
    Person buf;
    while (fscanf(fin, "%s\t%d\n", buf.m_Name, &buf.m_Age) != EOF){
    printf("%s\t%d\n", buf.m_Name, buf.m_Age);
    }
    fclose(fin);
    }
  • 结构化读写

    fread(), fwrite()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    class Person{
    public:

    char m_Name[64];
    int m_Age;
    };

    void test05(){
    Person p[3]= {{"张三", 18}, {"李四", 20}, {"王五", 19}};
    FILE* fout = fopen("person2.txt","w");
    fwrite(&p[0], sizeof(Person), 3, fout);
    fclose(fout);

    FILE* fin = fopen("person2.txt", "r");
    Person buf[3];
    fread(buf, sizeof (Person), 3, fin);
    for (auto & i : buf){
    printf("%s\t%d\n",i.m_Name, buf->m_Age);
    }
    fclose(fin);
    }

关闭文件

fclose(文件指针)

如果把 fopen 版的改成读写标准输入输出,只需赋值 fin=stdin; , fout=stdout; 即可,不要调用 fopenfclose