C++语法基础-学习笔记

编程入门

变量

计算机的内存

变量指的是会随着程序运算而改变的量
Tips: 比如用户在售卖机购买的可乐数量和爆米花数量都是不确定的,最后的总价也会各异,所以这些数量和总价都以变量的形式存在于计算机中。维护这些变量会用到计算机的存储功能

  • 计算机的存储功能使用内存实现。
  • 计算机中的操作系统一般会把内存划分成不同区域来存储数据,以便于管理。
  • 内存中每个基本存储单元可以存放一个字节的数据,每个字节具有8位,也就是8个比特(bit)。
  • 每个内存单元有一个唯一的地址,常用一个16进制数表示和区分。

变量的声明

  • 变量的声明就是向内存申请变量,用于存放数据的过程,一般的声明方式为数据类型 变量名称
// 声明可乐数量变量 coke
int coke;
// 声明爆米花数量变量 popcorn
int popcorn;
// 声明消费总额变量 money
int money;

变量的命名规则

变量名称也叫做标识符,有固定的构造规则

  • 只能由字⺟、数字和下划线组成;
  • 数字不可以出现在第一个位置上;
  • C++的关键字(保留字)不可以⽤做标识符;
  • 最好简单易懂,用具有对应含义的英文或者拼音来表示;
  • 在C++中,标识符中的英文字母是区分大小写的。

变量的初始化

在声明变量时,也可以赋予这个变量一个初值,这被称为变量的初始化

// 声明消费总额变量 money,并且初始化数值为 0
int money = 0;

变量的输出

使用C++代码来在屏幕上展示初始化过的的消费总额,这叫做变量的输出,输出的内容是变量中保存的数值,而不是变量名称。

//将初始化好的消费总额打印在屏幕上,并且进行换行
cout << money << endl;
// 导入系统输入输出头文件 iostream
#include <iostream>

// 使用标准命名空间 std
using namespace std;

int main() {
    // 声明可乐数量变量 coke
    int coke;
    // 声明爆米花数量变量 popcorn
    int popcorn;
    // 声明消费总额变量 money,并且初始化数值为 0
    int money = 0;
    
    // 将初始化好的消费总额打印在屏幕上,并且进行换行
    cout << money << endl;
    
    return 0;
}

常量

常量指不会随着程序运行而改变的量,分为两种:字面量符号常量

字面量

我们将存放在变量中的数据称之为字面量

把字面量装到变量中使用的操作叫做赋值,一般写法为变量名 = 字面量,比如:

// 小戴需要2瓶可乐,3个爆米花
coke = 2;       
popcorn = 3;

字面量分为以下几种不同类型:

  • 整数字面量:没有任何小数或指数部分的数字字面量,与数学中的整数直接关联。
    如十进制的13,八进制的0123,十六进制的0x12a

  • 浮点字面量:有分数形式或指数形式的数字字面量,与数学中的小数直接关联。
    如小数形式的0.66,指数形式的1.23E-1

  • 字符字面量:分为单字符字面量转义符字面量。如字母'a',换行符'\n',所有字符都采用ASCII编码,共有128个字符。

  • 字符串字面量:由一对双引号括起来的字符序列。
    "Hello, World!"

符号常量

随着程序运算不改变的量,赋予名称之后,有一个特定的名字,叫做符号常量,可以使用const关键字来定义并且初始化(在C++语言中,为正确使用符号常量,在定义时一定要初始化)。:

const 数据类型 常量名称 = 初始值;

或者

数据类型 const 常量名称 = 初始值;

比如:

const int kCokePrice = 5;
int const kPopcornPrice = 10;

注意:符号常量在声明时一定要赋初始值,而在程序中间不能改变其值。例如下面的语句是错误的:

const int a = 10;
a += 10;
const float PI;
PI = 3.1415926;

在C++语言中,字符串"C++"在内存中占用的字节数是:4。对于每个字符串最后,系统都会自动添加一个空字符'\0'

顺序结构程序设计

数据类型

  • 在C++的变量声明中,变量名之前需要指定数据类型
数据类型 变量名称;
  • 数据类型将会决定一个变量存储什么样的信息,从而决定变量的容量有多大,在内存中需要分配多大的空间。
  • C++种的基本类型包括了整数、浮点数两种。

数值整数类型

  • 数值整数用来表示没有小数部分的数字。
  • 可以按照占用内存大小分为short、int、long以及long long这四种,占用内存越大的类型能表示的数值范围就更大。
short类型一般占用2字节
int类型一般占用4个字节
long类型一般占用4个字节
long long类型一般占用8个字节
  • 同时又可以按照是否表示负值分为有符号版本无符号版本
    比如unsigned int就表示无符号的int类型,只能表示正值,表示范围为 0 ~ 2 32 − 1 0~2^{32}−1 02321
    signed int就表示有符号的int类型,可以表示负值,表示范围为 − 2 31 ~ 2 31 − 1 -2^{31}~2^{31} −1 2312311
    在不指定有无符号时,都默认是有符号版本。

我们可以需要根据实际需求,在变量声明或者初始化时指定变量的数据类型,例如:

short price = 500;    // 单价
int coupon = -2000;   // 优惠
long total = 48000;   // 总价

字符整数类型

  • 字符类型char是另一种特殊的整数类型,它专门用来存储计算机中的基本符号:英文字母、数字以及标点等。

  • 计算机通过ASCII编码,将128个字符映射到对应数字上,于是我们使用1个字节(8位) 就可以将所有的字符表示出来。

  • 可以既可以使用字符常量,也可以使用字符对应的ASCII编码,来给char类型的变量赋值。

// 用字符常量初始化一个 char 类型
char size_1 = 'L';
// 用整数常量初始化一个 char 类型,字符L的ASCII编码值为76
char size_2 = 76;

注意:字符常量应是单引号,下面的赋值语句是错误的。

char size = "L";

浮点类型

  • C++中的浮点数分为三种类型:float、double以及long double,分别表示不同的精度

  • 浮点数的精度在于它可以表示的有效位数以及指数范围。
    在三种浮点类型中,更大的内存空间可以表示更多的有效位数:
    float类型通常占用4个字节,有效位数为6位
    double类型占用的空间是float类型的两倍,即8个字节,有效位数为15位
    long double类型一般占用16个字节的空间

可以需要根据实际需求,在浮点数变量声明或者初始化时指定数据类型,例如:

float ratio = 0.618;           // 黄金分割比例
double pi = 3.1415926;          // 圆周率
long double atom = 1e80;   // 宇宙中原子个数
  • 若有 int x; char y; short z; 则表达式(x - z) / (x + y) * 2.0值的数据类型为double
    操作数为字符或短整形时,系统自动转换成整形。操作数为实型时,系统自动转换成双精度型。当两数操作数类型不同时,将精度低(或表示范围小的)的操作数的数据类型变换到与另一操作数类型相同再进行运算。

算术运算符与表达式

基本算术与赋值运算符

C++中,基本的算术运算分为如下5种:加法、减法、乘法、除法以及求模。C++ 使用运算符(operator)来完成这些算术运算。

  • 加法:+运算符,比如表达式3 + 2可以得到5
  • 减法:-运算符,比如表达式21 - 12结果为9
  • 乘法:*运算符,比如表达式4 * 5将得到20
  • 除法:/运算符,比如表达式18 / 6,19/6,我们得到3
  • 求模:%运算符,比如表达式32 % 5将会得到2

比如小键想买5袋饼干,小戴想买3袋饼干,计算两人需要购买的饼干的总袋数时,我们会用到以下语句:

int cookie;
cookie = 5 + 3;

以上计算语句中出现了赋值运算符=,代表了把表达式的值赋给变量的操作。

在实际使用中,很多表达式都包含了多个运算符。比如,5 + 3 * 6。C++使用优先级规则来决定首先使用哪个运算符。

  • 对于算术运算符,遵循的是通常的代数优先级,乘除取模在先,加减运算在后。
  • 我们可以使用()圆括号对来明确计算的顺序,在()中的表达式具有最高的计算优先级。
  • 对于优先级相同的两个运算符,将通过结合律来决定计算的先后:
    算术运算符的结合律都是从左到右的;
    赋值运算符的结合律是从右到左的。

例题:
定义变量int a;则表达式 a = 3, 5;执行过后,a的值和表达式的值分别是3, 5
先将a赋值为3,再执行5,表达式的值为5。

自增自减运算符

自增运算符++作用在变量操作数上,可以使该变量的值在原来基础上递增1

  • 当我们使用前缀模式时,该变量将先于赋值运算符进行递增,比如coffee_box = ++coffee
  • 当我们使用后缀模式时,该变量将后于赋值运算符进行递增,比如coffee_box = coffee++

自减运算符--同样作用在变量操作数上,可以使该变量的值在原来基础上递减1,用法同上。

计算且赋值运算符

C++提供了将算术运算与赋值操作相结合的运算符,称为计算且赋值运算符,符号表示与功能描述如下:
在这里插入图片描述

#include <iostream>
#include <cstdio>
using namespace std;

int main() {
    int days;
    // 计算购买之后,曲奇饼干够小键吃几天,赋值给变量 days
    days = (3 + 5) * 10 / 2;
    cout << days << endl;
    
    int coffee_box;
    int milk_box;
    int coffee = 0;
    int milk = 0;
    coffee_box = ++coffee;  //前缀模式
    milk_box = milk++;  //后缀模式
    printf("coffee_box = %d, coffee = %d. \n", coffee_box, coffee);
    printf("milk_box = %d, milk = %d. \n", milk_box, milk);
    
    int beer_box;
    int wine_box;
    int beer = 1;
    int wine = 1;
    beer_box = --beer;  //前缀模式
    wine_box = wine--;  //后缀模式
    printf("beer_box = %d, beer = %d. \n", beer_box, beer);
    printf("wine_box = %d, wine = %d. \n", wine_box, wine);
    
    int cake=10, nut=30, cheese=5, butter=4, flour=11;
    
    // 蛋糕加3块
    cake += 3;
    // 坚果减12颗
    nut -= 12;
    // 芝士变为之前的3倍
    cheese *= 3;
    // 黄油减半
    butter /= 2;
    // 面粉分为3斤每份打包,最后剩下多少没法打包
    flour %= 3;
    
    printf("cake = %d, nut = %d, cheese = %d, butter = %d, flour = %d. \n", cake, nut, cheese, butter, flour);
    
    return 0;
}

运行结果:

40
coffee_box = 1, coffee = 1. 
milk_box = 0, milk = 1. 
beer_box = 0, beer = 0. 
wine_box = 1, wine = 0. 
cake = 13, nut = 18, cheese = 15, butter = 2, flour = 2. 

位运算符与表达式

位运算是在数值的二进制形式表示下进行的运算,因此会涉及到许多特有的运算规则。C++中包含了6种按位运算符,分别为:

  • 按位与运算符&按位或运算符|异或运算符^
    &、|、^都用于对两个二进制操作数,按照逐个位进行与运算。假设ab是两个只能取二进制值即10的操作数,下表演示了这三个运算符的工作方式:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

令变量A = 60,B = 13作为计算样例,我们首先将他们转换为八位二进制数进行表示,分别得到:

A = 0011 1100
B = 0000 1101

计算后得到:

A & B = 0000 1100 // 12
A | B = 0011 1101 // 61
A ^ B = 0011 0001 // 49
  • 按位取反运算符~

C++中的取反运算符~,对单个二进制操作数进行计算,按照每个位进行取反运算,取反规则是0110

令变量A = 60作为计算样例:

A = 0011 1100
~A= 1100 0011 // -61
  • 左移运算符<<右移运算符>>

在C++中,左移运算符<<,可以将一个操作数的各二进制位全部左移若干位,得到结果左边多余的二进制位将被丢弃,右边不足的二进制位将被补0

我们对A = 60应用左移运算符进行1次位移,可以得到:

A = 0011 1100
A << 1 = 0111 1000

C++中右移运算符>>,可以将一个操作数的各二进制位全部右移若干位,右侧多余的位将会被舍弃,而左侧较为复杂:

  • 对于无符号数,会在左侧补0;
  • 对于有符号数,则会用符号位补齐,正数为0,负数为1。

我们对A = 60应用右移运算符进行2次位移,可以得到:

A = 0011 1100
A >> 2 = 0000 1111
#include <iostream>
using namespace std;

int main() {
    int A = 60; // 0011 1100
    int B = 13; // 0000 1101
    
    cout << (A & B) << endl;
    cout << (A | B) << endl;
    cout << (A ^ B) << endl;
    cout << (~A) << endl;
    
    cout << (A << 1) << endl; // 左移1位
    cout << (A >> 2) << endl; // 右移2位
    
    return 0;
}

运行结果:

12
61
49
-61
120
15

oct为输出八进制数。

int x = 040;
cout << oct << (x << 1);
//040为8进制数,左移一位,并按照8进制输出,结果为100。

循环结构程序设计

for循环

当需要重复执行一段代码很多遍的时候,可以使用循环结构来解决问题。

  • 循环结构是指在程序中需要反复执行某个功能而设置的一种程序结构。
  • 由循环体中的条件,判断继续执行该功能还是退出循环。

for循环语句的一般形式可以这样表示:

for (循环变量赋初始值;循环条件;更新循环变量) {
    循环体
}

除了正常的循环之外,循环控制语句可以更改程序执行的正常序列。循环控制语句常用的是 breakcontinue

  • break
    通常放在循环体中,当执行到这句语句时,跳出整个循环,也就是说整个循环立即终止。(break语句只能用在循环体内和switch语句体内)
  • continue
    通常也是放在循环体中,当执行到这句语句时,跳过当前循环体,强迫进入下一次循环

举例

  • 假设从第1到第100天,每天存和天数相同的钱,每天输出存款整数;
  • 如果天数是10的倍数(第10、20、30、…天),那么那天可以不存钱,也不输出存款总量;
  • 当存款总数到1000元时,就不存了。
  • 可以这么写:
// 输入一个整型变量表示存款总数量
int savings = 0;

// 用循环变量 i 表示现在的天数
// 最开始为1
// 每次循环后都加1天
// 不满足小于等于100天时就跳出循环
for (int i=1; i<=100; i++) {
   // 如果天数是10的倍数,直接进入下一天
   if (i % 10 == 0)
       continue;
   
   // 更新存款总数
   savings = savings + i;
   // 输出存款总数
   cout << i << ":总数" << savings << endl;
   
   // 当存到1000元时,跳出循环
   if (savings >= 1000)
       break;
}

输出闰年
输出21世纪(2001年1月1日至2100年12月31日的这一段期间称为21世纪)中截止某个年份以来的所有闰年年份。

注意:闰年的判别条件是该年年份能被4整除但不能被100整除,或者能被400整除。

输入描述:

输入在一行中给出21世纪的某个截止年份。

输出描述:

逐行输出满足条件的所有闰年年份,即每个年份占一行。输入若非21世纪的年份则输出"Invalid year!"。若不存在任何闰年,则输出“None”。

#include <iostream>
using namespace std; 

int main() {
    
    int end;
    bool is_leap_year,flag = false;
    cin >> end;
    if(end <= 2000 || end > 2100){
        cout << "Invalid year!" << endl;
        return 0;
    }

    for(int year=2001; year<=end; year++){
        is_leap_year = !(year%400)||(!(year%4)&&(year%100));
        if(is_leap_year){
            cout << year << endl;
            flag = true;
        }
    }

    if(!flag)
        cout << "None" <<endl;
    
    return 0;
}

多重循环

循环嵌套适用于循环的大操作里有重复的小操作的情景。

循环嵌套注意事项:

  • 可以任意嵌套,比如forforforwhilewhileforwhilewhile
  • 如果是forfor内外层的循环变量需要用不一样的变量,否则容易产生混乱
  • 缩进以增强代码可读性
  • 可以多层嵌套,但是太多层嵌套容易超时,需要引起注意
    用for循环内部嵌套for循环的方式来打印九九乘法表:
// 1-9,每个数字打印一行
for (int i = 1; i <= 9; i++) {
    // 在第i行中,对于每个小于i的数字,都打印一个等式和tab分隔
    for (int j = 1; j <= i ; j++) {
        cout << j << "*" << i << "=" << j*i << "\t";
    }
    // 第i行最后打印换行符
    cout << endl;
}
  • 小明种苹果
    在这里插入图片描述
    输入描述:
    在这里插入图片描述
    输出描述:
    在这里插入图片描述
示例 1:
输入:
4
4 74 -7 -12 -5
5 73 -8 -6 59 -4
5 76 -5 -10 60 -2
5 80 -6 -15 59 0
输出:
222 1 0
#include<iostream>

using namespace std;
int a[1005][1005];
int b[1005] = {0};//每棵树是否有落果的统计

int main() {
    int n;//共几棵树
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> a[i][0];//进行了几次操作
        for (int j = 1; j <= a[i][0]; j++) {
            cin >> a[i][j];
        }
    }
    int per_num = 0;//每棵树的苹果数
    int sum_num = 0;//最后的总剩余苹果数
    int dgcount = 0;//有果实掉落的苹果树的棵树
    for (int i = 0; i < n; i++) {
        per_num = a[i][1];//初始苹果的情况
        for (int j = 2; j <= a[i][0]; j++) {
            if (a[i][j] < 0)//疏果的情况
                per_num += a[i][j];
            else if (a[i][j] > 0) { //统计果子的情况
                if (a[i][j] != per_num) {//有落果的情况,掉果的棵树的统计不能在这,因为可能一棵树统计多次
                    b[i] = 1;
                }
                per_num = a[i][j];
            }
        }
        sum_num += per_num;
    }
    for (int i = 0; i < n; i++) {
        if (b[i]) {
            dgcount++;
        }
    }
    //判断连着三棵树都掉果的组数
    int e = 0;
    for (int i = 0; i <= n - 2; i++) {
        if (b[i] && b[i + 1] && b[i + 2]) {
            e++;
        }
    }
    if (b[n - 2] && b[n - 1] && b[0]) e++;
    if (b[n - 1] && b[0] && b[1]) e++;

    cout << sum_num << " " << dgcount << " " << e << endl;
    return 0;
}

数组

一维数组

数组是一个固定大小相同类型元素顺序集合,用于储存一系列数据。

数组的声明

在 C++ 中声明数组时,我们需要指定元素的类型和元素的数量。其中,元素的数量必须是一个固定的常量,不能是变量,这样才能分配固定的空间。

元素类型 数组名称[数组长度];

数组元素的访问

数组的每个元素可以通过索引访问,具体的形式是数组名称[索引序号],比如 sales[0], sales[1], sales[2]。数组的索引序号的范围是从 0 到 数组长度-1

数组的初始化

数组有两种方式进行初始化:

一种是在声明的同时直接对整个数组初始化

元素类型 数组名称[数组长度] = {元素1, 元素2, ..., 元素n};
  • 如果大括号中给出的元素数量少于或者等于数组长度,会把这些值依次赋给所有元素,后面的默认为0
  • 对整个数组初始化的时候,可以省略中括号中的数组长度,这样默认初始化的数组长度就是大括号中元素的数量

另一种初始化方式是在声明之后,访问数组中的每一个元素,逐个进行初始化赋值

数组的输入输出

普通的数组只能访问单个元素,对单个元素进行输入输出。

举例
假设商品第一个月的销量为1,接下来每个月的销量都是到前一个月为止的累计销量,也就是说:

  • 第二个月的销量是第一个月的销量1,总累计销量是2
  • 第三个月的销量是前二个月的累计销量2,总累计销量是4
  • 第四个月的销量是前三个月的累计销量4,总累计销量是8
  • 以此类推…

我们想要计算一年中每个月的累计销量并输出。

#include <iostream>
using namespace std;

int main() {
    // 给第一个月的销量赋初值,剩下的默认被初始化为0
    int sales[12] = {1};

    // 用循环依次计算每个月的累计销量
    for (int i = 1; i < 12; i ++)
        sales[i] = 2 * sales[i-1];

    // 输出12个月的累计销量
    for (int i = 0; i < 12; i ++)
        cout << sales[i] << " "; 
    
    return 0;
}
  • 查找整数
本题要求从输入的N个整数中查找给定的X。
如果找到,输出X第一次出现的位置(从1开始计数);
如果没有找到,输出“Not Found”。

输入描述:
第一行一个正整数N(≤100),第二行给出N个整数。
数字均不超过长整型,其间以空格分隔,第三行一个整数X

输出描述:
若找到则输出X的位置,否则输出“Not Found”。

示例 1:
输入:
5
1 4 2 3 5
3
输出:
4
#include <iostream>
using namespace std;

int main() {
    int n, x, idx;
    int nums[1001];
    bool flag = false;
    
    cin >> n;
    for (int i=0; i<n; i++)
        cin >> nums[i];
    cin >> x;
    
    for (int i=0; i<n; i++) {
        if (nums[i] == x) {
            flag = true;
            idx = i;
            break;
        }
    }
    
    if (flag)
        cout << idx+1 << endl;
    else
        cout << "Not Found" << endl;
    return 0;
}

多维数组

我们将从多维数组的声明访问元素初始化输入输出四个方面来介绍多维数组的用法。

多维数组的声明

声明多维数组仍然需要指定元素的类型和每个维度的数量:

元素类型 数组名称[数组第1个维度长度][数组第2个维度长度]...[数组第n个维度长度]

多维数组访问元素

和一维数组一样,多维数组依然是通过索引来访问元素,访问方式为数组名称[第1个维度索引][第2个维度索引]...[第n个维度索引]

多维数组的初始化

多维数组的初始化有两种方式:

第一种方式是在声明时对整个多维数组初始化,初始值都放在大括号中,如果给出的元素数量少于本身的数量,那么后面的会被默认初始化为0

假设是一个二维数组:

int scores[5][3] = {  
 {8, 4, 6},   /*  初始化索引号为 0 的行 */
 {5, 6, 3},   /*  初始化索引号为 1 的行 */
 {3, 2, 4},   /*  初始化索引号为 2 的行 */
 {8, 7, 6},   /*  初始化索引号为 3 的行 */
 {7, 9, 8}     /*  初始化索引号为 4 的行 */
};

// 或者省略掉第二层的大括号
int scores[5][3] = {8, 4, 6, 5, 6, 3, 3, 2, 4, 8, 7, 6, 7, 9, 8};

第二种初始化方式是在声明后分别对每一个元素逐一进行访问,对每个访问的元素进行初始化赋值,可以借助for语句嵌套来实现。

多维数组的输入输出

和一维数组一样,普通的多维数组也是只能对单个元素进行输入输出。

举例
现在有五位选手,三位评委分别在两轮比赛里都给他们评分了,如下图所示。

假设小键发现评委1对选手1第二轮的得分打错了,想要修正为重新输入的分数,修正后,输出每个评委对每个选手的打分,同时计算每位选手的总分并输出。总分的计算方法为两轮平均得分(向下取整)的和。
在这里插入图片描述

那此时我们就可以使用三维数组来描述这个数据结构,并模拟上述过程。

// 声明并初始化得分
int scores[5][3][2] = {
    {{8, 7}, {4, 3}, {6, 5}},
    {{5, 6}, {6, 5}, {3, 8}},
    {{3, 8}, {2, 9}, {4, 7}},
    {{8, 7}, {7, 5}, {6, 3}},
    {{7, 7}, {9, 7}, {8, 9}}
};

int avg_score, score_sum;

// 修正分数
scanf("%i", &scores[0][0][1]);

for (int i=0; i<5; i++) {
    score_sum = 0;
    cout << "选手" << (i+1);
    
    // 对该选手的每一轮打印分数并统计平均分
    for (int k=0; k<2; k++){
        cout << "第" << (k+1) << "轮得分:";
        avg_score = 0;
        
        // 计算该选手在该轮的平均分
        for (int j=0; j<3; j++) {
            avg_score += scores[i][j][k];
            cout << scores[i][j][k] << " ";
        }
        cout << endl;
        avg_score /= 3;
        
        // 该选手的总得分加上该轮的平均分
        score_sum += avg_score;
    }
    
    cout << score_sum << endl;
}
  • 行列互换
输入一个n*m大小的矩阵,将其行列互换,第1行换成第1列,第2行换成第2列,依此类推。

1 <= n, m <= 100

输入描述:
第一行两个整数n,m
接下来n行,每行m个整数

输出描述:
m行,每行n个整数,表示互换之后的矩阵。

示例 1:
输入:
2 3
1 2 3
4 5 6
输出:
1 4
2 5
3 6

#include <iostream>
using namespace std;

int main() {
    int n, m;
    cin >> n >> m;
    int nums[m][n];
    
    for (int i=0; i<n; i++)
        for (int j=0; j<m; j++)
            cin >> nums[j][i];
    
    for (int i=0; i<m; i++) {
        for (int j=0; j<n; j++)
            cout << nums[i][j] << " ";
        cout << endl;
    }
    return 0;
}

字符串

字符数组

字符数组是由若干个字符元素构成的数组,可以按照普通数组的方式去声明、初始化、访问元素、对元素逐个进行输入输出。

字符数组在绝大多数情况下,是用来存储字符串的,这种用字符数组存储的字符串,我们称之为字符数组型字符串

字符数组型字符串实际上是使用字符\0终止的一维数组,数组中元素的类型是字符型,字符数组型字符串的基本操作如下:

  • 声明:和普通字符数组一样,不过因为需要补充一个\0,字符数组的长度需要比字符串的字符数量多1个。

  • 访问元素:通过索引访问。

  • 初始化:有两种方式对字符数组型字符串进行初始化
    第一种初始化方式和普通的字符数组是相同的,给每个元素指定初始化值,不过需要补充上字符'\0'
    第二种方式是使用字符串常量初始化字符数组

// 第一种:给每个元素指定初始化值
char userName[11] = {'L', 'a', 'o', 'W', 'a', 'n', 'g', '\0'};
char userName[11] = {'L', 'a', 'o', 'W', 'a', 'n', 'g'};
char userName[] = {'L', 'a', 'o', 'W', 'a', 'n', 'g', '\0'};

//第二种:使用字符串常量初始化字符数组
char userName[11] = {"LaoWang"};
char userName[11] = "LaoWang";
  • 输入输出:除了像普通数组一样对每个元素逐个输入输出之外,字符数组型字符串也可以直接输入输出
char userName[11];

scanf("%s", userName);
printf("%s", userName);

cin >> userName;
cout << userName <<endl;

由于字符串是一个常用的类型,所以官方提供了一些字符串常用处理函数

  • strcat(s1, s2) 连接字符串
  • strlen(s1) 获取字符串长度
  • strcmp(s1, s2)比较字符串的字典序
  • strcpy(s1, s2)复制字符串

举例:

#include <iostream>
// 为了调用这些函数,我们必须加在cstring这个头文件
#include <cstring>
using namespace std;

int main() {
    char userName_old[10] = "LaoWang";
    char userName_new[10] = "Liu";

    // 用 strcat:返回把 userName_new 接到 userName_old 的后面的字符串
    cout << strcat(userName_old, userName_new) << endl;

    // 返回 userName_old 的长度
    cout << strlen(userName_old) << endl;

    // 返回新老用户名字典序的比较
    cout << strcmp("LaoWang", "Liu") << endl;

    //把 "Liu" 复制到 userName_old
    strcpy(userName_old, "Liu");
    cout << userName_old << endl;
    // 因为只修改了前4个元素,所以第5个元素还是'a'
    cout << userName_old[4] << endl;
    
    return 0;
}

string类型

C++提供了一套更好的工具,叫做标准模版库(Standard Template Library,STL),封装了很多功能,比如一些数据结构(队列等)和算法(排序等),其中一种叫 string 的数据类型可以专门用来处理字符串的相关问题。

string 类型直接把字符串当作一个整体,可以像整型变量、字符变量一样使用。

string 类型的用法有以下一些注意点:

  • 头文件
    首先,需要加载 string 类型所在的头文件。
# include <string>
  • 声明和初始化
    string 类型的声明和初始化和普通变量一样。
// 声明
string a;

// 声明与初始化
string b = "yes";
  • 输入输出
    string 类型的字符串一般直接通过cincout进行输入输出。

由于字符串是一个常用的类型,所以官方提供了一些字符数组型字符串常用处理函数,使用 string 类型来完成这些字符串之间的操作更简便:

s1 + s2 连接字符串
s1.size() 或者 s1.length() 获取字符串长度
strcmp(s1, s2) 比较字符串的字典序
s1 = s2复制字符串

举例

我们对小六的新老用户名做一些操作试试:

string userName_old = "LaoWang";
string userName_new = "Liu";
string str;
int len;

// 连接小六的新老用户名,并赋值给str
str = userName_old + userName_new;
cout << userName_old << " + " << userName_new << " = " << str << endl;

// 输出连接后str的总长度
cout << "str length:  " << str.size() << endl;
cout << "str length:  " << str.length() << endl;

// 比较新老用户名的字典序
cout << (userName_old > userName_new) << endl;

// 再把老用户名赋给str
str = userName_old;
cout << "str : " << str << endl;

另外,还有一些string类型的其他非常方便的操作函数可以了解一下,详见下面的表格:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 颠倒语序
输入三个单词组成的句子,将这三个单词反向输出,例如输入you love i,则输出i love you,每个单词长度不大于100(请用string类型实现)

输入描述:
一行,三个字符串,用空格分开

输出描述:
一行,颠倒三个字符串的顺序,字符串之间用空格分开

示例 1:
输入:
you love i
输出:
i love you
# include <iostream>
# include <string>

using namespace std;

int main() {
    string words[3];
    for (int i=0; i<3; i++)
        cin >> words[i];
    
    cout << words[2] << " " << words[1] << " " << words[0];
    
    return 0;
}

函数与结构体

由于数组只能存储一组同样数据类型的信息,我们可以使用结构体将一组不同类型的数据聚合成一个整体,以便于去处理这些信息。一个结构体所占空间是该结构体中各成员所需内存容量之和。

  • 结构体的定义
    每个结构体除了有名称,还有有很多个不同类型的成员变量用于表示这个结构体的属性。结构体的定义方式如下:
struct 结构体名称 {
    数据类型1 变量名1;
    数据类型2 变量名2, 变量名3;
    ...
    数据类型n 变量名m;
};
  • 结构体变量声明
    声明结构体变量的方式与声明普通变量类似。对于定义好的结构体Student,声明结构体类型的变量如下:
struct Student{    
    int number, birth_year;
    string name;
};

// 声明三个学生
Student zhang_san, li_si, wang_mazi;

也可以声明结构体变量的数组,以保存500个学生的信息。

// 声明一个学生数组,数组长度为500,可以保存500个学生的信息
Student students[500];
  • 初始化结构体 对于结构体变量,我们可以通过两种方式初始化它:使用初始化列表或构造函数。

使用初始化列表和数组的初始化类似,会根据给出的元素依次初始化结构体中的每个成员,如果给出的元素小于成员数量,排在后面的就会保持没有被初始化。举个例子:

struct Student{    
    int number, birth_year;
    string name;
};

// 初始化一个学号为1,2000年出生,名叫 ZhangSan的学生
Student zhang_san = {1, 2000, "ZhangSan"};

构造函数初始化,可以在创建一个结构体变量而不向其传递某些成员变量的值时,提供默认值。我们可以先在结构体内部完成一个构造函数,再调用这个构造函数来初始化结构体变量:

struct Student{    
    int number, birth_year;
    string name;
    
    // 在结构体里定义构造函数
    // 在初始化时所有学生的出生年份都被设置为2000
    // 可以传入两个参数,分别代表学号和姓名,如果不传入参数,默认学号为0,姓名为""
    Student (int num=0, string n="") {
        number = num;
        birth_year = 2000;
        name = n;
    }
};

// 初始化时只用传递两个参数即可
Student li_si(2, "LiSi");
  • 结构体成员访问
    如果要访问某一个结构体变量的某个成员,可以使用结构体变量名.结构体成员变量名的方式访问:
cout << zhang_san.number << endl;
cout << zhang_san.name << endl;
cout << li_si.birth_year << endl;
  • 代码:
  • 员工信息的存储
我们现在需要创建一个公司员工的结构体`Employee`,用于

- 保存员工的编号、姓名和每日需要工作小时数(默认8小时)
- 初始化小一(1号,XiaoYi),小二(2号,XiaoEr),小六(3号,XiaoLiu),小四(4号,XiaoSi),小五(5号,XiaoWu)这五位员工的信息
- 写一个函数,展示员工的信息

我们可以这样来完成:

#include <iostream>
using namespace std;

// 定义结构体 Employee,并声明长度为5的结构体数组 employees_info
struct Employee{    
    int number, working_hours;
    string name;
    
    // 构建函数
    Employee (int num=0, string n="") {
        number = num;
        working_hours = 8;
        name = n;
    }
} employees_info[5];

// 展示某位员工信息的函数
void showEmployee(const Employee &employee_info) {
    cout << "Employee Number : " << employee_info.number << endl;
    cout << "Name : " << employee_info.name << endl;
    cout << "Working Hours : " << employee_info.working_hours << endl;
}

int main() {
    // 初始化5位员工
    employees_info[0] = Employee(1, "XiaoYi");
    employees_info[1] = Employee(2, "XiaoEr");
    employees_info[2] = Employee(3, "XiaoLiu");
    employees_info[3] = Employee(4, "XiaoSi");
    employees_info[4] = Employee(5, "XiaoWu");
    
    // 调用函数展示5位的信息
    for(int i=0; i<5; i++) {
        showEmployee(employees_info[i]);
    }
    return 0;
}
  • 年龄最大学员
输入n个学生的信息,包括姓名、性别、年龄,再输出其中年龄最大的学生的信息。(保证最大年龄不重复)

1 <= n <= 10
姓名长度小于等于20
性别为M或F

输入描述:
第一行一个整数n
接下来n行,依次是学生的姓名、性别、年龄。

输出描述:
一行,依次是姓名、性别、年龄,中间用空格隔开。

示例 1:
输入:
2
Kal'tsit F 1000
Amiya F 14
输出:
Kal'tsit F 1000
#include 
<iostream>
#include <string>

using namespace std;

struct Student {
    string name;
    char gender;
    int age;
};

int main() {
    int num, max_age, max_age_idx;
    cin >> num;
    
    string n;
    char g;
    int a;
    
    Student students[11];
    for (int i=0; i<num; i++) {
        cin >> n >> g >> a;
        students[i] = {n, g, a};
    }
    
    max_age = 0;
    max_age_idx = 0;
    for (int i=0; i<num; i++) {
        if (students[i].age > max_age) {
            max_age = students[i].age;
            max_age_idx = i;
        }
    }
    cout << students[max_age_idx].name << " " << students[max_age_idx].gender << " " << students[max_age_idx].age;
    
    return 0;
}

学习资源

青舟智学:https://www.qingzhouzhixue.com/

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝净云

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值