C语言编程基础-15结构体 枚举 联合

结构体

可以用来统一存储不同类型的数据;

  • 结构体仅仅是一种数据类型,它本身不占有存储空间;
  • 结构体仅仅描述了多种数据之间的关系,是一种结构信息;
  • 结构体可以用来声明结构体变量,结构体变量实际占有内存空间;

结构体声明

  • 声明结构体需要使用 struct 关键字;
  • 结构体声明中包含多个变量声明语句,这些语句不会分配存储位置,因此不能在此初始化(会分配存储空间);
  • 结构体声明本身可以放在头文件中;
  • 可以在声明结构体的时候立刻声明结构体变量(但这种做法不好);
  • 可以使用 struct 关键字和结构体名称一起作为数据类型名称来声明或定义变量;

struct演示

/*
 * struct演示
 */
#include <stdio.h>
struct person {//声明结构体(名称)
    int id;
    char name[20]; //描述结构信息
    float salary; //不能在此初始化
}; //结构体声明语句,不占用存储空间
typedef struct person personinfo; //给结构体取别名
int main() {
    personinfo p1;    //声明结构体变量
    return 0;
}

typedef 关键字

typedef 关键字可以用来给数据类型起别名,例如:

typedef struct person personinfo;

其中 struct person 是已经存在的结构体,后面的personinfo是 struct person 的别名;

只能使用 typedef 关键字给数据类型起别名,而不可以使用宏;因为宏是在编译时直接替换;

int* p_num = NULL, p_num1 = NUll;其实只声明了第一个变量为指针变量;

可以在声明结构体的同时立刻起别名,这个时候可以省略结构体本身的名称;

在C语言中结构体内部不可以包含函数;

/*
 * struct演示
 */
#include <stdio.h>
typedef struct person { // 声明结构体(名称)
    int id;
    char name[20]; // 描述结构体信息
    float salary; // 不能在此初始化
} personinfo;
// 本方法同时完成两个工作,声明和起别名,此处可以省略person结构体名称
//typedef struct {
//    int id;
//    char name[20];
//    float salary;
//} personinfo;

int main() {
    personinfo p1;    //声明结构体变量
    return 0;
}

使用如下格式可以表示结构体变量中的某个子变量 结构体变量名称.子变量名称

注意.运算符的优先级非常高;

结构体变量也可以初始化,初始化的语法和数组初始化的语法一样;

结构体练习1

声明一个结构体表示手机,包含以下信息:品牌,型号,价格; 然后声明一个结构体变量表示iphone5;

/*
 * 声明一个结构体表示手机;包含以下信息:
 * 品牌,型号,价格;然后声明一个结构体变量表示iphone5
 */
#include <stdio.h>
#include <string.h>
typedef struct phone {
    char brand[10];
    char type[10];
    float price;
} phone;

int main() {
    //struct phone iphone5;
    phone iphone5 = {
        "Apple",
        "Iphone5",
        4000.0f
    }; //初始化的方式赋值
    //strcpy(iphone5.brand, "Apple");
    //strcpy(iphone5.type, "Iphone5");
    //iphone5.price = 4000.0f;

    printf("brand:%s,type:%s,price:%g\n", iphone5.brand, iphone5.type, iphone5.price);
    return 0;
}

同类型的结构体变量之间可以直接赋值,不需要逐个子变量赋值;

因此结构体变量可以作为函数的形参和返回值,但最好不这样做,因为结构体所占空间可能很大,作为参数传递时执行效率不高; 可以使用结构体指针类型作为形参或返回值;

结构体练习2

声明一个结构体表示屏幕上的一个点,从键盘得到两个点的位置信息,并记录在两个结构体变量中, 计算出两个点的中点位置信息并记录在一个新的结构体变量中,把新结构体变量的内容打印在屏幕上;

/*
 * 结构体变量练习
 */
#include <stdio.h>
typedef struct {
    int x, y;
} Point;
Point midpt(Point start, Point end) {
    Point mid = {};
    mid.x = (start.x + end.x) / 2;
    mid.y = (start.y + end.y) / 2;
    return mid;
    // 可以返回结构体类型,但是效率不高 
}
int main() {
    Point start = {}, end = {}, mid = {};    //初始化为0
    printf("请输入第一个点的位置: ");
    scanf("%d%d", &start.x, &start.y);
    printf("请输入第二个点的位置: ");
    scanf("%d%d", &end.x, &end.y);
    // 同类型结构体变量可以直接赋值 
    mid = midpt(start, end);
    printf("新坐标: %d,%d\n", mid.x, mid.y);
    return 0;
}

结构体变量指针

通过结构体指针表示结构体变量内部子变量时需要使用->操作符,例如

    p_start->num

p_start是一个结构体指针,p_start->num表示p_start所指向的结构体变量start中的num成员;

(*p_start).num等价于p_start->num;

以下三种形式等价

  1. start.成员名;
  2. (*p_start).成员名;
  3. p_start->成员名;

结构体指针->称为指向运算符;

  • p->n 得到p指向的结构体变量中的成员n的值;
  • p->n++ 得到p指向的结构体变量中的成员n的值,用完该值后使它+1;
  • ++p->n 得到p指向的结构体变量中的成员n的值+1,然后再使用它;

取成员运算符.->的优先级都很高,高于自增++和自减--运算符;

使用结构体变量作为形参或返回值会浪费时间和空间,所以应该使用结构体指针作为形参和返回值;

/*
 * 结构体变量指针做返回值
 */
#include <stdio.h>
typedef struct {
    int x, y;
} Point;
Point *midpt(const Point *p1, const Point *p2, Point *pm) {
    pm->x = (p1->x + p2->x) / 2;
    pm->y = (p1->y + p2->y) / 2;
    return pm; // 返回一个结构体指针; 
    // 数据通过一个指针带回 
}

int main() {
    Point start = {}, end = {}, mid = {}; //初始化为0;
    printf("请输入第一个点的位置: ");
    scanf("%d%d", &start.x, &start.y);
    printf("请输入第二个点的位置: ");
    scanf("%d%d", &end.x, &end.y);
    midpt(&start, &end, &mid); //&mid称为输出参数;
    printf("新坐标: %d,%d\n", mid.x, mid.y);
    return 0;
}

结构体变量指针练习

使用结构体变量,将一个代表点的位置打印在屏幕上

/*
 * 结构体变量与结构体指针
 */
#include <stdio.h>
typedef struct {
    int x, y;
} Point;
Point *midpt(const Point *p1, const Point *p2, Point *p_mid) {
    p_mid->x = (p1->x + p2->x) / 2;
    p_mid->y = (p1->y + p2->y) / 2;
    return p_mid;
}
void print(const Point *p_pt) {
    printf("(%d,%d)\n", p_pt->x, p_pt->y);
}
int main() {
    Point start = {}, end = {}, mid = {};
    printf("请输入第一个点的位置: ");
    scanf("%d%d", &start.x, &start.y);
    printf("请输入第二个点的位置: ");
    scanf("%d%d", &end.x, &end.y);
    midpt(&start, &end, &mid);
    printf("中心点坐标: %d,%d\n", mid.x, mid.y);
    print(midpt(&start, &end, &mid));
    return 0;
}

输入矩形对角线上的两个点的坐标,计算矩形的面积

/*
 * 输入矩形对角线上的两个点的坐标
 * 计算矩形的面积
 */
#include <stdio.h>
typedef struct {
    int x, y;
} Point;
typedef struct {
    Point pt1, pt2;
} Rectangle;
int area(const Rectangle *p_rect) {
    int ret = 0;
    ret = (p_rect->pt1.x - p_rect->pt2.x) * (p_rect->pt1.y - p_rect->pt2.y);
    return ret > 0 ? ret : 0 - ret; //保证返回非负
}
int main() {
    Rectangle r1 = { };
    printf("请输入第一个点的位置: ");
    scanf("%d%d", &r1.pt1.x, &r1.pt1.y);
    printf("请输入第二个点的位置: ");
    scanf("%d%d", &r1.pt2.x, &r1.pt2.y);
    printf("area is %d\n", area(&r1));
    return 0;
}

数据结构对齐补齐

  1. 数据对齐Alignment
  • 结构体中不同类型的数据在内存中按照一定的规则排列,而不总是一个挨一个的顺序排放(便于寻址);
  • 任何成员变量的地址必须是其对齐参数A的整数倍,这个规则叫做数据对齐;

数据对齐会造成结构体内部不同成员变量之间有空隙;

  • 每个成员变量的对齐参数A取值规则:
    • a=min(n, max(self)) 普通类型取#pragma pack(n)、自身大小,二者最小值;
    • a=min(n, max(sizeof element...)) 结构体类型取#pragma pack(n)、最大子变量,二者最小值;

    n为系统对齐参数,一般默认为4,可以通过#pragma pack()类宏自定义

  1. 数据补齐Completion
  • 一个结构体变量的大小必须是C的整数倍,这个规则叫数据补齐;
  • 这种补齐可能造成结构体在最后多占用一些浪费的字节;
  • 结构体补齐参数C的取值规则:c=max(A) 即所有成员的对齐参数A的最大值(一定不会超过pack值,试证明之);

分析举例

// x *
// y y
// z *
typedef struct {
    char c1;  //s=1, a=min(4,1), @+0;对齐参数a=1;
    short s;  //s=2, a=min(4,2), @+2;对齐参数a=2;
    char c2;  //s=1, a=min(4,1), @+4;对齐参数a=1;
} ST1; //6 = n*2, c=max(a)==2;补齐参数c=2

// x x y y
// y y y y
// z z z z
// m * * *
typedef struct {
    short s;  //s=2, a=min(4,2), @+0;对齐参数a=2;
    ST1 st1;  //s=6, a=min(4,max(1,2,1)), @+2;对齐参数a=2;
    int i;    //s=4, a=min(4,4), @+8;对齐参数a=4;
    char c;   //s=1, a=min(4,1), @+12;对齐参数a=1;
} ST7; //16 = n*4, c=max(a)==4;补齐参数c=4;

struct成员对齐,总体补齐

  • 普通类型成员,对齐参数A为(自身类型大小和指定对齐参数n)二者的最小值,即a=min(n, max(self));
  • 结构体类型的成员,对齐参数A为其所有成员对齐参数中的最大值,即a=min(n, max(sizeof element...));
  • 结构体总长度为补齐参数C的整数倍,补齐参数C为所有成员对齐参数最大值,即c=max(A);

结构体中子变量的顺序会影响结构体的大小,占用空间小的子变量写前边可以节约内存空间;

数据结构对齐补齐演示

// 数据结构对齐补齐演示
#include <stdio.h>
#pragma pack(4)

//假设存储位置都起始于0
#define OFFSET_OF(type, member)  ((size_t)(&((type *)0)->member))

#define OO1(t, m1)                 #m1,OFFSET_OF(t, m1)
#define OO2(t, m1, m2)             OO1(t, m1),             OO1(t, m2)
#define OO3(t, m1, m2, m3)         OO2(t, m1, m2),         OO1(t, m3)
#define OO4(t, m1, m2, m3, m4)     OO3(t, m1, m2, m3),     OO1(t, m4)
#define OO5(t, m1, m2, m3, m4, m5) OO4(t, m1, m2, m3, m4), OO1(t, m5)

#define SHOW_OO1(t, m1)                 \
    printf("\n%s offset: %s:%ld\n",                     #t,OO1(t,m1))
#define SHOW_OO2(t, m1, m2)             \
    printf("\n%s offset: %s:%ld, %s:%ld\n",                #t,OO2(t,m1,m2))
#define SHOW_OO3(t, m1, m2, m3)         \
    printf("\n%s offset: %s:%ld, %s:%ld, %s:%ld\n",           #t,OO3(t,m1,m2,m3))
#define SHOW_OO4(t, m1, m2, m3, m4)     \
    printf("\n%s offset: %s:%ld, %s:%ld, %s:%ld, %s:%ld\n",      #t,OO4(t,m1,m2,m3,m4))
#define SHOW_OO5(t, m1, m2, m3, m4, m5) \
    printf("\n%s offset: %s:%ld, %s:%ld, %s:%ld, %s:%ld, %s:%ld\n", #t,OO5(t,m1,m2,m3,m4,m5))

int main() {
    // x x x
    typedef struct {
        char c1; //s=1, a=1, @+0
        char c2; //s=1, a=1, @+1
        char c3; //s=1, a=1, @+2
    } STC; //3 = n*1, c=max(a)==1
    SHOW_OO3(STC, c1, c2, c3); //0,1,2
    printf("STC size: %ld\n", (long)sizeof(STC)); //3

    // x *
    // y y
    // y y
    typedef struct {
        char c;  //s=1, a=1, @+0
        short s1;//s=2, a=2, @+2
        short s2;//s=2, a=2, @+4
    } STS; //6 = n*2, c=max(a)==2
    SHOW_OO3(STS, c, s1, s2); //0,2,4
    printf("STS size: %ld\n", (long)sizeof(STS)); //6

    // x x * *
    // y y y y
    typedef struct {
        char str[2]; //s=2, a=min(4,2), @+0;
        int i;       //s=4, a=min(4,4), @+4;
    } ST0; //8 = n*4, c=max(a)==4
    SHOW_OO2(ST0, str, i); //0,4
    printf("ST0 size: %ld\n", (long)sizeof(ST0)); //8

    // x *
    // y y
    // z *
    typedef struct {
        char c1;  //s=1, a=min(4,1), @+0;
        short s;  //s=2, a=min(4,2), @+2;
        char c2;  //s=1, a=min(4,1), @+4;
    } ST1; //6 = n*2, c=max(a)==2
    SHOW_OO3(ST1, c1, s, c2);//0,2,4
    printf("ST1 size: %ld\n", (long)sizeof(ST1)); //6

    // x * y y
    // z * * *
    // m m m m
    typedef struct {
        char c1;  //s=1, a=min(4,1), @+0;
        short s;  //s=2, a=min(4,2), @+2;
        char c2;  //s=1, a=min(4,1), @+4;
        int i;    //s=4, a=min(4,4), @+8;
    } ST2; //12 = n*4, c=max(a)==4
    SHOW_OO4(ST2, c1, s, c2, i); //0,2,4,8
    printf("ST2 size: %ld\n", (long)sizeof(ST2)); //16

    // x * y y
    // z * * *
    // m m m m
    // m m m m
    typedef struct {
        char c1;  //s=1, a=min(4,1), @+0;
        short s;  //s=2, a=min(4,2), @+2;
        char c2;  //s=1, a=min(4,1), @+4;
        int *pi;  //s=8, a=min(4,8), @+8;
    } ST3; //16 = n*8, c=max(a)==8
    SHOW_OO4(ST3, c1, s, c2, pi); //0,2,4,8
    printf("ST3 size: %ld\n", (long)sizeof(ST3)); //12

    // x x * *
    // y y y y
    // z m * *
    typedef struct {
        short s;  //s=2, a=min(4,2), @+0;
        int i;    //s=4, a=min(4,4), @+4;
        char c1;  //s=1, a=min(4,1), @+8;
        char c2;  //s=1, a=min(4,1), @+9;
    } ST4; //12 = n*4, c=max(a)==4
    SHOW_OO4(ST4, s, i, c1, c2); //0,4,8,9
    printf("ST4 size: %ld\n", (long)sizeof(ST4)); //12

    // x x x x
    // x x * *
    // y y y y
    // z z m *
    typedef struct {
        ST1 st1;  //s=6, a=min(4,max(1,2,1)), @+0;
        int i;    //s=4, a=min(4,4), @+8;
        short s;  //s=2, a=min(4,2), @+12;
        char c;   //s=1, a=min(4,1), @+14;
    } ST5; //16 = n*4, c=max(a)==4
    SHOW_OO4(ST5, st1, i, s, c); //0,8,12,14
    printf("ST5 size: %ld\n", (long)sizeof(ST5)); //16

    // x x x x
    // x x y y
    // z z z z
    // m * * *
    typedef struct {
        ST1 st1;  //s=6, a=min(4,max(1,2,1)), @+0;
        short s;  //s=2, a=min(4,2), @+6;
        int i;    //s=4, a=min(4,4), @+8;
        char c;   //s=1, a=min(4,1), @+12;
    } ST6; //16 = n*4, c=max(a)==4
    SHOW_OO4(ST6, st1, s, i, c); //0,6,8,12
    printf("ST6 size: %ld\n", (long)sizeof(ST6)); //16

    // x x y y
    // y y y y
    // z z z z
    // m * * *
    typedef struct {
        short s;  //s=2, a=min(4,2), @+0;
        ST1 st1;  //s=6, a=min(4,max(1,2,1)), @+2;
        int i;    //s=4, a=min(4,4), @+8;
        char c;   //s=1, a=min(4,1), @+12;
    } ST7; //16 = n*4, c=max(a)==4
    SHOW_OO4(ST7, s, st1, i, c); //0,2,8,12
    printf("ST7 size: %ld\n", (long)sizeof(ST7)); //16

    return 0;
}

系统对齐参数定义

#pragma pack(n)         //设置编译器按照n个字节对齐,n可以取1,2,4,8,16
#pragma pack()          //默认4字节对齐

#pragma pack(push)      //将当前的对齐字节数压入栈顶,不改变对齐字节数
#pragma pack(push,n)    //将当前的对齐字节数压入栈顶,并按照n字节对齐
#pragma pack(pop)       //弹出栈顶对齐字节数,不改变对齐字节数
#pragma pack(pop,n)     //弹出栈顶并直接丢弃,按照n字节对齐
#pragma pack(push,1)    //可以指定结构的对齐和补齐的字节数
#pragma pack(pop)       //恢复push前的值

#pragma pack()应用示例

#define ASSERT_CONCAT_(a, b) a##b
#define STATIC_ASSERT(e, msg) ;enum { ASSERT_CONCAT_(assert_line_, __LINE__) = 1/(!!(e)) }
#pragma pack(push,1)

#if 0
RTP Header
 0               1               2               3
 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X|   CC  |M|     PT      |       sequence number         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           timestamp                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|            synchronization source (SSRC) identifier           |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|              contributing source (CSRC) identifier            |
|                         ...defined by CC                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+---------------------------------------------------------------+
|       payload                                                 |
|         ...                                                   |
+---------------------------------------------------------------+
#endif
// rfc3550 rtp header should encode to network sequence
// The struct must be 12 bytes long, so force the compiler to pack it
// into a 12bytes long struct and not padding it.

typedef struct _RTPHeader_T {
# if LOCAL_LITTLE_ENDIAN /* little endian */
    uint32_t CsrcCnt:4;   /* 4bit */
    uint32_t Extension:1; /* 1bit */
    uint32_t Padding:1;   /* 1bit */
    uint32_t Version:2;   /* 2bit */

    uint32_t PayloadType:7; /* 7bit */
    uint32_t Marker:1;    /* 1bit */
# else /* big endian */
    uint32_t Version:2;   /* 2bit */
    uint32_t Padding:1;   /* 1bit */
    uint32_t Extension:1; /* 1bit */
    uint32_t CsrcCnt:4;   /* 4bit */

    uint32_t Marker:1;    /* 1bit */
    uint32_t PayloadType:7; /* 7bit */
# endif
    uint32_t SeqNo:16;    /* 16bit */

    uint32_t TimeStamp;   /* 32bit */
    uint32_t Ssrc;        /* 32bit */
} RTPHeader; //12bytes

STATIC_ASSERT(sizeof(RTPHeader)==12, "RTPHeader size doesn't seem to be cool.");
#pragma pack(pop)
// decode from network sequence
RTPHeader rtpHeader;
# if _RTP_CODEC_MANUAL
    rtpHeader.Version = (((buff[0] & 0xFF) >> 6) & 0x03); /* 2bit */
    rtpHeader.Padding = (((buff[0] & 0xFF) >> 5) & 0x01); /* 1bit */
    rtpHeader.Extension = (((buff[0] & 0xFF) >> 4) & 0x01); /* 1bit */
    rtpHeader.CsrcCnt = (buff[0] & 0x0F); /* 4bit */
    rtpHeader.Marker = (((buff[1] & 0xFF) >> 7) & 0x01); /* 1bit */
    rtpHeader.PayloadType = (buff[1] & 0x7F); /* 7bit */
    rtpHeader.SeqNo = (buff[2] << 8) + buff[3]; /* 16bit */
    rtpHeader.TimeStamp = (buff[4] << 24) + (buff[5] << 16) + (buff[6] << 8) + buff[7]; /* 32bit */
    rtpHeader.Ssrc = (buff[8] << 24) + (buff[9] << 16) + (buff[10] << 8) + buff[11]; /* 32bit */
#else
    RTPHeader *pHeader = (RTPHeader *)buff;
    std::memcpy(&rtpHeader, buff, 12);
    rtpHeader.SeqNo = ntohs(pHeader->SeqNo);
    rtpHeader.TimeStamp = ntohl(pHeader->TimeStamp);
    rtpHeader.Ssrc = ntohl(pHeader->Ssrc);
# endif

结构体练习

一个班有5个学生,每个学生参加三门期末考试(语文,数学,英语); 用一个结构体记录一个学生的所有成绩;编写函数计算出这个班每门科目的平均成绩并记录在一个结构体中,最后把所有平均成绩打印在屏幕上;

/*
 * 班级成绩管理
 */
#include <stdio.h>
typedef struct {
    int chn, math, eng;
} rank;
rank *average(const rank *p_rank, int size, rank *p_ave) {
    int num = 0;
    for (num = 0; num < size; num++) {
        p_ave->chn += (p_rank + num)->chn;
        p_ave->math += (p_rank + num)->math;
        p_ave->eng += (p_rank + num)->eng;
    }
    p_ave->chn /= size;
    p_ave->math /= size;
    p_ave->eng /= size;
    return p_ave;
}
int main() {
    rank ranks[5] = { };
    rank ave_rank = { };
    int num = 0;
    for (num = 0; num <= 4; num++) {
        printf("请输入3门考试成绩: ");
        scanf("%d%d%d", &ranks[num].chn, &ranks[num].math,
              &ranks[num].eng);
    }
    average(ranks, 5, &ave_rank);
    printf("语文平均: %d, 数学平均: %d, 英语平均: %d\n",
            ave_rank.chn, ave_rank.math, ave_rank.eng);
    return 0;
}

枚举

枚举类型可以把一组相关名称转换成数字; 使用关键字 enum 声明枚举类型;与struct关键字用法类似,但成员之间用逗号,分隔;

  • 转换成的数字默认从0开始依次递增,也可以指定起始数据;
  • 如果不关心转换成的数值就可以使用枚举类型;
  • 如果对转换成的数值有要求就需要使用宏;

声明枚举默认从0开始

enum {
    SPR, SUM, AUT, WIN
}; //声明匿名枚举类型并使用默认起始数据

声明枚举指定起始数据

enum {
    SPR = 10, SUM, AUT, WIN 
}; //声明匿名枚举类型并指定起始数据

声明具名枚举

enum Season_E {
    SPR, SUM, AUT, WIN 
};

声明具名枚举并定义别名为Season

typedef enum Season_E {
    SPR, SUM, AUT, WIN 
} Season;

声明匿名枚举并定义别名为Season

typedef enum {
    SPR, SUM, AUT, WIN 
} Season;

枚举使用演示

#include <stdio.h>
typedef enum {
    SPR, SUM, AUT, WIN 
} Season;
int main() {
    // int season = 0;
    Season season = 0;
    printf("SUM = %d\n", SUM);
    printf("type season: ");
    scanf("%d", (int *)&season);
    printf("your type %s summer\n", (season == SUM) ? "is" : "is not");
    printf("AUT is %d\n", AUT);
    return 0;
}

联合

联合也是一种数据类型,其中包括多个子变量; 联合使用 union 关键字声明;

可以使用typedef给联合类型取别名;

typedef union un {
    char ch[2];
    int num;
} un;
  • 联合中所有子变量都占有同样的存储位置,他们之间是互相影响的;
  • 联合的大小是其中占用存储空间最大的子变量的大小;
  • 联合中不同的子变量可以看成是对同样内存空间的不同使用方式;
  • 联合主要在当内存较小时能够充分利用内存;
/*
 * union
 */
#include <stdio.h>
typedef union un {
    char ch[2];
    int num;
} un;
int main() {
    un un1 = { };
    un1.num = 0;
    un1.ch[0] = 'a';
    un1.ch[1] = 'b';
    printf("un1.num = 0x%x\n", un1.num); //0x6261 //'b','a'
    // ch和num的起始地址一样;
    printf("&un1.ch = %p, &un1.num = %p\n", &un1.ch, &un1.num);
    printf("sizeof(un1) = %d\n", sizeof(un1));
    return 0;
}

练习

编写函数从已知的两条线段中找到比较长的那条并赋值给返回值;

/*
 * 线段练习
 * 编写函数从已知的两条线段中找到比较长的那条并赋值给返回值;
 */
#include <stdio.h>

typedef struct Point {
    int x, y;
} Point;

typedef struct Line {
    Point start, end;
} Line;

//计算线段长度平方的宏
#define LEN2(p_l) \
    (((p_l)->start.x - (p_l)->end.x) * ((p_l)->start.x - (p_l)->end.x) \
    +((p_l)->start.y - (p_l)->end.y) * ((p_l)->start.y - (p_l)->end.y))

//计算线段长度平方
long length2(const Line *p_l) {
    return (((p_l)->start.x - (p_l)->end.x) * ((p_l)->start.x - (p_l)->end.x)
           +((p_l)->start.y - (p_l)->end.y) * ((p_l)->start.y - (p_l)->end.y));
}
// 找出较长的线段 
const Line *longer(const Line *p_l1, const Line *p_l2) {
    // return LEN2(p_l1) > LEN2(p_l2) ? p_l1 : p_l2;
    return length2(p_l1) > length2(p_l2) ? p_l1 : p_l2;
}

int main() {
    Line l1 = {}, l2 = {};
    printf("please input line1's position: ");
    scanf("%d%d%d%d", &l1.start.x, &l1.start.y, &l1.end.x, &l1.end.y);
    printf("please input line2's position: ");
    scanf("%d%d%d%d", &l2.start.x, &l2.start.y, &l2.end.x, &l2.end.y);
    Line const *p_l = NULL;
    p_l = longer(&l1, &l2);
    printf("longer line position: ((%d, %d), (%d, %d))\n",
            p_l->start.x, p_l->start.y, p_l->end.x, p_l->end.y);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值