美文网首页
C语言基础知识

C语言基础知识

作者: 十年之后_b94a | 来源:发表于2019-11-01 11:33 被阅读0次

    float类型

    #include <stdio>
    int main()
    {
      float num = 4.5f;
      float num1 = 5.2f;
      float s =  num  * num1;
      
      printf("结果为%f\n",s);  //注意这里是%f  int类型的为%d
      printf("结果为%.2f\n",s);  //打印为保留两位小数 真实的值还是6位小数
      return 0;
    }
    

    double类型

    #include <stdio>
    int main()
    {
      float num = 4.5f;
      float num1 = 5.2f;
      float s =  num  * num1;
      
      printf("结果为%lf\n",s);  //注意这里是%lf  int类型的为%d
      return 0;
    }
    

    char 类型

    #include <stdio>
    int main()
    {
      char a = 'A';
      printf("结果为%c\n",a);  //注意这里是%c  int类型的为%d
      return 0;
    }
    

    接收用户输入 scanf

    #include <stdio>
    int main()
    {
      int num;
      scanf("%d",&num);
      printf("结果为%c\n",a);  //注意这里是%c  int类型的为%d
      return 0;
    }
    
    #include <stdio.h>
    
    int main()
    {
        int value;
        printf("请输入一个整数:");
        while (scanf("%d", &value) == 0 || value <0) {
            printf("请重新输入:");
        }
        return 0;
    }
    

    scanf输入的值是可以判断的

    强制类型转换

    #include <stdio>
    int main()
    {
      double num = 6; //double占8字节但是整形6 占用4字节 小字节转大字节不用管
      int num1 = (int)num;   //小字节赚大字节我们通常加上(type)强制转换
      printf("结果为%c\n",a);  //注意这里是%c  int类型的为%d
      return 0;
    }
    

    位运算符

    运算符 作用 示例
    & 按位与 两个操作数同时为1结果为1
    | 按位或 两个操作数只要有一个为1结果为1
    ^ 按位异或 操作数为1,结果为0,操作数为0,结果为1
    ~ 按位非 两个操作数相同,结果为0,不相同为1
    << (重点 ) 左移 右侧空位补0
    >> (重点) 右移 左侧空位补符号位
    >> 无符号右移 左侧空位补0
    /*示例  位运算符都是处于二进制操作*/
    比如 5 & 6
    5的二进制 :101
    6的二进制 : 110
    那么  101 & 110 
          101
          ---
          110
    结果: 100   //相同的即为1  不同为0 
    //按照常识讲 个位数一个为1 一个为0  那么结果就是0 
    //按照常识讲 十位数一个为10一个为1  那么结果就是0 
    //按照常识讲 百位数都为1 那么结果就是1
    
    二进制结果为100  十进制是4
    
    那么 5 & 6 = 4
    

    什么是sizeof

    sizeof获取数据类型的占用内存空间大小

    sizeof(int)  //输出4
    sizeof(char)  //1
    sizeof(short)  //2
    sizeof(float)  //4
    sizeof(3.5) // 8 默认double
    sizeof(3.5f) //4
    

    思考题

    int num = 10;
    int result = num ++ >= 11 && -- num < 20;
    printf("result :%d,num:%d\n",reuslt);
    

    按照我以为的思维 结果应该是result为0,num为10
    但是现在结果是result 0 num 是11

    为什么呢?因为此时结果 num ++ >= 11 这里执行结果已经是假了
    **那么表达式就不会运行后面的 -- num < 20 **

    随机数 srand、rand

    首先使用随机数 需要导入随机数的头部文件
    <stdlib.h> 如果以时间为随机种子需要导入<time.h>

    rand的取值范围是0 - 32767之间

    例子如果去随机数5-15之间
    那么 rand() % 11 + 5
    取 a -b 之间
    随机数公式 rand() % (b-a+1)+a

    #include <stdio.h>
    #include <time.h>
    #include <stdlib.h>
    int main()
    {
        //使用时间作为种子 产生随机数字
        srand(time(NULL));
        printf("%d\n",rand());
    
        return 0;
    }
    
    

    Sleep(int)函数

    延迟的意思
    Sleep(5) 延迟5毫秒必须要加入头文件<windows.h>

    数组

    数组定义必须需要定义长度,不确定有多少的时候,尽量大点,可以不用初始化
    但如果初始化了值,那也可以不用定义长度
    拓展:数组的长度:sizeof(数组名)/sizeof(数组名[0])
    原理很简单:sizof(数组名) 得出的结果是整个数组所占用的空间,那么再除以一个元素所占的空间大小,那么就是数组的个数(长度)

    #include <stdio.h>
    int main()
    {
        //使用时间作为种子 产生随机数字
       int num[20] = {1,32,12,45,54};
       double asd[2] = {456.54,456.4253};
    
        printf("%d\n",rand());
    
        return 0;
    }
    
    

    冒泡排序

    冒泡排序的原理就是遍历和交换位置
    并且需要双重循环
    且外层循环只需要循环数组长度-1
    而内存循环的循环次数关系是数组长度-1 - i (外层循环的次数)

    #include <stdio.h>
    void main()
    {
      int nums[6] = {16,25,9,90,23};
      int temp;
      for(int i = 0;i<5;i++){
            for(int j = 0;j<5-i-1;j++){
                if(nums[j]<nums[j+1]){
                    temp = nums[j];
                    nums[j] = nums[j+1];
                    nums[j+1] = temp;
                }
            }
        }
        for(int i = 0;i<5;i++){
            printf("%d\n",nums[i]);
        }
    }
    

    二维数组

    二维数组相当于平面图,代表行和列
    既然是行和列那就相当于是需要双重循环来操作

    #include <stdio.h>
    void main()
    {
      int nums[6][3] = {  //代表有6行三列
        {78,56,80},
        {78,56,80},
        {78,56,80},
        {78,56,80},
        {78,56,80},
        {78,56,80}
      };
    }
    

    字符数组

    字符数组和之前的传统int float double类型的数组都不太一样,
    (type) name[length] = {};其中定义的length是数组长度及有多少元素;
    但在字符数组中,我们知道一个中文字符是占用两个字节的空间
    字符数组中length代表的是字节长度
    字符都是以\0结尾也就是空字符串

    #include <stdio.h>
    void main()
    {
     char nam[] = "jack";
    char nam1[] = {"j","a","c","k","\0"};
      printf("长度:%d\n",sizeof(nam));  //5
      printf("长度:%d\n",sizeof(nam1));//5
    }
    
    #include <stdio.h>
    void main()
    {
      char name[50]={"测试的哦"};
      printf("%s",name);//注意这里是%s 
      scanf("%s",name)  //这里是没有&符号的
      //重点
      char name[2]={"测试的哦"};
      printf("%s",name);//这里打印的是(测)  
    }
    

    思考
    那如果是{"测试","测试1","测试2","测试3","测试4"};这种的字符数组怎么定义?

    #include <stdio.h>
    #include <>
    void main()
    {
     char test[5][6] = {"测试","测试1","测试2","测试3","测试4"};
      for(int i = 0;i<5;i++)
      {
        printf("%s\n",test[i]); //依次打印
      }
    }
    

    字符串的重新赋值

    #include <stdio.h>
    void main()
    {
     char name[] = "哈哈哈哈";长度是9 一个中文占用两个空间最后一个以空字符结束\0
      printf("请重新输入值:\n") //重新赋值尽可能的不要超过9
      scanf("%s",name);//不用去地址符(&) 因为数组名本身就是一个指针
      printf("%s\n",)
    }
    

    gets 与puts输入/输出字符串

    注意使用gets可能会造成越界,就是超出字符串长度,因为gets接受是你输入多少,就接受多少

    #include <stdio.h>
    void main()
    {
      char name[50];
      gets(name); 相当于scanf("%s",name)
      puts(name);相当于printf("%s",name)
    }
    

    获取字符串大小strlen

    注意中文占两个字节

    #include <stdio.h>
    #inlcude <string.h> //使用头文件
    int getStrLength(char srt[])
    {
      int count= 0;
      while(str[count] != "\0")  我们知道字符串十一\0结束
      {
        count ++ ;
      }
      return count;
    }
    void main()
    {
      char name[50] = "测试测试测试测试";
      int len = getStrLength(char name); //自定义函数
      int lens = strlen(name);
    }
    

    字符串比较(strcmp()方法)

    引入字符串操作头文件<string.h>

    #include <stdio.h>
    #include <string.h>
    void main()
    {
       if(strcmp("a","A") == 0)
      {
        printf("两个字符相等\n");
      }else{
         printf("两个字符不相等\n");
      }
    }
    

    字符串的拼接strcat()

    注意strcat(参数1,参数2);
    参数1 的空间一定要比参数2大 不然可能越界

    #include <stdio.h>
    #include <Windows.h>
    #include <string.h>
    
    int main()
    {
        char name[50] = "原文示意";
        char test[30] = "测试用的";
        strcat(name, test);
        printf("%s\n", name);
        system("pause");
        return 0;
    }
    

    字符串数组的copy(strcpy()方法)

    引入字符串操作头文件<string.h>

    #include <stdio.h>
    #include <string.h>
    void main()
    {
        char str[] = "哈哈哈哈哈哈";
        strcpy(str,"则啧啧啧");
        printf("%s",str);
    }
    
    

    字符串实战 - 加密/解密

    #include <stdio.h>
    #include <string.h>
    #define K 30
    char *ensCode(char str[])
    {
        int count = strlen(str);
        for (int i = 0; i < count; i++)
        {
            str[i] = str[i] + K + i;
        }
        return str;
    }
    char *densCode(char str[])
    {
        int count = strlen(str);
        for (int i = 0; i < count; i++)
        {
            str[i] = str[i] - K - i;
        }
        return str;
    }
    int main()
    {
        char num[50] = "123456789";
        ensCode(num); 这里传值是指针哦  因为数组名就是指针,so不用定义值接收,直接会改变原来的值
        printf("%s\n", num);
        densCode(num);
        printf("%s\n", num);
        system("pause");
        return 0;
    }
    
    

    指针

    指针是一个值为内存地址的变量;
    *(符号)声明指针变量;
    &(取地址符号)去内存地址 ;
    %p 输出指针类型
    *指针变量名 取指针变量对应的值

    如果改变 *指针变量名的值 原来的变量值也会改变
    ---------- 拓展 -------------
    指针的值一般是 十六进制
    %x 输出结果十六进制小写字母且去头部0详情看例子
    注意指针变量赋值一定要用取地址符,不能直接赋值为十六进制的但是可以赋值为NULL
    int * test = NULL;

    image.png

    简单的例子

    #include <stdio.h>
    void main()
    {
      int num = 1024;
      int * ptr_num = &num;
      printf("num的内存地址是:%p,%x\n",&num,&num);//注意这里要用取地址符号
      我这里的结果是002811FC , 2811fc 
      printf("我这里用的是指针变量:%p",ptr_num);
    }
    
    

    取指针变量的值

    #include <stdio.h>
    void main()
    {
      int num = 1024;
      int * ptr_num = &num;
      printf("我的指针的指向的值是:%d\n",*ptr_num);注意这里是  *指针变量名
      //结果是1024;不是十六进制哦
     // *指针变量名   取值
      *ptr_num = 500;
      printf("我改变指针的值:%d,看看原来的num变了没?%d\n",*ptr_num,num);
      //500 ,500可以看到这里改变指针的值是会改变原变量的值
    }
    

    注意指针也有指针哦

    int num = 10;
    int * ptr_num = &num;   //0028FF14
    int * ptr_num2 = &ptr_num;  //0028FF18
    
    #include <stdio.h>
    void main()
    {
      int i = 1024;
      int y = 2048;
      int * p1 = &i;
      int * p2 = &y;
      printf("i的值是:%d,内存地址是%p\n",i,p1);
      printf("y的值是:%d,内存地址是%p\n\n",y,p2);
      /*//改变值
      y = i;
      printf("改变值之后\n");
      printf("i的值是:%d,内存地址是%p\n",i,p1);
      printf("y的值是:%d,内存地址是%p\n\n",y,p2); //值变量内存地址没变
      */
       // 说明一件事  y = i 相当于*p1 = *p2
    
      /*
      *p1 = *p2;
      printf("改变值之后\n");
      printf("i的值是:%d,内存地址是%p\n",i,p1);
      printf("y的值是:%d,内存地址是%p\n\n",y,p2); //值变量内存地址没变
      */
    
      p2 = p1;//这里是内存地址直接赋值
      *p2 = 400;
      printf("改变值之后\n");
      printf("i的值是:%d,内存地址是%p\n",i,p1);//这里i的值变了
      printf("y的值是:%d,内存地址是%p\n\n",y,p2); //值变量内存地址变了,但是值没变
    }
    
    

    指针数组

    指针数组是一块连续的内存空间。连续的内存空间,那可以连想那就会有首地址和尾地址。

    #include <stdio.h>
    void main()
    {
      double test[] = {50,60,70,80,9,450,40};
      printf("test的首地址:%p,取地址的首地址:%p",test,&test[0]);
      //结果是一样的
    }
    

    通过这个例子我们知道数组名,他就是指针数组的首地址;

    指针参与运算

    #include <stdio.h>
    void main()
    {
      double test[] = {50,60,70,80,9,450,40};
      double * ptr = test; //指向了test的首地址 这可以通过上述例子可以知道
      printf("运行前ptr的值是:%p\n",ptr);
      for(int i =0;i<5;i++ )
      {
        printf("运算第%d次,此时的值:%.2lf\n",i+1,*ptr ++);
        printf("运算第%d次,此时的ptr的内存地址:%p:%p\n",i+1,ptr);
        printf("-----------------\n\n");
      }
      printf("此时的ptr的值是:%p\n",ptr);
    }
    

    请注意上述例子容易导致误区!按照之前的学习逻辑 * 加上指针名,是取该指针指向的内存地址的值,但在字符数组里面,按照之前的逻辑应该是每次循环改变了test的第一个元素的值,但是这里并不是,而是指针向后位移,它是先向后位移,再取值他的运行顺序应该是 * (ptr ++)

    我们知道输出指针名它是会输出十六位的内存地址的,所以每次运行指针像下位移,位移多少长度我们这里定义的是double类型的数组,double占8个空间,所以每次运行ptr都像下运行8个位置

    拓展指针运算

    image.png

    对比以下这几句,看看结果会是什么?

    #include <stdio.h>
    void main()
    {
      double test[] = {50,60,70,80,9,450,40};
      double * ptr = test; //指向了test的首地址 这可以通过上述例子可以知道
      printf("运行前ptr的值是:%p\n",ptr);
      for(int i =0;i<5;i++ )
      {
        //分别运算这三句话
        //拓展第一次
        printf("运算%d次,此时的值是:%.2lf\n",i+1,*ptr ++);
        //拓展第二次
         printf("运算%d次,此时的值是:%.2lf\n",i+1,* ++ptr);
        //拓展第三次
        printf("运算%d次,此时的值是:%.2lf\n",i+1,++ *ptr);
    
    
    
         printf("运算第%d次,看下此时指针的位置:%p\n",i+1,ptr);
        printf("运算第%d次,看下猜想:%.2lf\n",i+1,*ptr);
        printf("-----------------\n\n");
      }
      
      printf("此时的ptr的值是:%p\n",ptr);
      printf("此时的test首元素的值是:%d\n",test[0]);
    }
    

    看运行结果我们对比下,在分析:
    运行拓展第一次:


    image.png

    运行拓展第二次:


    image.png

    运行拓展第三次:

    image.png

    指针不止+1哦

    image.png

    这里指针+2 不是十六进制+2哦,而是连续加两个连续的内存地址空间段,指向了数组元素为3的那个值

    image.png

    数组的尾地址

    #include <stdio.h>
    void main()
    {
      int num[] = {15,20,25,30,35,45,50,55,60};
      int * ptr_start = num; //首地址
      int * ptr_end = num + 8 ; //尾地址  下标为0的就是首地址那么下标为尾元素的就是尾地址
    }
    

    二维数组的指针

    #include <stdio.h>
    void main()
    {
        double num[3][5] = {
            {1,2,3,4,5},
            {6,7,8,9,10},
            {11,12,13,14,15}
        };
    
        //去除各个内run空间地址
    
        for(int i = 0 ;i<3;i++)
        {
            for(int j = 0 ;j<5;j++)
            {
                printf("%p\t",num[i]+j); //先取第i行的地址 在一次偏移j个
            }
            printf("\n");
        }
    }
    

    字符指针

    #include <stdio.h>
    void main()
    {
      char * str = "abcedefghigklmnopqrstuvwsyz";  含义是str执行了后面这段话的首地址,在vs2017中这种定义会报错 在定义前加上 const
      str += 9; 指针向后移动9个字节
      puts(str); 结果会是?igklmnopqrstuvwsyz 过滤掉前面九个字节了
    ;}
    

    理解字符串数组与字符指针的区别

    #include <stdio.h>
    #include <Windows.h>
    
    int main()
    {
        char s[] = "abcdefghijklmn";
        const char * str = "abcdefghijklmn";
        printf("字符的地址:%p\n", "abcdefghijklmn");
        printf("数组字符的地址:%p\n", s);
        printf("指针字符的地址:%p\n", str);
        system("pause");
        return 0;
    }
    
    image.png

    我们可以发现 字符指针 只是把字符串的地址给了这个指针,而数组字符,则是开辟一个新的内存空间存放这个字符

    函数

    内置函数

    #include <ctype.h>

    内置函数
    1、isupper() //是否是大写字母,是返回非0 否返回0
    printf("%d\n",isupper('a'));  // 0
    printf("%d\n",isupper('A')); // 1
    2、islower() //是否是小写字母
    printf("%d\n",islower('a'));  // 1
    printf("%d\n",islower('A')); // 0
    3、isalpha()//是否是字母
    4、isdigit()//是否是数字
    这些传入的数字不带引号''默认都是ascii码
    isdigit(97) //返回的是0 
    5、toupper();//转换成大写
    6、tolower();转换成小写
    

    常用的内置方法

    image.png
    image.png
    1、system("pause"); // 断点程序,将程序断在某处 按任意键继续往下执行
    2、system("cls"); //清楚屏幕
    3、system("color 4E"); //修改背景颜色
    4、system("shutdown/r/t 180"); 设置自动关机
    

    malloc()动态分配内存

    使用malloc之后建议使用free(name);释放掉内存

    #include <stdio.h>
    #inlcude <stdlib.h>
    
    void main()
    {
     /* int * num;
      num = (int *) malloc(20);  为num分配20个字节的内存空间
      我们知道一个整形占用4个字节空间  
      ||
      ∨
      int num[5] = {}; //就相当于我们定义了一个5长度的整形数组只不过用malloc是动态分配*/
      int num;
      num = (int *)malloc(20);
      或者 num = (int *)malloc(sizeof(int) * 5);推荐这个!!!
      for(int i = 0 ;i<5;i++)
      {
        printf("输入值:");
        scanf("%d",num + i); //为每个内存空间输入值
        printf("\n");
      }
      for(int i = 0 ;i<5;i++)
      {
         printf("各个内存值:%d\n",*(num + i));//或者num[i]
      }
       free(num);//释放掉内存
      nums = NULL;
    }
    

    calloc()动态分配内存意义与malloc一致(推荐使用)

    但是有一个好处就是calloc分配的内存默认会给个初始值0,而malloc初始值是乱的

    #include <stdio.h>
    #include <stdlib.h>
    void main()
    {
      int num;
      num = calloc(5,sizeof(int));比malloc少了一个强制类型转换(int *);
      用法都一致
      free(num);
    }
    

    自定义函数

    函数的三要素:返回值类型、函数名、参数

    #include <stdio.h>
    void sum()//无返回值的和求值 void代表的是无返回值
    {
      double num,num1;
      printf("请输入你需要求和的两个数:");
      scanf("%lf%lf",&num,&num1);
      printf("这两个数的和是:%.2lf\n", num + num1);
    }
    int aSum() //有返回值的求和
    {
      int sum=0;
      for(int i = 1 ;i<101;i++){
        sum += i;
      }
      return sum;
    }
    int nSum(int a,int b)//带参数有返回值
    {
      return a+b;
    }
    void main()
    {
      sum();//调用无返回值无参数的自定义函数
      int sum = aSum();
      printf("1-100的和是:%d\n",sum);
      int as = nSum(5,7);
      printf("和是:%d\n",as );
    }
    

    不能再函数内部定义函数如下:

    void fn()
    { //这是错误的
      void fn1()
      {
      
      }
    }
    

    所有的函数定义都是平行的

    void fn1()
    {
     }
    void fn()
    {
      fn1();
    }
    

    定义一个函数找到数组中某个数值的下标

    #include <stdio.h>
    
    int serchArr(double darr[],double a,double length)
    { //注意哦 这里的参数是数组哦!
        for(int i = 0 ;i<length;i++)
        {
            if(*(darr + i) == a){ //用指针的方式当然也可以用darr[i]
                return i;
            }
        }
        return -1;
    }
    
    void main()
    {
        double arr[10] = {3.14,0.25,0.38,0.45,0.78,12.45,457.152,12.55,457.4,4574.4};
        double result = serchArr(arr,0.25,10);
        printf("你要找的下标:%.2lf\n",result);
    }
    
    

    当然自定义函数可以单独放在一个文件中 用的时候include导进来

    变量的作用域

    #include <stdio.h>
    void main()
    {
      int num = 9;
      {
        int num = 90;  //因为这个90  是在{}这个代码块里
      }
    printf("%d\n",num);//打印的是9
    }
    

    思考题

    #include <stdio.h>
    void test(int a)
    {
        a++;
    }
    int main()
    {
        int n = 9;
        test(n);
        printf("%d\n",n);结果会是多少???结果是9为什么呢? 我们添加两句话就可以证明
        return 0;
    }
    
    #include <stdio.h>
    void test(int a)
    {
        a++;
        printf("形参的内存地址:%x\n", &a);
    }
    int main()
    {
        int n = 9;
        test(n);
        printf("%d\n",n);
        printf("实参的内存地址:%x\n", &n);
      结果是这两个内存地址不一致,既然内存地址都不一致 那怎么可能改变函数外部变量呢?
        return 0;
    }
    

    好通过内存地址我们知道了内存地址不一致 就不会改变外部的变量值那么我们再改下代码:

    #include <stdio.h>
    void test(int *a)
    {
      ++ *a; 
      printf("形参的内存地址:%x\n", a);//注意这里不需要取地址符,
    如果加上了取地址符,那就代表是找指向a指针的内存地址,而不是a的内存地址,因为指针也是有内存地址的
    }
    void main()
    {
      int num = 9;
      test(&num);
      printf("%d\n",num);
    printf("实参的内存地址:%x\n", &n);
    }
    

    这样就会改变外部变量!因为我们传了指针进去直接改变指针指向的值,当然会被改变

    static和auto

    用法

    static int a = 0;
    auto int a = 0;
    

    但是平时我们定义变量的时候 已经是auto类型的,所以可以省略
    auto:变量存放在动态存储区,随着生命周期的结束而立即释放。
    static:变量存放在静态存储区,在程序整个运行期间都不释放
    案例

    int num()
    {
        static int a = 0; //这里加了static代表他在整个运行期间该变量不会被销毁
        a++;
        return a;
    }
    int main()
    {
        int a = num();
        a = num();
        a = num();
        a = num();
        a = num();
        printf("%d\n",a); //结果会是5
        system("pause");
        return 0;
    }
    

    这个例子中我们不加static 或者改成auto,那么结果打印始终是1,因为每次运行num函数中a都会被重新赋值为0;

    函数参数的为数组

    注意哦如果传入的是数组进行操作,会改变原数组的值哦,因为这里传递的是数组的首地址,既然传的是地址,那么修改内存地址对应的值当然会改变原数组

    函数头部定义
    void test(double []);
    void test(double a[])
    {
    
    }
    
    

    函数不能返回数组

    头文件的使用

    当真正写项目的时候我们的代码量会非常多,但是不可能把所有的功能都放在主文件里面。不然会造成代码维护的代价较大,代码冗余,不利于阅读等。。这时候就有必要把各个功能单独放在一个文件中使用。

    新建.h为后缀名的文件
    我们新建文件名为test.h
    一般用来定义常量

    #ifndef _TEST_H_ //如果没有引入头文件file.h
        #define _TEST_H_ //那就引入头文件file.h
        //结构体和函数声明....
        
    #endif
    

    extern申明变量在另一个文件中
    如现在有两个.c文件 main.c 、test.c
    如果test.c有个全局变量a 那么定义

    在main.c头部main函数之前定义
    extern int a;
    
    在test.c
    int a = 10;
    

    面向对象

    初识结构

    关键字:struct=>定义结构体,说白点就类似javascript 构造函数
    结构体中某属性为字符数组的,赋值要用strcpy进行赋值
    而结构体某属性为指针字符的,可以直接进行赋值

    #include <stdio.h>
    #include <string.h>
    struct Hero  //定义英雄类
    {
      int id; //英雄ID
      const char * name; 也可以使用 字符数组 char name[]; 英雄名字使用指针字符可以直接进行赋值
      int leav; //英雄等级
      double hp;//英雄血量
      double mp;//英雄蓝量
      char skill[50]; //英雄技能
    };
    void main()
    {
      struct Hero MyHero1; //类似于javascript的 new 构造函数()
      MyHero1.id = 1;
      MyHero1.name = "我的第一个英雄名字";注意如果这里name属性是数组的需要用strcpy进行赋值
      MyHero1.leav = 10;
      MyHero1.hp = 100.00;
      MyHero1.mp = 100.00;  
      strcpy(MyHero1.skill,"大保健");注意这里不能用常规的字符赋值,必须用 字符复制函数
    打印的话逐个打印
      printf("%d\t%s\t%d\t%.2lf\t%.2lf\t%s\n",MyHero1.id,MyHero1.name,MyHero1.leav ,MyHero1.hp ,MyHero1.mp ,MyHero1.skill)
    
    //当然除了逐个录入数据也可以统一赋值但是要一一对应
    struct Hero MyHero2 = {2,"第二个名字",20,80.00,80.00,"测试的技能"};
     printf("%d\t%s\t%d\t%.2lf\t%.2lf\t%s\n",MyHero2 .id,MyHero2 .name,MyHero2 .leav ,MyHero2 .hp ,MyHero2 .mp ,MyHero2 .skill)
    
    //也可以其中的不用赋值
    struct Hero MyHero3 = {3,"第三个名字",20,80.00,.skill = "测试的技能"};
     printf("%d\t%s\t%d\t%.2lf\t%.2lf\t%s\n",MyHero3 .id,MyHero3 .name,MyHero3 .leav ,MyHero3 .hp ,MyHero3 .mp ,MyHero3 .skill)
    }
    

    注意当使用指针字符串为属性的话,那么当需求输入名字的情况:scanf就会报错
    因为此时这个属性是没有空间内存的!!!需要动态分配内存(malloc方法)

    #include <stdio.h>
    #include <string.h>
    struct Hero  //定义英雄类
    {
      int id; //英雄ID
      const char * name; 也可以使用 字符数组 char name[]; 英雄名字使用指针字符可以直接进行赋值
      int leav; //英雄等级
      double hp;//英雄血量
      double mp;//英雄蓝量
      char skill[50]; //英雄技能
    };
    void main()
    {
      struct Hero MyHero1; //类似于javascript的 new 构造函数()
      MyHero1.id = 1;
      MyHero1.name = (char *) malloc(50);//需要先动态分配内存
      scanf("%s",MyHero1.name);
      MyHero1.leav = 10;
      MyHero1.hp = 100.00;
      MyHero1.mp = 100.00;  
      strcpy(MyHero1.skill,"大保健");注意这里不能用常规的字符赋值,必须用 字符复制函数
    打印的话逐个打印
      printf("%d\t%s\t%d\t%.2lf\t%.2lf\t%s\n",MyHero1.id,MyHero1.name,MyHero1.leav ,MyHero1.hp ,MyHero1.mp ,MyHero1.skill)
    
    }
    
    

    嵌套结构

    结构中嵌套结构体

    #include <stdio.h>
    #include <Windows.h>
    #include <string.h>
    /*
        仓库类
    */
    struct Bag
    {
        int id;
        int Count;//仓库总数
        const char * name;//该仓库的名称
    };
    /*
        玩家类
    */
    struct Play
    {
        int id;
        const char * name;
        double hp;
        double mp;
        struct Bag bag; //玩家的仓库
    };
    int main()
    {
        struct Play play1 = { 1,"玩家1",100.00,100.00,{1,30,"破烂的铁皮仓库"} };
        printf("%s\t%s\n", play1.name, play1.bag.name);
        system("pause");
        return 0;
    }
    

    指针的补充->结构指针

    在c语言中万物都是有指针的,当然结构也是有指针的

    #include <stdio.h>
    #include <Windows.h>
    #include <string.h>
    /*
        仓库类
    */
    struct Bag
    {
        int id;
        int Count;//仓库总数
        const char * name;//该仓库的名称
    };
    /*
        玩家类
    */
    struct Play
    {
        int id;
        const char * name;
        double hp;
        double mp;
        struct Bag bag; //玩家的仓库
    };
    int main()
    {
        struct Play play1 = { 1,"玩家1",100.00,100.00,{1,30,"破烂的铁皮仓库"} };
        printf("%s\t%s\n", play1.name, play1.bag.name);
        struct Play *prt_play = &play1;  //和往常一样的指针方式取值,此时的指针类型 是结构体指针
        printf("%s\t%s\n", (*ptr_paly).name, (*ptr_paly).bag.name);
    或者
        printf("%s\t%s\n", ptr_paly->name, ptr_paly->bag.name);
        system("pause");
        return 0;
    }
    

    简化结构体的定义与使用

    typedef struct _Job //定义结构体
    {
      、、、
    }Job;
    使用:
    Job Myjob = {、、、}; 就不用使用关键字struct
    

    结构体中含有多个属性(结构体)

    typedef struct _bag
    {
      int id;
      char * name;
      
    }Bag;
    
    typedef struct _play
    {
      int id;
      int hp;
      int mp;
      char *name;
      Bag bag[5];玩家可以有5个背包 **重点**
    }Play;
    Play wanjia = { 1,100,100,"我的名字",{{1,"破烂的"},{2,"青铜的"},{3,"白银的"},{4,"华丽的"},{5,"尊贵的"}} };
        printf("%s\n", wanjia.bag[4].name);
    

    结构数组

    image.png

    结构体实战

    1、玩家输入名字,选择性别,选择职业,选择两个副技能(且两个技能不能重复),血量默认初始值为100,攻击速度输入
    2、给定职业让用户选择、及副技能

    这次我们把所有的功能放在头文件中使用

    //main.c文件
    #include <stdio.h>
    #include <stdlib.h>
    #include <Windows.h>
    #include "hero.h" //我们新建的头文件
    int main()
    {
      Input(); //输入功能
      showHear();//输出功能
      return 0;
    }
    

    新建hero.h头文件以及hero.c

    #ifndef _HERO_H_  //标准头文件开始
    #define _HERO_H_   //标准头文件开始
    /*
      职业类
    */
    typedef struct _occupation
    {
      int id;
      const char *name;
      const char *detail;
      const char *PutTime; //该职业上线的时间
    }Occupation;
    /*
      技能类
    */
    typedef struct _skill
    {
      int id;
      const char *name;
      const char *defail; //技能描述
    }Skill;
    /*玩家类*/
    typedef struct _play
    {
      char sex; //性别 f(女)/m(男)
      const char *name; //玩家名字
      Occupation occupation;//职业类
      Skill skills[2];//两个技能
      int hp;//血量
      double speed;//攻击速度
    }Play;
    void Input(); //输入
    void showHear(); 输出
    #endif   //标准头文件结束
    

    hero.c我们先填几个玩家以及职业,后期可以拓展进行职业类的添加函数

    #include "hero.h"
    #include <stdio.h>
    #include <stdlib.h>
    #include <conio.h>
    #include <string.h>
    int count = 2; //当前玩家数
    Occupation myOc[4] = { 
        {1,"战士","近战输出,攻击力中等。","2019-11-13"},
        {2,"刺客","近战输出,攻击力上等,具有隐身被动。","2019-11-13"},
        {3,"AD","远战输出,攻击力上等,具有较快攻击速度。","2019-11-13"},
        {4,"法师","远战输出,靠技能来进行输出","2019-11-13"}
    };
    Skill skills[6] = {
        {1,"闪现","一定距离闪现"},
        {2,"治疗","治疗英雄一定血量"},
        {3,"疾走","使英雄在30秒时间内已送增加60%"},
        {4,"传送","传送至指定位置"},
        {5,"点燃","引燃你的目标"},
        {6,"虚弱","虚弱你的目标"}
    };
    Play plays[100] = { //先初始化一些玩家,打印出来就好看点
        {"影流之主劫",'m',myOc[1],{skills[0],skills[1]},100,0.687},
        {"琴瑟仙女娑娜",'f',myOc[3],{skills[0],skills[1]},100,0.387},
        {"疾风剑豪",'m',myOc[0],{skills[0],skills[2]},100,0.887}
    };
    void InputPlay()
    {
        /*
            玩家输入名字,选择性别,选择职业,选择两个副技能(且两个技能不能重复),血量默认初始值为100,攻击速度输入
        */
        char anwesr = 'y';
        do
        {
            count++;
            if (count >100)
            {
                printf("你需要购买更多的英雄栏哦!");
                break;
            }
            printf("当前录入第%d个英雄,请输入你的昵称:", count + 1 );
            plays[count].name = (char *)malloc(50);
            scanf("%s", plays[count].name); //输入名字
            fflush(stdin);
            do
            {
                printf("\n");
                printf("请选择英雄性别(f女m男):");
                plays[count].sex = getch();
            } while (plays[count].sex != 'f' && plays[count].sex != 'm' && plays[count].sex != 'F' && plays[count].sex != 'M');
            fflush(stdin);
    
            int myOcLen = (int)(sizeof(myOc) / sizeof(myOc[0]));//获取职业的长度:总长度/单长度 = 个数
    
            printf("\n请选择英雄职业:\n\n");
    
            for (int i = 0; i < myOcLen; i++)
            {
                printf("编号:%d\t%s\t%s\n", myOc[i].id, myOc[i].name, myOc[i].defail);
            }
    
            int myOcNum;
            do
            {
                scanf("%d", &myOcNum);
            } while (myOcNum<1 && myOcNum>myOcLen); //必须在职业范围内
            fflush(stdin); //清除输入的缓冲区
            plays[count].occupation = myOc[myOcNum - 1]; //职业
    
            //选择两个职业技能
            printf("\n请选择两个职业技能:\n\n");
    
            int skillNum1;
            int skillLen = (int)(sizeof(skills) / sizeof(skills[0]));
            for (int i = 0; i < skillLen; i++)
            {
                printf("编号:%d\t%s\t%s\n", skills[i].id, skills[i].name, skills[i].defail);
            }
    
            do
            {//第一个技能
                scanf("%d", &skillNum1);
            } while (skillNum1<1 && skillNum1>skillLen); //必须在技能范围内
            plays[count].skill[0] = skills[skillNum1];
    
            int skillNum2=-1;
            printf("请选择第二个技能:\n");
            do
            {//第二个技能
                if(skillNum2 == skillNum1)
                { 
                    printf("选择的两个技能不能相同哦,请重新选择!\n");
                }
                scanf("%d", &skillNum2);
            } while (skillNum2 == skillNum1 || (skillNum2<1 && skillNum2>skillLen)); //必须在技能范围内
            plays[count].skill[1] = skills[skillNum2];
            plays[count].hp = 100;
            printf("请输入英雄的攻击速度:");
            scanf("%lf", &plays[count].speed);
    
            printf("是否继续录入英雄信息?(y/n)");
            anwesr = getch();
        } while (anwesr == 'y' || anwesr == 'Y');
    };
    void ShowPlay()
    {
        printf("\n英雄名\t\t性别\t职业\t技能(2)\t\t血量\t\t攻击速度\n");
        printf("-----------------------------------------------------------------------\n");
        for (int i = 0; i <= count; i++)
        {
            printf("%-2s\t%c\t%s\t%s、%s\t%d\t\t%.4lf\n\n", plays[i].name, plays[i].sex,plays[i].occupation.name, plays[i].skill[0].name, plays[i].skill[1].name, plays[i].hp, plays[i].speed);
        };
    };
    

    结果:


    image.png

    函数的参数为结构体

    #include <stdio.h>
    #include <Windows.h>
    #include <stdlib.h>
    typedef struct Account {
        const char *bankName;
        const char *useName;
        double limit;
        double bill;
    };
    
    double GetReq(struct Account acc)
    {//这个acc是个形参
        return acc.limit - acc.bill;
    };
    
    int main()
    {
        struct Account MyAcc = {"招商银行","卢本伟",500000,350000};
        double result = GetReq(MyAcc);
        printf("应还%.2lf", result);
        system("pause");
        return 0;
    };
    

    或者传指针

    #include <stdio.h>
    #include <Windows.h>
    #include <stdlib.h>
    typedef struct Account {
        const char *bankName;
        const char *useName;
        double limit;
        double bill;
    };
    
    double GetReq(const struct Account *acc)
    {//这个acc是个形参,如果是指针的话 这里最好加上const  防止 指针改变原数据
        return acc->limit - acc->bill;
    };
    
    int main()
    {
        struct Account MyAcc = {"招商银行","卢本伟",500000,350000};
        double result = GetReq(&MyAcc); //取地址
        printf("应还%.2lf", result);
        system("pause");
        return 0;
    };
    

    实战(2)

    image.png

    我们可以使用extern、结构指针快速实现该实战内容

    我们创建两个文件一个《game.h》《game.c》

    //main.c
    #include <stdio.h>
    #include <Windows.h>
    #include <stdlib.h>
    #include "game.h"
    
    extern Prop *prop;
    extern Player *plays;
    extern int *prt_prlen; //商店长度指针
    extern int *prt_Pllen; //玩家长度指针
    int main()
    {
        Init();
        showProp();
        showPlay();
        printf("-------------------------------购买前--------------------------\n");
        playGame((plays + 1),1);
        playGame((plays + 1), 1);
        playGame((plays + 1), 1);
        playGame((plays + 1), 1);
        playGame((plays + 1), 2);
        playGame((plays + 1), 2);
        playGame((plays + 1), 2);
        playGame((plays + 2), 2);
        printf("-------------------------------购买后--------------------------\n");
        showPlay();
        showProp();
        system("pause");
        return 0;
    }
    

    game.h

    #ifndef _GAME_H_
    #define _GAME_H_
    
    /*
        商城类
    */
    
    typedef struct _prop
    {
        int id;//商品ID
        const char *name;//名字
        double price;//价格
        int stock;//数量
        const char *desc; //描述
    }Prop;
    
    /*
        背包类
    */
    typedef struct _bag
    {
        int playId;//哪位玩家的背包
        Prop props; //背包里的物品
    
        int count; //这个物品在背包的数量
    }Bag;
    /*
        玩家类
    */
    typedef struct _palyer
    {
        int id;
        const char *name;
        const char *pass;
        Bag bag[8];
        double gold; //金币
    }Player;
    
    void Init();
    void showProp();
    void showPlay();
    void playGame(Player *nowPlay,int); //传入结构指针
    
    #endif
    

    game.c实现文件

    #include <stdio.h>
    #include <Windows.h>
    #include <stdlib.h>
    #include "game.h"
    
    Prop *prop = NULL;
    Player *plays = NULL;
    int *prt_prlen; //商店长度指针
    int *prt_Pllen; //玩家长度指针
    void Init()
    {
        static Prop props[] = {
            {1,"双倍经验卡",20,100,"经验加成"},
            {2,"幸运宝石",30,100,"幸运道具"},
            {3,"五行转换卡",50,100,"五行转换"},
            {4,"银两袋子",100,100,"银两"},
            {5,"高级灵珠宝箱",100,100,"高级灵珠"},
            {6,"置顶传音",300,100,"聊天置顶"},
            {7,"女娲转职卡",888,100,"转职卡"},
            {8,"武神转职卡",888,100,"转职卡"},
            {9,"魔尊转职卡",888,100,"转职卡"},
            {10,"初级农场礼包",10,100,"农场物品"},
            {11,"中级农场礼包",40,100,"农场物品"},
            {12,"高级农场礼包",188,100,"农场物品"}
        };
        prop = props; //本身就是地址 数组
        static int prlen = sizeof(props) / sizeof(props[0]);
    
        prt_prlen = &prlen;
        int id;
        const char *name;
        const char *pass;
        Bag bag;
        double gold; //金币
        static Player players[] = {
            {1,"王牌打野","123456",{},1000},
            {2,"王牌中单","123456",{},1000},
            {3,"王牌AD","123456",{},1000}
        };
        plays = players;
        static int plLen = sizeof(players) / sizeof(players[0]);
        prt_Pllen = &plLen;
    };
    
    void showProp()
    {
        printf("\n编号\t名称\t\t描述\t\t库存\t\t金币\n");
        for (int i = 0; i < *prt_prlen; i++)
        {
            printf("(%d)\t%-4s\t%s(描述)\t%d(库存)\t%.2lf(金币)\n", (prop + i)->id, (prop + i)->name, (prop + i)->desc, (prop + i)->stock, (prop + i)->price);
        }
    }
    void showPlay()
    {
    
        printf("\n玩家信息:\n编号\t\t昵称\t\t金币\n");
        for (int i = 0; i < *prt_Pllen; i++)
        {
            printf("%d\t\t%s\t\t%2.2lf\n", (plays + i)->id, (plays + i)->name, (plays + i)->gold);
            if ((plays + i)->bag[0].playId)
            {
                printf("《%s》玩家的背包信息:\n", (plays + i)->name);
                for (int k = 0; k < 8; k++)
                {
                    if ((plays + i)->bag[k].playId)
                    {
                        printf("物品名称《%s》,数量%d\n", (plays + i)->bag[k].props.name, (plays + i)->bag[k].count);
                    }
                }
                printf("\n");
            }
        }
    };
    
    void playGame(Player *nowPlay, int id)
    {
        /*
            购买物品,
            判断物品是否还有库存,
            判断金币是否充足
            玩家金币-
            库存-
            相同的背包物品累加
        */
        Prop *NowProp = NULL;
        for (int i = 0; i < *prt_prlen; i++)
        {
            if ((prop + i)->id == id)
            {
                NowProp = (prop + i);
            }
        }
        if (!NowProp)
        {
            printf("对不起,你购买的物品本商店暂无!\n");
            return;
        }
        if (NowProp->stock <=0)
        {//库存不足
            printf("对不起,你购买的物品暂无库存!\n");
            return;
        }
        if (nowPlay->gold< NowProp->price)
        {//金币不足
            printf("对不起,英雄你的金币不足,请先去冒险再来吧!\n");
            return;
        }
        nowPlay->gold -= NowProp->price;//扣除金币
        NowProp->stock--; //扣除库存
        int k;
        for (int i = 0; i < 8; i++)
        {
            if (!nowPlay->bag[i].props.id)
            {//如果循环到某个背包的商品id是空的 说明这个物品栏是空的 可以存放
                k = i;
                break;//跳出循环
            }
            if(nowPlay->bag[i].props.id == NowProp->id)
            { //背包里找到相同的物品 该物品数量加1
                nowPlay->bag[i].count++;
                return; //找到相同的直接退出函数
            }
        };
        nowPlay->bag[k].playId = nowPlay->id;
        nowPlay->bag[k].count = 1;
        nowPlay->bag[k].props = *(NowProp);
    
        printf("购买《%s》成功\n", NowProp->name);
    }
    

    运行效果

    image.png

    实战 · 笑傲江湖

    知识拓展

    1、enum ->枚举

    enum DAY
    {
        MON, TUE, WED, THU, FRI, SAT, SUN
    } day;
    int main()
    {
        day = MON;
        printf("枚举元素:%d \n", day);  结果是0
        system("pause");
        return 0;
    }
    

    2、union多选一

    union Data
    {
       int i;
       float f;
       char  str[20];
    };
    
    int main( )
    {
       union Data data;        
    
       data.i = 10;
       data.f = 220.5;
       strcpy( data.str, "C Programming");
    
       printf( "data.i : %d\n", data.i);
       printf( "data.f : %f\n", data.f);
       printf( "data.str : %s\n", data.str);
    
       return 0;
    }
    

    相关文章

      网友评论

          本文标题:C语言基础知识

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