目录
啊哈哈哈,本打工人先大跨一下,因为确实前面六章对于熟悉过C语言的同学来讲属于基础内容,作者准备从第七章先继续阅读总结,但是前六章我一定会回过头回去定时续更,确实是目前时间紧,工作日还是要打工的嘛,像读者们保证~~
1、C++函数的定义与声明
函数声明:
就是告诉编译器函数的名称、返回类型以及参数的类型和数量。它不包含函数体,即不包含实际执行的代码。函数声明通常在头文件(.h 或 .hpp 文件)中进行。
说白话,其实就是告诉编译器,小哥要写函数了,他是什么类型的,会用到一些什么样子的参数、返回值等。但是小哥有点忙,回头再给他准确定义,而编译器你只要知道我后面会写就够了,写好了你在拿去用。
函数定义:
函数定义包括函数的声明和函数体。函数体是函数执行的代码,它实现了函数声明中描述的功能。其实就是你希望上面声明的函数到底里面怎么搞,就好比你说你要做番茄鸡蛋,你在这里告诉计算机我的鸡蛋怎么炒,番茄什么时候炒,放多少调味料。也就是程序要在这里给他实现了。
2、函数指针与数组
数组是一种基本的数据结构,用于存储固定大小的相同类型的元素集合。都吃过超市里趣某多的曲奇饼干吧,里面放饼干的塑料壳子,就可以理解为数组。你可以把饼干按顺序的放入开辟好的数组里,同时,你可以可以选择按顺序吃饼干,也可以指定位置直接吃饼干。
定义数组
数组可以通过指定元素类型和数组大小来定义。以下是举例的一些简单示例,后面有时间的话,我会把数组的用法也续更在这里,但是现在是时间紧迫哈,暂时就不细更了,我只能先插眼,等我兄弟们!
int numbers[10];//定义数组
int numbers[5] = {1, 2, 3, 4, 5};//初始化数组
int firstElement = numbers[0]; // 访问第一个元素
int lastElement = numbers[4]; // 访问最后一个元素
int arraySize = sizeof(numbers) / sizeof(numbers[0]);
int matrix[3][2] = { //多维数组
{1, 2},
{3, 4},
{5, 6}};
int* dynamicArray = new int[10];//动态数组
const int constantNumbers[5] = {1, 2, 3, 4, 5};//常量数组
函数指针
谈到数组,那肯定离不开指针。还记着我当时刚入门C的时候被指针硬控的日子,直接回想起来快流泪了。不过指针还是核心的,一定要好好理解透彻。
先上定义,函数指针是一种特殊的指针,它指向一个函数。函数指针可以用来存储函数的地址,从而可以在运行时调用不同的函数。这对于实现回调函数、事件处理系统、策略模式等高级编程技术非常有用。
好了,定义上完了,我要说白话了,咱们就拿数组举例吧。在大多数情况下,C++和C语言一样,也将数组名视为指针,C++将数组名解释为其第一个元素的地址。举个例子:
#include <iostream>
int main() {
int numbers[5] = {10, 20, 30, 40, 50}; // 定义一个整型数组
int *ptr = numbers; // 定义一个整型指针并指向数组的第一个元素
// 使用指针遍历数组
for (int i = 0; i < 5; i++) {
std::cout << "Element at index " << i << " is: " << *(ptr + i) << std::endl;
}
// 也可以直接使用指针进行访问
for (int i = 0; i < 5; i++) {
std::cout << "Element at index " << i << " is: " << ptr[i] << std::endl;
}
// 使用指针进行数组元素的修改
for (int i = 0; i < 5; i++) {
*(ptr + i) = *(ptr + i) * 2; // 将数组中的每个元素乘以2
}
// 打印修改后的数组
for (int i = 0; i < 5; i++) {
std::cout << "Modified element at index " << i << " is: " << *(ptr + i) << std::endl;
}
return 0;
}
通过上述的案例,大家简单有点感觉了嘛?我记着我之前看过一本书,作者把指针和数组比喻成家与门牌号之间的关系。比如家就是数组,你的家里的房间就是里面的容量,比如home[4],这就是你的四室一家,我给里面每个房间标上门牌号0,1,2,3,这就是home[0],home[1],home[2],home[3]。
然后我觉着直接喊0,1,2,3太无聊了,我整了个Ikun的门贴,贴在哪个房间就从哪个房间开始做卫生,那我就从0号房间开始贴吧,我给起名叫int *ikun=home,这个ikun就是指针,指向房间0。如果我后面想指向2号房,我就只需要给他+2移过去就可以。
下面咱们来区分两个容易混淆的问题:
-
常量指针:这是一个可以改变指向的指针,但它指向的值是常量,不能通过这个指针修改其指向的值。换句话说,指针本身可以指向不同的地址,但是一旦指向了一个地址,就不能通过这个指针来修改指向的值。
-
指针常量:这是一个指针,它的指向是固定的,不能改变指向,但它指向的值是可以修改的。换句话说,一旦指针被初始化后,就不能指向其他地址,但是可以通过这个指针来修改它指向的值。
#include <iostream> int main() { int var = 10; const int *ptr = &var; // 常量指针,指向var std::cout << "Value of var: " << *ptr << std::endl; // 允许 // *ptr = 20; // 不允许,不能通过常量指针修改指向的值 // ptr = &var + 1; // 允许,可以改变指针的指向 return 0; } #include <iostream> int main() { int var1 = 10; int var2 = 20; int *const ptrConst = &var1; // 指针常量,指向var1 std::cout << "Value of var1: " << *ptrConst << std::endl; // 允许 *ptrConst = 30; // 允许,可以通过指针常量修改指向的值 // ptrConst = &var2; // 不允许,不能改变指针常量的指向 return 0; }
多维数组
多维数组是数组的数组,就像我们生活中的矩阵一样,可以有多个维度。在C++中,最常见的多维数组是二维数组,它类似于一个表格,有行和列。
#include <iostream>
int main() {
// 定义一个5行3列的二维数组并初始化
int table[5][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
{10, 11, 12},
{13, 14, 15}
};
// 访问并打印二维数组的元素
for (int i = 0; i < 5; i++) { // 行循环
for (int j = 0; j < 3; j++) { // 列循环
std::cout << table[i][j] << " ";
}
std::cout << std::endl; // 每打印完一行换行
}
return 0;
}
注意事项
- 多维数组的存储方式是按行优先的,即数组元素在内存中是按行依次存储的。
- 多维数组的维数过高会增加复杂性,通常不建议使用超过三维的数组。
- C++标准库提供了
std::vector
来替代数组,如果需要动态大小的多维数组,可以考虑使用嵌套std::vector
。
在C++中,结构体(struct)是一种用户自定义的数据类型,可以包含多个不同类型的成员。结构体可以通过值传递和地址传递两种方式传递给函数。
传递结构体
通过值传递:这种方式会将结构体的所有数据复制到函数内部,因此对结构体的修改不会影响原始数据。
通过地址传递:这种方式传递的是结构体的地址,函数内部通过指针来修改结构体的内容,因此对结构体的修改会影响原始数据。
#include <iostream>
// 定义一个结构体
struct Person {
std::string name;
int age;
};
//通过值传递
void modifyByValue(Person p) {
p.age = 30; // 修改结构体的age成员
std::cout << "Inside modifyByValue: " << p.name << " is now " << p.age << " years old." << std::endl;
}
int main() {
Person person = {"John", 25};
std::cout << "Before modifyByValue: " << person.name << " is " << person.age << " years old." << std::endl;
modifyByValue(person);
std::cout << "After modifyByValue: " << person.name << " is " << person.age << " years old." << std::endl;
return 0;
}
//通过地址传递
void modifyByAddress(Person *p) {
p->age = 30; // 修改结构体的age成员
std::cout << "Inside modifyByAddress: " << p->name << " is now " << p->age << " years old." << std::endl;
}
int main() {
Person person = {"John", 25};
std::cout << "Before modifyByAddress: " << person.name << " is " << person.age << " years old." << std::endl;
modifyByAddress(&person);
std::cout << "After modifyByAddress: " << person.name << " is " << person.age << " years old." << std::endl;
return 0;
}
通过地址传递结构体通常更高效,特别是对于大型结构体,因为它避免了数据的复制。然而,它也可能导致原始数据被意外修改,因此在设计函数时需要谨慎考虑。
总结
- 通过值传递:函数内部的修改不会影响原始数据,因为传递的是数据的副本。
- 通过地址传递:函数内部的修改会影响原始数据,因为传递的是数据的地址。