C语言 14.

1.一维数组名

1.1除了两种特殊情况外,都是指向数组第一个元素的指针

1.1.1特殊情况1 sizeof 统计数组长度

1.1.2特殊情况2 对数组名取地址,数组指针,步长整个数组长度

1.2数组名是指针常量,指针的指向不可以修改的,而指针指向的值可以改

1.3传参数时候,int arr[] 可读性更高

1.4数组索引下标可以为负数


// 打印数组
void printArray(int arr[], int len)  // int arr[]等价于 int *arr,前者更好,因为可读性更高
{
    for (size_t i = 0; i < len; i++)
    {
        printf("%d\n", arr[i]);
    }
}
/*
有两种特殊情况,一维数组名不是指向第一个元素是指针
1.sizeof
2.对数组名取地址,得到的数组指针,步长是整个数组长度
*/
void test01()
{
    // 一维数组名是不是指针?
    int arr[5] = { 1,2,3,4,5 };  // 20
    printf("%d\n", sizeof(arr));
    printf("%d\n", &arr);
    printf("%d\n", &arr + 1);   // 两个相差了一个数组的长度被称为数组指针
    //-1563427552
    //-1563427532
    int len = sizeof(arr) / sizeof(int);
    printArray(arr, len);
    // arr数组名,它是一个指针常量,指针的指向不可以修改,而指针指向的值可以修改  int * const a
    //arr[0] = 1000;      // 可以修改
    //arr = NULL;          // 会报错,不可以修改
    // 数组索引可不可以为负数
    int* p = arr;
    p = p + 3;
    printf("%d\n", p[-1]);         // 3         *(p-1)
    
}

2.数组指针的定义方式

2.1先定义出数组类型,再通过类型定义数组指针变量

2.1.1typedef int(ARRARY_TYPE)[5];//ARRARY_TYPE 代表存放5个int类型元素的数组 的数组类型

2.2先定义数组指针类型,再通过类型定义数组指针变量

2.2.1typedef int(*ARRARY_TYPE)[5];

2.3直接定义数组指针变量

2.3.1int(* p )[5] = &arr;


/*        数组指针的定义方式
1.先定义数组类型,再通过类型定义数组指针
2.先定义数组指针的类型,再通过类型定义在指针
3.直接定义数组指针
*/
void test01()
{
    int arr[5] = { 1,2,3,4,5 };       // 先定义数组类型
    typedef int(ARRAY_TYPE)[5];        // ARRAY_TYPE代表存放5个int类型元素的数组   的数组类型
    ARRAY_TYPE arr2;
    for (size_t i = 0; i < 5; i++)
    {
        arr2[i] = i + 100;
    }
    for (size_t i = 0; i < 5; i++)
    {
        printf("%d\n", arr2[i]);
    }
    ARRAY_TYPE* arrP = &arr;
    // *arrP = arr = 数组名
    for (size_t i = 0; i < 5; i++)
    {
        printf("%d\n", (*arrP)[i]);
    }
}
void test02()
{
    int arr[5] = { 1,2,3,4,5 };      
    typedef int(*ARRAY_TYPE)[5];     // 直接就是一个指针
    ARRAY_TYPE arrP = &arr;
    for (size_t i = 0; i < 5; i++)
    {
        printf("%d\n", (*arrP)[i]);
    }
}
void test03()
{
    int arr[5] = { 1,2,3,4,5 };
    int(*p)[5] = &arr;
    for (size_t i = 0; i < 5; i++)
    {
        printf("%d\n", (*p)[i]);
    }
}

3.二维数组名

3.1二维数组名 除了两种特殊情况外,是指向第一个一维数组的 数组指针

3.2两种特殊情况

3.2.1sizeof 统计二维数组大小

3.2.2对数组名称取地址 int(*p)[3][3] = &arr

3.3二维数组做函数参数

3.3.1//void printArray(int (*array)[3], int row, int col)

3.3.2//void printArray(int array[][3], int row ,int col)

3.3.3void printArray(int array[3][3], int row ,int col) 可读性比较高

3.4数组指针 和 指针数组?

3.4.1数组指针: 指向数组的指针

3.4.2指针数组: 由指针组成数组


/*
除了两种特殊情况外,二维数组名称是 指向第一个一维数组的数组指针
1.sizeof
2.对数组名取地址&arr  获取的是二维数组的  数组指针  int(*p)[3][3] = &arr;
*/
void test01()
{
    int arr[3][3] = {        
        {1,2,3} ,
        {4,5,6},
        {7,8,9}     // 最后一行‘,’可加可不加
    };
    /*int arr[3][3] = { 1,2,3,4,5,6,7,8,9 };
    int arr[][3] = { 1,2,3,4,5,6,7,8,9 };*/
    printf("%d\n", sizeof(arr));        // 36
    //2
    //int(*p)[3][3] = &arr;
    int(*Array)[3] = arr;
    // 访问二维数组中的6这个元素
    printf("%d\n", arr[1][2]);            // 给人看
    printf("%d\n", *(*(Array + 1) + 2));  // 给机器看
}
// array[3][3] 等价于一维数组指针  int(*array)[3]
void printArray(int array[3][3], int row, int col)         // int array[][3]
{
    for (size_t i = 0; i < row; i++)
    {
        for (size_t j = 0; j < col; j++)
        {
            //printf("%d ", array[i][j]);  // 给人看
            printf("%d ", *(*(array + i) + j) );   // 给机器看
        }
        printf("\n");
    }
}
// 二维数组做函数的参数
void test02()
{
    int arr[3][3] = {
        {1,2,3} ,
        {4,5,6},
        {7,8,9}     // 最后一行可加可不加
    };
    printArray(arr, 3, 3);
}

4.指针数组排序

4.1选择排序

4.1.1例如从小到大

4.1.2开始认定最小值下标为i,从j = i+1的位置起找真实最小值下标,如果计算的真实最小值下标与i不等,互换元素

4.2利用选择排序实现指针数组 从大到小排序

4.2.1字符串对比

4.2.2if ( strcmp(pArr[max],pArr[j]) == -1)
在这里插入图片描述

/*
选择排序法:
1.假设第一个数组最小,找到最小的数字的下标
2.将最小的数字和原来的数字交换
3.循环
*/
void mySort(int arr[], int len)
{
    for (size_t i = 0; i < len; i++)
    {
        int min = i; // 记录最小值下标为i
        for (int j = i + 1; j < len; j++)
        {
            if (arr[min] > arr[j])
            {
                // 更新真实最小值下标
                min = j;
            }
        }
        // 判断真实最小值下表是否与开始认定的i相等,如果不等,交换元素
        if (i != min)
        {
            int temp = arr[i];
            arr[i] = arr[min];
            arr[min] = temp;
        }
    }
}
void printArray(int arr[], int len)
{
    for (size_t i = 0; i < len; i++)
    {
        printf("%d\n", arr[i]);
    }
}
void test01()
{
    // 从小到大排序利用选择排序
    int arr[] = { 2,5,1,3,4 };
    int len = sizeof(arr) / sizeof(int);
    mySort(arr, len);
    printArray(arr, len);
}
void selectSort(char** pArr, int len)
{
    for (size_t i = 0; i < len; i++)
    {
        int max = i;
        for (size_t j = 0; j < len; j++)
        {
            if (strcmp(pArr[max], pArr[j])== -1)
            {
                max = j;
            }
        }
        if (i != max)
        {
            char* tmp = pArr[i];
            pArr[i] = pArr[max];
            pArr[max] = tmp;
        }
    }
}
void printArray2(char **pArr, int len)
{
    for (size_t i = 0; i < len; i++)
    {
        printf("%s\n", pArr[i]);
    }
}
void test02()
{
    // 对指针数组进行排序,排序的算法利用 选择排序  从大到小
    char* pArray[] = { "bbb","aaa","ccc","fff","ddd" };
    int len = sizeof(pArray) / sizeof(char*);
    selectSort(pArray, len);
    printArray2(pArray,len);
}

5.结构体基本概念

5.1加typedef 可以给结构体起别名

5.2不加typedef ,可以直接创建一个结构体变量

5.3结构体声明 可以是匿名

5.4在栈上创建和在堆区创建结构体

5.5在栈上和堆区创建结构体变量数组



/*************************结构体的定义******************************************/
struct Person
{
    char name[64];
    int age;
};
//typedef struct Person myPerson;
//typedef struct Person
//{
//    char name[64];
//    int age;
//}myPerson;
//struct Person2
//{
//    char name[64];
//    int age;
//}myPerson2;      // 已经是一个结构体变量
//myPerson2.age = 100;
//struct Person2
//{
//    char name[64];
//    int age;
//}myPerson2={"aaa",12};      // 已经是一个结构体变量
//printf("姓名:%s  年龄:%d\n", myPerson2.name, myPerson2.age);
// 匿名结构体
//struct
//{
//    char name[64];
//    int age;
//}myPerson3 = {"bbb",14};         // 一般不这样使用
/*****************结构体创建************/
void test04()
{
    //创建在栈上
    struct Person p = { "aaa",10 };
    printf("姓名:%s 年龄:%d\n", p.name, p.age);
    // 创建在堆区
    struct Person *p2 = malloc(sizeof(struct Person));
    strcpy(p2->name, "bbb");
    p2->age = 20;
    printf("姓名:%s 年龄:%d\n", p2->name, p2->age);
    if (p2 != NULL)
    {
        free(p2);
        p2 = NULL;
    }
}
/**************结构体变量数组创建***************/
void printArray(struct Person personArray[],int len)
{
    for (size_t i = 0; i < len; i++)
    {
        printf("姓名:%s 年龄:%d\n", personArray[i].name, personArray[i].age);
    }
}
void test05()
{
    // 在栈上分配内存
    struct Person persons[] =
    {
        {"aaa",1},
        {"bbb", 2},
        {"ddd", 3},
    };
    int len = sizeof(persons) / sizeof(struct Person);
    printArray(persons, len);
    // 在堆区分配内存
    struct Person* pArray = malloc(sizeof(struct Person) * 4);
    for (size_t i = 0; i < 4; i++)           // 堆中不能赋初值
    {
        sprintf(pArray[i].name, "name_%d", i + 1);
        pArray[i].age = 18 + i;
    }
    printArray(pArray, 4);        // 这里的4只能写不能算出来
    if (pArray != NULL)
    {
        free(pArray);
        pArray = NULL;
    }
}

6.结构体深浅拷贝

6.1系统提供的赋值操作是浅拷贝 – 简单值拷贝,逐字节拷贝

6.2如果结构体中有属性创建在堆区,就会出现问题,在释放期间,一段内存重复释放,一段内存泄露

6.3解决方案:自己手动去做赋值操作,提供深拷贝

结构体在栈上
在这里插入图片描述

结构体在堆上
在这里插入图片描述



struct Person
{
    char name[64];
    int age;
};
void test01()
{
    struct Person p1 = { "Tom",18 };
    struct Person p2 = { "Jerry " };
    printf("p1的姓名:%s\n  年龄:%d\n", p1.name, p1.age);
    printf("p2的姓名:%s\n  年龄:%d\n", p2.name, p2.age);
    p1 = p2;
    printf("p1的姓名:%s\n  年龄:%d\n", p1.name, p1.age);
    printf("p2的姓名:%s\n  年龄:%d\n", p2.name, p2.age);        // 打印出来都是Jerry
}
struct Person2
{
    char * name;
    int age;
};
void test02()
{
    struct Person2 p1;
    p1.name = malloc(sizeof(char) * 64);
    strcpy(p1.name, "Tom");
    p1.age = 18;
    struct Person2 p2;
    p2.name = malloc(sizeof(char) * 128);
    strcpy(p2.name, "Jerry");
    p2.age = 28;
    printf("p1的姓名:%s\n  年龄:%d\n", p1.name, p1.age);
    printf("p2的姓名:%s\n  年龄:%d\n", p2.name, p2.age);
    //p1 = p2;  // 系统提供的赋值操作是简单的浅拷贝,我们需要手动赋值,提供深拷贝
    
    /********************* 手动赋值************/
    // 1.先释放原来的堆区内存(防止内存不够用)
    if (p1.name != NULL)
    {
        free(p1.name);
        p1.name = NULL;
    }
    // 2.在堆区创建内存
    p1.name = malloc(strlen(p2.name) + 1);
    strcpy(p1.name, p2.name);
    p1.age = p2.age;
    /********************************************/
    printf("p1的姓名:%s\n  年龄:%d\n", p1.name, p1.age);
    printf("p2的姓名:%s\n  年龄:%d\n", p2.name, p2.age);
    if (p1.name != NULL)
    {
        free(p1.name);
        p1.name = NULL;        // 这个时候堆区的内存已经释放
    }
    if (p2.name != NULL)
    {
        free(p2.name);
        p2.name = NULL;          // 这里面堆区内容重复释放,而且有一个堆区的内存没有释放,会造成内存泄露
    }
}

7.结构体嵌套一级指针练习

7.1在堆区创建一个 结构体指针数组

7.1.1malloc(sizeof(struct Person *) *3 )

7.2在堆区创建出结构体变量

7.2.1malloc(sizeof(struct Person))

7.3在堆区创建出具体姓名

7.3.1malloc(sizeof(char )*64);

7.4打印数据

7.5释放数组
在这里插入图片描述

struct Person
{
 char* name;
 int age;
};
struct Person** allocateSpace()
{
 struct Person** temp = malloc(sizeof(struct Person*) * 3);       // 三个malloc对应三个free
 for (size_t i = 0; i < 3; i++)
 {
     //创建结构体内存
     temp[i] = malloc(sizeof(struct Person));
     // 将结构体姓名创建在堆区
     temp[i]->name = malloc(sizeof(char) * 64);
     // 给姓名赋值
     sprintf_s(temp[i]->name, "name_%d", i + 1);
     
     temp[i]->age = 18 + i;
 }
 return temp;
}
void printPerson(struct Person** pArray, int len)
{
 for (size_t i = 0; i < len; i++)
 {
     printf("姓名:%s 年龄:%d\n", pArray[i]->name, pArray[i]->age);
 }
}
void freeSpace(struct Person** pArray, int len)
{
 if (pArray == NULL)
 {
     return;
 }
 if (len <= 0)
 {
     return;
 }
 for (size_t i = 0; i < 3; i++)
 {
     if (pArray[i]->name = NULL)
     {
         printf("%s被释放了\n", pArray[i]->name);
         free(pArray[i]->name);
         pArray[i]->name = NULL;
     }
     free(pArray[i]);
     pArray = NULL;
 }
 free(pArray);
 pArray = NULL;
}
void test01()
{
 struct Person** pArray = NULL;
 pArray = allocateSpace();
 // 打印数组
 printPerson(pArray, 3);
 // 释放内存空间
 freeSpace(pArray, 3);
 pArray = NULL;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值