C++函数、指针、引用、数组和结构体

三、函数

函数:函数就是封装了特定功能的代码块,使用函数可以使程序简短清晰,减少代码的冗余,提高开发效率,可以重复使用

  • 面向过程:面向过程语言中,整个程序就是由函数组成的

  • 面向对象:面向对象语言中,整个程序就是由若干类组成的,函数是类的组成部分

3.1、函数的定义和调用

1)定义语法:

// 语法:
返回值类型 函数名称(参数){
  函数体;
  return 返回值;
}
void show(){
  cout << "hello c++" << endl;
}

2)函数调用:

void show(){
    cout << "hello c++" << endl;
}
​
int main(){
    // 函数调用
    show();
    return 0;
}

3.2、函数执行顺序

函数的执行需要压到栈中执行,先调用的会压到栈的底部,也就是先进后出

void show2(){
    cout << "hello java" << endl;
}
​
void show1(){
    cout << "hello c++" << endl;
    show2();
}
​
void show3(){
    show1();
    cout <<"hello c" << endl;
}
​
int main(){
    show3();
    return 0;
}

注意:定义的函数需要定义在调用者之前

3.3、函数参数

参数相当于变量,定义的位置不同,参数是定义在函数的参数列表的小括号中,即便是相同类型的参数,定义多个参数时需要明确每一个参数的类型,不可省略,调用时需要明确每一个参数,数量和类型需要一致。

void show(int num1,int num2){
    cout << "num1=" << num1 << ",num2=" << num2 << ",和为:" << (num2+num1) << endl;
}
​
int main(){
    // 有参函数的调用
    show(12,2);
    return 0;
}

有默认值的参数:定义参数时,可以给参数定义一个默认值

  • 有默认值的参数调用的时候可以传参也可以不传

  • 有默认值的参数,必须放到参数列表的末尾

void show(int num1,int num2 = 10){
    cout << "num1=" << num1 << ",num2=" << num2 << ",和为:" << (num2+num1) << endl;
}
​
int main(){
    show(12);
    return 0;
}

3.4、函数返回值

返回值,表示的是一个函数执行结束之后得到的结果

return关键字:

  • 结束一个函数的执行

  • return后面带有一个值,表示函数的执行结果,也就是返回值,返回给调用者

  • 只能在函数中使用

int show(int num1,int num2){
    int result = (num1+num2);
    return result;
}
​
int main(){
    int result = show(12,3);
    cout << "两数之和为:" << result << endl;
    return 0;
}

注意:如果有返回值类型,函数必须使用return关键字返回结果,返回值为void也可以使用return关键字,表示一个程序的结束

3.5、函数重载

函数重载(overload),一个类中的多个函数,满足以下条件构成重载关系:

  • 函数名相同

  • 参数不同(参数类型不同,数量不同,顺序不同)

注意:重载只与函数的名称和参数有关,与返回值无关

// 定义重载函数
int add(int num1,int num2){
    return (num1+num2);
}
​
double add(int num1,double num2){
    return (num1+num2);
}
​
double add(double num1,double num2){
    return (num1+num2);
}
​
double add(double num2,int num1){
    return (num1+num2);
}
​
int main(){
    cout << add(12,3) << endl;
    cout << add(12.2,3) << endl;
    cout << add(12,3.1) << endl;
    cout << add(12.1,3.2) << endl;
    return 0;
}

3.6、访问其他文件函数

.cpp文件中的内容无法跨文件使用,需要为其定义一个.h的文件,在要使用的文件头部导入头文件

文件split.cpp文件中的内容:

# include 
using namespace std;
void show(int num1,int num2){
    cout << "num1=" << num1 << ",num2=" << num2 << endl;
}

定义头文件:

// 头文件只做声明
#pragma once
​
void show(int num1,int num2);

调用split文件中的函数:

# include 
# include "sqlit.h"         // 导入被调用者的头文件
using namespace std;
int main(){
    // 调用其它文件中的函数,需要创建与被调用者名称相同的头文件
    show(12,3);
    return 0;
}

四、指针和引用

4.1、内存分区

程序在执行的时候会在内存中开辟一套空间,存储数据

1)代码区:

存放程序执行之后编译的二进制代码,函数在编译之后,会存放到代码区,调用函数时会压到栈区进行执行

2)全局区:

全局区的变量在程序编译的时候就已经分派好了内存空间并初始化,这块内存空间在程序运行期间一直存在,主要存放全局变量,静态变量和常量,局部变量存放在栈区,static修饰的局部变量存放在全局区

3)栈区:

栈区由系统运行的内存管理,主要存放函数的参数和局部变量,在函数完成执行后,系统自动释放内存

4)堆区:

由编程人员手动申请和释放,如果不手动释放,程序结束后系统自动释放,生命周期是整个程序运行期间

4.2、指针的定义

指针就是存放内存空间的地址,使用&符号可以获取指定变量的内存空间地址,使用指针接收

// 格式:数据类型* 指针名称 = &变量名;
int main(){
    // 定义局部变量(在栈中开批空间)
    int num = 20;
    // 获取num变量的空间地址,存放到指针p中
    int* p = #
    cout << *p << endl;         // 输出的结果为:20
    cout << p << endl;          // 结果为存放变量num的内存地址 
    return 0;
}

注意:指针p存放的是变量的地址,*p就是解引用相当于变量名称,获取的是变量的值,指针不管是什么数据类型,在32位先占用4字节,在64位先占8字节

4.3、空指针和野指针

1)空指针:

空指针就是没有存放任何内存地址的指针变量,一般使用NULL来表示一个空指针,一般使用空指针可以对指针变量进行初始化

// 定义空指针
int* p = NULL;
// 注意:空指针没有存放任何数据的地址,不能访问它的内存空间的值

2)野指针:

野指针中存储有一个内存地址,但这个内存地址不存在了,就会出现野指针,野指针也不能访问它的内存空间中的值

// 定义野指针
int* p - (int*)0x1234;

4.4、常量指针和指针常量

  • 常量指针:const放在*号之前,表示他是一个指针,指向常量的指针,指向可以修改,但指向的空间的值不能修改

  • 指针常量:const放在*号之后,表示指针常量,即指针是一个常量值,不能改变指针的指向,但可以修改指向空间的值

int main(){
    int num1 = 200,num2 = 300;
    // 定义常量指针
    const int* p1 = &num1;
    // 定义指针常量
    int* const p2 = &num2;
    return 0;
}

4.5、指针的使用

参数传入的是地址,在函数的内部可以修改值,如果传入的不是地址,函数内部修改的值不变

// 直接使用值传递,不能改变值
void show(int num){
    num = 200;
}
​
int main(){
    int num = 10;
    show(num);
    cout << num << endl;
}
​
// 使用指针传递,传入的是变量的内存地址,相当于同一块内存空间
void show(int* num){
    *num = 200;
}
int main(){
    int num = 30;
    show(&num);
    cout << num <

4.6、引用

变量名实质上是一段连续内存空间的别名,是一个标号,程序中通过变量来申请并命名内存空间,通过变量的名称可以使用存储空间,引用可以作为一个已定义变量的别名

//基础语法:
数据类型& 别名 = 要引用的变量名;
  • &在此处不是求地址运算符,这里起标识作用

  • 必须在声明引用变量是进行初始化,引用初始化之后就不能进行改变

  • 不能有NULL引用必须保证引用是和一块合法的存储单元关联

1)引用的基本使用:

int main(){
    // 定义一个整形变量
    int num = 20;
    // 定义num的引用(起一个别名)
    int& a = num;
    // 比较值
    cout << "a=" << a << ",num=" << num << endl;        // 结果为:a=20,num=20
    // 比较地址
    cout << "&num=" << &num << ",&a=" << &a << endl;    // 结果为:&num=0x7ff7bc01c798,&a=0x7ff7bc01c798
    return 0;
}

2)引用在函数中的使用:

// 创建函数,参数使用引用类型(相当于使用同一个空间)
void change(int& a1,int& a2){
    a1 = 23;
    a2 = 32;
}
​
int main(){
    // 定义两个局部变量
    int num1 = 12,num2 = 3;
    // 调用函数改变两个局部变量的值
    change(num1,num2);
    cout << "num1=" << num1 << ", num2=" << num2 << endl;
    return 0;
}

3)引用的本质:

引用的本质实际上就是指针常量,指向不可以修改,但值可以进行修改

4)常量引用:

用来修饰形参,防止误操作,相当于常量指针,不能修改值

#include 
using namespace std;
// 定义参数为常量引用的函数
void showPrint(const int& a){
    // 改变a的值会报错
    //a = 12;
    cout << a << endl;
}
int main(){
    int a = 10;
    showPrint(a);
    return 0;
}

五、数组和结构体

数组就是一个数据容器,里面可以存放相同数据类型的数据,数组是一个定长的数据容器,一旦初始化完成,长度就不能改变

5.1、数组的定义

格式:数据类型 数组名[ ];

1)定义指定长度的数组:

int array[10];

2)定义数组的同时,初始化数据:

int array[4] = {12,3,4,34};
int array[10] = {1,3,45,6,7}                        // 其它值为0

3)不指定长度,长度由元素个数决定:

int array[] = {12,4,3,2,5,5,6}                      // 数组长度为:7

5.2、元素的访问

数组中的每一个元素都有一个下标索引,访问数组中的元素就是通过下标索引访问的,下标是从0开始的

  • 数组长度的计算:sizeof(数组名) / sizeof(数据类型)

  • 使用下表访问时不能越界

  • 访问格式:数组名[下标]

int main(){
    int arry[] = {12,2,3,5};
    //遍历数组
    for(int i = 0; i < sizeof(arry)/sizeof(int);i++){
        cout << arry[i] << endl;
    }
    return 0;
}

5.3、数组的内存分析

数组是一个容器,在内存中进行空间开辟的时候,并不是一个整体的空间,而是开辟了若干个连续的空间

  • 数组名表示的数数组的首地址

  • 数组作为参数传递到函数中的时候,传递的只是首元素的地址

  • 在其它函数中使用数组长度时,需要传入参数,在定义数组的函数中计算长度,传入的只是首元素的地址,计算不正确

int main(){
    int array[3] = {12,3,4};
    cout << &array << endl;                     //结果为:0x7ff7b841a78c
    cout << &array[0] << endl;                  //结果为:0x7ff7b841a78c
    cout << &array[1] << endl;                  //结果为:0x7ff7b841a790
    cout << &array[2] << endl;                  //结果为:0x7ff7b841a794
    return 0;
}

5.4、数组的遍历

数组的遍历,依次取出数组中的每一个元素

1)下标遍历法:

可以修改元素中的值

int main(){
    int arry[] = {12,2,3,5};
    //遍历数组
    for(int i = 0; i < sizeof(arry)/sizeof(int);i++){
        cout << arry[i] << endl;
    }
    return 0;
}

2)元素迭代法:

依次将数组中的每一个元素给指定的变量ele进行赋值,依次输出ele即可

  • 如果将数组通过参数传递到另一个函数中,就不能使用元素迭代法进行遍历

  • 在迭代的过程中不能通过ele修改元素的值

int main(){
    int array[5] = {1,2,4,6,7};
    for(int ele : array){
        cout << ele << endl;
    }
    return 0;
}

5.5、结构体的基本使用

结构体是用户自定义的数据类型允许用户输入不同地数据类型,结构体对象创建的时候struct可以省略

// 结构体的创建格式:
struct 结构体名{
  结构体内容;
};
​
// 结构体对象的创建
struct 结构体名 结构体对象名;
struct 结构体名 结构体对象名 = {值};

创建学生结构体,并创建学生对象

/*
 * 创建结构体--自定义数据类型
 */
struct student{
    // 成员列表
    string name;
    int age;
    double score;
};
​
/*
 * 创建结构体对象
 */
struct student stu1;
struct student stu2 = {"张三",23,78.9};

5.6、结构体数组

将自定义的结构体放入到数组中方便维护

// 格式:
struct 结构体名 结构体数组名[元素个数] = {{},{},····{}};

示例:

// 创建结构体
struct student{
    string name;
    int age;
    double score;
};
​
int mian(){
    // 创建结构体数组
    struct student stuArray[2] = {
            {"虾米",23,55.5},
            {"张三",34,66.5}
    };
  
    // 给结构体中的数组元素进行处理
    stuArray[1].name = "李四";
  
    // 遍历结构体数组中的内容
    for (int i = 0; i < 2; ++i) {
        cout << "name = " << stuArray[i].name << ", age = " << stuArray[i].age << endl;
    }
}

5.7、结构体指针

通过指针访问结构体中的元素,定义结构体指针,使其指向结构体对象

// 创建结构体
struct student{
    string name;
    int age;
    double score;
};
​
int main(){
    // 创建结构体对象
    student stu = {"张三",12,55.5};
    
    // 创建结构体指针
    struct student* p = &stu;
    
    // 使用指针访问结构体中的元素
    p->name = "xiami";
    return 0;
}

5.8、结构体的嵌套

结构体中嵌套结构体

// 创建学生结构体
struct student{
    string name;
    int age;
    double score;
};
​
// 创建老师结构体
struct teacher{
    string name;
    int age;
    struct student stu;             // 学生结构体对象
};
​
// 嵌套调用
int main(){
    // 创建老师对象
    teacher tea;
    // 定义指针指向老师对象
    teacher* p = &tea;
    p->name = "张三";
    //嵌套调用
    p->stu.name = "小明";
    return 0;
}

5.9、结构体作函数参数

将结构体作为函数参数传入函数中,有值传递和地址传递两种

1)值传递:函数中不能改变值的数据

// 定义参数为结构体的函数
void showStudent(student stu){
    cout << "name = " << stu.name << " ,age = " << stu.age << endl;
    stu.age = 44;
}
​
// 值传递
int main(){
    // 创建学生对象
    student stu = {"zhangshan",23,55.5};
    // 参数传递结构体对象
    showStudent(stu);
    cout << "name = " << stu.name << " ,age = " << stu.age << endl;
    return 0;
}

2)地址传递:操作同一块内存地址,值可以同时改变

// 定义参数为结构体的函数
void showStudent(student* stu){
    cout << "name = " << stu->name << " ,age = " << stu->age << endl;
    stu->age = 44;
}
​
// 值传递
int main(){
    // 创建学生对象
    student stu = {"zhangshan",23,55.5};
    // 参数传递结构体对象
    showStudent(&stu);
    cout << "name = " << stu.name << " ,age = " << stu.age << endl;
    return 0;
}

你可能感兴趣的:(c++,开发语言)