[C语言]进阶|结构类型: 枚举, 结构, 类型定义
---------------------------------------------------------------------
枚举:
// main.c #include <stdio.h> const int red = 0; const int yellow = 1; const int green = 2; int main(int argc, const char * argv[]) { /* 常量符号化:用符号而不是具体的数字来表示程序中的数字; 这样看到符号才能知道背后的意思 */ int color = -1; char *colorName = NULL; printf("输入颜色代码:"); scanf("%d", &color); switch (color) { case red: colorName = "red"; break; case yellow: colorName = "yellow"; break; case green: colorName = "green"; break; default: colorName = "unknow"; break; } printf("颜色是:%s\n", colorName); return 0; }
// main.c #include <stdio.h> enum COLOR {RED, YELLOW, GREEN}; int main(int argc, const char * argv[]) { /* 枚举:用枚举而不是定义独立的const int变量 */ int color = -1; char *colorName = NULL; printf("输入颜色代码:"); scanf("%d", &color); switch (color) { case RED: colorName = "red"; break; case YELLOW: colorName = "yellow"; break; case GREEN: colorName = "green"; break; default: colorName = "unknow"; break; } printf("颜色是:%s", colorName); return 0; }
// main.c #include <stdio.h> enum color {red, yellow, green}; //声明数据类型color void f(enum color c); //f函数需要传入叫color的枚举类型 int main(int argc, const char * argv[]) { /* 枚举是一种用户定义的数据类型,用关键字enum以如下语法来声明: enum 枚举类型名字 {名字0, ..., 名字n}; 枚举类型名字通常并不是真的使用,要用的实在大括号里的名字,因为它们就是常量符号,它们的类型是int,值依次从0到n。如: enum colors {red, yellow, green}; 这样就创建了三个常量,red的值是0,yellow是1,green是2。 当需要一些可以排列起来的常量值时,定义枚举的意义就是给了这些常量值名字。 */ /* 枚举量可以作为值 枚举类型可以跟上enum作为类型 但是实际上是以整数来做内部计算和外部输入输出的 */ enum color t = red; //t是color的枚举类型 scanf("%d", &t); f(t); return 0; } void f(enum color c) { printf("%d\n", c); }
// main.c
#include <stdio.h> enum COLOR {RED, YELLOW, GREEN, NumCOLORS}; int main(int argc, const char * argv[]) { /* 套路:自动计数的枚举(如:NumCOLORS) 这样需要遍历所有的枚举量或者需要建立一个用枚举量做下标的数组的时候就很方便了 枚举量:声明枚举量的时候可以指定值 enum COLOR {RED=1, YELLOW, GREEN = 5, NumCOLORS}; */ 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); /* 虽然枚举类型可以当作类型使用,但是实际上很少用; 如果有意义上排比的名字,用枚举比const int方便; 枚举比宏(macro)好,因为枚举有int类型。 */ return 0; }
结构:
// main.c // Created by weichen on 15/6/28. // Copyright (c) 2015年 weichen. All rights reserved.
#include <stdio.h> // 声明一种新的结构类型,非实体;.运算符左边的结构变量才是实体 struct date { int month; int day; int year; }; int main(int argc, const char * argv[]) { /*
- 结构类型 -
结构类型:复合数据类型, 成员变量可以是不同类型,声明结构类型是一条语句,结尾需要分号。 1.和本地变量一样,在函数内部声明的结构类型只能在函数内部使用。 2.所以通常在函数外部声明结构类型,这样就可以被多个函数所使用了。 3.结构类型和结构变量是两件事情,通过一种结构类型可以定义出很多结构变量,每个结构变量按照结构声明的样子具有相同的成员变量。 声明结构的形式: 1. struct point { int x; int y; }; struct point p1,p2; // p1和p2都是point里面有x和y的值(变量)。 2. struct { int x; int y; } p1, p2; // p1和p2都是一种无名结构,里面有x和y;p1和p2并不是类型的名字,这里只是想要两个变量p1和p2,而且以后不想继续使用这个结构,所以没有起名字;做法不常见。 3. struct point { int x; int y; } p1, p2; // 声明一个point结构,并定义两个变量p1和p2,常见。 结构的初始化: struct date today = {07, 31, 2014}; //以数组的方式顺序赋值 struct date thismonth = {.month=7, .year=2014}; //数组内以.成员变量的方式赋值,没有赋值的为0 结构成员: 结构和数组有点像 数组的成员类型必须一样,结构的成员类型可以不一样 数组用[]运算符和下标访问其成员,a[0] = 10; 结构用.运算符和名字访问其成员,today.day; student.firstName; p1.x; p1.y; 结构运算: 要访问整个结构,直接用结构变量的名字 对于整个结构,可以做赋值、取地址,也可以传递给函数参数 p1 = (struct point){5, 10}; //将5,10强制转换为point结构的变量, 赋给p1,相当于p1.x = 5; p1.y = 10; p1 = p2; //相当于p1.x = p2.x; p1.y = p2.y; 数组变量在初始化后无法做这种赋值的运算 结构指针: 和数组不同,结构变量的名字并不是结构变量的地址,必须使用&运算符 struct date *pDate = &today; */ /* 定义一个结构的变量today(类型是struct date,下面就可以使用today) struct date today; today.month = 06; today.day = 28; today.year = 2015; */ struct date today = {06, 28, 2015}; struct date thismonth = {.month=6, .year=2015}; struct date t; //未赋值的结构变量 t = (struct date){06, 28, 2015}; //赋值 struct date m; m = t; //m和t是两个不同的变量,赋值之后,m就得到了t所有成员的值 //m.year = 2016; 给m单独赋值,不会改变t的值 struct date *pDate = &today; printf("Today's date is %i-%i-%i\n", today.year, today.month, today.day); //Today's date is 2015-6-28 printf("This month is %i-%i-%i\n", thismonth.year, thismonth.month, thismonth.day); //This month is 2015-6-0 printf("t is %i-%i-%i\n", t.year, t.month, t.day); //t is 2015-6-28 printf("m is %i-%i-%i\n", m.year, m.month, m.day); //m is 2015-6-28 printf("%p", &pDate); //0x7fff5fbff7d8 return 0; }
// main.c // Created by weichen on 15/6/29. // Copyright (c) 2015年 weichen. All rights reserved. #include <stdio.h> struct point { int x; int y; }; void getStruct(struct point); struct point getStruct2(void); void output(struct point); int main(int argc, const char * argv[]) { /* - 结构和函数 - 结构作为函数参数: int numberOfDays(struct date d); 整个结构可以作为参数的值传入函数 这时候是在函数内新建一个结构变量,并复制调用者的结构的值 也可以返回一个结构 这与数组是不一样的 输入结构: 没有直接的方式可以一次scanf一个结构(就像%d这种) 写一个函数来读入结构 -> 但是读入的结构如何送回来呢? 记住C在函数调用时是传值的 在函数中的p与main中的y是不同的 在函数读入了p的数值之后,没有任何东西回到main,所以y还是{0, 0} 解决方案: 之前的方案,把一个结构传入了函数,然后在函数中操作,但是没有返回回去 问题在于传入函数的是外面那个结构的克隆体,而不是指针 传入结构和传入数组是不同的 在这个输入函数中,完全可以创建一个临时的结构变量,然后把这个结构返回给调用者 结构指针作为参数: 如果一个大的结构传到函数里,通常更有效的是传递一个指针而不是拷贝整个结构 指向结构的指针: struct date { int month; int day; int year; } myday; //声明一个date结构和结构变量myday struct date *p = &myday; (*p).month = 12; //点运算符 p->month = 12; //用->运算符表示指针所指的结构变量中的成员 结构指针参数: struct point* getStruct2(struct point *p) { scanf("%d", &(p->x)); scanf("%d", &(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); } void main() { struct point y = {0, 0}; getStruct2(&y); output(y); output(*getStruct2(&y)); printf(getStruct2(&y)); getStruct(&y)->x = 0; *getStruct(&y) = (struct point){1, 2}; } */ struct point y = {0, 0}; getStruct(y); output(y); //0, 0 传的是结构变量y的"值",而不是指针,所以在getStruct内即使成员变量被赋值,不会影响y y = getStruct2(); //两个结构变量可以赋值 output(y); //4, 5 return 0; } void getStruct(struct point p) { scanf("%d", &p.x); //2 scanf("%d", &p.y); //3 printf("%d, %d\n", p.x, p.y); //2, 3 } struct point getStruct2(void) { struct point p; //本地变量 scanf("%d", &p.x); //4 scanf("%d", &p.y); //5 printf("%d, %d\n", p.x, p.y); //4, 5 return p; } void output(struct point p) { printf("%d, %d\n", p.x, p.y); //0, 0 }
// main.c // Created by weichen on 15/7/5. // Copyright (c) 2015年 weichen. All rights reserved. #include <stdio.h> struct time { int hour; int minutes; int seconds; }; struct time timeUpdate(struct time now); int main(int argc, const char * argv[]) { /* - 结构中的结构 - 结构数组 struct date dates[100]; // 声明一个结构变量为数组 struct date dates[] = { // 初始化数组,每一个成员是struct {4,5,2005}, {2,4,2005} }; 结构中的结构 struct dateAndTime { struct date sdate; struct time stime; }; 嵌套的结构 struct point { int x; int y; }; struct rectangle { struct point pt1; struct point pt2; }; 如果有变量: struct rectangle r; 就可以有: r.pt1.x 、r.pt1.y, r.pt2.x 和 r.pt2.y 如果有变量定义: struct rectangle r.*rp; rp = &r; 那么下面的四种形式是等价的: r.pt1.x #正常形式 rp->pt1.x #指针访问结构变量的成员 (r.pt1).x #加括号形式,不影响 (rp->pt1).x #加括号形式,不影响 但是没有rp->pt1->x(因为pt1不是指针),示意图: |--------------| | |-----| | r . |pt1 . |x | | | |-----| | +-> | |y | | | | |-----| | | |pt2 . x | | | y | | |--------------| | | |--------------| rp | 指针 | |--------------| 结构中的结构的数组: 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 rectangle rects[] = { //2rectangles, 1,2是rects[0]的pt1的x和y,3,4是rects[0]的pt2的x和y {{1,2},{3,4}}, {{5,6},{7,8}} }; for (i=0; i<2; i++) { printRect(rects[i]); } } */ 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\n", testTimes[i].hour, testTimes[i].minutes, testTimes[i].seconds); testTimes[i] = timeUpdate(testTimes[i]); printf("one second later is %.2i:%.2i:%.2i\n", testTimes[i].hour, testTimes[i].minutes, testTimes[i].seconds); } // 结构成员变量的sizeof即为类型的大小,结构变量的sizeof为成员变量的sizeof之和 // 结构内成员之间连续,相邻成员的地址差不等于对应成员的sizeof struct time ab = {1, 5, 7}; printf("%ld, %ld, %ld, %ld\n", sizeof(ab), sizeof(ab.hour), sizeof(ab.minutes), sizeof(ab.seconds)); //12,4,4,4 printf("%p, %p, %p, %p, %ld\n", &ab, &(ab.hour), &(ab.minutes), &(ab.seconds), (&(ab.minutes) - &(ab.hour))); //0x7fff5fbff798, 0x7fff5fbff798, 0x7fff5fbff79c, 0x7fff5fbff7a0, 1 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; }
结构和数组的联系和区别
原本结构和数组没什么联系。FORTRAN就出现了数组的概念,结构是在Algol中出现的,C++开始结构成为定义新的数据类型的主要方式。结构在编程语言中的地位是为了让用户可以定义集成在一起的一些量,给这个整体一个名字;而数组是为了表达能表达一连串相同类型的数据的。现在,如果离开C语言,站在更高的高度,我们如何看到结构和数组?
类型定义
// main.c // Created by weichen on 15/7/6. // Copyright (c) 2015年 weichen. All rights reserved. #include <stdio.h> int main(int argc, const char * argv[]) { /* - 自定义数据类型(typedef) - C语言提供了一个叫做typedef的功能来声明一个已有的数据类型的新名字,如: typedef int Length; // Length成为int类型的别名, 等价于int类型 typedef *char[10] Strings; // Strings是10个字符串的数组的类型,10个字符指针的数组 typedef struct node { int data; struct node *next; } aNode; 或 typedef struct node aNode; // 这样用aNode就可以代替struct node 这样,Length这个名字就可以代替int出现在变量定义和参数声明的地方了: Length a,b,len; Length numbers[10]; Typedef 声明新的类型的名字 新的名字是某种类型的别名 改善了程序的可读性 typedef long int64_t; // 重载已有的类型名字,新名字的含义更清晰,具有可移植性 typedef struct ADate { // 在typedef和最后单词中间的所有东西是它原来的类型 int month; int day; int year; } Date; // 简化了复杂的名字 int64_t i = 1000000000; Date d = {9, 1, 2005}; // 把一个struct命名为Date (struct名字我们不关心,因为有更好的方式来表达它了 - Date) typedef struct { int month; int day; int year; } Date */ typedef struct { int month; int day; int year; } Date; Date a = {1,2,3}; printf("%ld,%ld,%ld", a.month, a.day, a.year); //1,2,3 return 0; }
// main.c // Created by weichen on 15/7/6. // Copyright (c) 2015年 weichen. All rights reserved. #include <stdio.h> typedef union { int i; char ch[sizeof(int)]; } CHI; int main(int argc, const char * argv[]) { /* - 联合 - 和struct非常相似 union AnElt { int i; char c; } elt1, elt2; // 成员是一个int i 还是一个char c, sizeof(union ...) = sizeof(每个成员)的最大值 elt1.i = 4; elt2.c = 'a'; elt2.i = 0xDEADBEEF; 和struct不一样的是: 存储 所有的成员共享一个空间 同一时间只有一个成员是有效的 union的大小是其最大的成员 初始化 对第一个成员做初始化 可以得到整数/float/double内部的各个字节 */ CHI chi; int i; chi.i = 1234; for (i=0; i<sizeof(int); i++) { printf("%02hhX", chi.ch[i]); //D2040000 } printf("\n"); return 0; }
Refer:C语言进阶用法