C++ 基础二

四、流程控制语句

顺序结构、选择结构、循环结构

4.1 选择结构

4.1.1 if语句

用于执行基于条件的代码块。

if语句的三种形式
单行格式if语句
多行格式if语句
多条件的if语句

(1).单行格式if语句:if(条件){ 条件满足执行的语句 }
if条件表达式后不要加分号
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

if (sum==100)
	{
	  cout<<"sum=="<<100<<endl;
	}

( 2 ) .多行格式if语句:

`if(条件){ 条件满足执行的语句 }
else{ 条件不满足执行的语句 };`

( 3 ).多行格式if语句:

`if(条件){ 条件满足执行的语句 }
else{ 条件不满足执行的语句 };`

4.1.2 三目运算符

语法:表达式1 ? 表达式2 :表达式3

	int a = 10;
	int b = 20;

	int c = a > b ? a : b;
	cout << "c = " << c << endl;

4.1.3 switch语句

switch - case

每个case标签之后不会自动执行到下一个case标签,除非使用break语句来显式终止switch块。

switch(表达式)

{

	case 结果1:执行语句;break;

	case 结果2:执行语句;break;

	...

	default:执行语句;break;

}

举例代码:

#include <iostream>

int main() {
    int choice = 2;

    switch (choice) {
        case 1:
            std::cout << "选项1" << std::endl;
        case 2:
            std::cout << "选项2" << std::endl;
        case 3:
            std::cout << "选项3" << std::endl;
        default:
            std::cout << "默认选项" << std::endl;
    }

    return 0;
}

这是因为没有break语句来终止case标签,导致控制流“直通”到后续的case标签。如果您想要避免这种行为,应在每个case块的末尾使用break语句来显式退出switch块。

注意事项

  1. switch语句中的表达式类型通常只能是整型或字符型。这是因为case标签中的常量值需要与表达式的类型匹配。

  2. 如果在case标签中没有使用break语句,程序会继续执行下一个case标签,这就是所谓的“直通”行为。

  3. switch语句在处理多个条件判断时具有结构清晰和执行效率高的优点。它适用于在一系列离散值中选择执行不同的代码块。

  4. 一个switch语句的缺点是它不能直接判断区间,只能针对离散的值进行判断。要处理区间,通常需要使用一系列if语句或其他逻辑结构。

这些特点使switch成为一种适用于某些情况下的有效控制结构,但在其他情况下可能需要使用不同的条件控制方式。

4.1.4 if和switch的区别【CHAT】

`if`更灵活,适用于各种条件逻辑,
`switch`更适用于多个等值条件的情况下,代码更具可读性。
  1. 用法

    • if语句适用于任何条件判断,可以处理各种复杂的条件表达式,包括比较、逻辑运算等。它可以用来实现灵活的条件控制。
    • switch语句通常用于根据一个表达式的不同值来选择执行不同的代码块。它适用于多个等值条件的情况。
  2. 条件

    • if语句可以处理各种条件,包括布尔表达式、比较表达式、逻辑表达式等,可以实现更灵活的条件逻辑。
    • switch语句通常用于处理整数或字符类型的表达式,用于离散的值判断,而不适用于范围或其他类型的条件。
  3. 多条件

    • if语句可以轻松处理多个条件,通过嵌套if语句或使用else if来实现多个条件分支。
    • switch语句通过多个case标签来处理多个等值条件,通常更适用于处理相对简单的多条件情况。
  4. 可读性

    • switch语句通常在处理多个等值条件时更具可读性,因为所有条件都集中在一起。
    • if语句用于处理复杂的条件逻辑时可能需要更多的嵌套,可读性可能较差。
  5. 执行流程

    • if语句是根据条件的真假来决定是否执行某个代码块。
    • switch语句是基于表达式的值来选择执行哪个case块,然后执行该块内的代码,之后需要使用break语句来终止switch块,否则会"直通"到下一个case

综上所述,选择使用if还是switch取决于您的需求。

4.2 循环结构

4.2.1 while循环语句

语法: while(循环条件){ 循环语句 }
在这里插入图片描述

int num = 0;
	while (num < 10)
	{
		cout << "num = " << num << endl;
		num++;
	}

执行循环语句时确实必须提供跳出循环的出口,否则可能会陷入死循环,导致程序永远不会停止执行。以下是一些常见的方法来实现循环的退出

4.2.2 do…while循环语句

语法: do{ 循环语句 } while(循环条件);
与while的区别在于do…while会先执行一次循环语句,再判断循环条件
在这里插入图片描述

4.2.3 for循环语句

语法: for(起始表达式;条件表达式;末尾循环体) { 循环语句; }
for循环中的表达式,要用分号进行分隔

九九乘法表

在这里插入图片描述
在这里插入图片描述

4.3 跳转语句

4.3.1 break语句

  1. switch语句中:break语句用于终止当前的case标签块,并跳出switch语句。这可以防止"直通"到下一个case标签。

  2. 在循环语句中(如forwhiledo-while):break语句用于提前跳出当前循环,即使循环条件仍然满足。它用于终止循环的执行,使程序流程进入循环后的下一个语句。

  3. 在嵌套循环中:如果存在多个嵌套循环,break语句通常跳出最近的内层循环,而不是整个外层循环。这允许您有选择性地退出嵌套循环中的一个循环,而不必退出所有嵌套层次。

break语句是控制流的重要工具,用于在满足特定条件时改变程序的执行路径。要注意,滥用break可能会导致代码难以理解和维护,因此应该慎重使用。

4.3.2 continue语句

continue并没有使整个循环终止,而break会跳出循环

4.3.3 goto语句:- goto关键字。

语法: goto 标记;

`goto`语句是一种在编程中用来无条件跳转到程序中的标记(label)处的控制语句。
goto 标记;
  • 标记是在程序中的一个标签,通常是一个带有冒号的标识符,例如 label:

使用goto语句可以使程序跳转到指定标记的位置,继续执行代码。这可以在某些情况下用于实现特定的控制流程,但要小心使用,因为滥用goto可能导致程序难以理解和维护,产生不可预测的行为。

大多数现代编程语言鼓励避免使用goto,并提供更结构化的控制结构,如条件语句和循环,以更清晰和可维护的方式实现控制流程。在实际编程中,通常不需要使用goto,并且可以通过其他方法来实现相同的目标。

五、数组

同样的数组在C++11后也是可以省略掉“=”号的double num[3]{1.2e4,2e1,1.2,3.3};
类型、数组名、数量
只有在定义数组时才能使用初始化,此后就不能使用了,也不能将一个数组赋给另一个数组

//可以:
int a[2]={1,2};
int b[2];
//不可以
b[2]={2.1}
b=a;
//其中的97虽然看起来是整数,但是其是应该转换的字符
char arr[4]{'h',97,'\n','1}

(1)数据类型相同: 数组中的每个数据元素都必须是相同的数据类型。这意味着如果您创建一个整数数组,每个元素都必须是整数;如果是字符数组,每个元素都必须是字符,以此类推。

(2)连续内存跑【要确保只使用有效的下标值】: 数组的元素在内存中是连续存储的,这意味着数组中的元素在内存中相邻,没有额外的空间分隔它们。这也是数组的一个重要特点,因为它允许通过索引来快速访问元素,索引值可用于计算元素的内存地址。

5.1 一维数组

一维数组定义的三种方式:

  1. 数据类型 数组名[ 数组长度 ];
  2. 数据类型 数组名[ 数组长度 ] = { 值1,值2 ...};
  3. 数据类型 数组名[ ] = { 值1,值2 ...};

(1). 可以统计整个数组在内存中的长度sizeof(arr) / sizeof(arr[0])
(2)可以获取数组在内存中的首地址(int)arr
在这里插入图片描述

CLion中有错误:cast from ‘int*’ to ‘int’ loses precision [-fpermissive]问题解决

该这里是因为基于Linux内核的64位系统上指针类型占用8个字节,而int类型占用4个字节,所以会出现loses precision。

可以先将int* 转成long类型,long类型可以隐式类型转换到int类型。直接修改为long long即可

cout << "数组首地址为:" << (long long)arr << endl;  
cout << "数组第一个元素地址为:" << (long long)&arr[0] << endl;
cout << "数组第二个地址为:" << (long long)&arr[1] << endl;

冒泡排序

int main() {

	int arr[9] = { 1,7,5,3,4,8,6,2,3};

	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;
}

C语言内置的qsort

#include <stdio.h>
#include <stdlib.h>

// 比较函数,用于告诉qsort如何比较元素
int compare(const void *a, const void *b) {
    return (*(int *)a - *(int *)b);
}

int main() {
    int arr[] = {5, 2, 9, 1, 5, 6};
    int length = sizeof(arr) / sizeof(arr[0]);

    // 使用qsort对整数数组进行升序排序
    qsort(arr, length, sizeof(int), compare);

    // 打印排序后的数组
    printf("排序后的数组:");
    for (int i = 0; i < length; ++i) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    return 0;
}

5.2 二维数组

二维数组定义的四种方式:

  1. 数据类型 数组名[ 行数 ][ 列数 ];
  2. 数据类型 数组名[ 行数 ][ 列数 ] = { {数据1,数据2 } ,{数据3,数据4 } };
  3. 数据类型 数组名[ 行数 ][ 列数 ] = { 数据1,数据2,数据3,数据4};
  4. 数据类型 数组名[ ][ 列数 ] = { 数据1,数据2,数据3,数据4};

5.3 数组的替代品(1)vector

普通数组的大小在编译时确定且不可变,而vector的大小在运行时动态变化。此外,vector提供了许多便利的方法来管理元素,如动态添加、删除和访问元素。

#include <iostream>
#include <vector>

using namespace std;

int main() {
    vector<int> numbers = {1, 2, 3, 4, 5};

    for (int number : numbers) {
        cout << number << " ";
    }

    return 0;
}

在这里插入图片描述

举例子说明区别,最好对比

#include <iostream>
#include <vector>

using namespace std;

int main() {
    // 普通数组
    int arr[3] = {1, 2, 3};
    arr[2] = 4;  // 可以修改元素的值
    // arr = {1, 2, 4};  // 无法直接改变数组的大小
    cout<<arr<<endl;

    // vector
    vector<int> numbers = {1, 2, 3};
    numbers.push_back(4);  // 可以动态添加元素
    // numbers = {1, 2, 3, 4};  // 大小可以动态改变
//    cout<<numbers<<endl;

    return 0;
}

在C++中,cout 不能直接输出整个 vector 对象,因为 vector 是一个复杂的数据结构,而 cout 无法直接处理它。要输出 vector 中的元素,您需要使用循环或迭代器来逐个输出元素。

#include <iostream>
#include <vector>

using namespace std;

int main() {
    // 普通数组
    int arr[3] = {1, 2, 3};
    arr[2] = 4;  // 可以修改元素的值
    cout << "Array: ";
    for (int i : arr) {
        cout << i << " ";
    }
    cout << endl;

    // vector
    vector<int> numbers = {1, 2, 3};
    numbers.push_back(4);  // 可以动态添加元素
    cout << "Vector: ";
    for (int number : numbers) {
        cout << number << " ";
    }
    cout << endl;

    return 0;
}

在这里插入图片描述

5.3 数组的替代品(2)array对象

#include <iostream>
#include <array>

using namespace std;

int main() {
    // 使用模板类 array
    array<int, 3> arr = {1, 2, 3};
    arr[2] = 4;  // 可以修改元素的值
    cout << "Array: ";
    for (int i : arr) {
        cout << i << " ";
    }
    cout << endl;

    return 0;
}

5.4 比较数组、vector对象和array对象

// 在 C++ 中,数组、vector 对象和 array 对象都可以用来存储一系列的元素。

// 1. 数组(Array)是一种静态数据结构,其大小在编译时确定,不可改变。
int arr[5]; // 声明一个包含 5 个整数的数组

// 2. vector 对象是 C++ 标准库中的动态数组,其大小可以动态增长或缩小。
#include <vector>
std::vector<int> vec; // 声明一个整数类型的 vector 对象

// 3. array 对象是 C++ 标准库中的固定大小数组,其大小在编译时确定,但提供了更多的操作函数。
#include <array>
std::array<int, 5> arr; // 声明一个包含 5 个整数的 array 对象
#include <iostream>
#include <vector>
#include <array>

int main() {
    // 操作数组
    int arr[5] = {1, 2, 3, 4, 5};
    std::cout << "Array elements: ";
    for (int i : arr) {
        std::cout << i << " ";
    }
    std::cout << std::endl;

    // 操作 vector 对象
    std::vector<int> vec = {1, 2, 3, 4, 5};
    std::cout << "Vector elements: ";
    for (int i : vec) {
        std::cout << i << " ";
    }
    std::cout << std::endl;

    // 操作 array 对象
    std::array<int, 5> arrObj = {1, 2, 3, 4, 5};
    std::cout << "Array elements using range-based for loop: ";
    for (int num : arrObj) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

六、字符串

风格

/*【1】c语言的*/
//(1)每个单引号至少一个字符,空字符停止
char arr[4] = {'a','s','c','\0'};
    cout<<arr;
 //   (2)字符串常量或者字符串字面量用双引号扩起的
 char brr[]="hello world";
 char crr[3]="aa";//因为需要结束


在这里插入图片描述
拼接
在这里插入图片描述

注意,拼接时不会在被连接的字符串之间添加空格,第二个字符串的第一个字符将紧跟在第一个字符串的最后一个字符(不考虑\0)后面。第一个字符串中的\0字符将被第二个字符串的第一个字符取代。

每次读一行getline()

在C++中,getline()函数是std::istream类的一个成员函数,用于从输入流中读取一行文本。这个函数可以读取换行符之前的所有字符,并将其存储在给定的字符数组中。

getline()函数有两个参数:

第一个参数是一个指向字符数组的指针,这个数组用于存储从输入流中读取的文本。
第二个参数是最大的读取字符数。如果这个数字为20,那么函数最多会读取19个字符,因为最后一个位置需要存储空字符(‘\0’)。

#include <iostream>  
#include <string>  
  
int main() {  
    char buffer[20];  
    std::cin.getline(buffer, 20);  
    std::cout << "You entered: " << buffer << '\n';  
    return 0;  
}

string类

char arr[]={"12"}
char arr[]{"12"}
string vrr={"dws"}
string vrr{"dws"}

使用string类时,某些操作比使用数组时更简单。例如,不能将一个数组赋给另一个数组,但可以将一个string对象赋给另一个string对象:

七、函数

将一段经常使用的代码封装起来,减少重复代码

(cpp中的模块)

返回值类型 函数名 (参数列表)
{

       函数体语句

       return表达式

}
int add(int num1, int num2)
{
    //函数体语句
	int sum = num1 + num2;
    //return表达式
	return sum;
}

调用 函数名(参数)

int add(int num1, int num2) //定义中的num1,num2称为形式参数,简称形参(形参列表)
{
    int sum = num1 + num2;
    return sum;
}

int add(int num1, int num2, int num3) {
    int sum = num1 + num2 + num3;
    return sum;
}

int main() {

    int a = 10;
    int b = 10;
    //调用add函数
    int sum = add(a, b);//调用时的a,b称为实际参数,简称实参
    cout << "sum = " << sum << endl;

    int sum1 = add(a, b, 10);
    cout << "sum1 = " << sum1 << endl;

    a = 100;
    b = 100;

    sum = add(a, b);
    cout << "sum = " << sum << 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
}

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;
    cout << "&num1="<< &num1<<endl;
    cout << "&num2="<< &num1<<endl;


    int temp = num1;
    num1 = num2;
    num2 = temp;
    cout << "&temp="<<&temp<<endl;

    cout << "交换后:" << endl;
    cout << "num1 = " << num1 << endl;
    cout << "num2 = " << num2 << endl;
    cout << "&num1="<< &num1<<endl;
    cout << "&num2="<< &num1<<endl;

    //return ; 当函数声明时候,不需要返回值,可以不写return
}

int main() {

    int a = 10;
    int b = 20;

    swap(a, b);

    cout << "mian中的 a = " << a << endl;
    cout << "mian中的 b = " << b << endl;
    cout << "&a="<< &a<<endl;
    cout << "&b="<< &b<<endl;
    system("pause");

    return 0;
}

在这里插入图片描述

八、结构体


#include <iostream>

using namespace std;
struct student{
    string name;
    int age;
    double  score;
    //...
};
void printStudent(student stu){
    cout << "name: " << stu.name << endl;
    cout << "age: " << stu.age << endl;
    cout << "score: " << stu.score << endl<<endl;
}
int main() {
    student stu;
    stu.name = "Y";
    stu.age = 20;
    stu.score = 99.5;

    student stu2{"Jerry", 19, 99.5};
    printStudent(stu);
    printStudent(stu2);
    return 0;
}

在这里插入图片描述
结构是用户定义的类型,而结构声明定义了这种类型的数据属性。定义了类型后,便可以创建这种类型的变量。因此创建结构包括两步。首先,定义结构描述–它描述并标记了能够存储在结构中的各种数据类型。然后按描述创建结构变量(结构数据对象)。

8.2 结构体中的带上一个

#include <iostream>
using namespace std;

struct teacher{
    string name="T";
    int age=12;
};

struct student{
    string name;
    int age;
    double  score;
    teacher t;
};

void printStudent(student stu){
    cout << "name: " << stu.name << endl;
    cout << "age: " << stu.age << endl;
    cout << "score: " << stu.score << endl<<endl;
    cout<<"teacher's name: "<<stu.t.name<<endl;
    cout<<"teacher's age: "<<stu.t.age<<endl;
    cout<<endl;
}

int main() {
    student stu;
    stu.name = "Y";
    stu.age = 20;
    stu.score = 90;

    student stu2{"LZ", 19, 99.5, {"Tom", 30}};
    printStudent(stu);
    printStudent(stu2);
    return 0;
}

在这里插入图片描述

九、共用体

union 的大小取决于其成员中占用空间最大的那个成员的大小。这是因为 union 中所有成员共享同一块内存,因此为了能够容纳最大的成员,整个 union 的大小会被设置为最大成员的大小。

//
// Created by 86156 on 2023/12/26.
//
#include <iostream>

using namespace std;
union test01 {
    int int_testa;
    long long_testa;
    char char_testa;
    double double_testa;
    float float_testa;
    bool bool_testa;

};

int main() {
    union test01 test01;
    test01.int_testa = 10;
    cout << test01.int_testa <<" "<< typeid(test01.int_testa).name() <<" "<<&test01.int_testa<<endl;
    cout << test01.long_testa <<" "<< typeid(test01.int_testa).name() <<" "<<&test01.int_testa<<endl;
    cout << test01.char_testa <<" "<< typeid(test01.int_testa).name() <<" "<<&test01.int_testa<<endl;
    cout << test01.double_testa <<" "<< typeid(test01.int_testa).name() <<" "<<&test01.int_testa<<endl;
    cout << test01.float_testa <<" "<< typeid(test01.int_testa).name() <<" "<<&test01.int_testa<<endl;
    cout << test01.bool_testa <<" "<< typeid(test01.int_testa).name() <<" "<<&test01.int_testa<<endl;
}

在这里插入图片描述
sizeof(test01.int_testa) 返回的是 int_testa 的大小,而 sizeof(test01) 返回的是整个 union 的大小。在这个例子中,int_testa 的大小是 4 字节,而整个 union 的大小是 8 字节,因为它会被对齐到最大的成员的大小。
在这里插入图片描述
union 的大小取决于其成员中占用空间最大的那个成员的大小。这是因为 union 中所有成员共享同一块内存,因此为了能够容纳最大的成员,整个 union 的大小会被设置为最大成员的大小。
在这里插入图片描述

十、引用

作用:起别名

C++ 中的引用本质上是一个别名,
它允许我们通过一个变量名访问另一个变量的内容。
引用在声明时必须初始化,
并且一旦初始化后就不能再指向其他变量。
引用通常用于函数参数传递和返回值,
以及在某些情况下作为变量的别名使用_**

写法:数据类型 &别名 = 原名

int main() {
    int a=10;
    int &b=a;
    int* c=&a;
    int* d=&b;
    cout<<a<<" "<<&a<<" "<<*(&a)<<endl;
    cout<<b<<" "<<&b<<" "<<*(&b)<<endl;
    cout<<c<<" "<<&c<<" "<<*c<<endl;
    cout<<d<<" "<<&d<<" "<<*d<<endl<<endl;

    a=200;
    cout<<a<<" "<<&a<<" "<<*(&a)<<endl;
    cout<<b<<" "<<&b<<" "<<*(&b)<<endl;
    cout<<c<<" "<<&c<<" "<<*c<<endl;
    cout<<d<<" "<<&d<<" "<<*d<<endl;
}

第一段cout输出了变量a的值、地址和解引用值,以及引用b的值、地址和解引用值,指针c的值、地址和解引用值,以及指针d的值、地址和解引用值。

第二段cout输出了变量a的新值、地址和解引用值,引用b的值、地址和解引用值,指针c的值、地址和解引用值,以及指针d的值、地址和解引用值。

在这里插入图片描述

10.1 注意事项

1. 引用必须初始化,不能像指针一样,先声明后赋值。
2. 引用一旦初始化后,就不能再指向其他变量。
3. 引用必须与被引用的变量具有相同的类型。
#include <iostream>

using namespace std;
int main(){
    int a=12;
    int &b=a;
    int c=213;

//   将变量b赋值为c实际上是一个赋值操作,而不是更改引用。
//因为引用不允许更改,所以这行代码实际上将a的值改为了c的值,
//而不是改变了引用b指向的变量。

    b=c;
    cout<<a<<endl;
    cout<<b<<endl;
    cout<<c<<endl;
    return 0;
}

10.1.1 为什么不可改变

个整数变量a,并且创建了一个引用ref指向a。在main函数中,a被赋值为20,然后输出a和ref的值。接着调用了func函数,该函数接受一个整数的引用,并将其值设为100。

#include <iostream>

using namespace std;
//转换为 int* const ref =&a;
void func(int& ref){
    ref=100; //转换为 *ref=100;
}
int main(){

    int a=10;
    //指针常量是指向不可以更改(值可以更改是什么),说明了为什么引用不课更改哦
    int &ref=a;
    ref=20; // *ref=20;

    cout<<a<<endl;
    cout<<ref<<endl;
    func(ref);
    return 0;
}

十一、引用做函数参数

11.1. 值传递

#include <iostream>
using namespace std;

void swap(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
}

int main() {
    int x = 10;
    int y = 20;
    swap(x, y);
    cout << "x = " << x << ", y = " << y << endl;
    return 0;
}   

11.2地址传递

#include <iostream>
using namespace std;

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 10;
    int y = 20;
    swap(&x, &y);
    cout << "x = " << x << ", y = " << y << endl;
    return 0;
}

11.3 引用传递

#include <iostream>
using namespace std;

void swap(int &a, int &b) {
    int temp = a;
    a = b;
    b = temp;
}

int main() {
    int x = 5;
    int y = 10;
    cout << "Before swap: x = " << x << ", y = " << y << endl;
    swap(x, y);
    cout << "After swap: x = " << x << ", y = " << y << endl;
    return 0;
}

引用传递和地址传递都可以用来在函数间传递参数,但它们有一些重要的区别:

  1. 关系和区别:
  • 引用传递:使用引用作为参数,可以直接操作原始变量,不需要通过指针来访问。在函数内部对引用的修改会影响原始变量。
  • 地址传递:使用指针作为参数,需要通过解引用操作来访问原始变量,需要注意指针的空值和指针算术运算。
  1. 用法上需要注意的:
  • 引用传递:需要注意避免悬空引用(指向已释放的内存)和引用的生命周期问题。
  • 地址传递:需要注意指针的空值和指针算术运算,以及指针的生命周期管理。

在使用时,引用传递更直观和安全,而地 址传递更灵活,可以实现更复杂的功能,但需要更多的注意和小心。

11.4 引用函数做返回值

//1.不要返回局部变量的引用。
//2.函数的调用作为左值

在函数test02中,使用static关键字声明了一个静态变量a,并返回了它的引用。这意味着可以将函数test02的返回值作为左值使用,可以对其进行赋值操作,就像对变量一样。在main函数中,演示了如何使用函数返回的引用进行赋值操作,并且对其进行加法运算。需要注意的是,静态变量的生命周期延长到程序结束,因此在整个程序执行过程中,test02函数返回的引用都指向同一个静态变量。

#include <iostream>
//引用函数的返回值:

using namespace std;
//1.不要返回局部变量的引用。
//int& test01(){
//    int a=10;
//    return a;
//}

//2.函数的调用作为左值
int &test02() {
    static int a = 1022;//static 静态变量不在栈上,再全局去,会再程序结束后释放
    return a;
}

int main() {
//    int &a=test01();
    int &a2 = test02();
    cout << a2 << endl;
    cout << test02() << endl;
//函数的返回追是引用,这函数的调用苦于作为左值

/*    函数 test02() 返回的是一个引用,因此可以将函数调用作为左值使用。
    这意味着你可以对函数返回的引用进行赋值操作,就像对变量一样。
    在这段代码中,test02() 函数返回的引用可以被赋值给其他变量,也可以直接被赋值。*/
    test02() = 990;
    cout << test02() << endl;
    cout << a2 + 9 << endl << endl;


    int c = test02();
    cout << test02() << endl;
    cout << c << endl;
    test02() = 1;
    cout << test02() << endl;
    cout << c << endl;
    return 0;

11.5 引用的本质

C++中的引用在内部实际上被实现为指针变量。

//转换为 int* const ref =&a;
void func(int& ref){
    ref=100; //转换为 *ref=100;
}
int main(){
    
    int a=10;
    //指针常量是指向不可以更改(值可以更改是什么),说明了为什么引用不课更改哦
    int &ref=a;
    ref=20;
    
    cout<<a<<endl;
    cout<<ref<<endl;
    func(ref);
    return 0;
}

看05.cpp的介绍

11.6常量引用

常量引用在C++中的存在有几个目的:

  1. 防止意外修改:常量引用可以用来传递参数,同时防止被调用函数意外修改传入的数值。
    防止形参改变实参
    2节约内存:使用常量引用可以避免在函数调用时产生参数的副本,从而节约内存和提高效率。
  2. 与C语言兼容:C++中的常量引用也可以用于与C语言的接口交互,以便在C++中使用C语言的函数和数据结构。
#include <iostream>
using namespace std;
// 通过常量引用传递参数,防止意外修改
// 通过引用修改传入的值的函数
void displayValue1(int &value) {
    value = 10; // 将传入的值修改为10
    cout << "Value: " << value << endl << endl; // 输出修改后的值
}

// 通过常量引用显示传入的值的函数
void displayValue2(const int &value) {
    // value = 10; // 这行被注释掉,因为不允许修改常量引用
    cout << "Value: " << value << endl; // 输出传入的常量引用的值
}

int main() {
    const int& ref = 10; // 创建一个指向值为10的常量引用 ref

    int num = 5; // 用值5初始化变量 num
    cout << "Value: " << num << endl; // 输出 num 的初始值
    displayValue1(num); // 调用函数修改 num 的值

    displayValue2(num); // 调用函数显示 num 的值
    cout << "Value: " << num << endl; // 输出 num 的最终值
    return 0;
}

示了如何使用常量引用来防止意外修改参数。在函数displayValue1中,参数value被声明为非常量引用,试图修改它会导致编译错误。而在函数displayValue2中,参数value被声明为常量引用,因此试图修改它也会导致编译错误。在main函数中,演示了如何调用这两个函数,并展示了参数num的值不会被修改。

十二、函数

12.1简单说明

函数用于封装可重复使用的代码块,
提高代码的模块化和可维护性。
函数还可以帮助组织代码,使其更易于理解和调试。

自定义函数示例(C++):

#include <iostream>
using namespace std;

// 自定义函数
int add(int a, int b) {
    return a + b;
}

int main() {
    int result = add(3, 4);
    cout << "The result is: " << result << endl;
    return 0;
}

内置函数示例(C++):

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

int main() {
    double num = 16.0;
    double squareRoot = sqrt(num); // 使用内置函数计算平方根
    cout << "The square root of " << num << " is: " << squareRoot << endl;
    return 0;
}

12.2创造函数需要注意哪些方面

定义(函数名、参数、返回值)、实现原型(函数体、可见性、注释)、调用。

函数名:

  • 函数名应该能够清晰地表达函数的功能。
  • 函数名应该遵循命名规范,使用驼峰命名方式,以提高代码的可读性和可维护性。

参数:

  • 参数应该尽可能地精简,只包含必要的参数,避免传递不必要的参数。
  • 参数应该按照重要性排序,重要的参数放在前面,不重要的参数放在后面。
  • 参数应该提供默认值,避免在函数调用时传递不必要的参数。

返回值:

  • 返回值应该尽可能地精简,只包含必要的返回值,避免返回不必要的值。
  • 返回值应该按照重要性排序,重要的返回值放在前面,不重要的返回值放在后面。
  • 返回值应该提供默认值,避免在函数调用时传递不必要的返回值。

实现原型:

  • 实现原型应该与定义相匹配,包括函数名、参数、返回值。
  • 实现原型应该提供详细的注释,包括函数的功能、参数的类型和含义、返回值的类型和含义。
  • 实现原型应该提供单元测试,确保函数的正确性和稳定性。

调用:

  • 调用函数时,应该按照参数的顺序传递参数值。
  • 调用函数时,应该提供必要的参数值
//1.>默认参数必须从右到左进行赋值
//如果某个位置已经有了默认参数,那么从这个位置往后,从左到右都必须有默认值
int func1(int a,int b,int c=10){
    return a+b+c;
}


//2.声明有默认参数,函数实现就不能有默认参数,声明和实现只能其中一个带上默认参数
int func2(int a,int b=1,int c=20);
int func2(int a,int b,int c){
    return a+b+c;
}
//占位参数
//数据类型 函数名(数据类型){}
void func(int a,int) {
    cout<<"hello"<<a<<endl;
}
int main(){
    cout<<func1(1,2 )<<endl;
    cout<<func1(1,2,13 )<<endl;


    cout<<func2(1,2 )<<endl;
    cout<<func2(1,2 ,13)<<endl;

	 func(1,2);
}

12.2.1 定义函数

分为有无返回值的函数。

void 函数名(参数列表)
{
    函数体;
}

返回值类型 函数名(参数列表)
{
    函数体;
    return 返回值;
}

12.2.2 参数

分为有参和无参函数。

void 函数名(参数列表)
{
    函数体;
}
void 函数名()
{
    函数体;
}

12.3 重载

#include <iostream>

using namespace std;
void test01() {
        cout << "hello world_空参" << endl;
}
void test01(int  n) {
    for (int i = 0; i < n; ++i){
        cout << "hello world—"<<n << endl;
    }
}
//注意事项:
// 函数的返回值不可以作为函数重载的条件A
int test01(long n){
    return n+1;
}
float test01(float n){
    return n+1.1f;
}
int test02(int n){
    return n+1;
}
int test02(int n,int m){
    return n+m+1;
}
int main() {
    cout<<"test01()函数调用前"<<endl;
    test01();
    test01(5);
    cout<<"test01()函数调用完成"<<endl;
    cout<<test02(10)<<endl;
    cout<<test02(10,20)<<endl;
    return 0;
}
  • 19
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

你会魔法吗✧(≖ ◡ ≖✿)

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

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

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

打赏作者

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

抵扣说明:

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

余额充值