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只是对已经存在的类型指定一个新的类型名,而没有创造新的类型。

猜你喜欢

转载自blog.csdn.net/fengzhixinfei/article/details/80333928