• C语言程序设计笔记(浙大翁恺版) 第十一周:结构类型


    按照中国大学MOOC上浙江大学翁恺老师主讲的版本所作,B站上也有资源。原课程链接如下:

    https://www.icourse163.org/course/ZJU-9001

    由于是大三抽空回头整理的,所以可能前五章会记的内容比较简略。此外,作为选学内容的A0:ACLLib的基本图形函数和链表两章也没有做。西电的考试是机试,理论上学到结构体就能够应付考试了,但为了以后的学习考虑建议全学。

     

    其他各章节的链接如下:

    C语言程序设计笔记(浙大翁恺版) 第一周:程序设计与C语言

    C语言程序设计笔记(浙大翁恺版) 第二周:计算

    C语言程序设计笔记(浙大翁恺版) 第三周:判断

    C语言程序设计笔记(浙大翁恺版) 第四周:循环

    C语言程序设计笔记(浙大翁恺版) 第五周:循环控制

    C语言程序设计笔记(浙大翁恺版) 第六周:数据类型

    C语言程序设计笔记(浙大翁恺版) 第七章:函数

    C语言程序设计笔记(浙大翁恺版) 第八周:数组

    C语言程序设计笔记(浙大翁恺版) 第九周:指针

    C语言程序设计笔记(浙大翁恺版) 第十周:字符串

    C语言程序设计笔记(浙大翁恺版) 第十一周:结构类型

    C语言程序设计笔记(浙大翁恺版) 第十二周:程序结构

    C语言程序设计笔记(浙大翁恺版) 第十三周:文件

     

    结构类型

    枚举

    枚举

    常量符号化

    用符号而不是具体的数字来表示程序中的数字

     

     

    枚举

    用枚举而不是定义独立的const int 变量

    枚举是一种用户定义的数据类型,它用关键字enum以如下语法来声明:enum 枚举类型名字 { 名字0, ..., 名字n };

    枚举类型名字通常并不真的使用,要用的是在大括号里的名字,因为它们就是常量符号,它们的类型是int,值依次从0到n。如:enum colors { red, yellow, green };就创建了三个常量,red的值是0,yellow是1,而green是2

    当需要一些可以排列起来的常量值时,定义枚举的意义就是给了这些常量值名字

     

    示例:

    #include 
    
    enum color { red, yellow, green };
    
    void f(enum color c);
    
    int main(void)
    {
        enum color t = red;
        
        scanf("%d", &t);
        f(t);
        
        return 0;
    }
    
    void f(enum color c)
    {
        printf("%d\n", c);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

     

    枚举量可以作为值

    枚举类型可以跟上enum作为类型

    但是实际上是以整数来做内部计算和外部输入输出的

     

     

    套路:自动计数的枚举

    示例:

    #include 
    
    enum COLOR {RED, YELLOW, GREEN, NumCOLORS};
    
    int main(int argc, char const *argv[])
    {
        int color = -1;
        char *ColorNames[NumCOLORS] = {
            "red", "yellow", "green",
        };
        char *colorName = NULL;
        
        printf("输入你喜欢的颜色的代码:");
        scanf("%d", &color);
        if ( color >= 0 && color < NumCOLORS) {
            colorName = ColorNames[color];
        } else {
            colorName = "unknown";
        }
        printf("你喜欢的颜色是%s\n", colorName);
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    这样需要遍历所有的枚举量或者需要建立一个用枚举量做下表的数组的时候就很方便了

     

     

    枚举量

    声明枚举量的时候可以指定值

    示例:

    #include 
    
    enum COLOR {RED=1, YELLOW, GREEN=5, NumCOLORS};
    
    int main(int argc, char const *argv[])
    {
        printf("code for YELLOW is %d\n", YELLOW);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    code for YELLOW is 2
    
    • 1

     

     

    枚举只是int

    即使给枚举类型的变量赋不存在的整数值也没有任何warning或error

    示例:

    #include 
    
    enum COLOR {RED=1, YELLOW, GREEN=5, NumCOLORS};
    
    int main(int argc, char const *argv[])
    {
        enum COLOR color = 0;
        
        printf("code for GREEN is %d\n", GREEN);
        printf("and color is %d\n", color);
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    code for GREEN is 5
    and color is 0
    
    • 1
    • 2

     

     

    虽然枚举类型可以当作类型使用,但是实际上不好用

    如果有意义上排比的名字,用枚举比const int方便

    枚举比宏(macro)好,因为枚举有int类型

     

    结构

    结构类型

    声明结构类型

    和本地变量一样,在函数内部声明的结构类型只能在函数内部使用。所以通常在函数外部声明结构类型,这样就可以被多个函数所使用了

     

    示例:

    #include 
    
    struct date
    {
        int month;
        int day;
        int year;
    };
    
    int main(int argc, char const *argv[])
    {
        struct date today;
        
        today.day = 07;
        today.month = 31;
        today.year=2014 ;
        
        printf("Today's date is %i-%i-%i.\n",
             today.year,today.month,today.day);
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    Today's date is 2014-31-7.
    
    • 1

     

     

    声明结构的形式

    struct point {
        int x;
        int y;
    };
    
    struct point p1,p2;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    struct {
        int x;
        int y;
    } p1,p2;
    
    • 1
    • 2
    • 3
    • 4

    p1p2并不是类型的名字,而是前面这种无名结构类型的两个变量

     

    struct point {
        int x;
        int y;
    } p1,p2;
    
    • 1
    • 2
    • 3
    • 4

    声明结构pointer,并且定义这种结构的两个变量p1p2

     

    对于第一种和第三种形式,都声明了结构point。但是第二种形式没有声明point,至是定义了两个变量

     

     

    结构变量

    struct date today;
    today.month=06;
    today.day=19;
    today.year=2005;
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

     

     

    结构的初始化

    #include 
    
    struct date
    {
        int month;
        int day;
        int year;
    };
    
    int main(int argc, char const *argv[])
    {
        struct date today = {07,31,2014};
        struct date thismonth = {.month=7, .year=2014};
        
        printf("Today's date is %i-%i-%i.\n",
             today.year,today.month,today.day);
        printf("This month is %i-%i-%i.\n",
             thismonth.year,thismonth.month,thismonth.day);
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    Today's date is 2014-7-31.
    This month is 2014-7-0.
    
    • 1
    • 2

     

     

    结构成员

    结构和数组有点像,数组用[]运算符和下标访问其成员,结构用.运算符和名字访问其成员(如:today.daystudent.firstNamep1.xp1.y

     

     

    结构运算

    要访问整个结构,直接用结构变量的名字

    对于整个结构,可以做赋值、取地址,也可以传递给函数参数

     

    示例:

    p1 = (struct point){5, 10}; // 相当于p1.x = 5; p1.y = 10;
    
    p1 = p2;                    // 相当于p1.x = p2.x; p1.y = p2.y;
    
    • 1
    • 2
    • 3

     

     

    结构指针

    和数组不同,结构变量的名字并不是结构变量的地址,必须使用&运算 如:struct date *pDate = &today;

     

    结构与函数

    结构作为函数参数

    int numberofDays(struct date d)

    整个结构可以作为函数的值传入函数,这时候是在函数内新建一个结构变量,并复制调用者的结构的值

    也可以返回一个结构

    这与数组完全不同

     

    示例:

    #include 
    #include 
    
    struct date {
        int month;
        int day;
        int year;
    };
    
    bool isLeap(struct date d);
    int numberofDays(struct date d);
    
    int main(int argc, char const *argv[])
    {
        struct date today, tomorrow;
        
        printf("Enter today's date (mm dd yyyy):");
        scanf("%i %i %i", &today.month, &today.day, &today.year);
        
        if( today.day != numberofDays(today) ) {
            tomorrow.month = today.month;
            tomorrow.day = today.day+1;
            tomorrow.year = today.year;
        } else if ( today.month == 12 ) {
            tomorrow.month = 1;
            tomorrow.day = 1;
            tomorrow.year = today.year+1;
        } else {
            tomorrow.month = today.month+1;
            tomorrow.day = 1;
            tomorrow.year = today.year;
        }
         printf("Tomorrow's date is %i-%i-%i.\n",
             tomorrow.year, tomorrow.month, tomorrow.day);
        
        return 0;
    }
    
    int numberofDays(struct date d)
    {
        int days;
        
        const int daysPerMonth[12] = 
        {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
        
        if( d.month == 2 && isLeap(d) )
            days=29;
        else
            days=daysPerMonth[d.month-1];
        
        return days;
    }
    
    bool isLeap(struct date d)
    {
        bool leap = false;
        
        if ( (d.year %4 ==0 && d.year %100 !=0) || d.year%400 == 0 )
            leap=true;
        
        return leap;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

     

     

    输入结构

    没有直接的方式可以一次scanf一个结构

     

    示例:

    如果我们打算写一个函数来读入结构

    #include 
    
    struct point {
        int x;
        int y;
    };
    
    void getStruct(struct point);
    void output(struct point);
    
    int main(int argc, char const *argv[])
    {
        struct point y = {0, 0};
        getStruct(y);
        output(y);
    }
    
    void getStruct(struct point p)
    {
        scanf("%d", &p.x);
        scanf("%d", &p.y);
        printf("%d, %d", p.x, p.y);
    }
    
    void output(struct point p)
    {
        printf("%d, %d", p.x, p.y);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OzQU7Bn7-1659891264609)(C语言程序设计.assets/image-20220731221957755.png)]

    C在函数调用时是传值的,所以函数中的pmain中的y是不同的,在函数读入了p的数值之后,没有任何东西回到了main,所以y还是{0,0}

     

     

    解决的方案

    之前的方案,把一个结构传入了函数,然后在函数中操作,但是没有返回回去。问题在于传入函数的是外面那个结构的克隆体,而不是指针。传入指针和传入数组是不同的

    在这个输入函数中,完全可以创建一个临时的结构变量,然后把这个结构返回给调用者

    示例:

    void main()
    {
        struct point y = {0, 0};
        y = inputPoint();
        output(y);
    }
    
    struct point inputPoint()
    {
        struct point temp;
        scanf("%d",&temp.x);
        scanf("%d",&temp.y);
        return temp;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

     

     

    指向结构的指针

    ->表示指针所指的结构变量中的成员

    示例:

    struct date {
        int month;
        int day;
        int year;
    } myday;
    
    struct date *p = &myday;
    
    (*p).month = 12;
    p->month = 12;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

     

    示例2:

    void main()
    {
        struct point y = {0, 0};
        inputPoint(&y);
        output(y);
    }
    
    struct point* inputPoint(struct point *p)
    {
        scanf("%d",&(p->x)); 
        scanf("%d",&(p->y));
        return p;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

     

    示例3:

    #include 
    
    struct point {
        int x;
        int y;
    };
    
    struct point* getStruct(struct point*);
    void output(struct point);
    void print(const struct point *p);
    
    int main(int argc, char const *argv[])
    {
        struct point y = {0, 0};
        getStruct(&y);
        output(y);
        output(*getStruct(&y));
        print(getStruct(&y));
        getStruct(&y)->x = 0;
        *getStruct(&y) = (struct point){1,2};
    }
    
    struct point* getStruct(struct point *p)
    {
        scanf("%d", &p->x);
        scanf("%d", &p->y);
        printf("%d, %d", p->x, p->y);
        return p;
    }
    
    void output(struct point p)
    {
        printf("%d, %d", p.x, p.y);
    }
    
    void print(const struct point *p)
    {
        printf("%d, %d", p->x, p->y);   
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

     

     

    结构中的结构

    结构数组

    struct date dates[100];
    struct date dates[] = {
        {4,5,2005},{2,4,2005};
    }
    
    • 1
    • 2
    • 3
    • 4

    示例:

    #include 
    
    struct time {
        int hour;
        int minutes;
        int seconds;
    };
    
    struct time timeUpdate(struct time now);
    
    int main(void)
    {
        struct time testTimes[] = {
            {11,59,59}, {12,0,0}, {1,29,59}, 
            {23,59,59}, {19,12,27}
        };
        int i;
        
        for ( i=0; i<5; ++i ) {
            printf("Time is %.2i:%.2i:%.2i", 
                testTimes[i].hour,  testTimes[i].minutes,
                   testTimes[i].seconds);
            
            testTimes[i] = timeUpdate(testTimes[i]);
            
            printf(" ...one second later it's %.2i:%.2i:%.2i\n",
                testTimes[i].hour, testTimes[i].minutes,
                   testTimes[i].seconds);
        }
        
        return 0;
    }
    
    
    struct time timeUpdate(struct time now)
    {
        ++now.seconds;
    	if( now.seconds == 60 ) {
            now.seconds = 0;
            ++now.minutes;
    		
    		if( now.minutes == 60 ) {
                now.minutes = 0;
                ++now.hour;
                
                if ( now.hour == 24 ) {
    				now.hour = 0;
    			}
    		}
    	}
        
        return now;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

     

     

    结构中的结构

    struct dateAndTime {
        struct date sdate;
        struct time stime;
    };
    
    • 1
    • 2
    • 3
    • 4

     

     

    嵌套的结构

    示例:

    struct point {
        int x;
        int y;
    };
    
    struct rectangle {
        struct point pt1;
        struct point pt2;
    };
    
    struct rectangle r;
    r.pt1.x = 2;
    r.pt2.y = 3;
    
    
    
    
    
    struct rectangle r,*rp;
    rp = &r;
    
    // 下面的四种形式是等价的
    r.pt1.x = 2;
    rp->pt1.x = 2;
    (r.pt1).x = 2;
    (rp->pt1).x = 2;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

     

     

    结构中的结构的数组

    示例:

    #include 
    
    struct point {
        int x;
        int y;
    };
    
    struct rectangle {
        struct point p1;
        struct point p2;
    };
    
    void printRect(struct rectangle r)
    {
        printf("<%d, %d> to <%d, %d>\n",
               r.p1.x, r.p1.y, r.p2.x, r.p2.y);
    }
    
    int main(int argc,char const *argv[])
    {
        int i;
        struct retangle rects[] = {
            {{1, 2}, {3, 4}},
            {{5, 6}, {7, 8}}
        }; // 2 rectangle
        for ( i=0; i<2; i++ ) {
            printRect(rects[i]);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

     

    联合

    类型定义

    自定义数据类型(typedef

    C语言提供了一个叫做typedef的功能来声明一个已有的数据类型的新名字。比如:typedef int Length; 使得Length成为int类型的别名。这样,Length这个名字就可以代替int出现在变量定义和参数声明的地方了:Length a,b,len;Length numbers[10];

     

     

    typedef

    声明了新的类型的名字,新的名字是某种类型的别名,改善了程序的可读性

    在这里插入图片描述

    最后一个单词是新的名字,出现在typedef和新的名字之间的是原来的类型

    示例:

    typedef struct {
        int month;
        int day;
        int year;
    } Date;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    表示不关心struct叫什么,将这样的struct命名为Date。如果没有typedef,表示一个没有名字的struct有一个变量叫Date

     

    示例2:

    typedef int Length;  // Length就等价于int类型
    
    typedef *char[10] Strings; // Strings是10个字符串的数组的类型 
    // typedef char* Strings[10]; ?
    
    
    typedef struct node {
        int data;
        struct node *next;
    } aNode;
    
    typedef struct node aNode; //这样用aNode就可以代替struct node
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

     

    联合

    存储时所有的成员共享一个空间,同一时刻只有一个成员是有效的,union大小是其最大的成员

    初始化对第一个成员做初始化

     

    示例:

    选择成员是一个int i 还是一个char c

    在这里插入图片描述

     

    示例2:

    用Union得到一个数据内部的各个字节

    #include 
    
    typedef union {
        int i;
        char ch[sizeof(int)];
    } CHI;
    
    int main(int argc, char const *argv[])
    {
        CHI chi;
        int i;
        chi.i = 1234;
        for ( i=0; i<sizeof(int); i++ ) {
            printf("%02hhX", chi.ch[i]);
        }
        printf("\n");
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    D2040000
    
    • 1

    %02hhX表示期望输出16进制,并且就是1个字节不做扩展,显示为2个16进制数字,不足补零

    我们现在用的x86 CPU采用小端存储,内存中的一个数据低位在前

    在这里插入图片描述

  • 相关阅读:
    IntelliJ IDEA 2023.2.1 Android开发变化
    MySQL索引
    大数据,对于生活的改变
    操作系统备考学习 day7 (2.3.4 ~ 2.3.5)
    自动驾驶感知的AI力量:大模型在车辆感知中的应用
    使用sqlmap获取数据步骤
    jmeter接口测试
    ClickHouse监控及备份
    【毕设级项目】基于嵌入式的智能家居控制板(完整工程资料源码)
    自动从Android上拉取指定文件
  • 原文地址:https://blog.csdn.net/zimuzi2019/article/details/126220044