从结构体到指针

//结构体 也是一种构造数据类型,之前我们学过的构造数据类型还有 枚举、数组

    //结构体和数组很像,都是聚合变量,都可以存放一组数据,但是数组只能存放相同数据类型的数据,而结构体可以存放不同数据类型的数据。并且数组中的数据称为元素,结构体中的数据称为成员。

    //定义结构体的语法格式:

    /*

        struct 结构体名称 {

            类型说明符 成员1;

            类型说明符 成员2;

            ...

            类型说明符 成员n;

        };

     */

    

    struct student {

        char name[20];//姓名

        char gender;//性别

        Zombie zom;

        int age;//年龄

        float score;//成绩

    };

    printf("student is %lu\n",sizeof(struct student));

    

    //先定义结构体类型,再定义结构体变量

    //结构体变量名是 stu,类型是 struct student

//    struct student stu = {"heyifan", 'f', 18, 38.0};

    //重定义的关键字是typedef

    //type 原有类型, name 是新类型

//    typedef <#type#> <#name#>

    typedef int Integer;

    int a = 10;

    Integer b = 20;

    printf("a + b = %d\n",a + b);

    

    int array[5] = {1, 2, 4, 6, 9};

    typedef int Array[5];

    Array arr = {1, 2, 4, 6, 9};

    

    typedef struct student Student;

    //Student 等价于 struct student

    Student stu1 = {"hezhonger", 'f', 18, 38.0};

    

    struct teacher {

        char name[20];//姓名

        int age;//年龄

        char gender;//性别

        char position[20];//职位

        float salary;//薪水

    } iOSTeacher = {"chenqingyuan", 28, 'm', "jiangshi", 100};//在定义结构体类型时  直接声明结构体变量

    printf("iOSTeacher is %lu\n",sizeof(iOSTeacher));

    struct {

        char brand[20];//品牌

        float price;//价格

        float volumn;//容量

        float weight;//重量

    } cup = {"fuguang", 10.0, 500, 100};//匿名结构体,省略结构体名称,并且在定义结构体时,声明结构体变量。

 

//    typedef struct zombie Zombie;

    Zombie zom = {"tietong", 200, 5.0, 15};

    //访问结构体成员使用.操作符,可以认为.就是的意思

    printf("zom's type is %s",zom.type);

    zom.hp = 300;

//    zom.type = "xueren";

    strcpy(zom.type, "xueren");//如果想要修改 字符串(姓名、品牌、类型等等)不能直接赋值,需要使用字符串的拷贝函数strcpy()

    printZombie(zom);

    

    

//例题

typedef struct zombie {

    char type[20];//种类

    int hp;//血量

    float speed;//行走速度

    float attack;//攻击力

}Zombie;

 在.m文件中

//打印僵尸结构体的信息

void printZombie(Zombie zombie);

 

#import "Zombie.h"

 

void printZombie(Zombie zombie) {

    printf("zombie type is %s, hp is %d, speed is %.2f, attack is %.2f\n",zombie.type, zombie.hp, zombie.speed, zombie.attack);

}

 

 

 

#import <Foundation/Foundation.h>

#import "Student.h"

 

int main(int argc, const char * argv[]) {

    

    //创建一个结构体数组

    Student students[5] = {

        {"zhonger", 18, 31, 38.0},

        {"zhubada", 17, 17, 10.0},

        {"zuizui", 20, 2, 100.0},

        {"hanghang", 22, 41, 60.0},

        {"qiuxiang", 18, 13, 90.0}

    };

    printf("zhubada's number is %d\n",students[1].number);

    printf("-----华丽的分割线-----\n");

    

    //打印

    for (int i = 0; i < 5; i ++) {

        printStudent(students[i]);

    }

    

    printf("-----华丽的分割线-----\n");

    Student maxStudent = findMaxScoreStudent(students, 5);

    printStudent(maxStudent);

    printf("-----华丽的分割线-----\n");

 

    

    sortStudentsByScore(students, 5);

    for (int i = 0; i < 5; i ++) {

        printStudent(students[i]);

    }

    printf("-----华丽的分割线-----\n");

 

    sortStudentsByName(students, 5);

    for (int i = 0; i < 5; i ++) {

        printStudent(students[i]);

    }

    

    

    

    

    

    

    

    return 0;

}

//学生的例题(结构体)

/创建一个学生的结构体

typedef struct student {

    char name[20];//姓名

    int age;//年龄

    int number;//学号

    float score;//成绩

}Student;

 

//打印学生信息

void printStudent(Student student);

 

//找到成绩最高的学生

Student findMaxScoreStudent(Student stus[], int count);

 

//将学生按照成绩从高到低排序

void sortStudentsByScore(Student stus[], int count);

 

//将学生按照姓名从低到高排序

void sortStudentsByName(Student stus[], int count);

 

#import "Student.h"

 

//打印学生信息

void printStudent(Student student) {

    printf("student's name is %s, age is %d, number is %d, score is %.2f\n",student.name, student.age, student.number, student.score);

}

 

//找到成绩最高的学生

Student findMaxScoreStudent(Student stus[], int count) {

    Student maxStu = {"", 0, 0, 0.0};

    for (int i = 0; i < count; i ++) {

        maxStu = maxStu.score > stus[i].score ? maxStu : stus[i];

    }

    return maxStu;

}

 

//将学生按照成绩从高到低排序

void sortStudentsByScore(Student stus[], int count) {

    for (int i = 0; i < count - 1; i ++) {

        for (int j = 0; j < count - 1 - i; j ++) {

            if (stus[j].score < stus[j + 1].score) {

                Student tempStu = stus[j];

                stus[j] = stus[j + 1];

                stus[j + 1] = tempStu;

            }

        }

    }

}

 

//将学生按照姓名从低到高排序

void sortStudentsByName(Student stus[], int count) {

    for (int i = 0; i < count - 1; i ++) {

        for (int j = 0; j < count - 1 - i; j ++) {

            if (strcmp(stus[j].name, stus[j + 1].name) > 0) {

                Student tempStu = stus[j];

                stus[j] = stus[j + 1];

                stus[j + 1] = tempStu;

            }

        }

    }

}

 

//指针 

 

void sortArray(int *r, int count);

void sortArray(int *r, int count) {

    for (int i = 0; i < count - 1; i ++) {

        for (int j = 0; j < count - 1 - i; j ++) {

            if (*(r + j) > *(r + j + 1)) {

                int temp = *(r + j);

                *(r + j) = *(r + j + 1);

                *(r + j + 1) = temp;

            }

        }

    }

}

int main(int argc, const char * argv[]) {

    

    //指针,指针变量,此变量存储的内容是内存中某块内存的首地址。也就是我们所说的内存编号。

    //我们通过指针可以间接访问到内存中存储的值,程序中需要的内容是值,而指针,也就是地址只是一个位置信息。

    //指针变量所占内存大小只与操作系统位数有关,64位操作系统下,指针变量所占内存大小为8个字节。而内存编号对应的内存单元为一个字节。字节是内存的最小单位。

    //既然指针也是变量,那么声明指针变量的语法格式 也是     数据类型 变量名 = 初值;

    

    //在定义指针变量时,*挨着变量名。指针变量名通常以pqr开头。

    int *p = NULL;//p 是指针变量名;NULL是初值,是一个空地址;int * 是指针变量的类型,代表p是一个整型指针,也就是说,p可以从首地址往下取四个字节大小的空间,获取里面的值。

    

    //声明一个整型变量number,然后通过取址运算符获取到存储number的内存首地址,然后赋值给整型指针变量q

    int number = 10;

    int *q = &number;

    printf("q is %p\n",q);//打印地址采用%p占位符。

    

    int digital = 20;

    q = &digital;//指针赋值相当于指针的重指向

    *q = 200;//此时是取到digital变量保存的值,并且重新赋值为200

    printf("digital is %d\n",digital);

    

    /*

        *的含义

        1、如果*号出现在声明指针变量时,此时的*号仅仅是一个标识符,代表变量是一个指针变量。

        2、如果*号与指针变量单独出现,比如*p,此时代表取值,取到p指向的内存区域中存储的值。

     */

    

    //当指针做算数运算时,需要注意两个点

    /*

        1+ - 决定了偏移是往内存地址高位还是低位移动,+是往高位,-是往低位

        2、往高位还是低位移动多少个字节,是由指针类型决定的,如果是整型指针int *p = &a;,那么此指针算数运算p + 1或者p - 1,一次移动4个字节。

     */

    

    /*

    int a = 10, b = 20;

    int *r = &a;

    

    printf("a = %p, b = %p\n",&a, &b);

//    *r, *r + 1,*(r - 1), r - 1;

    printf("*r is %d, *r + 1 is %d, *(r - 1) is %d, r - 1 is %p\n",*r, *r + 1, *(r - 1), r - 1);

    

    //指针与数组

    int arr[10] = {0};

    

    //数组名已经是数组的首地址,并且是数组第一个元素的首地址

    printf("arr is %p, arr[0] is %p, &arr is %p\n",arr, &arr[0], &arr);

    printf("arr + 1 is %p, &arr + 1 is %p\n",arr + 1, &arr + 1);

    

    short array[2] = {3, 7};

    int *p3 = array;

    char *p4 = array;

    printf("p3 is %d, p4 is %d",*p3, *p4);*/

    

    //使用指针实现数组的冒泡排序

    int array[10] = {0};

    

    //随机赋值

    for (int i = 0; i < 10; i ++) {

        array[i] = arc4random() % (100 - 50 + 1) + 50;

        printf("%d ",array[i]);

    }

    printf("\n");

    

    //定义一个整型指针指向数组的首地址

    int *r = array;

    sortArray(r, 10);

    

    for (int i = 0; i < 10; i ++) {

        printf("%d ",*(r + i));

    }

    printf("\n");

    

    //指针与字符串

    char string[20] = "iPhone";

    

    //定义一个字符指针指向声明好的字符串

    char *s = string;

    

    *(s + 1) = 'p';

    

    printf("*s is %c, *s + 1 is %c, *(s + 1) is %c, s is %s, s + 1 is %s\n",*s, *s + 1, *(s + 1), s, s + 1);

    

    

//    char *s1 = "iPhone";

//    *(s1 + 1) = 'p';

    

    //利用指针求字符串长度

    //1

    int count = 0;

    char *s2 = "iPhone6sPlus";

    

    /*

    while (*s2 != '\0') {

        s2 ++;

        count ++;

    }

    printf("count is %d\n",count);*/

    

    //2

    while (*(s2 + count ++)) {}

    printf("count is %d\n",count - 1);

 

    //指针数组实现字符串排序

    //数组name里面有三个字符型指针,分别指向三个字符串首地址

    char *name[3] = {"iPhone", "plus", "hello"};

    for (int i = 0; i < 3 - 1; i ++) {

        for (int j = 0; j < 3 - 1 - i; j ++) {

            if (strcmp(name[j], name[j + 1]) > 0) {

                char *temp = name[j];

                name[j] = name[j + 1];

                name[j + 1] = temp;//地址是可以交换的,交换地址,相当于间接交换了字符串,因为不同地址访问到的字符串不一样

                

                //此种写法是错误的,因为常量字符串是不能直接修改的,其次strcpy()一定要保证容量的问题。

//                char temp[255] = {0};

//                strcpy(temp, name[j]);

//                strcpy(name[j], name[j + 1]);

//                strcpy(name[j + 1], temp);

            }

        }

    }

    

    for (int i = 0; i < 3; i ++) {

        printf("%s\n",name[i]);

    }

    

    

    

    

    

    

    

    

    

    

    

    

    

    return 0;

}

//内存管理 

void func() {

    int a = 10;

    printf("%d ",a ++);

}

 

char * getAddress() {

    char string[] = "iPhone";

    return string;//当此函数执行完毕之后,string这个地址就已经不能访问了,因为这块空间已经被系统回收。当外界再去访问这块内存时,就会出现野指针。

    //例如,金康达现在住在丽德酒店C208,四个月之后,搬走了,那么这间房就被回收了,如果四个月之后,还去开门,那就有可能看见不该看见的东西。

}

 

 

int main(int argc, const char * argv[]) {

    

    //内存分区按照内存地址从高到低的顺序分为栈区、堆区、静态区(全局区)、常量区、代码区

    //栈区:存放所有的局部变量,包括函数的形参

    //栈区的特点:栈区的内存是由系统自动开辟和回收的,采用先进后出的管理原则管理局部变量,栈区变量只要函数执行完毕,就会被系统回收,所以返回栈区地址是一种非常愚蠢的操作。

    

    //堆区:是系统留给开发者使用的内存。这块内存完全由程序猿把控。程序猿决定什么时候使用,使用多大,什么时候回收,系统不做任何干预。如果只开辟不回收,那么堆区域可用内存就会越来越少,当可用内存为0时,程序就会崩溃,crash。如果开辟回收之后,又去访问了这块内存空间,也会出现崩溃,叫做野指针。

    //所以,堆区域的内存我们必须遵守一个原则,有开辟必须有释放,而且释放之后,不允许再去访问。

    

    //静态区,也可以叫做全局区。主要用来存储静态变量以及全局变量。

    //由系统为变量分配内存空间,程序猿不能控制,但是空间的回收只能当程序退出时才会执行。静态区的变量,有一个特点,初始化只有一次,而且并不会随着函数的执行完毕被回收。

    

    //常量区:存放系统内部所有的常量 比如 104.5’a‘“iPhone”

    //我们不会对常量区进行修改或者操作,一旦改了常量区的内容,程序会立即崩溃。常量区中的内容只有一份,并且是只读的。

    

    //代码区:代码区存放的不是源代码,函数都存放在栈区,代码区存放的是源代码编译之后形成的可执行文件,也可以叫做二进制流文件,或者CPU指令。

    for (int i = 0; i < 10; i ++) {

        func();

    }

    

    //堆区域分配函数

    //1malloc,作用是开辟一段给定字节大小的堆区域空间,并且返回该内存空间的首地址,void *不是空指针,而是泛型指针,根据需求来给定类型

    char *str = malloc(8);

    strcpy(str, "iPhone");

    printf("*str is %c, *(str + 1) is %c, *str + 1 is %c, str is %s, str + 2 is %s\n",*str, *(str + 1), *str + 1, str, str + 2);

    

    int *q = malloc(sizeof(int) * 3);

    q[0] = 12;

    *(q + 1) = 13;

    q[2] = 14;

    

    for (int i = 0; i < 3; i ++) {

        printf("number is %d\n",*(q + i));

    }

    free(q);//free将堆区域开辟好的空间回收,标记为不可用。

    

    short *r = malloc(sizeof(short) * 6);

    

    /*

     有一已知字符串,其中包含 数字 字符,提取其中的 数字字符 ,要求动态分配内存保存。

     提示: 先计算出有几个数字字符,然后根据数字字符的个数在堆区开辟空间。*/

    

    /*

    char string[] = "i12Ph34on56e";

    

    //定义循环增量 以及 计数变量

    int i = 0, count = 0;

    //遍历字符串,得到数字字符的个数

    while (string[i] != '\0') {

        if (string[i] >= '0' && string[i] <= '9') {

            count ++;

        }

        i ++;

    }

    printf("count is %d\n",count);

    //动态分配内存保存数字

    int *number = malloc(sizeof(int) * count);

    

    i = 0;//让循环增量变为0,遍历重新开始。

    int j = 0;

    while (string[i] != '\0') {

        if (string[i] >= '0' && string[i] <= '9') {

            *(number + j) = string[i] - '0';

            j ++;

        }

        i ++;

    }

    for (int i = 0; i < count; i ++) {

        printf("number is %d\n",*(number + i));

    }

    free(number);*/

    

    //输入3个单词,动态分配内存保存单词,并在最后输出。

    //定义一个包含三个指针变量的数组,来存储开辟的堆区域空间首地址

    char *words[3] = {0};

    

    char temp[255] = {0};//定义一个字符数组来接收,用户每一次输入的字符串。

    printf("请输入三个字符串(中间以空格隔开):");

    

    for (int i = 0; i < 3; i ++) {

        scanf("%s",temp);

        unsigned long length = strlen(temp);//先获取输入的字符串长度

        words[i] = malloc(length + 1);//动态分配内存

        strcpy(words[i], temp);//将输入的字符串拷贝进堆区域。

    }

    

    for (int i = 0; i < 3; i ++) {

        printf("%s\n",words[i]);

        free(words[i]);//释放

    }

    

    // memset()重置函数

    int *number = malloc(sizeof(int) * 5);

    

    //number首地址开始将sizeof(int) * 5的空间上的每一个字节全部置空为0

    memset(number, 0, sizeof(int) * 5);

    

    //memcmp() 内存比较函数,比较的不是内存大小,而是内存中存储的值的大小

    char *str1 = "iPhone";

    char *str2 = "iPhoneplus";

    

    int result = memcmp(str1, str2, 7);//四代表比较内存中前四个字节中的值

    printf("result is %d\n",result);

    

    int *number1 = malloc(4);

    *number1 = 10;

    int *number2 = malloc(4);

    *number2 = 11;

    

    int result1 = memcmp(number1, str1, 4);

    printf("result is %d\n",result1);

    

    

    

    

    

    

    

    

    

    

    return 0;

}

void func() {

    int a = 10;

    printf("%d ",a ++);

}

 

char * getAddress() {

    char string[] = "iPhone";

    return string;//当此函数执行完毕之后,string这个地址就已经不能访问了,因为这块空间已经被系统回收。当外界再去访问这块内存时,就会出现野指针。

    //例如,金康达现在住在丽德酒店C208,四个月之后,搬走了,那么这间房就被回收了,如果四个月之后,还去开门,那就有可能看见不该看见的东西。

}

 

 

int main(int argc, const char * argv[]) {

    

    //内存分区按照内存地址从高到低的顺序分为栈区、堆区、静态区(全局区)、常量区、代码区

    //栈区:存放所有的局部变量,包括函数的形参

    //栈区的特点:栈区的内存是由系统自动开辟和回收的,采用先进后出的管理原则管理局部变量,栈区变量只要函数执行完毕,就会被系统回收,所以返回栈区地址是一种非常愚蠢的操作。

    

    //堆区:是系统留给开发者使用的内存。这块内存完全由程序猿把控。程序猿决定什么时候使用,使用多大,什么时候回收,系统不做任何干预。如果只开辟不回收,那么堆区域可用内存就会越来越少,当可用内存为0时,程序就会崩溃,crash。如果开辟回收之后,又去访问了这块内存空间,也会出现崩溃,叫做野指针。

    //所以,堆区域的内存我们必须遵守一个原则,有开辟必须有释放,而且释放之后,不允许再去访问。

    

    //静态区,也可以叫做全局区。主要用来存储静态变量以及全局变量。

    //由系统为变量分配内存空间,程序猿不能控制,但是空间的回收只能当程序退出时才会执行。静态区的变量,有一个特点,初始化只有一次,而且并不会随着函数的执行完毕被回收。

    

    //常量区:存放系统内部所有的常量 比如 104.5’a‘“iPhone”

    //我们不会对常量区进行修改或者操作,一旦改了常量区的内容,程序会立即崩溃。常量区中的内容只有一份,并且是只读的。

    

    //代码区:代码区存放的不是源代码,函数都存放在栈区,代码区存放的是源代码编译之后形成的可执行文件,也可以叫做二进制流文件,或者CPU指令。

    for (int i = 0; i < 10; i ++) {

        func();

    }

    

    //堆区域分配函数

    //1malloc,作用是开辟一段给定字节大小的堆区域空间,并且返回该内存空间的首地址,void *不是空指针,而是泛型指针,根据需求来给定类型

    char *str = malloc(8);

    strcpy(str, "iPhone");

    printf("*str is %c, *(str + 1) is %c, *str + 1 is %c, str is %s, str + 2 is %s\n",*str, *(str + 1), *str + 1, str, str + 2);

    

    int *q = malloc(sizeof(int) * 3);

    q[0] = 12;

    *(q + 1) = 13;

    q[2] = 14;

    

    for (int i = 0; i < 3; i ++) {

        printf("number is %d\n",*(q + i));

    }

    free(q);//free将堆区域开辟好的空间回收,标记为不可用。

    

    short *r = malloc(sizeof(short) * 6);

    

    /*

     有一已知字符串,其中包含 数字 字符,提取其中的 数字字符 ,要求动态分配内存保存。

     提示: 先计算出有几个数字字符,然后根据数字字符的个数在堆区开辟空间。*/

    

    /*

    char string[] = "i12Ph34on56e";

    

    //定义循环增量 以及 计数变量

    int i = 0, count = 0;

    //遍历字符串,得到数字字符的个数

    while (string[i] != '\0') {

        if (string[i] >= '0' && string[i] <= '9') {

            count ++;

        }

        i ++;

    }

    printf("count is %d\n",count);

    //动态分配内存保存数字

    int *number = malloc(sizeof(int) * count);

    

    i = 0;//让循环增量变为0,遍历重新开始。

    int j = 0;

    while (string[i] != '\0') {

        if (string[i] >= '0' && string[i] <= '9') {

            *(number + j) = string[i] - '0';

            j ++;

        }

        i ++;

    }

    for (int i = 0; i < count; i ++) {

        printf("number is %d\n",*(number + i));

    }

    free(number);*/

    

    //输入3个单词,动态分配内存保存单词,并在最后输出。

    //定义一个包含三个指针变量的数组,来存储开辟的堆区域空间首地址

    char *words[3] = {0};

    

    char temp[255] = {0};//定义一个字符数组来接收,用户每一次输入的字符串。

    printf("请输入三个字符串(中间以空格隔开):");

    

    for (int i = 0; i < 3; i ++) {

        scanf("%s",temp);

        unsigned long length = strlen(temp);//先获取输入的字符串长度

        words[i] = malloc(length + 1);//动态分配内存

        strcpy(words[i], temp);//将输入的字符串拷贝进堆区域。

    }

    

    for (int i = 0; i < 3; i ++) {

        printf("%s\n",words[i]);

        free(words[i]);//释放

    }

    

    // memset()重置函数

    int *number = malloc(sizeof(int) * 5);

    

    //number首地址开始将sizeof(int) * 5的空间上的每一个字节全部置空为0

    memset(number, 0, sizeof(int) * 5);

    

    //memcmp() 内存比较函数,比较的不是内存大小,而是内存中存储的值的大小

    char *str1 = "iPhone";

    char *str2 = "iPhoneplus";

    

    int result = memcmp(str1, str2, 7);//四代表比较内存中前四个字节中的值

    printf("result is %d\n",result);

    

    int *number1 = malloc(4);

    *number1 = 10;

    int *number2 = malloc(4);

    *number2 = 11;

    

    int result1 = memcmp(number1, str1, 4);

    printf("result is %d\n",result1);

    

    

    

    

    

    

    

    

    

    

    return 0;

}

 

typedef struct student {

    char name[20];

    int age;

    float score;

}Student;

 

//求两个整数的和 以及 求两个整数的最大值

int sum(int a, int b);

 

int maxValue(int a, int b);

 

typedef int (*PFUN)(int, int);

 

void printHello();

 

int getValue(int a, int b, PFUN p);

 

void replace(char destination[], char source[]);//替换

 

void match(char destination[], char source[]);//拼接

 

typedef void (*PF)(char [], char []);

 

void printStudent(Student student);

 

void markStudent(Student stus[], int count, char *str, PF p);

 

 

int sum(int a, int b) {

    return a + b;

}

 

int maxValue(int a, int b) {

    return a > b ? a : b;

}

 

void printHello() {

    printf("hello world\n");

}

 

int getValue(int a, int b, PFUN p) {

    return p(a, b);

}

 

void replace(char destination[], char source[]) {

    strcpy(destination, source);

}//替换

 

void match(char destination[], char source[]) {

    strcat(destination, source);

}//拼接

 

void printStudent(Student student) {

    printf("%s %d %.2f\n",student.name, student.age, student.score);

}

 

void markStudent(Student stus[], int count, char *str, PF p) {

    for (int i = 0; i < count; i ++) {

        if (stus[i].score > 90.0) {

            p(stus[i].name, str);

        }

    }

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

    

    

    

    

    

    

    

posted on 2016-01-29 19:57  沈少磊5436238  阅读(313)  评论(0)    收藏  举报