前言
本文讲解 C++ 的条件判断、循环及数组相关内容。
内容注意
范围 for 的原理涉及后续内容,此处只需理解其核心功能及可直接使用的场景即可。
C++条件判断、循环结构及数组操作详解。涵盖 auto 关键字、范围 for 遍历语法及其适用场景(静态数组与动态指针的区别)、内存函数 memset 与 memcpy 的使用规范、字符数组输入输出及常用字符串函数。重点解析编译器配置 C++11 标准的方法,以及不同数组类型在初始化、拷贝时的注意事项。

本文讲解 C++ 的条件判断、循环及数组相关内容。
范围 for 的原理涉及后续内容,此处只需理解其核心功能及可直接使用的场景即可。
在 C++ 的条件判断与循环中包括:if-else 语句、关系操作符、条件操作符、逻辑操作符、switch 语句、while 循环语句、for 循环语句、do-while 循环语句、break 和 continue 语句、循环嵌套。
除了 C++ for 循环比 C 多了个范围 for,其他与 C 语言完全相同,这里重点讲解范围 for 的相关知识。
范围 for 和数组相联系,放在数组讲解的板块进行讲解。
数组的创建和初始化,元素访问及元素打印与 C 一样,这里只进行 auto 关键字、范围 for 和 memset 设置数组内容、memcpy 拷贝数组内容相关知识。
功能:auto 的主要用途是让编译器自动推导出变量的类型。
#include<iostream>
using namespace std;
int main(){
auto a = 3.14;
auto b = 100;
auto c = 'x';
return 0;
}
我们在遍历数组元素时,通常用 for 循环及 while 等等,除了这些方法外,还有一个更方便的方式——使用范围 for。
我们在这个函数中只需学会直接可以用范围 for 的场景,以及了解不能直接用范围 for 的场景即可。
范围 for 是 C++11 及以上版本引入的遍历语法,核心作用是:
auto 关键字可自动推导元素类型,进一步简化代码。相当于将数组中取到的元素依次存到元素变量中进行遍历。
基础语法:
for(元素类型 元素变量 : 可迭代对象){
// 对元素变量的操作
}
常用简化写法(用 auto 自动推导元素类型):
// 只读遍历(元素变量是拷贝)
for(auto 元素变量 : 可迭代对象){...}
// 可修改元素(元素变量是引用)
for(auto& 元素变量 : 可迭代对象){...}
我们先简单了解一下它的功能,再进行分析。
#include<iostream>
using namespace std;
int main(){
int arr[10]={1,2,3,4,5,6,7,8,9,10};
for(int e : arr){
cout << e << " ";
}
return 0;
}
上面代码中的 for 就是 范围 for,代码的意思是将 arr 数组中的元素,依次放在 e 变量中,然后打印 e,直到遍历完整个数组的元素。这里的 e 是一个单独的变量,不是数组的元素,所以对 e 的修改,不会影响数组。
范围 for 能工作的核心前提是:编译器能自动获取「可迭代对象」的遍历起点(begin)和终点(end)。(这里 begin 表示数组起始位置,end 表示数组结束位置的下一个位置)
以下场景满足这个前提:
(如 int arr[5])
元素类型 [长度]」(比如 int[5]),编译器能通过数组类型直接知道元素个数,从而生成 begin(arr)(数组首地址)和 end(arr)(数组首地址 + 长度)的迭代器。示例:
int arr[5]={1,2,3,4,5};
for(auto num : arr){// 编译器知道 arr 是 int[5],自动遍历 5 个元素
cout << num << " ";
}
(如 int arr[3][4])
int[4]),外层用 auto& row(引用每行的一维数组),编译器能识别每行的长度,进而内层遍历元素。示例:
int arr[3][4]={{1,2},{3,4},{5,6}};
for(auto& row : arr){// row 是 int[4] 的引用,编译器知道每行长度
for(auto num : row){
cout << num << " ";
}
}
(如 vector、string、list)
begin() 和 end() 成员函数,编译器直接调用这两个函数获取迭代器,无需额外信息。示例:
vector<string> vec ={"a","b","c"};
for(auto& s : vec){// 调用 vec.begin()/vec.end()
s += "!";// 可修改元素
}
这里可以先不看,后面会有几篇会进行讲解,可以先把这个跳过去,不影响,学完容器后再来看也可以。
以下场景中,编译器无法自动获取遍历的起点/终点,因此范围 for 无法直接使用:
(如 int* arr = (int*)malloc(5*sizeof(int)))
int*)」,指针仅记录内存起始地址,不包含任何长度信息,编译器不知道要遍历多少个元素,无法生成 begin(arr) 和 end(arr)。错误示例(编译报错):
int* arr =(int*)malloc(5*sizeof(int));
// for (auto num : arr) { ... } // 报错:无法确定 arr 的遍历边界
(如 int** arr)
int*)」,指针同样没有长度信息,内层范围 for 无法识别要遍历多少个元素。错误示例(编译报错):
int** arr =(int**)malloc(2*sizeof(int*));
arr[0]=(int*)malloc(3*sizeof(int));
// for (auto& row : arr) { for (auto num : row) { ... } } // 报错:row 是 int*,无长度信息
注意:这里仅做了解,在我们学完包装指针后再来看。
如果想让动态数组支持范围 for,可通过包装指针 + 长度的方式,告诉编译器遍历边界(C++20+ 推荐用 std::span):
#include<span>// C++20 及以上
int* arr =(int*)malloc(5*sizeof(int));
// 用 span 包装'指针 + 长度',告诉编译器要遍历 5 个元素
for(auto num : std::span<int>(arr,5)){
cout << num << " ";
}
范围 for 的核心是'依赖编译器自动识别遍历边界'——能识别(静态数组、STL 容器)就可以直接用;不能识别(裸指针、动态指针数组)就无法直接用。
但是对于范围 for 要慎重使用!范围 for 是对数组中所有元素进行遍历的,但是我们实际在做题的过程中,可能只需要遍历数组中指定个数的元素,这样范围 for 就不合适了。
上面讲解的范围 for 是在 C++11 这个标准中引入的,如果你使用的编译器默认不支持 C++11,可能需要配置才能使用。
勾选【编译时加入一下命令】,然后在下方的编译框中加入:
-std=c++11
点击确定即可。 要想支持其他 C++ 的标准也是一样的方法。
-std=c++14
-std=c++17
-std=c++20 等
函数原型
void*memset(void* ptr,int value, size_t num );
参数解释
memset 是用来设置内存的,将内存中的值以字节为单位设置成想要的内容,需要头文件 <cstring>。
#include<cstring>
#include<iostream>
#include<cstring>
using namespace std;
int main(){
char str[]="hello world";
memset(str,'x',6);
cout << str << endl;
int arr[]={1,2,3,4,5};
memset(arr,0,sizeof(arr));//这里数组的大小也可以自己计算
for(auto i : arr){
cout << i << " ";
}
cout << endl;
return 0;
}
这里就把数组 arr 内容设置为全为 0 了。
这是关于 memset 使用的关键注意事项,核心是基于其'按字节填充内存'的特性,明确不同数组类型的使用限制,可拆解为以下两点并补充解读:
memset 以字节为单位设置内存内容,因此:int/double 数组):仅能设置为 0。因为这类数组的元素是多字节类型(如 int 占 4 字节),若设置非 0 值,每个字节会被填充为目标值,最终元素值会是多个字节拼接的结果(如设置 1 会得到 0x01010101,而非预期的 1);只有 0 的每个字节都是 0,填充后元素值符合预期。char)本身占 1 字节,按字节填充的结果与预期一致。memset 设置。这类数组的内存是连续的整块存储,memset 能一次性覆盖所有元素对应的内存区域。memset。这类数组的内存是分散的(每个行指针指向独立的内存块),直接用 memset 无法覆盖所有行的内容,因此要对每个行指针指向的内存块单独执行 memset 操作。这里用代码实现所有类型数组设置,便于大家理解。
#include<iostream>
#include<cstring>
#include<cstdlib>
using namespace std;
int main(){
// -------------------------- 1. 一维数组设为 0 --------------------------
int a1[5]={1,2,3,4,5};// a1=array 1(一维数组)
memset(a1,0,sizeof(a1));// 内存连续,直接整体填充
cout <<"1. 一维数组设为 0 结果:";
for(int num : a1) cout << num << " ";
cout <<"\n"<< endl;
// -------------------------- 2. 二维静态数组设为 0 --------------------------
int a2[3][4]={// a2=array 2(二维静态数组)
{1,2,3,4},
{5,6,7,8},
{9,10,11,12}
};
memset(a2,0,sizeof(a2));// 静态数组内存连续,直接填充总字节数
cout <<"2. 二维静态数组设为 0 结果:"<< endl;
for(auto& row : a2){
for(int num : row) cout << num << " ";
cout << endl;
}
cout << endl;
// -------------------------- 3. 二维动态指针数组设为 0 --------------------------
const int rows =3, cols =4;
int* ad[rows];// ad=array dynamic(二维动态数组)
// 为每行分配内存并逐行填充
for(int i =0; i < rows; i++){
ad[i]=(int*)malloc(cols *sizeof(int));
memset(ad[i],0, cols *sizeof(int));// 内存分散,逐行填充
}
// 验证结果
cout <<"3. 二维动态指针数组设为 0 结果:"<< endl;
for(auto rowPtr : ad){
for(int j =0; j < cols; j++) cout << rowPtr[j]<<" ";
cout << endl;
}
// 释放内存
for(int i =0; i < rows; i++)free(ad[i]);
cout << endl;
// -------------------------- 4. 字符数组设为 'w' --------------------------
char ac[10]="hello";// ac=array char(字符数组)
memset(ac,'w',sizeof(ac));// char 占 1 字节,直接填充任意字符
cout <<"4. 字符数组设为 'w' 结果:"<< ac << endl;
return 0;
}
在使用数组的时候,有时候我们需要将数组的内容给数组 b,比如:
int a[10]={1,2,3,4,5,6,7,8,9,10};
int b[10]={0};
函数原型
void*memcpy(void* destination,const void* source,size_t num );
//destination -- 目标空间的起始地址
//source -- 源数据空间的起始地址
//num -- 拷贝的数据的字节个数
memcpy 其实是用来做内存块的拷贝的,当然用来做数组内容的拷贝也是没问题的。 和 memset 注意点一样,这里就不做说明了。
#include<cstring>
这里直接展示对所有类型数组进行操作的代码,便于理解。
#include<iostream>
#include<cstring>
#include<cstdlib>
using namespace std;
int main(){
// -------------------------- 1. 一维数组拷贝 --------------------------
int s1[5]={10,20,30,40,50};// 源:s1=source 1(一维)
int d1[5];// 目标:d1=dest 1(一维)
memcpy(d1, s1,sizeof(s1));
cout <<"1. 一维数组拷贝结果:";
for(int num : d1) cout << num << " ";
cout <<"\n"<< endl;
// -------------------------- 2. 二维静态数组拷贝 --------------------------
int s2[3][4]={// 源:s2=source 2(二维静态)
{1,2,3,4},
{5,6,7,8},
{9,10,11,12}
};
int d2[3][4];// 目标:d2=dest 2(二维静态)
memcpy(d2, s2,sizeof(s2));
cout <<"2. 二维静态数组拷贝结果:"<< endl;
for(auto& row : d2){
for(int num : row) cout << num << " ";
cout << endl;
}
cout << endl;
// -------------------------- 3. 二维动态指针数组拷贝 --------------------------
const int rows =3, cols =4;
// 源:ds=dynamic source(二维动态)
int* ds[rows];
for(int i =0; i < rows; i++){
ds[i]=(int*)malloc(cols *sizeof(int));
for(int j =0; j < cols; j++){
ds[i][j]= i * cols + j +1;
}
}
// 目标:dd=dynamic dest(二维动态)
int* dd[rows];
for(int i =0; i < rows; i++){
dd[i]=(int*)malloc(cols *sizeof(int));
memcpy(dd[i], ds[i], cols *sizeof(int));
}
cout <<"3. 二维动态指针数组拷贝结果:"<< endl;
for(auto rowPtr : dd){
for(int j =0; j < cols; j++) cout << rowPtr[j]<<" ";
cout << endl;
}
// 释放内存
for(int i =0; i < rows; i++){
free(ds[i]);
free(dd[i]);
}
cout << endl;
// -------------------------- 4. 字符数组拷贝 --------------------------
char cs[]="Hello, memcpy!";// 源:cs=char source(字符源)
char cd[20];// 目标:cd=char dest(字符目标)
memcpy(cd, cs,strlen(cs)+1);
cout <<"4. 字符数组拷贝结果:"<< cd << endl;
return 0;
}
范围 for——遍历数组 memset——初始化普通数组或设置字符数组内为特定字符 memcpy——拷贝数组内容
三者对静态数组均适用,核心差异在于范围 for 侧重遍历便捷,memset/memcpy 侧重底层内存操作。
字符数组的初始化,strlen,输入输出都和 C 语言一致,我们在这再进行字符数组输入中 gets 和 fgets 两个函数,以及简单回顾一下 strcpy 和 strcat 这两个函数。
我们在读取字符串时直接用 scanf 和 cin 到空格就停止了,无法继续读取,如果需要继续读取我们怎么办呢?
函数原型
char*gets(char* str );
char*fgets(char* str,int num, FILE * stream );
注意:
使用 gets 函数的方式,这种方式能解决问题,但是因为 gets 存在安全性问题,在 C+11 中取消了 gets,给出了更加安全的方案:fgets。
实现
方案 1
#include<cstdio>//方案 1
int main(){
char arr[10]={0};
gets(arr);
printf("%s\n", arr);
return 0;
}
替代方案 - 方法 2
//替代方案 - 方法 2
#include<cstdio>
int main(){
char arr[10]={0};
fgets(arr,sizeof(arr),stdin);
printf("%s\n", arr);
return 0;
}
差异:
gets 读取直到换行符,但不保留换行符;fgets 读取直到换行符或达到指定数量,会保留换行符。
C 语言中用 scanf 也能读取带空格的字符串,只需将格式符 "%s" 改为 "%[^ ]s":其中 [^ ] 表示一直读取直到遇到 ,遇到空格不会结束读取。
这种方式不会将 读入,但会在读取的字符串末尾自动加上 \0。
实现
#include<cstdio>
int main(){
char arr[10]="xxxxxxxx";
scanf("%[^
]s", arr);
printf("%s\n", arr);
return 0;
}
其实我们上一篇已经顺便介绍了 getchar 逐个字符读取,这里直接实现。
#include<cstdio>
int main(){
char arr[10]={0};
int ch =0;
int i =0;
while((ch =getchar())!='\n'){
arr[i++]= ch;
}
printf("%s\n", arr);
return 0;
}
strcpy 这个属于字符数组的复制。 函数链接:strcpy 函数原型
// 函数声明
char*strcpy(char* destination,constchar* source );
// 注释说明
// destination - 是目标空间的地址
// source - 是源头空间的地址
// 需要的头文件 <cstring>
代码实现
#include<cstdio>
#include<cstring>
int main(){
char arr1[]="abcdef";
char arr2[20]={0};
strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
我们发现这两个函数功能相似,我们来分析一下他们的区别。 核心区别:strcpy 是「字符串专用拷贝」,memcpy 是「通用内存拷贝」(本质差异)。 两者的所有差异都源于此定位,以下是关键对比(简洁版):
| 对比维度 | strcpy | memcpy |
|---|---|---|
| 核心用途 | 仅拷贝字符串(char 数组/指针) | 拷贝任意类型数据(int、struct、字符串等) |
| 拷贝依据 | 以字符串结束符 \0 为终止信号 | 以用户指定的「字节数」为终止依据 |
| 长度指定 | 无需手动指定(自动识别 \0) | 必须手动指定拷贝的总字节数 |
| 适用数据类型 | 仅支持 char 类型(字符串) | 支持所有数据类型(通用) |
| 安全性 | 无长度检查,目标空间不足会溢出 | 无长度检查,但需手动确保字节数≤目标空间 |
| 头文件 | <cstring>(C/C++) | <cstring>(C/C++) |
关键补充(实际使用重点)
\0 为止(包括 \0 本身,保证字符串完整);\0 或目标空间太小,会越界溢出(未定义行为);strcpy(dest, "abc") → 自动拷贝 'a'/'b'/'c'/\0 共 4 字节。memcpy(dest, src, 4*sizeof(int)) → 拷贝 4 个 int(共 16 字节)。一句话总结
strcpy 是「字符串专属工具」,靠 \0 自动终止;memcpy 是「底层内存工具」,靠字节数精准控制,适用于所有数据类型。
有时候我们需要在一个字符的末尾再追加一个字符串,需要用到 strcat。 函数链接:strcat 函数原型
// 函数声明
char*strcat(char* destination,constchar* source );
// 注释说明
// destination - 是目标空间的地址
// source - 是源头空间的地址
// 需要的头文件 <cstring>
代码实现
#include<cstdio>
#include<cstring>
int main(){
char arr1[20]="hello ";
char arr2[]="world";
strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
除了上面的两个字符串相关函数外,其实 C/C++ 中还提供了一些其他的函数,有兴趣的可以拓展学习。 链接:其他函数
主要涉及 <iostream>, <cstring>, <cstdio>, <cstdlib> 等。
本篇到这里就结束啦!主要讲解了条件判断与循环及数组的相关内容,本篇中范围 for 讲的有点超纲,大家可以先学会范围 for 其中前三中的内容即可,后面学到相关知识后再来继续理解!

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online