2018-05-iOS开发技能树之C语言-构造类型、自建数据类型

一、结构体

C语言中语序用户自己建立由不同类型数据组成的集合型的数据结构,他称为结构体。相较于另一个数据集合数组而言,它可以存放不同类型的数据,但是数组只能存储相同类型的数据。

类似C++、Java中的类。

1、声明结构体类型与定义结构体变量

  • 1、声明结构体一般式:
struct 结构体名 {
    类型名 成员名1;
    类型名 成员名2;
    ...
};
struct Dog { //声明Dog结构体
    char *name;
    int age ;
}
struct Dog dog3 = {"有才",5} ; //初始化Dog结构体类型变量dog3;
printf("dog: %-20s %d \n",dog3.name,dog3.age);

  • 2、声明和定义放在一起进行
struct 结构体名 {
    类型名 成员名1;
    类型名 成员名2;
    ...
} 变量名列表;

struct Dog {
    char *name;
    int age ;
} dog2 = {"旺财",2}; //定义结构体的同时,初始化结构体变量dog2
printf("dog: %-20s %d \n",dog2.name,dog2.age);
  • 3、不指定类型名直接定义结构体类型变量
struct { //定义结构体类型,但没有结构体名
    char *name;
    int age ;
} dog1 = {"旺旺",2}; //初始化结构体变量dog1
printf("dog: %-20s %d \n",dog1.name,dog1.age);
  

注:

1、定义结构体变量并初始化,不允许先定义再直接初始化,如struct Dog dog3;dog3 = {"xxx",1};这是错误的,如果在定义结构体变量的时候未初始化,可以分别赋值:dog3.name = "xiaosan";dog3.age=3;

2、结构体中可以引用结构体类型,前面要加struct关键字。

struct Date {
    int year;
    int month;
    int day;
};
struct Dog{
    char name[20];
    int age ;
    struct Date birthday;
}

struct Dog dog3 = {"有才",5,{2017,2,3}} ;
    
printf("dog: %-20s %d %d-%2d-%2d\n",dog3.name,dog3.age,dog3.birthday.year,dog3.birthday.month,dog3.birthday.day);

3、结构体变量的成员的值可以通过成员运算符.函数的方式获取:结构体成员变量名.成员名,dog.name,dog.age。如果成员本身有属于一个结构体类型,则要若干个成员运算符,一级一级的找到最低一级的成员。同时,只能对最低一级的成员进行赋值、存取以及运算。

4、结构体变量成员可以像普通变量一样进行各种运算。

5、同类型的结构体变量可以互相赋值。

6、可以引用结构体变量成员的地址,也可以引用结构体变量的地址:&dog3,&dog3.age

2、结构体数组

  • 1、定义结构体数组一般形式
struct 结构体名 {
    成员表列;
} 数组名[数组长度] = {初值列表};

struct Student {
    int number;
    char name[20];
    float score;
} stus[4] = {1,"zhangsan",100,2,"lisi",89,3,"wangwu",90,4,"zhaoliu",67};

//{{1,"zhangsan",100},{2,"lisi",89},{3,"wangwu",90},{4,"zhaoliu",67}}

  • 2、先声明一个结构体类型,在用此类型定义结构体数组

struct 结构体名 {
    成员表列;
};
结构体类型 数组名[数组长度] = {初值列表};

struct Student {
    int number;
    char name[20];
    float score;
};

struct Student stus[4] = {1,"zhangsan",100,2,"lisi",89,3,"wangwu",90,4,"zhaoliu",67};
//{{1,"zhangsan",100},{2,"lisi",89},{3,"wangwu",90},{4,"zhaoliu",67}}

3、结构体指针

  • 1、结构体指针
    结构体指针就是指向结构体变量的指针,一个结构体变量的起始地址就是这个结构体变量的指针。如果把一个结构体变量的起始地址存放在一个指针变量中,那么,这个指针变量就指向该结构体变量。

指向结构体变量的指针可以指向结构体变量,也可以指向结构体数组中的元素。指针变量的基类型必须与结构体变量的类型相同:struct 结构体名 * p;


struct Student {
    int number;
    char name[20];
    float score;
};

struct Student student1 = {100,"xiaoming",120};
struct Student *ps = &student1;
    
printf("%5d %-20s %.2f \n",(*ps).number,(*ps).name,(*ps).score);
printf("%5d %-20s %.2f \n",ps->number,ps->name,ps->score);
printf("%5d %-20s %.2f \n",student1.number,student1.name,student1.score);

打印结果 :
100 xiaoming             120.00 
100 xiaoming             120.00 
100 xiaoming             120.00 

注:

struct Student *ps = &student1;(*ps)表示ps指向的结构体变量,(*ps).number是ps指向结构体变量中的成员number(*p)两侧的括号不可省略,因为成员运算符.优先于*运算符

C语言中可以用->表示指向结构体变量中成员的符号,如下面三种用法是等价的:

结构体成员变量.成员名 (student1.number);
(*ps).成员名 ((*p).number);
p->成员名 (p->number);
  • 2、指向结构体数组的指针
    在用法上与一般数组指针相同,内容元素对应结构体。
struct Student stus[4] = {{1,"zhangsan",100,{199,5,12}},{2,"lisi",89},{3,"wangwu",90},{4,"zhaoliu",67}};
    
for (int i = 0; i < 4; i ++) {
    printf("%5d %-20s %6.2f\n",stus[i].number,stus[i].name,stus[i].score);
}

struct Student *p ; //定义结构体指针
p = stus; //指向结构体体数组

for (; p  < stus + 4; p ++) { //遍历数组
    printf("%5d %-20s %6.2f\n",(*p).number,(*p).name,(*p).score);
}

数组指针p指向的是结构体数组的首元素地址,对应的是一个结构体变量,是一个结构体变量的指针。

二、链表

链表是一种数据结构,他是动态地进行存储分配的一种结构。链表中各元素在内存中的地址可以是不连续的,要找某一元素,必须先找到上一个元素,根据它提供下一元素地址才能找到下一元素。链表是根据需要开辟内存单元,不同于数组需要开辟固定的长度。

链表有一个“头指针”变量,存放一个地址,指向一个元素。链表中每一个元素成为节点,每个节点包括两部分:(1)用户需要用的实际数据,(2)下一个节点地址。

链表,必须利用指针标量才能实现,即一个节点中应包含一个指针变量,用它存放下一节点的地址。

前面说了结构体变量,用它建立链表是最合适的。

1、简单静态链表

struct Student {
    int number;
    float score;
    struct Student *next;
};

int main(int argc, const char * argv[]) {
    
    struct Student a,b,c,*head,*p;
    a.number = 1001;a.score = 100;
    b.number = 1002;b.score = 90;
    c.number = 1003;c.score = 98;
    head = &a;
    a.next = &b;
    b.next = &c;
    c.next = NULL;
    p = head;
    
    do {
        printf("%5d %5f \n",p->number,p->score);
        p = p->next;
    } while (p != NULL) ;
    
    
    return 0;
}

打印结果:
 1001 100.000000 
 1002 90.000000 
 1003 98.000000 

2、动态链表

这是一个不断开辟存储空间,增加数据的过程,并把后一个元素指针存放到前一个元素的成员里。

struct Student {
    long number;
    float score;
    struct Student *next;
};

int n;//链表长度

struct Student * create(void) {
    struct Student *p,*np,*head = NULL;
    n = 0;
    p = np = (struct Student *)malloc(LEN);//开辟一个新单元
    printf("请输入学生编号 成绩(编号为0退出):");
    scanf("%ld %f",&np->number,&np->score);
    while (np -> number != 0) { //设定条件,如果输入编号为0,退出
        n += 1;
        if (n == 1) { //第一个元素
            head = np;
        } else {
            p->next = np;
        }
        p = np;
        
        np = (struct Student *)malloc(LEN);//开辟动态存储区,把起始地址赋值给np
        printf("请输入学生编号 成绩(编号为0退出):");
        scanf("%ld %f",&np->number,&np->score);
    }
    p->next = NULL;//链表最后一个元素的next为NULL
    return head;
}

void prin(struct Student *head) {
    struct Student *pt;
    pt = head;
    if (pt != NULL) {
        do {
            printf("%5ld  %.2f \n",pt->number,pt->score);
            pt = pt->next;
        } while (pt != NULL);
    }
    
}

int main(int argc, const char * argv[]) {
    
    struct Student *pt;
    pt = create();
    
    prin(pt);
 
    return 0;
}

打印结果:
请输入学生编号 成绩(编号为0退出):1001 100
请输入学生编号 成绩(编号为0退出):1002 90
请输入学生编号 成绩(编号为0退出):1003 89
请输入学生编号 成绩(编号为0退出):1008 98
请输入学生编号 成绩(编号为0退出):0 12
 1001  100.00 
 1002  90.00 
 1003  89.00 
 1008  98.00 

结构体和指针的应用领域很广,除了单向链表之外,还有环形链表和双向链表。此外还有队列、树、栈、图等数据结构。这里只是简单了解链表的数据结构,有个初步认识。

三、共用体

共用体类型结构:几个不同的变量共享同一段内存的结构。有些地方也被叫做“联合”

定义共同体的一般形式:

union 共同体名{
    成员表列
} 变量表列;

如:
//方式1
union Data {
    int i;
    char c;
    float f;
}a,b,c;

//方式2
union Data {
    int i;
    char c;
    float f;
};

union Data a,b,c;

//方式3
union {
    int i;
    char c;
    float f;
}a,b,c;

与结构体有些类似,但含义不同。

结构体变量所占内存长度是各成员占的内存长度之和。每个成员分别占有其自己的内存单元。二共用体变量所占的内存长度等于最长成员的长度。如上面共用体Data的变量a,b,c各占4个字节,而不是4+1+4=9个字节。

共同体类型数据的特点:

1、同一个内存段可以存放几种不同类型的成员,但在每一瞬间只能存放其中一个成员,而不能同时存放几个。在每一个瞬间,存储单元只能有唯一的内容,也就是说,在共用体变量中只能存放一个值

2、可以对共用体变量初始化,但初始化表中只能有一个常量。

union Data {
    int i;
    char c;
    float f;
} a = {1,'a',1.2};
//这种方法是不对的。三个成员变量占用同一个存储单元。

union Data a = {16};
union Data a = {.c = 'a'};

3、共用体变量中起作用的成员是最后一次被赋值的成员,在对共用体中的一个成员赋值后,原有变量存储单元中的值就取代。

举个简单例子
union Data {
    int i;
    int sum;
    int oth;
};

int main(int argc, const char * argv[]) {
    
    union Data a;
    a.i = 10;
    printf("%d \n",a.i);
    a.sum = 100;
    printf("%d \n",a.sum);
    printf("%d \n",a.i); //值被覆盖
    a.i = 11;
    printf("%d \n",a.sum);
    printf("%d \n",a.i); //值被覆盖
    printf("%d \n",a.oth);//即使没有给这个成员赋值,它依然会有共用体其他成员赋值的结果
}

打印结果:
10 
100 
100 
11 
11 
11 

4、共用体变量的地址和它的成员地址都是统一地址。

5、不能对共用体变量名赋值,也不能企图引用变量名来得到一个值。a = 1;int m = a;,都是不对的。

6、共用体类型可以出现在结构体类型定义中,也可以定义共用体数组。反之,结构体也可以出现在共用体类型定义中,数组也可以作为共用体成员。

什么情况下会用到共用体类型数据呢?往往在数据处理中,有时需要对同一段空间安排不同的哦用途,这时用共用体类型比较方便,能增加程序处理的灵活性。

struct Person {
    char *name;
    char *job;
    union {
        int number;    //员工 编号
        char *roomname; //经理 办公室名称
    } position;
};

//对于同一个Person结构体来说
//员工与经理的办公位置是同一个属性,
//但是却有很大差别,这个时候就可以通过共同体来处理这个成员。

四、枚举类型

枚举,就是指把可能的值一一列举出来,变量的值只限于列举出来的值的范围内。

枚举类型,具有有限个可能值的数据类型,C语言对枚举类型的枚举元素按常量处理,故称枚举常量。

声明枚举一般形式:enum 枚举名 {枚举元素列表}

enum WeekDay{
    sun,
    mon,
    tue,
    wed,
    thu,
    fri,
    sta,
    
};

int main(int argc, const char * argv[]) {
    
    enum WeekDay workDay;
    workDay = sun;
    
    //每一个枚举元素代表一个整数,枚举成员默认值从0开始。
    for (; workDay <= sta; workDay ++) {
        printf("%d \n",workDay);
    }
    
    return 0;
}

五、用typedef声明新类型名

typedef用来指定新的类型名来代替已有的类型名。

1、简单地用一个新的类型名代替原有类型名

typedef int  Integer;

typedef int  Count;

int main(int argc, const char * argv[]) {
   
    Integer index = 100;
    printf("%d\n",index);
    
    Count number = 10;
    printf("%d \n",number);
    
    return 0;
}

2、命名一个简单的类型名代替复杂的类型表示方法

按照定义变量的方式,把变量名换上新类型名,并且在最前面加typedef,就声明了新类型名代替原来的类型。

声明一个新类型名的方法:

  • 1、先按定义变量的方法写出定义体(如:int i;)
  • 2、将变量名换成新类型名(如:间隔i换成Count)
  • 3、在最前面加typedef(如:typedef int Count)
  • 4、然后可以用新类型名去定义变量。
char *p;
char * String;
typedef char * String;
String p;

typedef是在编译阶段处理的,它不是作简单的字符替换。是为特定的类型指定了一个同义字(synonyms)。typedef只是对已经存在的类型指定一个新的类型名,而没有创造新的类型。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354

推荐阅读更多精彩内容

  • 指针是C语言中广泛使用的一种数据类型。 运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构; ...
    朱森阅读 3,444评论 3 44
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy阅读 9,516评论 1 51
  • 包装盒采用黑白拼接设计貌似是致敬iPhone3gs? 很可能抢先一步采用logo上面安装指纹识别!
    科技音浪阅读 193评论 0 2
  • 明天语文和数学就要单元测试了,今天放学回家吃完饭后,妈妈把赵老师和徐老师发到群里明天要考的重点都给我复习了...
    莹宝ying阅读 551评论 0 0
  • 此刻 归心似箭 文 羽蒙 此刻 黑夜如离弦的箭 腐蚀白昼 和白昼里的一切 夜幕之下的苍...
    羽蒙1阅读 319评论 3 2