C语言基础知识

float类型

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

接收用户输入 scanf

#include 
int main()
{
  int num;
  scanf("%d",&num);
  printf("结果为%c\n",a);  //注意这里是%c  int类型的为%d
  return 0;
}
#include 

int main()
{
    int value;
    printf("请输入一个整数:");
    while (scanf("%d", &value) == 0 || value <0) {
        printf("请重新输入:");
    }
    return 0;
}

scanf输入的值是可以判断的

强制类型转换

#include 
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

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

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

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

#include 
#include 
#include 
int main()
{
    //使用时间作为种子 产生随机数字
    srand(time(NULL));
    printf("%d\n",rand());

    return 0;
}

Sleep(int)函数

延迟的意思
Sleep(5) 延迟5毫秒必须要加入头文件

数组

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

#include 
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 
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]

二维数组

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

#include 
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 
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 
void main()
{
  char name[50]={"测试的哦"};
  printf("%s",name);//注意这里是%s 
  scanf("%s",name)  //这里是没有&符号的
  //重点
  char name[2]={"测试的哦"};
  printf("%s",name);//这里打印的是(测)  
}

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

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

字符串的重新赋值

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

gets 与puts输入/输出字符串

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

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

获取字符串大小strlen

注意中文占两个字节

#include 
#inlcude  //使用头文件
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()方法)

引入字符串操作头文件

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

字符串的拼接strcat()

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

#include 
#include 
#include 

int main()
{
    char name[50] = "原文示意";
    char test[30] = "测试用的";
    strcat(name, test);
    printf("%s\n", name);
    system("pause");
    return 0;
}

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

引入字符串操作头文件

#include 
#include 
void main()
{
    char str[] = "哈哈哈哈哈哈";
    strcpy(str,"则啧啧啧");
    printf("%s",str);
}

字符串实战 - 加密/解密

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

取指针变量的值

#include 
void main()
{
  int num = 1024;
  int * ptr_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 = #   //0028FF14
int * ptr_num2 = &ptr_num;  //0028FF18
#include 
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 
void main()
{
  double test[] = {50,60,70,80,9,450,40};
  printf("test的首地址:%p,取地址的首地址:%p",test,&test[0]);
  //结果是一样的
}

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

指针参与运算

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

二维数组的指针

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

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

#include 
#include 

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

内置函数
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 
#inlcude 

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 
#include 
void main()
{
  int num;
  num = calloc(5,sizeof(int));比malloc少了一个强制类型转换(int *);
  用法都一致
  free(num);
}

自定义函数

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

#include 
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 

int serchArr(double darr[],double a,double length)
{ //注意哦 这里的参数是数组哦!
    for(int i = 0 ;i

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

变量的作用域

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

思考题

#include 
void test(int a)
{
    a++;
}
int main()
{
    int n = 9;
    test(n);
    printf("%d\n",n);结果会是多少???结果是9为什么呢? 我们添加两句话就可以证明
    return 0;
}
#include 
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 
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 
#include 
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 
#include 
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 
#include 
#include 
/*
    仓库类
*/
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 
#include 
#include 
/*
    仓库类
*/
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 
#include 
#include 
#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 
#include 
#include 
#include 
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 
#include 
#include 
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 
#include 
#include 
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 
#include 
#include 
#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 
#include 
#include 
#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语言基础知识)