C++(四)

📒 笔记 · 2023-09-11

C++语法:指针、结构体


C++ (一):常量、变量、关键字、标识符、数据类型(整形、浮点型、字符型、字符串型、布尔型)
C++ (二):选择、循环、嵌套、跳转语句
C++ (三):数组、函数
C++ (四):指针、结构体


指针

  1. 指针概念

指针的作用:可以通过指针间接访问内存。

  • 内存编号是从0开始记录的,一般用十六进制数字表示。
  • 可以利用指针变量保存地址。
  1. 指针变量定义和使用

指针变量定义语法:数据类型 * 变量名;

#include <iostream>
using namespace std;

int main()
{
    //1、定义指针
    int a = 10;

    //指针定义的语法:数据类型 * 指针变量
    int * p; //创建p为指针变量

    //让指针记录变量a的地址
    p = &a;  // &为取址符号

    cout << "a的地址为:" << &a << endl;                                                //a的地址为:00FBFC78

    cout << "指针p为:" << p << endl;  // 指针p存储的值 和变量a的地址是一样的            //指针p为:00FBFC78

    //2、使用指针
    //可以通过解引用的方式来找到指针指向的内存
    //指针前加 * 代表解引用,找到指针指向的内存中的数据

    *p = 1000;  //通过*p访问内存,并修改内存的值
    cout << "a= " << a << endl;                                                        //a= 1000
    cout << "*p:" << *p << endl;                                                    //*p:1000

    system("pause");   //按任意键继续

    return 0;
}
  1. 指针所占内存空间
  • 在32位操作系统下,不管什么类型的指针都占4个字节的内存。
  • 在64位操作系统下,不管什么类型的指针都占8个字节的内存。
#include <iostream>
using namespace std;

int main()
{
    //指针所占内存空间
    int a = 10;

    /*
    int * p; //p变量是 int * 数据类
    p = &a;

    等价于:
    int * p = &a;
    */

    int * p = &a;

    cout << "sizeof(int * )= " << sizeof(p) << endl;                    //sizeof(int * )= 8    我的操作系统是64位的。
    cout << "sizeof(int * )= " << sizeof(int(*)) << endl;                //sizeof(int * )= 8
    cout << "sizeof(float * )= " << sizeof(float(*)) << endl;            //sizeof(float * )= 8
    cout << "sizeof(double * )= " << sizeof(double(*)) << endl;            //sizeof(double * )= 8
    cout << "sizeof(char * )= " << sizeof(char(*)) << endl;                //sizeof(char * )= 8

    system("pause");   //按任意键继续

    return 0;
}
  1. 空指针

指针变量指向内存中编号为0的空间。

用途:初始化指针变量。

空指针指向的内存是不可以访问的。

#include <iostream>
using namespace std;

int main()
{
    //空指针
    //1、空指针用于给指针变量进行初始化
    int* p = NULL;

    //2、空指针是不可以进行访问的
    //0~255之间的内存编号是系统占用的,因此不可以访问。
    *p = 100;  //对空指针解引用,然后操作它,这样是报错的

    system("pause");   //按任意键继续

    return 0;
}
  1. 野指针

野指针:指针变量指向非法的内存空间。

野指针和空指针都不是我们申请的空间,所以不要访问。

例如,创建了一个整型变量a,是申请了一个整型变量的空间,由于是申请的空间,所以可以通过指针访问它。

#include <iostream>
using namespace std;

int main()
{
    //野指针
    int* p = (int*)0x1100; // 0x1100是一个十六进制数,int*使得十六进制数强行转换为地址。
                           // 拿指针随便指向并没有申请这个内存的访问权限的内存。

    cout << *p << endl; // 报错,地址并没有申请,你还要去访问它,就会报错

    system("pause");

    return 0;
}
  1. const修饰指针

看const右侧紧跟着的是指针还是常量,是指针就是常量指针,是常量就是指针常量。

  • 如果 const后面跟的是指针(),就不能做 p=20 操作,即不能修改指针指向的值。
  • 如果const 后面跟的常量(p),就不能做 p = &b 操作,即不能修改指针的指向。
#include <iostream>
using namespace std;

int main()
{
    int a = 10;
    int b = 10;

    //1、const修饰指针 常量指针
    const int* p = &a;  // const(常量) *(指针) → 常量指针
    // *p = 20;  错误,常量指针 → 指针指向的值不可以改,指针的指向可以改
    p = &b; // 正确

    //2、const修饰指针 指针常量
    int* const p2 = &a; // *(指针)const(常量) → 指针常量
    *p2 = 100; //正确的
    p2 = &b;  //错误,指针的指向不可以改,指针指向的 值可以改

    //3、const修饰指针和常量
    const int* const p3 = &a;
    *p3 = 100;  //错误
    p3 = &b;  //错误

    system("pause");
    return 0;
}
  1. 指针和数组

利用指针访问数组中元素。

#include <iostream>
using namespace std;

int main()
{
    //指针和数组
    //利用指针访问数组中的元素

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

    cout << "第一个元素为:" << arr[0] << endl;

    int* p = arr; //数组名arr就是数组的首地址

    cout << "利用指针访问第一个元素:" << *p << endl;

    p++;

    cout << "利用指针访问第二个元素:" << *p << endl;

    cout << "利用指针遍历数组:" << endl;

    int* p2 = arr;
    for (int i = 0; i < 10; i++) 
    {
        //cout << arr[i] << endl;
        cout << *p2 << endl;
        p2++;

    }
    system("pause");

    return 0;
}

输出:

第一个元素为:1
利用指针访问第一个元素:1
利用指针访问第二个元素:2
利用指针遍历数组:
1
2
3
4
5
6
7
8
9
10
请按任意键继续. . .
  1. 指针和函数

利用指针作函数的参数,可以修改实参的值。

地址传递可以改变实参的数据,值传递不可以改变实参的值。

如果不想修改实参,就用值传递,如果想修改实参,就用地址传递。

代码例子中,temp的值会传递给 *p2 地址中的值,所以a、b实参的值改变了。

#include <iostream>
using namespace std;

void swap(int* p1, int* p2)
{
    int temp = *p1;
    *p1 = *p2;
    *p2 = temp;
}

int main()
{
    //指针和函数
    int a = 10;
    int b = 20;

    swap(&a, &b);

    //如果是地址传递,可以修改实参,原来 a = 10;b = 20,地址传递后 a = 20,b = 10.
    cout << "a =" << a << endl;                //a=20
    cout << "b =" << b << endl;                //a=10
    //如果是值传递,不可以修改实参,原来 a = 10;b = 20,值传递后 a = 10,b = 20.

    system("pause");

    return 0;
}
  1. 指针配合数组和函数案例

案例描述:封装一个函数,利用冒泡排序,实现对整型数字的升序排列。

例如: int arr[10] = {4,3,6,9,1,2,10,8,7,5}

#include <iostream>
using namespace std;

//冒泡排序函数
void bubbleSort(int * arr, int len)
{
    for (int i = 0; i < len - 1; i++)
    {
        for (int j = 0; j < len - i - 1; j++)
        {
            //如果j>j+1的值,交换数字
            if (arr[j] > arr[j + 1])
            {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

//打印数组
void printArray(int* arr, int len)
{
    for (int i = 0; i < len; i++)
    {
        cout << arr[i] << endl;
    }
}

int main()
{
    //1、先创建数组
    int arr[10] = { 4,3,6,1,2,9,10,8,7,5 };

    //数组长度
    int len = sizeof(arr) / sizeof(arr[0]);

    //2、创建函数,实现冒泡排序
    bubbleSort(arr, len);

    //3、打印排序后的数组
    printArray(arr, len);


    system("pause");

    return 0;

}

运行结果:

1
2
3
4
5
6
7
8
9
10
请按任意键继续. . .

结构体

结构体

结构体属于用户自定义的数据类型,允许用户存储不同的数据类型。

  1. 结构体定义和使用

语法:struct 结构体名 { 结构体成员列表 }

通过结构体创建变量的方式有三种:

  • struct 结构体名 变量名
  • struct 结构体名 变量名 = { 成员1值,成员2值 }
  • 定义结构体时顺便创建变量

结构体变量利用点.访问成员

方法1,方法2:

#include <iostream>
using namespace std;
#include <string>
//自定义数据类型,一些类型的集合组成一个类型
//语法 struct 类型名称 { 成员列表 }
//结构体定义的时候,struct关键字不能省略
//1、创建学生数据类型:学生包括(姓名,年龄,分数)
struct Student
{
    //成员列表

    //姓名
    string name;
    //年龄
    int age;
    //分数
    int score;
};

int main()
{
    //方法1: struct Student s1;  s1类似结构体的实例值,类似变量赋值:int a = 10 → 属性 变量 变量值
    //结构体创建的时候,struct 关键字可以省略;上面结构体定义的时候 struct 可以省略
    struct Student s1;
    //给s1属性赋值,通过点.访问结构体变量中的属性
    s1.name = "张三";
    s1.age = 18;
    s1.score = 100;

    cout << "姓名:" << s1.name << "年龄:" << s1.age << "分数:" << s1.score << endl;

    //方法2: struct Student s2 = { ... }
    struct Student s2 = { "李四",19,80 };
    cout << "姓名:" << s2.name << "年龄:" << s2.age << "分数:" << s2.score << endl;

    system("pause");

    return 0;
}

运行结果:

姓名:张三年龄:18分数:100
姓名:李四年龄:19分数:80
请按任意键继续. . .

方法3:创建结构体的时候,顺便创建个结构体变量

#include <iostream>
using namespace std;
#include <string>
//自定义数据类型,一些类型的集合组成一个类型
struct Student
{
    //成员列表

    //姓名
    string name;
    //年龄
    int age;
    //分数
    int score;
}s3;   //方法3: 创建结构体的时候,顺便创建个结构体变量

int main()
{
    //方法3
    s3.name = "王五";
    s3.age = 20;
    s3.score = 60;

    cout << "姓名:" << s3.name << "年龄:" << s3.age << "分数:" << s3.score << endl;

    system("pause");

    return 0;

}

运行结果:

姓名:王五年龄:20分数:60
请按任意键继续. . .
  1. 结构体数组

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

语法:struct 结构体名 数组名[元素个数] = { {},{},...,{} }

#include <iostream>
using namespace std;
#include <string>
//自定义数据类型,一些类型的集合组成一个类型
struct Student
{
    //成员列表

    //姓名
    string name;
    //年龄
    int age;
    //分数
    int score;
};  

int main()
{
    //2、创建结构体数组
    struct Student stuArray[3] =
    {
        {"张三",18,100},
        {"李四",28,99},
        {"王五",38,66}
    };

    //3、给结构体数组中的元素赋值
    stuArray[2].name = "赵六";
    stuArray[2].age = 80;
    stuArray[2].score = 60;

    //4、遍历结构体数组
    for (int i = 0; i < 3; i++)
    {
        cout << "姓名:" << stuArray[i].name << "年龄:" << stuArray[i].age << "分数:" << stuArray[i].score << endl;
    }

    system("pause");

    return 0;
}

运行结果:

姓名:张三年龄:18分数:100
姓名:李四年龄:28分数:99
姓名:赵六年龄:80分数:60
请按任意键继续. . .
  1. 结构体指针

作用:通过指针访问结构体中的成员。

利用操作符 · > 可以通过结构体指针访问结构体属性。

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

//结构体指针

//定义学生结构体
struct Student
{
    string name; //姓名
    int age; //年龄
    int score; //分数
};

int main()
{
    // 1、创建学生结构体变量,这里的 struct 可以省略
    struct Student s = { "张三",18,100 };

    //2、通过指针指向结构体变量
    struct Student* p = &s;  //对s取地址, tudent * p 类似 int * p,这里的 struct 可以省略

    //3、通过指针访问结构体变量中的数据
    //通过结构体指针 访问结构体中的属性,需要利用'->'
    cout << "姓名:" << p->name << "年龄:" << p->age << "分数:" << p->score << endl;        //姓名:张三年龄:18分数:100

    system("pause");

    return 0;
}
  1. 结构体嵌套结构体

结构体中的成员可以是另一个结构体。

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

//因为老师的结构体里有学生的结构体,所以学生结构体要在老师结构体前面先定义
//定义学生结构体
struct student
{
    string name; //姓名
    int age; //年龄
    int score; //分数
}; 

//定义老师结构体
struct teacher
{
    int id; //教师编号
    string name; //教师姓名
    int age; //年龄
    struct student stu; //学生结构体
};

int main()
{
    //结构体嵌套结构体
    //创建老师
    teacher t;
    t.id = 10000;
    t.name = "老王";
    t.age = 50;
    t.stu.name = "小王";
    t.stu.age = 20;
    t.stu.score = 60;

    cout << "老师姓名:" << t.name << "老师编号:" << t.id << "老师年龄:" << t.age
        << "老师辅导的学生姓名:" << t.stu.name << "学生年龄:" << t.stu.age 
        << "学生考试分数:" << t.stu.score << endl;

    //老师姓名:老王 老师编号:10000 老师年龄:50 老师辅导的学生姓名:小王 学生年龄:20 学生考试分数:60


    system("pause");

    return 0;
}
  1. 结构体做函数参数

作用:将结构体作为参数向函数中传递,传递方式有两种。

传递方式有两种:

  • 值传递
  • 地址传递
#include <iostream>
using namespace std;
#include <string>

//定义学生结构体
struct student
{
    string name; //姓名
    int age; //年龄
    int score; //分数
};

//定义老师结构体
struct teacher
{
    int id; //教师编号
    string name; //教师姓名
    int age; //年龄
    struct student stu; //学生结构体
};

//打印学生信息的函数
//1、值传递
void printStudent1(struct student s)
{
    cout << "子函数 值传递前 姓名:" << s.name << "年龄:" << s.age << "分数:" << s.score << endl;

    s.age = 100;
}

//2、地址传递
void printStudent2(struct student* p)
{
    cout << "子函数 地址传递前 姓名:" << p->name << "年龄:" << p->age << "分数:" << p->score << endl;

    p->score = 90;
}

int main()
{
    //结构体做函数参数
    //将学生传入到一个参数中,打印学生身上的所有信息

    //创建结构体变量
    struct student s;
    s.name = "张三";
    s.age = 20;
    s.score = 85;

    cout << "main函数 传递前 姓名:" << s.name << "年龄:" << s.age << "分数:" << s.score << endl;

    printStudent1(s);

    cout << "子函数  值传递后 姓名:" << s.name << "年龄:" << s.age << "分数:" << s.score << endl;

    printStudent2(&s);

    cout << "子函数 地址传递后 姓名:" << s.name << "年龄:" << s.age << "分数:" << s.score << endl;


    cout << "main函数 传递后 姓名:" << s.name << "年龄:" << s.age << "分数:" << s.score << endl;

    system("pause");

    return 0;
}

运行结果:

main函数 传递前 姓名:张三年龄:20分数:85
子函数 值传递前 姓名:张三年龄:20分数:85
子函数 值传递后 姓名:张三年龄:20分数:85
子函数 地址传递前 姓名:张三年龄:20分数:85
子函数 地址传递后 姓名:张三年龄:20分数:90
main函数 传递后 姓名:张三年龄:20分数:90
请按任意键继续. . .
  1. 结构体中const使用
Theme Jasmine by Kent Liao