枚举

常量符号化

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

枚举

  • 用枚举而不是定义独立的const int变量
  • 枚举是一种用户定义的数据类型,它用关键字enum以如下语法来声明:
    • enum name {name1, ..., namen};
  • 枚举类型的名字并不真的使用,要用的是在大括号里的名字,因为它们就是常量符号,它们的类型是int,值则依次从0n。如:
    • enum colors {red, yellow, green};
  • 这样就创建了三个常量,red的值是0yellow是1,而green是2。
  • 当需要一些可以排列起来的常量值时,定义枚举的意义就是给了这些常量名字
  • 注意:
    • 枚举量可以作为值
    • 枚举类型可以跟上enum作为类型
    • 但是实际上是以整数来做内部计算和外部输入输出的
    • 虽然枚举类型可以当作类型使用,但是实际上很少用
    • 如果有意义上排比的名字,用枚举比const int方便
    • 枚举比宏(macro)好,因为枚举有int类型
  1. #include "stdio.h"
  2. enum color
  3. {
  4. red,
  5. yellow,
  6. green,
  7. };
  8. void f(enum color c);
  9. int main(void)
  10. {
  11. enum color temp = red;
  12. scanf("%d", &temp);
  13. f(temp);
  14. return 0;
  15. }
  16. void f(enum color c)
  17. {
  18. printf("%d", c);
  19. }

套路:自动计数的枚举

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

枚举量

  • 声明枚举量的时候可以指定值
  1. enum color
  2. {
  3. red = 1,
  4. yellow,
  5. green = 5,
  6. };

枚举只是int

  • 即使给枚举类型的变量符不存在的整数值也没有任何warningerror

结构

结构类型

声明结构类型

  1. int main()
  2. {
  3. struct date
  4. {
  5. int year;
  6. int month;
  7. int day;
  8. };
  9. struct date today;
  10. today.year = 2022;
  11. today.month = 1;
  12. today.day = 9;
  13. return 0;
  14. }

在函数内/外?

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

声明结构的形式

  1. // 第一种形式
  2. struct point
  3. {
  4. int x;
  5. int y;
  6. };
  7. struct point p1, p2;
  8. // p1 和 p2都是point 里面有x和y的值
  9. // 第二种形式
  10. struct
  11. {
  12. int x;
  13. int y;
  14. }p1, p2;
  15. // p1和p2都是一种无名结构,里面有x和y
  16. // 第三种形式
  17. struct point
  18. {
  19. int x;
  20. int y;
  21. }p1, p2;
  22. // p1和p2都是point里面有x和y的值
  23. // 第一种和第三种形式,都申明了结构point。但是第二种形式没有声明point,只是定义了两个变量

结构变量

struct date today;
today.month = 06;
today.day = 19;
today.year = 2005;

image.png

结构的初始化

#include "stdio.h"

struct date
{
    int month;
    int day;
    int year;
};

int main()
{
    struct date today = {01, 9, 2022};
    struct date thismonth = {.month = 1, .year = 2022};

    printf("Today's date is %i-%i-%i\n", today.year, today.month, today.day);
    printf("Today's date is %i-%i-%i", thismonth.year, thismonth.month, thismonth.day);

    return 0;
}

// Today's date is 2022-1-9
// Thismonth's date is 2022-1-0

结构成员

  • 结构和数组有点像(单元和成员)
  • 数组用[]运算符和下标访问其成员
    • a[0] = 10;
  • 结构用.运算符和名字访问其成员
    • toady.day
    • student.firstName
    • pl.x
    • pl.y

结构运算

  • 要访问整个结构,直接用结构变量的名字
  • 对于整个结构,可以做赋值,取地址,也可以传递给函数参数
    • p1 = (struct point){5, 10}; // 相当于pl.x = 5; pl.y = 10;
    • p1 = p2; // 相当于p1.x = p2.x; p1.y = p2.y;

结构指针

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

结构与函数

结构作为函数参数

int fnNumberOfDays(struct date d)

  • 整个结构可以作为参数的值传入参数
  • 这时候是在函数内新建一个结构变量,并复制调用者的结构的值
  • 也可以返回一个结构
  • 这与数组完全不同

输入结构

  • 没有直接的方式可以一次scanf一个结构
  • 没有我们打算写一个函数来读入结构
  • 但是读入的结构如何送回来呢?
  • 记住C在函数调用时是传值的
    • 所以函数中的pmain中的y是不同的
    • 在函数读入了p的数值之后,没有任何东西回到main,所以y还是{0, 0}
#include "stdio.h"

struct point
{
    int x;
    int y;
};

void getStruct(struct point);
void outStruct(struct point);

int main()
{
    struct point y = {0, 0};
    getStruct(y);
    outStruct(y);

    return 0;
}

void getStruct(struct point p)
{
    scanf("%d", &p.x);
    scanf("%d", &p.y);
    printf("p.x=%d, p.y=%d\n", p.x, p.y);
}

void outStruct(struct point p)
{
    printf("p.x=%d, p.y=%d\n", p.x, p.y);
}

// 12 24
// p.x=12, p.y=24
// p.x=0, p.y=0

解决的方案

  • 之前的方案,把一个结构传入了函数,然后在函数中操作,但是没有返回回去
    • 问题在于传入函数的是外面那个结构的克隆体,而不是指针
      • 传入结构和传入数组是不同的
  • 在这个输入函数中,完全可以创建一个临时的结构变量,然后把这个结构返回给调用者
int main()
{
    struct point y = {0, 0};
    y = getStruct(y);
    outStruct(y);

    return 0;
}

struct point getStruct(struct point p)
{
    scanf("%d", &p.x);
    scanf("%d", &p.y);
    printf("p.x=%d, p.y=%d\n", p.x, p.y);

    return p;
}

结构指针作为参数(推荐)

“If a large structure is to be passed o a function, it is generally more efficient to pass a pointer than to copy the whole structure”

指向结构的指针

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

struct date
{
    int month;
    int day;
    int year;
} myday;

struct date *p = &myday;

(*p).month = 12;
p->month = 12;

结构指针参数

#include "stdio.h"

struct point
{
    int x;
    int y;
};

struct point *getStruct(struct point *);
void outStruct(struct point);

int main()
{
    struct point y = {0, 0};
    getStruct(&y);
    outStruct(y);

    return 0;
}

struct point *getStruct(struct point *p)
{
    scanf("%d", &(p->x));
    scanf("%d", &(p->y));
    printf("p.x=%d, p.y=%d\n", p->x, p->y);

    return p;
}

void outStruct(struct point p)
{
    printf("p.x=%d, p.y=%d\n", p.x, p.y);
}

// 12 24
// p.x=12, p.y=24
// p.x=12, p.y=24

结构中的结构

结构数组

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

结构中的结构

struct dateAndTime
{
    struct date sdate;
    struct time stime;
};

嵌套的结构

struct point
{
    int x;
    int y;
};

struct rectangle
{
    struct point point1;
    struct point point2;
};

struct rectangle r;
r.point1.x, r.point1.y
r.point1.x, r.point1.y

如果有变量定义:

struct rectangle r, *rp;
rp = &r;

那么下面的四种形式是等价的

r.pt1.x
rp->pt1.x
(r.pt1).x
(rp->pt1).x
// 从左往右

但是没有rp->pt1->x(因为pt1不是指针)

一些讨论

结构和数组的联系与区别

结构与数组都是数据的集合,但是数组只能存放相同类型的元素,结构可以不同类型元素。其次,数组的类型名就代表数组的首地址,不需要取地址符号&

使用sizeof&深入研究结构

联合

类型定义

自定义数据类型(typedef

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

    • Length a, b, len;
    • Lenth numbrs[10];

      Typedef

  • 声明行的类型的名字

    • 新的名字是某种类型的别名
    • 改善了程序的可读性 ```c typedef long int64_t; // 重载已有的类型名字新名字的含义更加清晰具有可移植性 typedef struct ADate { int month; int day; int year; } Date; // 简化了复杂的名字

int64_t i = 10000000000; Data a = {9, 1, 2005};

<a name="UybjO"></a>
### `typedef`
```c
typedef struct
{
    int month;
    int day;
    int year;
} Date;

// 如果没有typedef没有名字的struct的变量Date
typedef int Length; // Length就等价于int类型
typedef char *Strings[10]; // Strings是10个字符串的数组类型

typedef struct node
{
    int date;
    struct node *next;
} aNode;

或

typedef struct node aNode; // 这样用aNode就可以代替

联合

  • 存储
    • 所有的成员共享一个空间
    • 同一时间只有一个成员是有效的
    • union的大小是其最大的成员
  • 初始化
    • 对第一个成员做初始化

union的用处

可以得到一个整数,在内存中的存储问题

#include "stdio.h"

typedef union
{
    int i;
    char ch[sizeof(int)];
} CHI;

int main()
{
    CHI chi;
    chi.i = 1234;
    for (int i = 0; i < sizeof(int), i++)
    {
        printf("%02hhx", chi.ch[i]);
    }
    printf("\n");
    return 0;
}