1. 概述
所谓数组,就是一个集合,里面存放了相同类型的数据元素
特点1:数组中的每个数据元素都是相同的数据类型
特点2:数组是由连续的内存位置组成的
2. 一维数组
2.1 一维数组定义方式
一维数组定义的三种方式:
-
数据类型 数组名[ 数组长度(常量表达式) ];
-
数据类型 数组名[ 数组长度 ] = { 值1,值2 ...};
-
数据类型 数组名[ ] = { 值1,值2 ...};
示例:
int main() {
//定义方式1
//数据类型 数组名[元素个数];
int score[10]; // 这个整型数组中含有10个元素
//利用下标赋值
score[0] = 100;
score[1] = 99;
score[2] = 85;
//利用下标输出
cout << score[0] << endl;
cout << score[1] << endl;
cout << score[2] << endl;
//第二种定义方式:聚合方式赋值
//数据类型 数组名[元素个数] = {值1,值2 ,值3 ...};
//如果{}内不足10个数据,剩余数据用0补全
int score2[10] = { 100, 90,80,70,60,50,40,30,20,10 };
//逐个输出
//cout << score2[0] << endl;
//cout << score2[1] << endl;
//一个一个输出太麻烦,因此可以利用循环进行输出
for (int i = 0; i < 10; i++)
{
cout << score2[i] << endl;
}
//定义方式3
//数据类型 数组名[] = {值1,值2 ,值3 ...};
int score3[] = { 100,90,80,70,60,50,40,30,20,10 };
for (int i = 0; i < 10; i++)
{
cout << score3[i] << endl;
}
system("pause");
return 0;
}
总结1:数组名的命名规范与变量名命名规范一致,不要和变量重名
总结2:数组中下标是从0开始索引
2.2 一维数组数组名
一维数组名称的用途:
-
可以统计整个数组在内存中的长度
-
可以获取数组在内存中的首地址
示例:
int main() {
//数组名用途
//1、可以获取整个数组占用内存空间大小
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
cout << "整个数组所占内存空间为: " << sizeof(arr) << endl;
cout << "每个元素所占内存空间为: " << sizeof(arr[0]) << endl;
cout << "数组的元素个数为: " << sizeof(arr) / sizeof(arr[0]) << endl;
//2、可以通过数组名获取到数组首地址
cout << "数组首地址为: " << (int)arr << endl;
cout << "数组中第一个元素地址为: " << (int)&arr[0] << endl;
cout << "数组中第二个元素地址为: " << (int)&arr[1] << endl;
//arr = 100; 错误,数组名是常量,因此不可以赋值
system("pause");
return 0;
}
注意:数组名是常量,不可以赋值
总结1:直接打印数组名,可以查看数组所占内存的首地址
总结2:对数组名进行sizeof,可以获取整个数组占内存空间的大小
练习案例1:五只小猪称体重
案例描述:
在一个数组中记录了五只小猪的体重,如:int arr[5] = {300,350,200,400,250};
找出并打印最重的小猪体重。
#include <iostream>
using namespace std;
int main() {
int pig[5] = { 300,350,200,400,250 };
int b[5];
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5 - i; j++) {
if (pig[j] < pig[j + 1]) {
int a = 0;
a = pig[j];
pig[j] = pig[j + 1];
pig[j + 1] = a;
}
}
}
for (int i = 0; i < 5; i++)
{
cout << pig[i] << endl;
}
system("pause");
return 0;
}
练习案例2:数组元素逆置
案例描述:请声明一个5个元素的数组,并且将元素逆置.
(如原数组元素为:1,3,2,5,4;逆置后输出结果为:4,5,2,3,1);
#include <iostream>
using namespace std;
int main() {
int pig[5] = { 1,2,3,4,5 };
// 逆向排列数组
for (int i = 0; i < 5 / 2; ++i) {
int temp = pig[i];
pig[i] = pig[5 - 1 - i];
pig[5 - 1 - i] = temp;
}
for (int i = 0; i < 5; i++)
{
cout << pig[i] << endl;
}
system("pause");
return 0;
}
2.3 冒泡排序
int main() {
int arr[9] = { 4,2,8,0,5,7,1,3,9 };
for (int i = 0; i < 9 - 1; i++)
{
for (int j = 0; j < 9 - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
for (int i = 0; i < 9; i++)
{
cout << arr[i] << endl;
}
system("pause");
return 0;
}
3. 二维数组
二维数组就是在一维数组上,多加一个维度。
3.1 二维数组定义方式
二维数组定义的四种方式:
-
数据类型 数组名[ 行数 ][ 列数 ];
-
数据类型 数组名[ 行数 ][ 列数 ] = { {数据1,数据2 } ,{数据3,数据4 } };
-
数据类型 数组名[ 行数 ][ 列数 ] = { 数据1,数据2,数据3,数据4};
-
数据类型 数组名[ ][ 列数 ] = { 数据1,数据2,数据3,数据4};
建议:以上4种定义方式,利用第二种更加直观,提高代码的可读性
示例:
int main() {
//方式1
//数组类型 数组名 [行数][列数]
int arr[2][3];
// 赋值
arr[0][0] = 1;
arr[0][1] = 2;
arr[0][2] = 3;
arr[1][0] = 4;
arr[1][1] = 5;
arr[1][2] = 6;
// 打印数据
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++)
{
cout << arr[i][j] << " ";
}
cout << endl;
}
//方式2
//数据类型 数组名[行数][列数] = { {数据1,数据2 } ,{数据3,数据4 } };
int arr2[2][3] =
{
{1,2,3},
{4,5,6}
};
//方式3
//数据类型 数组名[行数][列数] = { 数据1,数据2 ,数据3,数据4 };
int arr3[2][3] = { 1,2,3,4,5,6 };
//方式4
//数据类型 数组名[][列数] = { 数据1,数据2 ,数据3,数据4 };
int arr4[][3] = { 1,2,3,4,5,6 };
system("pause");
return 0;
}
#include <iostream>
using namespace std;
int main() {
int arr2[2][3] = {
{1,2,3},
{4,5,6}
};
cout << arr2[0][0] << endl; // 输出打印 1
cout << arr2[0][1] << endl; // 输出打印 2
cout << arr2[0][2] << endl; // 输出打印 3
cout << arr2[1][2] << endl; // 输出打印 6
system("pause");
return 0;
}
总结:在定义二维数组时,如果初始化了数据,可以省略行数
3.2 二维数组数组名
-
查看二维数组所占内存空间
-
获取二维数组首地址
示例:
int main() {
//二维数组数组名
int arr[2][3] =
{
{1,2,3},
{4,5,6}
};
cout << "二维数组大小: " << sizeof(arr) << endl;
cout << "二维数组一行大小: " << sizeof(arr[0]) << endl;
cout << "二维数组元素大小: " << sizeof(arr[0][0]) << endl;
cout << "二维数组行数: " << sizeof(arr) / sizeof(arr[0]) << endl;
cout << "二维数组列数: " << sizeof(arr[0]) / sizeof(arr[0][0]) << endl;
//地址
cout << "二维数组首地址:" << arr << endl;
cout << "二维数组第一行地址:" << arr[0] << endl;
cout << "二维数组第二行地址:" << arr[1] << endl;
cout << "二维数组第一个元素地址:" << &arr[0][0] << endl;
cout << "二维数组第二个元素地址:" << &arr[0][1] << endl;
system("pause");
return 0;
}
总结1:二维数组名就是这个数组的首地址
总结2:对二维数组名进行sizeof时,可以获取整个二维数组占用的内存空间大小
3.3 二维数组应用案例
考试成绩统计:
案例描述:有三名同学(张三,李四,王五),在一次考试中的成绩分别如下表,请分别输出三名同学的总成绩
语文 | 数学 | 英语 | |
---|---|---|---|
张三 | 100 | 100 | 100 |
李四 | 90 | 50 | 100 |
王五 | 60 | 70 | 80 |
参考答案:
int main() {
int scores[3][3] =
{
{100,100,100},
{90,50,100},
{60,70,80},
};
string names[3] = { "张三","李四","王五" };
for (int i = 0; i < 3; i++)
{
int sum = 0;
for (int j = 0; j < 3; j++)
{
sum += scores[i][j];
}
cout << names[i] << "同学总成绩为: " << sum << endl;
}
system("pause");
return 0;
}
4.函数
4.1 概述
作用:将一段经常使用的代码封装起来,减少重复代码
一个较大的程序,一般分为若干个程序块,每个模块实现特定的功能。
4.2 函数的定义
函数的定义一般主要有5个步骤:
1、返回值类型
2、函数名
3、参数表列
4、函数体语句
5、return 表达式
语法:
返回值类型 函数名 (参数列表)
{
变量的声明 // 函数体内部
函数体语句 // 函数体内部
return表达式
}
4.3 函数的调用
功能:使用定义好的函数
语法:函数名(参数)
示例:
//函数定义
int add(int num1, int num2) //定义中的num1,num2称为形式参数,简称形参
{
int sum = num1 + num2;
return sum;
}
int main() {
int a = 10;
int b = 10;
//调用add函数
int sum = add(a, b);//调用时的a,b称为实际参数,简称实参
cout << "sum = " << sum << endl;
a = 100;
b = 100;
sum = add(a, b);
cout << "sum = " << sum << endl;
system("pause");
return 0;
}
总结:函数定义里小括号内称为形参,函数调用时传入的参数称为实参
4.4 值传递
-
所谓值传递,就是函数调用时实参将数值传入给形参
-
值传递时,如果形参发生,并不会影响实参
示例:
void swap(int num1, int num2)
{
cout << "交换前:" << endl;
cout << "num1 = " << num1 << endl;
cout << "num2 = " << num2 << endl;
int temp = num1;
num1 = num2;
num2 = temp;
cout << "交换后:" << endl;
cout << "num1 = " << num1 << endl;
cout << "num2 = " << num2 << endl;
//return ; 当函数声明时候,不需要返回值,可以不写return
}
int main() {
int a = 10;
int b = 20;
swap(a, b);
cout << "mian中的 a = " << a << endl;
cout << "mian中的 b = " << b << endl;
system("pause");
return 0;
}
总结: 值传递时,形参是修饰不了实参的
4.4.1 副本传递
如下swap函数是将两个变量的值对换:
#include<iostream>
using namespace std;
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
cout << "cat = " << a << endl;
cout << "dog = " << b << endl;
}
void main() {
int cat = 11;
int dog = 22;
cout << "交换前的cat值:" << cat << endl;
cout << "交换前的dog值:" << dog << endl;
swap(cat, dog);
cout << "交换后的cat值:" << cat << endl;
cout << "交换后的dog值:" << dog << endl;
}
但是运行发现,变量cat和dog在交换前后的值并未发生变化。
原因:
问题出在我们的swap
函数中。在这个函数中,传递了cat
和dog
的副本而不是它们的引用。这意味着函数内部的a
和b
只是cat
和dog
的副本,对它们的修改不会影响到cat
和dog
的值。在C++中,如果你要实现交换两个变量的值,你需要传递它们的引用或指针。
要修复你的代码,你可以将swap
函数修改如下:
void swap(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}
注意函数参数现在使用了引用(&
),这将允许函数修改传递给它的实际变量的值。然后,我们的main
函数可以保持不变。
当你调用swap(cat, dog);
时,cat
和dog
的值将在swap
函数内部交换,然后它们的交换后的值将反映在main
函数中。
以下是修复后的代码示例:
#include <iostream>
using namespace std;
void swap(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}
int main()
{
int cat = 11;
int dog = 22;
cout << "交换前的cat值:" << cat << endl;
cout << "交换前的dog值:" << dog << endl;
swap(cat, dog);
cout << "交换后的cat值:" << cat << endl;
cout << "交换后的dog值:" << dog << endl;
return 0;
}
效果:
4.5 函数的常见样式
常见的函数样式有4种
-
无参无返
-
有参无返
-
无参有返
-
有参有返
示例:
//函数常见样式
//1、 无参无返
void test01()
{
//void a = 10; //无类型不可以创建变量,原因无法分配内存
cout << "this is test01" << endl;
//test01(); 函数调用
}
//2、 有参无返
void test02(int a)
{
cout << "this is test02" << endl;
cout << "a = " << a << endl;
}
//3、无参有返
int test03()
{
cout << "this is test03 " << endl;
return 10;
}
//4、有参有返
int test04(int a, int b)
{
cout << "this is test04 " << endl;
int sum = a + b;
return sum;
}
4.6 函数的声明
作用: 告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义。
-
函数的声明可以多次,但是函数的定义只能有一次
-
当把自定义函数写在main()函数之前时,不对函数进行申明也可以
-
当把自定义函数写在main()函数之后时,需要在main函数之前对自定义的函数进行声明,否则会报错。
写在main()函数之前时:
#include <iostream>
using namespace std;
void swap(int num1, int num2)
{
cout << "交换前:" << endl;
cout << "num1 = " << num1 << endl;
cout << "num2 = " << num2 << endl;
int temp = num1;
num1 = num2;
num2 = temp;
cout << "交换后:" << endl;
cout << "num1 = " << num1 << endl;
cout << "num2 = " << num2 << endl;
//return ; 当函数声明时候,不需要返回值,可以不写return
}
int main() {
int a = 10;
int b = 20;
swap(a, b);
cout << "mian中的 a = " << a << endl;
cout << "mian中的 b = " << b << endl;
system("pause");
return 0;
}
写在main()函数之后时:
#include <iostream>
using namespace std;
int main() {
int a = 10;
int b = 20;
swap(a, b);
cout << "mian中的 a = " << a << endl;
cout << "mian中的 b = " << b << endl;
system("pause");
return 0;
}
void swap(int num1, int num2)
{
cout << "交换前:" << endl;
cout << "num1 = " << num1 << endl;
cout << "num2 = " << num2 << endl;
int temp = num1;
num1 = num2;
num2 = temp;
cout << "交换后:" << endl;
cout << "num1 = " << num1 << endl;
cout << "num2 = " << num2 << endl;
//return ; 当函数声明时候,不需要返回值,可以不写return
}
对比发现:当自定义函数写在main函数之后时,按照原来的写法,在main函数中将无法调用自定义函数。
解决方式:函数声明
示例:
//声明可以多次,定义只能一次
//声明
int max(int a, int b);
int max(int a, int b);
//定义
int max(int a, int b)
{
return a > b ? a : b;
}
int main() {
int a = 100;
int b = 200;
cout << max(a, b) << endl;
system("pause");
return 0;
}
4.7 函数的分文件编写
作用:当代码量非常大的时候, 将代码写在同一个文件里会显得代码杂乱,分文件编写会让代码结构更加清晰
函数分文件编写一般有4个步骤:
-
创建后缀名为.h的头文件
-
创建后缀名为.cpp的源文件
-
在头文件中写函数的声明
-
在源文件中写函数的定义
示例:
将代码写在同一个文件中的时候:
//声明可以多次,定义只能一次
//声明
int max(int a, int b);
int max(int a, int b);
//定义
int max(int a, int b)
{
return a > b ? a : b;
}
int main() {
int a = 100;
int b = 200;
cout << max(a, b) << endl;
system("pause");
return 0;
}
分文件编写:
//swap.h文件
#include<iostream>
using namespace std;
// 函数声明
void swap(int a, int b);
//swap.cpp文件
#include "swap.h"
// 函数的定义
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}
//main函数文件
#include "swap.h"
int main() {
int a = 100;
int b = 200;
swap(a, b);
system("pause");
return 0;
}
4.7.1 创建后缀名为.h的头文件
4.7.2 创建后缀名为.cpp的源文件
4.7.3 在头文件中写函数的声明
4.7.4 在源文件中写函数的定义
4.7.5 在任意代码中使用分写于头文件和源文件中的自定义函数
4.7.6 这样做的好处
把一种自定义函数先后分写于头文件和源文件中,这样可以将该自定义函数做成一个工具,以方便日后任意代码中能够直接应用该自定义函数。
要注意的就是,在程序中引用该函数时,需要先引入其头文件。
4.8 重载函数
C++中的函数重载是一种允许你定义多个具有相同名称但不同参数列表的函数的机制。这允许你使用相同的函数名来执行不同的操作,具体操作取决于函数调用时提供的参数。函数重载有助于编写更具表现力和灵活性的代码。
以下是关于C++函数重载的重要概念和规则:
-
函数名相同,参数列表不同:函数重载要求函数名称相同,但参数列表必须不同,要么通过参数的数量不同,要么通过参数的类型不同来区分。返回值类型通常不用于函数重载的区分。
-
重载解析:当你调用一个重载函数时,C++编译器将根据提供的参数来确定要调用哪个函数。这个过程称为重载解析。编译器会尽力匹配与提供的参数最匹配的函数。
-
举例:下面是一个简单的示例,展示了函数重载的用法:
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
在这里,我们定义了两个名为add
的函数,一个接受两个整数参数,另一个接受两个双精度浮点数参数。根据传递的参数类型,编译器会选择适当的函数进行调用。
4.函数重载与默认参数:函数重载可以与默认参数一起使用。如果函数重载中的某些版本具有默认参数,那么调用时可以根据需要省略这些参数。
5.函数模板:C++还引入了函数模板,它允许你编写一种通用形式的函数,可以用于多种不同的数据类型。函数模板是函数重载的扩展,允许你编写具有参数类型的参数化模板。
函数重载提供了一种强大的方式来编写清晰、一致和易于理解的代码,因为你可以使用相同的名称来描述不同版本的函数功能,而无需为每个功能选择不同的名称。这有助于提高代码的可读性和可维护性。
以下是一个完整的C++代码示例,演示了如何使用函数重载来实现不同类型数据的加法操作:
#include <iostream>
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
std::string add(const std::string& a, const std::string& b) {
return a + b;
}
int main() {
int intResult = add(5, 3);
double doubleResult = add(2.5, 3.7);
std::string stringResult = add("Hello, ", "world!");
std::cout << "Integer addition result: " << intResult << std::endl;
std::cout << "Double addition result: " << doubleResult << std::endl;
std::cout << "String concatenation result: " << stringResult << std::endl;
return 0;
}
在这个示例中,我们定义了三个不同版本的add
函数,每个版本接受不同类型的参数。一个版本用于整数相加,一个版本用于双精度浮点数相加,还有一个版本用于字符串连接。在main
函数中,我们演示了如何调用这些不同版本的add
函数来执行不同类型的加法操作,分别得到整数相加、双精度浮点数相加和字符串连接的结果。
4.9 变量的作用域
变量的声明位置以及存储方式都影响着函数对变量的调用。
变量的作用域(scope)指的是变量在程序中可见和可访问的部分。作用域规定了变量的生存期和可访问性,以及变量在不同部分的程序中如何引用。
在C++和许多其他编程语言中,有以下主要的作用域类型:
局部作用域(Local Scope):局部作用域是在一个代码块内部定义的变量的作用域。这些变量只在该代码块内部可见和可访问。一旦代码块执行完毕,局部变量通常会被销毁。例如,函数内部定义的变量就具有局部作用域。
void myFunction() {
int x = 10; // x 具有局部作用域
// 只能在 myFunction 函数内访问 x
}
全局作用域(Global Scope):全局作用域中定义的变量在整个程序中都可见和可访问。这些变量通常在程序的最顶层定义,超出任何函数或代码块之外。
int globalVar = 20; // globalVar 具有全局作用域
// 可以在整个程序中的任何地方访问 globalVar
命名空间作用域(Namespace Scope):C++支持命名空间,其中变量可以具有命名空间作用域。这些变量可以在特定命名空间中访问,避免命名冲突。
namespace MyNamespace {
int x = 30; // x 具有命名空间作用域
// 可以在 MyNamespace 中访问 x
}
类作用域(Class Scope):在类定义中声明的成员变量具有类作用域,它们可以在类的方法中访问。
class MyClass {
public:
int memberVar = 40; // memberVar 具有类作用域
// 可以在 MyClass 的方法中访问 memberVar
};
4.10 变量的生存周期
变量的存储方式不同,变量的生存期也会不同,生存期表示了变量存在的时间。
C++定义4中变量存储类别,分别是:
auto变量static变量
register变量
extern变量