结构体
可以用来统一存储不同类型的数据;
- 结构体仅仅是一种数据类型,它本身不占有存储空间;
- 结构体仅仅描述了多种数据之间的关系,是一种结构信息;
- 结构体可以用来声明结构体变量,结构体变量实际占有内存空间;
结构体声明
- 声明结构体需要使用
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
;
以下三种形式等价
start.成员名
;(*p_start).成员名
;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; }
数据结构对齐补齐
- 数据对齐
Alignment
- 结构体中不同类型的数据在内存中按照一定的规则排列,而不总是一个挨一个的顺序排放(便于寻址);
- 任何成员变量的地址必须是其对齐参数A的整数倍,这个规则叫做数据对齐;
数据对齐会造成结构体内部不同成员变量之间有空隙;
- 每个成员变量的对齐参数A取值规则:
a=min(n, max(self))
普通类型取#pragma pack(n)
、自身大小,二者最小值;a=min(n, max(sizeof element...))
结构体类型取#pragma pack(n)
、最大子变量,二者最小值;
n为系统对齐参数,一般默认为4,可以通过
#pragma pack()
类宏自定义
- 数据补齐
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; }