重拾c语言基础-记录笔记

作者: 未见哥哥 | 来源:发表于2018-10-31 22:47 被阅读10次

    自从出来工作之后就没在用过 c 了,为了更加深入的学习一些底层的东西,不得已重新来过一遍基础的知识点。

    C的基本数据类型

    signed&unsigned

    • int是4个字节,32位
    • int能表示2的32次方个数字
    • 把最高位抽出来,用于表示正负,只剩下31位表示数值
    • 此时int能表示2的31次方
    • 如果最高位的值是0,那么int就是正数,范围就是0~2的31次方-1
    • 如果最高位的值是1,那么int就是负数,范围就是-(2的31次方)~-1
    • 那么int的总范围,就是-2的31次方~2的31次方-1
    • signed:有符号,表示最高位用于表示正负
    • unsigned:无符号,表示最高位用于表示数值
    • 无符号int型只能表示正数,并且取值范围是0~2的32次方-1

    原码、反码、补码

    • 原码:把数值转换成二进制数值
    • 反码:除最高位,其他位依次取反
    • 补码:在补码的基础上,除最高位,其他位依次取反,然后+1
    • 正数的原码、反码、补码相同

    基本数据类型的长度

    JAVA

    • byte:1
    • short:2
    • int:4
    • long:8
    • float:4
    • double:8
    • boolean:1
    • char:2

    C

    • short:2
    • int:4
    • long:32位操作系统是4个字节,64位操作系统是8字节
    • float:4
    • double:8
    • char:1

    示例代码

    #include <stdio.h>
    
    int main(int argc, const char * argv[]) {
        
        //基本数据类型的长度
        //sizeof返回的 unsign long 无符号的long类型
        printf("%lu\n",sizeof(int));//4
        printf("%lu\n", sizeof(short));//2
        
        printf("%lu\n", sizeof(long));//在32位操作系统中long的长度4个字节,64位操作系统中long的长度为8个字节
        printf("%lu\n", sizeof(float));//4
        printf("%lu\n", sizeof(double));//8
        printf("%lu\n", sizeof(char));//1
        
        
    
        printf("%lu\n", sizeof(long long));//8
    
        //以下两种声明的方式,我觉得是没有必要的,因为要表示2个长度的数据类型,直接使用 short 就好的,要表8位长度的数据使用 long 就行了。
        printf("%lu\n",sizeof(short int));//2
        printf("%lu\n",sizeof(long int));//8
    
        
        //定义一个无符号的变量a,最大值42亿
        unsigned int a = 4294967295;
        
       
        
        //最高位表示符号位,只能表示无符号的一半大小,默认定义的整形类型就是有符号位的
        signed int b = 2100000000;
        
        //如果使用无符号来表示-1的话,它表示的是无符号的最大值4294967295。
        unsigned int c = -1;
        
        
        
        printf("%u\n",a);
        
        printf("%d\n",b);
        
        printf("%u\n",c);
        
        //char只能存放一个字符,并且使用''表示
        char ch = 'b';
        
        printf("%c",ch);
        
        
        return 0;
    }
    

    指针

    内存地址

    • 内存中每一个字节都有一个内存地址,这样才能保证不管数据存在内存的哪一个位置,都可以通过内存地址找到。
    • 访问内存的数据,要通过内存地址。

    指针变量

    • 指针变量就是保存内存地址的变量。
    • 指针变量通常简称为指针。
    • int* p;定义一个int型的指针变量,用于保存内存地址,该地址存放的值必须是int型的数据。
    • *p 获取指针变量 p 指向的内存地址所存放的数据。
    #include<stdio.h>
    #include<stdlib.h>
    int main(){
        
        int i;
        
        i = 10;
        
        //打印变量的地址,注意占位符使用%#x
        //printf("%#x\n",&i);
        
        int b = 3;
        
        //定义一个int类型的指针,存放是int类型变量的地址。
        //这是一个一级指针
        int *p = &b;
        
        
        //定义一个二级指针,存放的是指针的地址。
        int **q = &p;
        
        printf("p指针所存放内存地址对应的值是%d\n",*p);
        
        printf("q指针所存放内存地址对应的值是%d\n",**q);
        
        
        //指针的常见错误
        //常见错误1:野指针
        int *pp ;
        //定义了,但没有赋值的指针,称为野指针。
        //printf("pp的内存地址是%#x\n",&pp);
        int a = 3;
        //在这里直接将a变量赋值给一个指针变量的指向的内存单元。这是不行的,因为这是野指针。
        //*pp = a;
        
        //正确做法应该是:给指针pp赋值,让其指向一个有效的内存地址。
        pp = &a;
        
        printf("%d\n",*pp);
        //常见错误2:指针类型错误
        
        double d = 3.14;
        //给指针变量赋值错了数据类型的地址。
        //pp = &d;
        
        return 0;
    }
    

    指针的长度

    • 在 32 位操作系统中,指针的长度是 4 个字节,在 64 位操作系统中,指针的长度是 8 位。

    • 那为什么还要给指针分不同的类型呢?

      因为在做指针位移时,需要计算指针位移运算。

    多级指针&多级指针的访问

    多级指针的访问

    引用传递

    #include<stdio.h>
    
    //引用传递
    void swap(int *a ,int *b){
        
        int temp = *a;
        
        *a = *b;
        
        *b = temp;
        
    }
    int main() {
        
        int a = 10;
        
        int b = 20;
        
        swap(&a,&b);
        
        printf("a = %d\n",a);
        printf("b = %d\n",b);
        return 0;
    }
    
    

    主函数获取子函数的变量的内存地址

    #include<stdio.h>
    //错误版本
    //void function(int* p){
    //
    //    int i = 3;
    //    p = &i;
    //    printf("i 的地址是%p\n",&i);
    //}
    
    
    //i 的地址是0x7fff4fda8994
    //mainp指针存放的地址是:0x7fff4fda89e8
    //int main() {
    //
    //
    //    //定义一个可以接收地址的指针
    //    int* mainp;
    //
    //    function(mainp);
    //
    //    printf("mainp指针存放的地址是:%p\n",mainp);
    //    return 0 ;
    //}
    
    //正确版本
    void function(int** p){
        
        int i = 3;
        *p = &i;
        printf("i 的地址是%p\n",&i);
    }
    
    
    //i 的地址是0x7fff5607a994
    //mainp指针存放的地址是:0x7fff5607a994
    int main() {
        
        
        //定义一个可以接收地址的指针
        int* mainp;
        
        function(&mainp);
        
        printf("mainp指针存放的地址是:%p\n",mainp);
        return 0 ;
    }
    
    

    常见错误

    • 指针没赋值就使用,没赋值的指针叫野指针
    • 指针类型弄错,给int型指针指定了一个非int型数据的地址

    数组

    • C语言的数组不检测越界
    • 数组的内存地址是连续的空间
    • 数组变量名保存的地址就是数组第0个元素的地址
    • 内存地址可以加减运算
    • 内存地址+1表示右移一个单位
    • 指针的长度就是4个字节
    • 不同的指针类型的长度都是一样的,分不同类型的指针是在位移时比较方便。
    #include <stdio.h>
    
    
    int main(){
        
        char c[] = "hello";
        
        printf("第8个元素的值是%c\n",c[8]);//c语言的数组是不检测越界。
        
        //数组每一个元素的地址都是连续的。
        printf("c[0]的地址%#x\n",&c[0]);//c[0]的地址0xeef9f98a
        printf("c[1]的地址%#x\n",&c[1]);//c[1]的地址0xeef9f98b
        printf("c[2]的地址%#x\n",&c[2]);//c[2]的地址0xeef9f98c
        printf("c[3]的地址%#x\n",&c[3]);//c[3]的地址0xeef9f98d
        printf("c[4]的地址%#x\n",&c[4]);//c[4]的地址0xeef9f98e
        
        //打印数组变量名字的地址
        printf("c数组变量名字的地址是%#x\n",&c);//c数组变量名字的地址是0xeef9f98a
        
        int data[] = {1,2,3,4,5};
        
        //数组每一个元素的地址都是连续的。整型数据每一个元素之间相差4个字节
        printf("data[0]的地址%#x\n",&data[0]);//data[0]的地址0xe538f970
        printf("data[1]的地址%#x\n",&data[1]);//data[1]的地址0xe538f974
        printf("data[2]的地址%#x\n",&data[2]);//data[2]的地址0xe538f978
        printf("data[3]的地址%#x\n",&data[3]);//data[3]的地址0xe538f97c
        printf("data[4]的地址%#x\n",&data[4]);//data[4]的地址0xe538f980
        
        //打印数组变量名字的地址
        printf("data数组变量名字的地址是%#x\n",&data);//data数组变量名字的地址是0xe538f970
        //打印的数组变量和打印数组变量的地址是一样的,都是表示数组第一个元素的地址。
        printf("data数组变量是%#x\n",data);//data数组变量名字的地址是0xe538f970
        
        //数组变量名字的地址就是数组第一个元素的地址
        
        
        //内存地址是可以进行加减运算的。
        //定义一个 cp 指针,存放数组cy首元素的地址
        //指针+1表示内存地址位移一位
        char* cp = &c;
        
        printf("第1个元素的值%c\n",*(cp+0));//h
        printf("第2个元素的值%c\n",*(cp+1));//e
        printf("第3个元素的值%c\n",*(cp+2));//l
        printf("第4个元素的值%c\n",*(cp+3));//l
        printf("第5个元素的值%c\n",*(cp+4));//o
        
        int* datap = &data;
        
        printf("第1个元素的值%d\n",*(datap+0));//1
        printf("第2个元素的值%d\n",*(datap+1));//2
        printf("第3个元素的值%d\n",*(datap+2));//3
        printf("第4个元素的值%d\n",*(datap+3));//4
        printf("第5个元素的值%d\n",*(datap+4));//5
        
    }
    

    数组与指针的关系

    如何用指针表示数组

    int a[] = {1,2,3,4};
        
    //使用指针p存放数组a首个元素的地址。
    int *p = a;
    

    如何通过指针访问数组元素

    //使用指针修改数组的值
    *(p+0) = 10;
    

    示例代码

    #include<stdio.h>
    
    int main() {
        
        int a[] = {1,2,3,4};
        
        //使用指针p存放数组a首个元素的地址。
        int *p = a;
        
        printf("数组变量:%p\n",a);//0x7ffeede7d960
        printf("指针地址:%p\n",p);//0x7ffeede7d960
        
        //使用指针修改数组的值
        *(p+0) = 10;
        
        printf("指针访问数组元素:%d\n",*(p+0));
        
        return 0;
    }
    

    常见错误

    • 指针没赋值就使用,没赋值的指针叫野指针
    • 指针类型弄错,给int型指针指定了一个非int型数据的地址
    #include<stdio.h>
    int main() {
        int * p;
        int a = 1;
        double d = 4.0;
        
        
        //printf("%p\n",&p);//指针没有指向不能轻易使用,它是一个野指针
        
        p = &a;
        
        printf("*p = %d\n",*p);//*p = 1
        
        //指针指向地址存放的类型必须和声明指针的类型一致
        //p = & d;
        
        return 0;
    }
    

    堆与栈

    • 栈是自动分配,自动释放
    • 大小固定
    • 从栈分配的内存空间叫静态内存

    • malloc或者new申请
    • c语言需要用free释放
    • 从堆分配内存空间叫动态内存
    • realloc 是在原来的基础上再分配内存空间

    malloc

    • 动态申请内存空间
    • 一个malloc申请出来的内存地址是连续的
    • malloc(需要申请的字节数) 返回一个指针

    malloc 的图解

    malloc 的图解

    示例代码

    #include <stdio.h>
    #include <stdlib.h>
    
    
    int main() {
        
        int len ;
        
        printf("请输入数组的长度\n");
        
        scanf("%d",&len);
        
        //malloc动态分配内存,p指针指向首字节地址
        int *p = (int*)malloc(sizeof(int)*len);
        
    
        for(int i = 0;i<len;i++){
            *(p+i) = i*10;
        }
        
        
        for(int i = 0;i<len;i++){
            printf("第%d个数据是%d\n",(i+1),p[i]);
        }
        
        printf("p指针变量的地址:%p\n",p);
        printf("第1个元素的值:%d\n",*(p));
        
        
        
        int a = p[1];
        printf("malloc 分配的内存空间指针相当于一个数组,%d\n",a);
        
        //释放在堆中动态分配的内存空间;
        //free()释放的是指针指向的内存。指针变量依然存在,只有程序结束时才被销毁。不过现在指针指向的内容是未定义的垃圾,所以现在指针又成为了野指针。
        //void free(void *FirstByte):该函数是将之前用malloc分配的空间还给程序或者是操作系统,也就是释放了这块内存,让它重新得到自由。
        free(p);
        
        printf("p指针变量的地址:%p\n",p);
        printf("第1个元素的值:%d\n",*(p));
        
        for(int i = 0;i<len;i++){
            printf("第%d个数据是%d\n",(i+1),p[i]);
        }
        
        /**
         
         在 Mac OS 系统:
         
            free(p) 之后 *P 没有变化,p的地址没有发生变化
         
         
         在 Linux 系统:
         
            free(p) 之后 *p 的值不再是 free 之前的值
         */
         
               
        return 0;
    }
    

    realloc

    重新分配主存

    示例代码

    #include <stdio.h>
    #include <stdlib.h>
    
    
    int main() {
        
        int len ;
        
        printf("请输入数组的长度\n");
        
        scanf("%d",&len);
        
        //malloc动态分配内存,p指针指向首字节地址
        int *p = (int*)malloc(sizeof(int)*len);
        
    
        for(int i = 0;i<len;i++){
            *(p+i) = (i+1)*10;
        }
        
        
        for(int i = 0;i<len;i++){
            printf("第%d个数据是%d\n",(i+1),p[i]);
        }
        
        for(int i = 0;i<len;i++){
            printf("指针p[%d]的地址是:%p\n",(i+1),(p+i));
        }
        
        int size = 0;
        printf("请输入需要补充的值\n");
        scanf("%d",&size);
        
        
        
        p = (int *)realloc(p,size+len);
        
        for(int i = 0;i<len+size;i++){
            *(p+i) = (i+1)*10;
        }
        
        for(int i = 0;i<len+size;i++){
            printf("第%d个数据是%d\n",(i+1),p[i]);
        }
        
        
        for(int i = 0;i<len+size;i++){
            printf("指针p[%d]的地址是:%p\n",(i+1),(p+i));
        }
        
        
        
        free(p);
        
        
        
        return 0;
    }
    
    /*
     
     realloc函数的使用
     
     函数名: realloc
     功  能: 重新分配主存
     用  法: void *realloc(void *ptr, unsigned newsize);
     
     在指针 ptr 后面分配新的内存空间。
     
     请输入数组的长度
     4
     第1个数据是10
     第2个数据是20
     第3个数据是30
     第4个数据是40
     指针p[1]的地址是:0x7fd4e7402ab0
     指针p[2]的地址是:0x7fd4e7402ab4
     指针p[3]的地址是:0x7fd4e7402ab8
     指针p[4]的地址是:0x7fd4e7402abc
     请输入需要补充的值
     3
     第1个数据是10
     第2个数据是20
     第3个数据是30
     第4个数据是40
     第5个数据是50
     第6个数据是60
     第7个数据是70
     指针p[1]的地址是:0x7fd4e7402ab0
     指针p[2]的地址是:0x7fd4e7402ab4
     指针p[3]的地址是:0x7fd4e7402ab8
     指针p[4]的地址是:0x7fd4e7402abc
     指针p[5]的地址是:0x7fd4e7402ac0
     指针p[6]的地址是:0x7fd4e7402ac4
     指针p[7]的地址是:0x7fd4e7402ac8
     */
    

    自定义类型

    结构体

    • 结构体的定义
    //1.定义一个结构体
    struct Student {
        int age;
        char *name;
        char sex;
        //eatp表示函数指针的名字
        void(*eatp)();
    };
    
    • 结构体成员的赋值
    //方式1 
    struct Student st = {        
        11,"张三",'m'
    };
    
    //方式2
    //给结构体内的成员赋值
    st.age = 12;
    st.name = "李四";
    st.sex = 'm';
    
    • 结构体函数指针

    结构体内指针不能定义函数,只能定义函数指针。

    //给结构体指针赋值
    st.eatp = eat;
    
    • 结构体成员的使用
    //用结构体变量引用变量
     printf("年龄:%d\n名字:%s\n性别:%c\n",st.age,st.name,st.sex);
     
     //用指针变量的方式引用结构体属性
    struct Student *p = &st;
    printf("年龄:%d\n名字:%s\n性别:%c\n",(*(p)).age,(*(p)).name,(*(p)).sex);
    
    #include<stdio.h>
    
    void eat() {
        printf("学生吃饭\n");
    }
    
    struct Student {
        int age;
        char *name;
        char sex;
        //eatp表示函数指针的名字
        void(*eatp)();
    };
    
    int main(void){
    
        //定义一个结构体变量,并给结构体成员赋值
    //    struct Student st = {
    //        11,"张三",'m'
    //    };
        
        
        struct Student st ;
    
        //给结构体内的成员赋值
        st.age = 12;
        st.name = "李四";
        st.sex = 'm';
        //给结构体指针赋值
        st.eatp = eat;
       //输出结构体成员的值
    
        printf("年龄:%d\n名字:%s\n性别:%c\n",st.age,st.name,st.sex);
        
        
        struct Student *p = &st;
        
        
        printf("年龄:%d\n名字:%s\n性别:%c\n",(*(p)).age,(*(p)).name,(*(p)).sex);
        
        
        printf("年龄:%d\n名字:%s\n性别:%c\n",p->age,p->name,p->sex);
        //调用结构体指针。
        p->eatp();
        return 0;
    }
    

    文件处理

    • 文件处理流程
      • 打开文件
      • 文件的处理
        • fwrite写入
        • fread读取
        • rewind 将文件游标指向文件的开头
      • 关闭文件

    示例代码

    //文件的操作
    #include<stdio.h>
    
    
    int main(void){
        
        //定义一个文件类型的变量
        
        FILE *file = NULL;
        
        //打开文件
        //FILE *fopen(char *filename, char *type);
        //a+ 表示 append 追加 +表示如果没有这个文件就创建一个文件
        
        file = fopen("hello.txt","a+");
        
        char *str = "hello world";
        
        //int fwrite(void *ptr, int size, int nitems, FILE *stream);
        fwrite(str,1,11,file);
        
        
        //将文件指针重新指向一个流的开头
        //int rewind(FILE *stream);
        //因为文件的打开mode是 a+ 追加的方式,所以要将处理文件的指针指向一个流的开头。才能读取到数据
        rewind(file);
        char buf[1024] = {};
        //读出来
        // int fread(void *ptr, int size, int nitems, FILE *stream);
        fread(buf,1,11*2,file);
        
        printf("读出来的数据是%s\n",buf);
        
        fclose(file);
        
        return 0;
    }
    /*
     "r" = "rt"
     打开一个文本文件,文件必须存在,只允许读
     "r+" = "rt+"
     打开一个文本文件,文件必须存在,允许读写
     "rb"
     打开一个二进制文件,文件必须存在,只允许读
     “rb+”
     打开一个二进制文件,文件必须存在,允许读写
     "w" = “wt”
     新建一个文本文件,已存在的文件将被删除,只允许写
     "w+" = "wt+"
     新建一个文本文件,已存在的文件将被删除,允许读写
     “wb”
     新建一个二进制文件,已存在的文件将被删除,只允许写
     “wb+”
     新建一个二进制文件,已存在的文件将被删除,允许读写
     "a" = "at"
     打开或新建一个文本文件,只允许在文件末尾追写
     "a+" = "at+"
     打开或新建一个文本文件,可以读,但只允许在文件末尾追写
     “ab”
     打开或新建一个二进制文件,只允许在文件末尾追写
     “ab+”
     打开或新建一个二进制文件,可以读,但只允许在文件末尾追写
     对于文件使用方式有以下几点说明:
     1) 文件使用方式由r,w,a,t,b,+六个字符拼成,各字符的含义是:
     r(read): 只读
     w(write): 只写
     a(append): 追加
     t(text): 文本文件,可省略不写
     b(binary): 二进制文件
     */
    
    

    C语言的编译器

    编译命令

    编译 c 代码,不同的操作系统使用的编译工具不一样,Mac OS 使用的 clang ,Linux 使用的是 gcc

    gcc/clang -g -o main main.c -I 关联的头文件位置 -L 关联的库位置 -l关联的库名字 -l关联的库名字2...
    

    编译过程

    .c (预处理) -> .i 文件(编译) -> .s(汇编) -> .o(链接) -> 可执行文件

    预处理

    获取预处理后的.i文件

    • 编写一个 helloworld.c 文件
    • gcc 进行编译
    • gcc -o helloworld.i helloworld.c -E 在当前目录下得到一个 hellowold.i 文件

    helloworld.i文件

    查看这个文件可以知道,在 .c 预处理之后的 helloworld.i 文件可以看出,源码会被保留,并且会将头文件的内容写到出头文件的内容上面。

    • 编译关联库的c文件为add.out
    clang -c add.c -o add.out
    
    • 编译静态库

    将 add.out 编译为静态库 mylib

    libtool -static -o libmylib.a add.out
    
    • 编译带有main函数的main.c文件
    clang -o main main.c -I . -L . -lmylib
    
    • 执行最终编译后的文件
    ./main
    

    源代码如下

    • main.c
    #include<stdio.h>
    #include "add.h"
    int main(void){  
        int result = add(1,2);
        printf("输出结果是%d\n",result);
        return 0;
    }
    
    • add.h
    int add(int a,int b);
    
    • add.c
    #include<stdio.h>
    #include "add.h"
    int add(int a,int b){
        return a+b;
    }
    

    宏定义

    就是将 #define 定义的内容安装字符串进行替换。

    #include<stdio.h>
    
    //宏定义是没有c语言语法规则的,因此可以不需要写;
    #define R 10
    #define _main int main(
    //传递参数
    #define N(n) n*10
    //定义一个加法运算的宏
    //对比一个函数来说,这样定义宏的好处是什么?
    //1.它的参数的类型不限制
    //2.返回值类型不限制
    #define ADD(a,b) a+b
    _main) {
        
        int a = R;
        
        printf("a = %d\n",a);
        
        
        int b = N(5);
        printf("b = %d\n",b);
        
        
        int c = ADD(5,6);
        printf("c = %d\n",c);
        
        
        float d = ADD(5,6.3);
        printf("d = %f\n",d);
        
        //注意:宏定义只是将字符串进行替换操作,不会考虑一些逻辑运算符关系的,需要手动控制。
        //2+3*4+5 = 2+12+5 = 19
        int e = ADD(2,3)*ADD(4,5);
        
        int f = (ADD(2,3))*(ADD(4,5));
        
        printf("e = %d\n",e);//19
        printf("f = %d\n",f);//45 正确的
        return 0;
    }
    

    类型定义 typedef

    一般会是用于给自定义类型起别名,例如在jni中,就大量的使用了typedef来将c的类型起别名为java类型。

    typedef int tni;
    
    int main() {
        //在预处理后的.i文件,这里的tni并不会像宏定义一样被替换。
        tni a = 10;
    
        printf("a = %d\n",a);
    
        return 0;
    }
    

    联合体

    • 可以将不同类型的变量定义在同一块内存空间中,并且同一时间只有一个变量会被存储。
    • union 关键字
    #include<stdio.h>
    //1.定义一个联合体data
    union data {
        int a;
        int b;
        char c;
    };
    
    int main() {
        
        union  data _data;
        
        _data.a = 1;
        _data.b = 2;
        _data.c = 'c';
        
        printf("a = %d\n",_data.a);
        printf("b = %d\n",_data.b);
        printf("c = %c\n",_data.c);
        
        //取出地址,每一个变量存储的地址都是一样的
        //%p打印地址
        printf("a = %p\n",&_data.a);//0x7fff54c929c8
        printf("b = %p\n",&_data.b);//0x7fff54c929c8
        printf("c = %p\n",&_data.c);//0x7fff54c929c8
        
        
        //这里只会输出最后一个赋值的元素 也就是char c = 'c' 会被存储、其他两个会被 c 覆盖
        printf("a = %c\n",*(&_data.a));//99
        printf("c = %c\n",_data.c);//99
        return 0;
    }
    

    链表

    静态链表

    • 预先定义好一些元素通过指针的方式连接起来的一条链子就是链表。
    • 先进后出
    #include<stdio.h>
    
    struct weapon {
        
        int price;
        int atk;
        
        struct weapon * next;
        
    };
    
    int main() {
        
        struct weapon a,b,c,*head;
        
        a.price = 100;
        a.atk = 101;
        
        b.price = 200;
        b.atk = 201;
        
        c.price = 300;
        c.atk = 301;
        
        
        //head指向第一个元素
        head = &a;
        
        a.next = &b;
        
        b.next = &c;
        
        //最后一个元素的next属性为空
        c.next = NULL;
        
        
        struct weapon *p;
        p = head;
        while(p!=NULL){
            printf("price = %d,atk = %d\n",p->price,p->atk);
            
            //下面两种方式都行
            //p = (*p).next;
            p = p->next;
            
        }
        return 0;
    }
    

    动态链表

    动态的往链表中添加数据

    #include<stdio.h>
    //mac下<malloc.h>头文件找不到,具体原因未查明
    #include<mm_malloc.h>
    
    struct weapon {
        int price;
        int atk;
        struct weapon *next;
        
    };
    
    
    struct weapon * create() {
        struct weapon *head;//指向第一个元素
        struct weapon *p1,*p2;//p1指向当前输入的元素,p2指向上一个元素
        
        int n = 0;//表示输入的元素的次数
        
        p1 = p2 = (struct weapon*)malloc(sizeof(struct weapon));
        
        //p1->atk = 10;
        //p1指向的是一个 weapon 结构体
        //我们向往这个结构体里面的属性赋值,首先就需要取出结构体的元素的地址: &(p->price)
        
    
        //下面两种输入方式都可以
        scanf("%d,%d",&(p1->price),&p1->atk);
        
        head = NULL;
        
        while (p1->price!=0) {
            n++;
            
            if(n == 1){
                //第一次输入,保存到head中
                head = p1;
            }else{
                p2->next = p1;
            }
            //p2指向上一个元素(相对下一次循环来说的)
            p2 = p1;
            
            //给p1重新分配内存空间
            p1 = (struct weapon*)malloc(sizeof(struct weapon));
            scanf("%d,%d",&p1->price,&p1->atk);
        }
        
        p2->next = NULL;
        return head;
        
    }
    
    int main(){
        struct weapon * head = create();
        while (head!=NULL) {
        
            printf("price = %d,atk = %d\n",head->price,head->atk);
            
            //下面两种方式都行
            //p = (*p).next;
            head = head->next;
        }
        return 0;
    }
    
    

    工具

    我们在使用 c 语言开发时,不可能将所有的 API都记住,因此我们应该常备 API文档在身,在 Mac平台比较好用的应该就是 Dash 了。

    Dash 可以下载不同语言的 API 文档,非常方便,推荐大家使用.

    image.png

    相关文章

      网友评论

        本文标题:重拾c语言基础-记录笔记

        本文链接:https://www.haomeiwen.com/subject/nkgztqtx.html