C++ 学习_流程控制

C++ 常量

常量是固定值,在程序执行期间不会改变。这些固定的值,又叫做字面量。

常量可以是任何的基本数据类型,可分为整型数字、浮点数字、字符、字符串和布尔值。

常量就像是常规的变量,只不过常量的值在定义后不能进行修改。

整数常量

整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制。

整数常量也可以带一个后缀,后缀是 U 和 L 的组合,U 表示无符号整数(unsigned),L 表示长整数(long)。后缀可以是大写,也可以是小写,U 和 L 的顺序任意。

下面列举几个整数常量的实例:

c 复制代码
212         // 合法的
215u        // 合法的
0xFeeL      // 合法的
078         // 非法的:8 不是八进制的数字
032UU       // 非法的:不能重复后缀

以下是各种类型的整数常量的实例:

c 复制代码
85         // 十进制
0213       // 八进制 
0x4b       // 十六进制 
30         // 整数 
30u        // 无符号整数 
30l        // 长整数 
30ul       // 无符号长整数

浮点常量

浮点常量由整数部分、小数点、小数部分和指数部分组成。您可以使用小数形式或者指数形式来表示浮点常量。

当使用小数形式表示时,必须包含整数部分、小数部分,或同时包含两者。当使用指数形式表示时, 必须包含小数点、指数,或同时包含两者。带符号的指数是用 e 或 E 引入的。

下面列举几个浮点常量的实例:

c 复制代码
3.14159       // 合法的 
314159E-5L    // 合法的 
510E          // 非法的:不完整的指数
210f          // 非法的:没有小数或指数
.e55          // 非法的:缺少整数或分数

布尔常量

布尔常量共有两个,它们都是标准的 C++ 关键字:

  • true 值代表真。
  • false 值代表假。

我们不应把 true 的值看成 1,把 false 的值看成 0。

字符常量

字符常量是括在单引号中。如果常量以 L(仅当大写时)开头,则表示它是一个宽字符常量(例如 L'x'),此时它必须存储在 wchar_t 类型的变量中。否则,它就是一个窄字符常量(例如 'x'),此时它可以存储在 char 类型的简单变量中。

字符常量可以是一个普通的字符(例如 'x')、一个转义序列(例如 '\t'),或一个通用的字符(例如 '\u02C0')。

在 C++ 中,有一些特定的字符,当它们前面有反斜杠时,它们就具有特殊的含义,被用来表示如换行符(\n)或制表符(\t)等。下表列出了一些这样的转义序列码:

转义序列 含义
\ \ 字符
' ' 字符
" " 字符
? ? 字符
\a 警报铃声
\b 退格键
\f 换页符
\n 换行符
\r 回车
\t 水平制表符
\v 垂直制表符
\ooo 一到三位的八进制数
\xhh . . . 一个或多个数字的十六进制数

下面的实例显示了一些转义序列字符:

c 复制代码
#include <iostream>

using namespace std;

int main()
{
    cout << "Hello\tWorld\n\n";
    return 0;
}

// 输出:
// Hello   World

字符串常量

字符串字面值或常量是括在双引号 "" 中的。一个字符串包含类似于字符常量的字符:普通的字符、转义序列和通用的字符。

您可以使用\做分隔符,把一个很长的字符串常量进行分行。

下面的实例显示了一些字符串常量:

c 复制代码
#include <iostream>
#include <string>
using namespace std;

int main()
{
    string greeting1 = "Hello World";
    cout << greeting1;
    cout << "\n";   // 换行符
    string greeting2 = "Hello \ 
World";
    cout << greeting2;
    return 0;
}

// 输出:
// Hello World
// Hello World

定义常量

在 C++ 中,有两种简单的定义常量的方式:

  • 使用 #define 预处理器。
  • 使用 const 关键字。

#define 预处理器

下面是使用 #define 预处理器定义常量的形式:

c 复制代码
#define identifier value

具体请看下面的实例:

c 复制代码
// 宏定义的使用
#include <iostream>
using namespace std;

#define LENGTH 10
#define WIDTH 5
#define NEWLINE '\n'

int main()
{

    int area;

    area = LENGTH * WIDTH;
    cout << area;
    cout << NEWLINE;
    return 0;
}

// 输出:
// 50

const 关键字

您可以使用 const 前缀声明指定类型的常量,如下所示:

复制代码
const type variable = value;

具体请看下面的实例:

c 复制代码
#include <iostream>

using namespace std;

int main()
{
    const int LENGTH = 10;
    const int WIDTH = 5;
    const char NEWLINE = '\n';
    int area;

    area = LENGTH * WIDTH;
    cout << area;
    cout << NEWLINE;
    return 0;
}

// 输出:
// 50

请注意,把常量定义为大写字母形式,是一个很好的编程实践。

C++ 修饰符类型

C++ 允许在 char、int 和 double 数据类型前放置修饰符。

修饰符是用于改变变量类型的行为的关键字,它更能满足各种情境的需求。

下面列出了数据类型修饰符:

  • signed:表示变量可以存储负数。对于整型变量来说,signed 可以省略,因为整型变量默认为有符号类型。
  • unsigned:表示变量不能存储负数。对于整型变量来说,unsigned 可以将变量范围扩大一倍。
  • short:表示变量的范围比 int 更小。short int 可以缩写为 short。
  • long:表示变量的范围比 int 更大。long int 可以缩写为 long。
  • long long:表示变量的范围比 long 更大。C++11 中新增的数据类型修饰符。
  • float:表示单精度浮点数。
  • double:表示双精度浮点数。
  • bool:表示布尔类型,只有 true 和 false 两个值。
  • char:表示字符类型。
  • wchar_t:表示宽字符类型,可以存储 Unicode 字符。

修饰符 signed、unsigned、long 和 short 可应用于整型,signed 和 unsigned 可应用于字符型,long 可应用于双精度型。

这些修饰符也可以组合使用,修饰符 signed 和 unsigned 也可以作为 long 或 short 修饰符的前缀。例如:unsigned long int。

C++ 允许使用速记符号来声明无符号短整数 或无符号长整数 。您可以不写 int,只写单词 unsigned、short 或 long ,int 是隐含的。例如,下面的两个语句都声明了无符号整型变量。

c 复制代码
signed int num1 = -10; // 定义有符号整型变量 num1,初始值为 -10
unsigned int num2 = 20; // 定义无符号整型变量 num2,初始值为 20

short int num1 = 10; // 定义短整型变量 num1,初始值为 10
long int num2 = 100000; // 定义长整型变量 num2,初始值为 100000

long long int num1 = 10000000000; // 定义长长整型变量 num1,初始值为 10000000000

float num1 = 3.14f; // 定义单精度浮点数变量 num1,初始值为 3.14
double num2 = 2.71828; // 定义双精度浮点数变量 num2,初始值为 2.71828

bool flag = true; // 定义布尔类型变量 flag,初始值为 true

char ch1 = 'a'; // 定义字符类型变量 ch1,初始值为 'a'
wchar_t ch2 = L'你'; // 定义宽字符类型变量 ch2,初始值为 '你'

为了理解 C++ 解释有符号整数和无符号整数修饰符之间的差别,我们来运行一下下面这个短程序:

c 复制代码
#include <iostream>
using namespace std;

/**
 * 这个程序演示了有符号整数和无符号整数之间的差异
 */

 int main()
 {
    short int i;            // 有符号短整数
    short unsigned int j;   // 无符号短整数

    j = 50000;
    i = j;
    cout << i << " " << j;

    return 0;
 }

// 输出:
// -15536 50000

上述结果中,无符号短整数 50,000 的位模式被解释为有符号短整数 -15,536。

C++ 中的类型限定符

类型限定符提供了变量的额外信息,用于在定义变量或函数时改变它们的默认行为的关键字。

限定符 含义
const const 定义常量,表示该变量的值不能被修改。
volatile 修饰符 volatile 告诉该变量的值可能会被程序以外的因素改变,如硬件或其他线程。。
restrict 由 restrict 修饰的指针是唯一一种访问它所指向的对象的方式。只有 C99 增加了新的类型限定符 restrict。
mutable mutable 用于修饰类的成员变量。被 mutable 修饰的成员变量可以被修改,即使它们所在的对象是 const 的。
static 用于定义静态变量,表示该变量的作用域仅限于当前文件或当前函数内,不会被其他文件或函数访问。
register 用于定义寄存器变量,表示该变量被频繁使用,可以存储在CPU的寄存器中,以提高程序的运行效率。在 C++11 中被标记为弃用(deprecated) 在 C++17 中被正式移除。

const 实例

复制代码
const int NUM = 10; // 定义常量 NUM,其值不可修改
const int* ptr = &NUM; // 定义指向常量的指针,指针所指的值不可修改
int const* ptr2 = &NUM; // 和上面一行等价

volatile 实例

复制代码
volatile int num = 20; // 定义变量 num,其值可能会在未知的时间被改变

mutable 实例

复制代码
class Example {
public:
    int get_value() const {
        return value_; // const 关键字表示该成员函数不会修改对象中的数据成员
    }
    void set_value(int value) const {
        value_ = value; // mutable 关键字允许在 const 成员函数中修改成员变量
    }
private:
    mutable int value_;
};

static 实例

复制代码
void example_function() {
    static int count = 0; // static 关键字使变量 count 存储在程序生命周期内都存在
    count++;
}

register 实例

复制代码
void example_function(register int num) {
    // register 关键字建议编译器将变量 num 存储在寄存器中
    // 以提高程序执行速度
    // 但是实际上是否会存储在寄存器中由编译器决定
}

C++ 存储类和运算符

存储类定义

存储类定义 C++ 程序中变量/函数的范围(可见性)和生命周期。这些说明符放置在它们所修饰的类型之前。下面列出 C++ 程序中可用的存储类:

  • auto:这是默认的存储类说明符,通常可以省略不写。auto 指定的变量具有自动存储期,即它们的生命周期仅限于定义它们的块(block)。auto 变量通常在栈上分配。
  • register:用于建议编译器将变量存储在CPU寄存器中以提高访问速度。在 C++11 及以后的版本中,register 已经是一个废弃的特性,不再具有实际作用。
  • static:用于定义具有静态存储期的变量或函数,它们的生命周期贯穿整个程序的运行期。在函数内部,static变量的值在函数调用之间保持不变。在文件内部或全局作用域,static变量具有内部链接,只能在定义它们的文件中访问。
  • extern:用于声明具有外部链接的变量或函数,它们可以在多个文件之间共享。默认情况下,全局变量和函数具有 extern 存储类。在一个文件中使用extern声明另一个文件中定义的全局变量或函数,可以实现跨文件共享。
  • mutable (C++11):用于修饰类中的成员变量,允许在const成员函数中修改这些变量的值。通常用于缓存或计数器等需要在const上下文中修改的数据。
  • thread_local (C++11):用于定义具有线程局部存储期的变量,每个线程都有自己的独立副本。线程局部变量的生命周期与线程的生命周期相同。

从 C++ 17 开始,auto 关键字不再是 C++ 存储类说明符,且 register 关键字被弃用。

中的存储类说明符为程序员提供了控制变量和函数生命周期及可见性的手段。

合理使用存储类说明符可以提高程序的可维护性和性能。

从 C++11 开始,register 已经失去了原有的作用,而 mutable 和 thread_local 则是新引入的特性,用于解决特定的编程问题。

下面是一个展示不同存储类说明符的实例:

c++ 复制代码
#include <iostream>

// 全局变量,具有外部链接,默认存储类为extern
int globalVar;

void function() {
    // 局部变量,具有自动存储期,默认存储类为auto
    //auto int localVar = 10;    // C++11 及后续标准(C++14/C++17/C++20)这句报错

    // 静态变量,具有静态存储期,生命周期贯穿整个程序
    static int staticVar = 20;

    const int constVar = 30; // const变量默认具有static存储期

    // 尝试修改const变量,编译错误
    // constVar = 40;

    // mutable成员变量,可以在const成员函数中修改
    class MyClass {
    public:
        mutable int mutableVar;

        void constMemberFunc() const {
            mutableVar = 50; // 允许修改mutable成员变量
        }
    };

    // 线程局部变量,每个线程有自己的独立副本
    thread_local int threadVar = 60;
}

int main() {
    extern int externalVar; // 声明具有外部链接的变量

    function();

    return 0;
}

算术运算符

下表显示了 C++ 支持的算术运算符。

假设变量 A 的值为 10,变量 B 的值为 20,则:

运算符 描述 实例
+ 把两个操作数相加 A + B 将得到 30
- 从第一个操作数中减去第二个操作数 A - B 将得到 -10
* 把两个操作数相乘 A * B 将得到 200
/ 分子除以分母 B / A 将得到 2
% 取模运算符,整除后的余数 B % A 将得到 0
++ 自增运算符,整数值增加 1 A++ 将得到 11
-- 自减运算符,整数值减少 1 A-- 将得到 9
c++ 复制代码
#include <iostream>
using namespace std;
 
int main()
{
   int a = 21;
   int b = 10;
   int c;
 
   c = a + b;
   cout << "Line 1 - c 的值是 " << c << endl ;
   c = a - b;
   cout << "Line 2 - c 的值是 " << c << endl ;
   c = a * b;
   cout << "Line 3 - c 的值是 " << c << endl ;
   c = a / b;
   cout << "Line 4 - c 的值是 " << c << endl ;
   c = a % b;
   cout << "Line 5 - c 的值是 " << c << endl ;
 
   int d = 10;   // 测试自增、自减
   c = d++;
   cout << "Line 6 - c 的值是 " << c << endl ;
 
   d = 10;       // 重新赋值
   c = d--;
   cout << "Line 7 - c 的值是 " << c << endl ;
   return 0;
}

// 输出:
// Line 1 - c 的值是 31
// Line 2 - c 的值是 11
// Line 3 - c 的值是 210
// Line 4 - c 的值是 2
// Line 5 - c 的值是 1
// Line 6 - c 的值是 10
// Line 7 - c 的值是 10

关系运算符

下表显示了 C++ 支持的关系运算符。

假设变量 A 的值为 10,变量 B 的值为 20,则:

运算符 描述 实例
== 检查两个操作数的值是否相等,如果相等则条件为真。 (A == B) 不为真。
!= 检查两个操作数的值是否相等,如果不相等则条件为真。 (A != B) 为真。
> 检查左操作数的值是否大于右操作数的值,如果是则条件为真。 (A > B) 不为真。
< 检查左操作数的值是否小于右操作数的值,如果是则条件为真。 (A < B) 为真。
>= 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。 (A >= B) 不为真。
<= 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。 (A <= B) 为真。
c 复制代码
#include <iostream>
using namespace std;
 
int main()
{
   int a = 21;
   int b = 10;
   int c ;
 
   if( a == b )
   {
      cout << "Line 1 - a 等于 b" << endl ;
   }
   else
   {
      cout << "Line 1 - a 不等于 b" << endl ;
   }
   if ( a < b )
   {
      cout << "Line 2 - a 小于 b" << endl ;
   }
   else
   {
      cout << "Line 2 - a 不小于 b" << endl ;
   }
   if ( a > b )
   {
      cout << "Line 3 - a 大于 b" << endl ;
   }
   else
   {
      cout << "Line 3 - a 不大于 b" << endl ;
   }
   /* 改变 a 和 b 的值 */
   a = 5;
   b = 20;
   if ( a <= b )
   {
      cout << "Line 4 - a 小于或等于 b" << endl ;
   }
   if ( b >= a )
   {
      cout << "Line 5 - b 大于或等于 a" << endl ;
   }
   return 0;
}

// 输出:
// Line 1 - a 不等于 b
// Line 2 - a 不小于 b
// Line 3 - a 大于 b
// Line 4 - a 小于或等于 b
// Line 5 - b 大于或等于 a

逻辑运算符

下表显示了 C++ 支持的关系逻辑运算符。

假设变量 A 的值为 1,变量 B 的值为 0,则:

运算符 描述 实例
&& 称为逻辑与运算符。如果两个操作数都 true,则条件为 true。 (A && B) 为 false。
|| 称为逻辑或运算符。如果两个操作数中有任意一个 true,则条件为 true。 (A || B) 为 true。
! 称为逻辑非运算符。用来逆转操作数的逻辑状态,如果条件为 true 则逻辑非运算符将使其为 false。 !(A && B) 为 true。

C++循环和判断语句

循环类型

C++ 编程语言提供了以下几种循环类型。点击链接查看每个类型的细节。

循环类型 描述
while 循环 当给定条件为真时,重复语句或语句组。它会在执行循环主体之前测试条件。
for 循环 多次执行一个语句序列,简化管理循环变量的代码。
do...while 循环 除了它是在循环主体结尾测试条件外,其他与 while 语句类似。
嵌套循环 您可以在 while、for 或 do...while 循环内使用一个或多个循环。

循环控制语句

循环控制语句更改执行的正常序列。当执行离开一个范围时,所有在该范围中创建的自动对象都会被销毁。

C++ 提供了下列的控制语句。点击链接查看每个语句的细节。

控制语句 描述
break 语句 终止 loop 或 switch 语句,程序流将继续执行紧接着 loop 或 switch 的下一条语句。
continue 语句 引起循环跳过主体的剩余部分,立即重新开始测试条件。
goto 语句 将控制转移到被标记的语句。但是不建议在程序中使用 goto 语句。

无限循环

如果条件永远不为假,则循环将变成无限循环。for 循环在传统意义上可用于实现无限循环。由于构成循环的三个表达式中任何一个都不是必需的,您可以将某些条件表达式留空来构成一个无限循环。

c++ 复制代码
#include <iostream>
using namespace std;
 
int main ()
{
 
   for( ; ; )
   {
      printf("This loop will run forever.\n");
   }
 
   return 0;
}

当条件表达式不存在时,它被假设为真。您也可以设置一个初始值和增量表达式,但是一般情况下,C++ 程序员偏向于使用 for(;😉 结构来表示一个无限循环。

判断语句

C++ 编程语言提供了以下类型的判断语句。点击链接查看每个语句的细节。

语句 描述
if 语句 一个 if 语句 由一个布尔表达式后跟一个或多个语句组成。
if...else 语句 一个 if 语句 后可跟一个可选的 else 语句,else 语句在布尔表达式为假时执行。
嵌套 if 语句 您可以在一个 if 或 else if 语句内使用另一个 if 或 else if 语句。
switch 语句 一个 switch 语句允许测试一个变量等于多个值时的情况。
嵌套 switch 语句 您可以在一个 switch 语句内使用另一个 switch 语句。

? : 运算符

我们已经在前面的章节中讲解了 条件运算符 ? :,可以用来替代 if...else 语句。它的一般形式如下:

复制代码
Exp1 ? Exp2 : Exp3;

其中,Exp1、Exp2 和 Exp3 是表达式。请注意,冒号的使用和位置。

? 表达式的值是由 Exp1 决定的。如果 Exp1 为真,则计算 Exp2 的值,结果即为整个 ? 表达式的值。如果 Exp1 为假,则计算 Exp3 的值,结果即为整个 ? 表达式的值。

C++ 函数

函数是一组一起执行一个任务的语句。每个 C++ 程序都至少有一个函数,即主函数 main() ,所有简单的程序都可以定义其他额外的函数。

您可以把代码划分到不同的函数中。如何划分代码到不同的函数中是由您来决定的,但在逻辑上,划分通常是根据每个函数执行一个特定的任务来进行的。

函数声明 告诉编译器函数的名称、返回类型和参数。函数定义提供了函数的实际主体。

C++ 标准库提供了大量的程序可以调用的内置函数。例如,函数 strcat() 用来连接两个字符串,函数 memcpy() 用来复制内存到另一个位置。

函数还有很多叫法,比如方法、子例程或程序,等等。

定义函数

C++ 中的函数定义的一般形式如下:

c++ 复制代码
return_type function_name( parameter list )
{
   body of the function
}

在 C++ 中,函数由一个函数头和一个函数主体组成。下面列出一个函数的所有组成部分:

  • **返回类型:**一个函数可以返回一个值。return_type 是函数返回的值的数据类型。有些函数执行所需的操作而不返回值,在这种情况下,return_type 是关键字 void。
  • **函数名称:**这是函数的实际名称。函数名和参数列表一起构成了函数签名。
  • **参数:**参数就像是占位符。当函数被调用时,您向参数传递一个值,这个值被称为实际参数。参数列表包括函数参数的类型、顺序、数量。参数是可选的,也就是说,函数可能不包含参数。
  • **函数主体:**函数主体包含一组定义函数执行任务的语句。

以下是 max() 函数的源代码。该函数有两个参数 num1 和 num2,会返回这两个数中较大的那个数:

c++ 复制代码
#include <iostream>
using namespace std;
 
// 函数声明
int max(int num1, int num2);
 
int main ()
{
   // 局部变量声明
   int a = 100;
   int b = 200;
   int ret;
 
   // 调用函数来获取最大值
   ret = max(a, b);
 
   cout << "Max value is : " << ret << endl;
 
   return 0;
}
 
// 函数返回两个数中较大的那个数
int max(int num1, int num2) 
{
   // 局部变量声明
   int result;
 
   if (num1 > num2)
      result = num1;
   else
      result = num2;
 
   return result; 
}

C++ 数组

C++ 支持数组数据结构,它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据,但它往往被认为是一系列相同类型的变量。

数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 numbers0、numbers1、...、numbers99 来代表一个个单独的变量。数组中的特定元素可以通过索引访问。

所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素,最高的地址对应最后一个元素。

声明数组

在 C++ 中要声明一个数组,需要指定元素的类型和元素的数量,如下所示:

复制代码
type arrayName [ arraySize ];

这叫做一维数组。arraySize 必须是一个大于零的整数常量,type 可以是任意有效的 C++ 数据类型。例如,要声明一个类型为 double 的包含 10 个元素的数组 balance,声明语句如下:

复制代码
double balance[10];

现在 balance 是一个可用的数组,可以容纳 10 个类型为 double 的数字。

初始化数组

在 C++ 中,您可以逐个初始化数组,也可以使用一个初始化语句,如下所示:

复制代码
double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};

大括号 { } 之间的值的数目不能大于我们在数组声明时在方括号 中指定的元素数目。

如果您省略掉了数组的大小,数组的大小则为初始化时元素的个数。因此,如果:

复制代码
double balance[] = {1000.0, 2.0, 3.4, 7.0, 50.0};

您将创建一个数组,它与前一个实例中所创建的数组是完全相同的。下面是一个为数组中某个元素赋值的实例:

复制代码
balance[4] = 50.0;

上述的语句把数组中第五个元素的值赋为 50.0。所有的数组都是以 0 作为它们第一个元素的索引,也被称为基索引,数组的最后一个索引是数组的总大小减去 1。以下是上面所讨论的数组的的图形表示:

访问数组元素

数组元素可以通过数组名称加索引进行访问。元素的索引是放在方括号内,跟在数组名称的后边。例如:

复制代码
double salary = balance[9];

上面的语句将把数组中第 10 个元素的值赋给 salary 变量。下面的实例使用了上述的三个概念,即,声明数组、数组赋值、访问数组:

c++ 复制代码
#include <iostream>
using namespace std;
 
#include <iomanip>
using std::setw;
 
int main ()
{
   int n[ 10 ]; // n 是一个包含 10 个整数的数组
 
   // 初始化数组元素          
   for ( int i = 0; i < 10; i++ )
   {
      n[ i ] = i + 100; // 设置元素 i 为 i + 100
   }
   cout << "Element" << setw( 13 ) << "Value" << endl;
 
   // 输出数组中每个元素的值                     
   for ( int j = 0; j < 10; j++ )
   {
      cout << setw( 7 )<< j << setw( 13 ) << n[ j ] << endl;
   }
 
   return 0;
}

上面的程序使用了 setw() 函数 来格式化输出。当上面的代码被编译和执行时,它会产生下列结果:

C++ 字符串

C++ 提供了以下两种类型的字符串表示形式:

  • C 风格字符串
  • C++ 引入的 string 类类型

C 风格字符串

C 风格的字符串起源于 C 语言,并在 C++ 中继续得到支持。字符串实际上是使用 null 字符 \0 终止的一维字符数组。因此,一个以 null 结尾的字符串,包含了组成字符串的字符。

下面的声明和初始化创建了一个 RUNOOB 字符串。由于在数组的末尾存储了空字符,所以字符数组的大小比单词 RUNOOB 的字符数多一个。

复制代码
char site[7] = {'R', 'U', 'N', 'O', 'O', 'B', '\0'};

依据数组初始化规则,您可以把上面的语句写成以下语句:

复制代码
char site[] = "RUNOOB";

以下是 C/C++ 中定义的字符串的内存表示:

其实,您不需要把 null 字符放在字符串常量的末尾。C++ 编译器会在初始化数组时,自动把 \0 放在字符串的末尾。让我们

尝试输出上面的字符串:

c++ 复制代码
#include <iostream>
 
using namespace std;
 
int main ()
{
   char site[7] = {'R', 'U', 'N', 'O', 'O', 'B', '\0'};
 
   cout << "这是地址:";
   cout << site << endl;
 
   return 0;
}

//输出:
//这是地址:RUNOOB

C++ 中有大量的函数用来操作以 null 结尾的字符串:

序号 函数 & 目的
1 strcpy(s1, s2); 复制字符串 s2 到字符串 s1。
2 strcat(s1, s2); 连接字符串 s2 到字符串 s1 的末尾。连接字符串也可以用 + 号,例如: string str1 = "runoob"; string str2 = "google"; string str = str1 + str2;
3 strlen(s1); 返回字符串 s1 的长度。
4 strcmp(s1, s2); 如果 s1 和 s2 是相同的,则返回 0;如果 s1<s2 则返回值小于 0;如果 s1>s2 则返回值大于 0。
5 strchr(s1, ch); 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。
6 strstr(s1, s2); 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。

下面的实例使用了上述的一些函数:

c++ 复制代码
#include <iostream>
#include <string>
 
using namespace std;
 
int main ()
{
   string str1 = "runoob";
   string str2 = "google";
   string str3;
   int  len ;
 
   // 复制 str1 到 str3
   str3 = str1;
   cout << "str3 : " << str3 << endl;
 
   // 连接 str1 和 str2
   str3 = str1 + str2;
   cout << "str1 + str2 : " << str3 << endl;
 
   // 连接后,str3 的总长度
   len = str3.size();
   cout << "str3.size() :  " << len << endl;
 
   return 0;
}

//输出:
//strcpy( str3, str1) : runoob
//strcat( str1, str2): runoobgoogle
//strlen(str1) : 12

C++ 中的 String 类

C++ 标准库提供了 string 类类型,支持上述所有的操作,另外还增加了其他更多的功能。我们将学习 C++ 标准库中的这个类,现在让我们先来看看下面这个实例:

现在您可能还无法透彻地理解这个实例,因为到目前为止我们还没有讨论类和对象。所以现在您可以只是粗略地看下这个实例,等理解了面向对象的概念之后再回头来理解这个实例。

c++ 复制代码
#include <iostream>
#include <string>
 
using namespace std;
 
int main ()
{
   string str1 = "runoob";
   string str2 = "google";
   string str3;
   int  len ;
 
   // 复制 str1 到 str3
   str3 = str1;
   cout << "str3 : " << str3 << endl;
 
   // 连接 str1 和 str2
   str3 = str1 + str2;
   cout << "str1 + str2 : " << str3 << endl;
 
   // 连接后,str3 的总长度
   len = str3.size();
   cout << "str3.size() :  " << len << endl;
 
   return 0;
}

//输出:
//str3 : runoob
//str1 + str2 : runoobgoogle
//str3.size() :  12

实训:学生管理系统

c++ 复制代码
#include <iostream>
// 用于字符串比较(如姓名/学号查找)
#include <cstring>
// 用于输入校验的数值限制
#include <limits>

// 定义常量:最大学生数量(可按需修改)
const int MAX_STUDENTS = 50;

// 定义学生结构体:封装学生的属性(C基础的结构体,C++中可直接用)
struct Student {
    // 学号(字符串,长度10足够存储学号)
    char id[10];
    // 姓名(字符串,长度20存储姓名)
    char name[20];
    // 数学成绩
    float math;
    // 语文成绩
    float chinese;
    // 英语成绩
    float english;
    // 总分(方便排序和统计)
    float total;
};

// 函数声明:将功能拆分为独立函数,便于维护和调用
// 显示主菜单
void showMenu();
// 添加学生成绩(参数:学生数组、当前学生数量的引用,用于修改数量)
void addStudent(Student students[], int &count);
// 查看所有学生成绩
void viewStudents(Student students[], int count);
// 计算统计信息(单科平均分、总分平均分)
void calculateStats(Student students[], int count);
// 查找学生(按学号或姓名)
void findStudent(Student students[], int count);
// 按总分排序(冒泡排序,简单易实现)
void sortStudents(Student students[], int count);

int main() {
    // 定义结构体数组:存储所有学生信息
    Student students[MAX_STUDENTS];
    // 当前学生数量,初始为0
    int studentCount = 0;
    // 存储用户的菜单选择
    int choice;

    // 主循环:持续显示菜单,直到用户选择退出
    while (true) {
        showMenu();
        std::cout << "请输入你的选择(1-6):";
        std::cin >> choice;

        // 输入校验:避免用户输入非数字导致程序崩溃
        if (std::cin.fail()) {
            std::cout << "输入错误!请输入1-6之间的数字。" << std::endl;
            std::cin.clear(); // 清除错误状态
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // 忽略无效输入
            continue;
        }

        // 清除输入缓冲区中的换行符,防止影响后续输入
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

        // 功能分支:根据用户选择调用对应函数
        switch (choice) {
            case 1:
                addStudent(students, studentCount);
                break;
            case 2:
                viewStudents(students, studentCount);
                break;
            case 3:
                calculateStats(students, studentCount);
                break;
            case 4:
                findStudent(students, studentCount);
                break;
            case 5:
                sortStudents(students, studentCount);
                std::cout << "排序完成!已按总分从高到低排列。" << std::endl;
                break;
            case 6:
                std::cout << "感谢使用,系统已退出!" << std::endl;
                return 0; // 退出程序
            default:
                std::cout << "无效选择!请输入1-6之间的数字。" << std::endl;
                break;
        }

        // 每次操作后暂停,方便用户查看结果(可选,提升体验)
        std::cout << "\n按回车键继续...";
        std::cin.get();
        // 清屏(Windows系统用system("cls"),Linux/Mac用system("clear"))
        // system("cls");  // 如果不需要清屏可以注释掉
    }

    return 0;
}

// 函数定义:显示主菜单
void showMenu() {
    std::cout << "============================" << std::endl;
    std::cout << "      学生成绩管理系统      " << std::endl;
    std::cout << "============================" << std::endl;
    std::cout << "1. 添加学生成绩" << std::endl;
    std::cout << "2. 查看所有学生成绩" << std::endl;
    std::cout << "3. 计算统计信息" << std::endl;
    std::cout << "4. 查找学生成绩" << std::endl;
    std::cout << "5. 按总分排序" << std::endl;
    std::cout << "6. 退出系统" << std::endl;
    std::cout << "============================" << std::endl;
}

// 函数定义:添加学生成绩
void addStudent(Student students[], int &count) {
    // 检查是否超出最大学生数
    if (count >= MAX_STUDENTS) {
        std::cout << "错误:学生数量已达上限(" << MAX_STUDENTS << "),无法添加!" << std::endl;
        return;
    }

    // 输入学生信息
    std::cout << "请输入第" << count + 1 << "个学生的信息:" << std::endl;
    
    std::cout << "学号:";
    std::cin.getline(students[count].id, 10);
    
    std::cout << "姓名:";
    std::cin.getline(students[count].name, 20);
    
    std::cout << "数学成绩:";
    while (!(std::cin >> students[count].math)) {
        std::cout << "输入错误,请重新输入数学成绩:";
        std::cin.clear();
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }
    
    std::cout << "语文成绩:";
    while (!(std::cin >> students[count].chinese)) {
        std::cout << "输入错误,请重新输入语文成绩:";
        std::cin.clear();
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }
    
    std::cout << "英语成绩:";
    while (!(std::cin >> students[count].english)) {
        std::cout << "输入错误,请重新输入英语成绩:";
        std::cin.clear();
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }

    // 计算总分
    students[count].total = students[count].math + students[count].chinese + students[count].english;

    // 清除输入缓冲区中的换行符
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

    // 学生数量加1
    count++;

    std::cout << "添加成功!当前共有" << count << "个学生。" << std::endl;
}

// 函数定义:查看所有学生成绩
void viewStudents(Student students[], int count) {
    // 检查是否有学生信息
    if (count == 0) {
        std::cout << "暂无学生信息!" << std::endl;
        return;
    }

    // 输出表头
    std::cout << "========================================================================" << std::endl;
    std::cout << "学号\t\t姓名\t\t数学\t语文\t英语\t总分" << std::endl;
    std::cout << "========================================================================" << std::endl;

    // 遍历数组,输出每个学生的信息
    for (int i = 0; i < count; i++) {
        std::cout << students[i].id << "\t\t"
                  << students[i].name << "\t\t"
                  << students[i].math << "\t"
                  << students[i].chinese << "\t"
                  << students[i].english << "\t"
                  << students[i].total << std::endl;
    }

    std::cout << "========================================================================" << std::endl;
}

// 函数定义:计算统计信息
void calculateStats(Student students[], int count) {
    if (count == 0) {
        std::cout << "暂无学生信息,无法统计!" << std::endl;
        return;
    }

    // 定义变量存储总分
    float mathTotal = 0, chineseTotal = 0, englishTotal = 0, totalTotal = 0;

    // 遍历数组,累加各科成绩和总分
    for (int i = 0; i < count; i++) {
        mathTotal += students[i].math;
        chineseTotal += students[i].chinese;
        englishTotal += students[i].english;
        totalTotal += students[i].total;
    }

    // 计算平均分
    float mathAvg = mathTotal / count;
    float chineseAvg = chineseTotal / count;
    float englishAvg = englishTotal / count;
    float totalAvg = totalTotal / count;

    // 输出统计结果
    std::cout << "============================" << std::endl;
    std::cout << "      成绩统计信息        " << std::endl;
    std::cout << "============================" << std::endl;
    std::cout << "数学平均分:" << mathAvg << std::endl;
    std::cout << "语文平均分:" << chineseAvg << std::endl;
    std::cout << "英语平均分:" << englishAvg << std::endl;
    std::cout << "总分平均分:" << totalAvg << std::endl;
    std::cout << "============================" << std::endl;
}

// 函数定义:查找学生(按学号或姓名)
void findStudent(Student students[], int count) {
    if (count == 0) {
        std::cout << "暂无学生信息,无法查找!" << std::endl;
        return;
    }

    // 存储用户输入的查找关键词
    char keyword[20];
    std::cout << "请输入要查找的学号或姓名:";
    std::cin.getline(keyword, 20);

    // 标记是否找到
    bool found = false;

    std::cout << "\n查找结果:" << std::endl;
    std::cout << "============================" << std::endl;
    std::cout << "学号\t\t姓名\t\t数学\t语文\t英语\t总分" << std::endl;
    std::cout << "============================" << std::endl;

    // 遍历数组,匹配学号或姓名
    for (int i = 0; i < count; i++) {
        // strcmp是C的字符串比较函数,返回0表示相等
        if (strcmp(students[i].id, keyword) == 0 || strcmp(students[i].name, keyword) == 0) {
            std::cout << students[i].id << "\t\t"
                      << students[i].name << "\t\t"
                      << students[i].math << "\t"
                      << students[i].chinese << "\t"
                      << students[i].english << "\t"
                      << students[i].total << std::endl;
            found = true;
        }
    }

    if (!found) {
        std::cout << "未找到匹配的学生信息!" << std::endl;
    }

    std::cout << "============================" << std::endl;
}

// 函数定义:按总分排序(冒泡排序)
void sortStudents(Student students[], int count) {
    if (count <= 1) {
        return; // 无需排序
    }

    // 冒泡排序核心:相邻元素比较,大的往后移
    for (int i = 0; i < count - 1; i++) {
        for (int j = 0; j < count - 1 - i; j++) {
            // 如果前一个学生的总分小于后一个,交换位置(降序)
            if (students[j].total < students[j + 1].total) {
                // 临时变量存储学生信息,用于交换
                Student temp = students[j];
                students[j] = students[j + 1];
                students[j + 1] = temp;
            }
        }
    }
}
相关推荐
2601_9622186121 分钟前
C++ string 类原理、踩坑与对象语义详解
开发语言·c++
李游Leo21 分钟前
HarmonyOS 7 + ArkTS + NAPI 学习笔记:Native 模块桥接、CMake 构建与跨语言调用实践【鸿蒙心迹】
笔记·学习·harmonyos
爱写代码的小朋友36 分钟前
Windows 下 MSYS2 + MinGW64 安装与使用 OpenCV 全指南(含踩坑记录)
c++·人工智能·windows·opencv
重生之小比特1 小时前
【C++进阶】封装红⿊树实现mymap和myset
开发语言·c++
边境悍匪1 小时前
蜗牛学苑 Java 智能体学习 Day49|贯穿项目 5 订单下单业务 思维导图复盘
java·开发语言·spring boot·学习·阿里云
YYYing.1 小时前
【设计模式系列 (四) 】建造者模式
c++·后端·设计模式·建造者模式·c/c++
羑悻的小杀马特1 小时前
Docker高阶实战:从Redis集群到C++微服务,全面解析镜像优化与生产环境部署+镜像制作常见问题详解
c++·redis·docker·镜像制作·dockefile
别动我齐刘海1 小时前
ROS2 Jazzy + C++ 时间系统——Time / Clock / Duration
linux·c++·人工智能·学习·机器学习·机器人·自动驾驶
小范的技术工坊1 小时前
大模型学习操作文档(13个核心概念串讲)
人工智能·学习·算法·大模型
索端阳1 小时前
I.MX6ULL 裸机开发:LCD Framebuffer 与 RGB888 学习笔记
笔记·vscode·嵌入式硬件·学习