013-结构体-C语言笔记

/ 0

返回指针的函数

指针是可以作为函数返回值的,不能返回局部变量的指针,因为当函数执行结束后指针变量就释放了。

如果我们真的需要返回一个指针变量,那就要保证我们的函数执行完毕之后,指针指向的变量仍然存储在内存之中。那我们可以将变量创建在堆空间中,使用malloc或者calloc申请空间。或者直接声明为全局变量或用 static 修饰的局部变量。

如果函数需要返回一个字符串,我们可以返回 char * 类型字符串,不要使用字符数组,因为字符数组在函数执行结束后就释放了。而使用 char * 声明的字符串是存储在数据段的,直到程序执行结束才会释放(直接返回字符串常量)。

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

char *getWeekDay(int day){
    return "星期N";//返回的是字符串在数据段(常量区)中的地址
}

int main(){
    char *weekDay = getWeekDay(2);
    return 0;
}

指向函数的指针

程序在运行的时候,函数是存储在内存(代码段)之中的,存储函数的空间肯定也是有地址的,所以我们可以用指针指向函数。

语法:返回值类型 (*指针名)([参数列表]);

注意:

1.函数的地址就是函数名。

2.指向函数的指针变量,本质上还是一个变量,那么我们就可以声明、赋值给另外一个同类型的指针变量使用。

无参无返回值

#include <stdio.h>

void testFunction(){
    printf("这是一个无参无返回值的函数\n");
}
int main(int argc, const char * argv[]) {

    //指针指向函数
    void (*pFunction)() = testFunction;//存储没有返回值没有参数的地址,函数名就是函数的地址
    //指针调用函数
    (*pFunction)();
    pFunction();//简写

    return 0;
}

有参有返回值

#include <stdio.h>

int getSum(int num1,int num2){
    return num1 + num2;
}
int main(int argc, const char * argv[]) {

    //指针指向函数,参数列表可以省略
    int (*pFunction)() = getSum;

    //指针调用函数
    int sum = pFunction(10,12);

    printf("sum = %d\n",sum);
    return 0;
}

应用场景:多种方式对字符串数组进行排序

#include <stdio.h>
#include <string.h>

/**
 *  对字符串进行各种排序
 *
 *  @param countries 需要被排序的字符串数组
 *  @param length    字符串数组的长度
 *  @param pCompare  指向控制排序方式的函数指针
 */
void sort(char *countries[],int length,int (*pCompare)()){
    for (int i = 0; i < length; i++) {
        for (int j = 0; j < length - 1 -i; j++) {
            //调用比较字符串的函数进行比较
            int res = pCompare(countries[j],countries[j+1]);
            if (res > 0) {
                //交换位置
                char *temp = countries[j];
                countries[j] = countries[j+1];
                countries[j+1] = temp;
            }
        }
    }
}

/**
 *  比较字符串大小
 *
 *  @param str1 第一个字符串
 *  @param str2 第二个字符串
 *
 *  @return 第一个大字符串大返回正数,第二个字符串大返回负数,相同返回0
 */
int compare(char *str1,char *str2){
    return (int)strcmp(str1, str2);
}

/**
 *  比较字符串的长度
 *
 *  @param str1 第一个字符串
 *  @param str2 第二个字符串
 *
 *  @return 第一个字符串大返回1,第二个字符串大返回-1,相同返回0
 */
int compare1(char *str1,char *str2){
    unsigned long len1= strlen(str1);
    unsigned long len2 = strlen(str2);
    if (len1 > len2) {
        return 1;
    }else if(len1 < len2){
        return -1;
    }
    return 0;
}

int main(int argc, const char * argv[]) {
    char *countries[] = {
        "fldjfljfaf",
        "fdahfiyyyyfh",
        "aadjflui",
        "jabfaf"
    };

    //计算字符串数组长度
    int length = sizeof(countries)/sizeof(char *);

    //传入字符串数组,数组长度,指向函数的指针。按字符串大小排序
    sort(countries, length, compare);

    //传入字符串数组,数组长度,指向函数的指针。按字符串长度排序
    sort(countries, length, compare1);

    //遍历打印字符串
    for (int i = 0; i < length; i++) {
        printf("%s\n",countries[i]);
    }

    return 0;
}

结构体的声明

在实际应用中,我们通常需要由不同类型的数据来构成一个整体,比如学生信息这个整体可以由姓名、年龄、身高等数据构成,这些数据都具有不同的数据类型,姓名可以是字符指针类型,年龄可以是整型,身高可以是浮点型。C语言提供了一种构造类型来解决这个问题,由程序员自己声明多个数据类型组成一个整体当做一种新的数据类型,就是结构体。结构体里声明的多个数据类型变量叫做结构体的成员。

声明结构体类型语法:

struct 类型名 {
数据类型1 成员名1;
数据类型2 成员名2;
……….
};

声明结构体变量语法:struct 类型名 变量名;

//声明类型的同时声明变量
struct Student {
    char *name;
    int age;
    float heigth;
}stu,stu1;//可以同时声明多个变量

//匿名结构体。声明类型的同时声明变量,可以省略类型名,但是这种方法不能再次声明新的变量了
struct {
    char *name;
    int age;
    float heigth;
}stu,stu1;//也可以同时声明多个变量

//声明类型,再声明变量
struct Student {
    char *name;
    int age;
    float heigth;
};
struct Student stu1,stu2;//变量名是stu1,stu2.可以同时声明多个变量
struct Student stu2;//变量名是stu3

为结构体变量中的成员赋值

#include <stdio.h>

int main(int argc, const char * argv[]) {
    //声明一个结构体Student1
    struct Student1 {
        char *name;
        int age;
        float heigth;
    };

    struct Student stu1;
    stu1.name = "zhoujianfeng";//单独为变量的每个成员赋值
    stu1.age = 18;
    stu1.heigth = 1.9f;

    //声明一个结构体Student2
    struct Student2 {
        char *name;
        int age;
        float heigth;
    };

    struct Student stu2 = {"zhoujianfeng",18,1.9f};//声明变量的同时初始化
    struct Student stu3 = {"zhoujianfeng",18};//初始化部分
    struct Student stu4 = {.age = 18};//指定成员初始化
    struct Student stu5 = stu2;//将stu2赋值给stu5

    return 0;
}

使用结构体注意:

1.这个新声明的结构体也是一个数据类型,由程序员自己声明的新类型,所以可以声明这个结构体类型的变量。

2.定义在大括号之中的变量,叫新类型的成员。必须要先声明结构体类型,再声明结构体变量。

3.结构体类型名首字母大写,并且结构体末尾需要加分号,

4.声明一个结构体变量没有初始化,成员的值是垃圾值,如果声明的同时初始化了部分,其他成员初始化为0。

5.相同类型的结构体变量是可以相互赋值的,赋值是将原结构体中的每一个成员的值挨个拷贝给目标结构体变量。

6.结构体变量名,直接代表整个结构体变量。在外面学习到的所有数据类型中,只有数组名、函数名才代表他们的地址。其他都是代表变量本身

结构体与数组

语法:struct 结构体类型名 数组名[长度];

#include <stdio.h>

int main(int argc, const char * argv[]) {
    struct Student {
        char *name;
        int age;
        int score;
    };

    //stuArray数组的每一个元素的类型是一个struct Student结构体类型
    struct Student stuArray[] = {
        {"小明",19,99},
        {"小芳",19,99},
        {"小东",18,20}
    };

    //单独初始化一个元素,需要做类型强转,不然编译器不知道赋值给数组还是结构体
    stuArray[0] = (struct Student){"小明",19,100};

    //数组长度 = 数组占的总字节数  /  每个元素占的字节数
    int length = sizeof(stuArray)/sizeof(struct Student);

    //遍历数组,打印3个学生的信息
    for(int i = 0; i < length; i++){
        printf("%s %d %d\n",stuArray[i].name,stuArray[i].age,stuArray[i].score);
    }

    return 0;
}

结构体与指针

结构体变量也是一个变量,那么这个结构体变量一定是有内存地址的,所以我们就可以搞个指针指向这个结构体变量。然后我们就可以通过指针间接操作结构体变量。

语法:struct 结构体类型名 *指针名;

#include <stdio.h>

struct Student {
    char *name;
    int age;
    int score;
};

int main(int argc, const char * argv[]) {
    //初始化变量
    struct Student stu = {"静静",19,100};

    //p指针指向stu变量
    struct Student *p = &stu;

    //第一种方式
    (*p).name = "芳芳";
    (*p).age = 18;
    (*p).score = 99;

    //第二种方式
    p->name = "东东";
    p->age = 18;
    p->score = 99;

    printf("%s %d %d\n",p->name,p->age,p->score);
    return 0;
}

结构体的嵌套

我们在为结构体写成员的时候,发现某个成员也是一个需要多个数据组成一个整体的数据,这个时候我们就可以使用结构体嵌套。在结构体内声明另一个结构体类型的变量作为成员来使用。

#include <stdio.h>

//日期结构体类型
struct Date{
    int year;
    int month;
    int day;
};

//人结构体类型
struct Person {
    char *name;
    int age;
    struct Date birthday;
};

int main(int argc, const char * argv[]) {
    //创建一个人并赋值
    struct Person zhouJianFeng = {"周剑峰",23,{2015,9,18}};
//    struct Person zhouJianFeng = {"周剑峰",23,2015,9,18};//可以成员的省略大括号

    //打印出这个人的信息
    printf("姓名:%s\n年龄:%d\n生日:%d年%d月%d日\n",zhouJianFeng.name,zhouJianFeng.age,zhouJianFeng.birthday.year,zhouJianFeng.birthday.month,zhouJianFeng.birthday.day);
    return 0;
}

结构体与函数

结构体作为函数的参数

#include <stdio.h>

//人结构体类型
struct Person {
    char *name;
    int age;
};

void test(struct Person *stu){
    stu->name = "周XX";
    stu->age = 18;
}

int main() {
    struct Person zhou = {"静静",19};
    test(&zhou);//调用函数,传入地址

    printf("%s %d\n",zhou.name,zhou.age);
    return 0;
}

结构体作为函数的返回值

#include <stdio.h>

//人结构体类型
struct Person {
    char *name;
    int age;
};

struct Person getStu(){
    struct Person stu = {"静静",19};
    return stu;//是返回一个变量的值
}

int main() {
    struct Person stu = getStu();
    printf("%s %d\n",stu.name,stu.age);
    return 0;
}