C语言第七节-结构体-枚举-typedef

来源:互联网 发布:上帝软件wpe1.0 编辑:程序博客网 时间:2024/06/05 06:56
fgets():是一个文件操作相关的函数,暂时使用这个函数可以从键盘接受一个字符串,保存在数组中
原型:fgets(char *p, int len, FILE)
键盘接收:fgets(str, 50,stdin)  //键盘输入缓冲区
         接受字符串的方法:char str[50]
          1、scanf(“%s”, str)    //缺点:不能接收空格,可能越界,不安全
          2、gets(str)                //优点:可以接受空格
                                             //会有一个警告:当字符串长度为50时,就没有空间存结束符\0
          3、fgets()         //继承1,2的优点,弥补2的缺点,当长度大于50,自动在第50个位置存上\0,替换输入的第50个字符;小于50时,会保存回车符\n,然后再存上\0

fputs():也是一个文件操作相关的函数
原型:fputs(char *p , FILE)
fputs(数组名,stdout)                //输出到控制台,和puts一样也不能格式化输出,但是puts可以自动换行


const:类型修饰符,使用const修饰变量则可以让变量的值不能修改

const使用的地方:
1、修饰变量:使得变量变常量,不可改
2、修饰指针变量:
     1)const int  *P   指向可变,指向的变量的值不可变
     2) int  * const p   指向不可变,指向的变量的值可变
     3)const int * const p  指向不可变,指向的变量的值也不可变
记忆技巧:const和*的位置,左,右,两则
3、修饰数组

内存管理的概念和内存分区
概念:是指软件运行时对计算机内存资源的分配和使用技术。主要目的是如何高效,快速地分配,并且再适合的时候释放和回收内存资源。
内存的分配方式
1、从静态存储区域分配
2、从栈上分配
3、从堆上分配,亦称动态内存分配


内存分区
BSS段:未初始化的全局变量和静态变量
数据段(常量区):已初始化的全局变量和静态变量,字符串常量
代码段:程序执行代码的一块内存区域
堆(heap):动态分配的内存段
栈(stack):用户存放程序临时创建的局部变量

常见内存分配函数(堆区):
1、malloc
               void*malloc (unsigned size),size是内存分配的字节,库函数stdlib.h,一般要判断是否分配成功
               从内存的堆区分配大小为size个字节的连续的内存空间
               如果内存分配成功 返回内存的首地址
               失败  NULL
//从内存中申请一块内存空间,可以存储4个整数
  int *p = (int *)malloc(4* sizeof(int));  //16个字节,void *强制转换为int *
   if (p!=NULL) {
       
//申请成功做的事情
       //p中存放的是新申请的内存空间的首地址
               //注意:malloc申请的空间如果不赋值,则存放的是垃圾数
        *p =10;
        *(p +
1) =100;
        *(p +
2) =1000;
        *(p +
3) =10000;//存放4个整数
    }
else {
       
//内存申请失败
       
printf("内存申请失败!\n");
    }
                 
2、calloc
               calloc(块数,长度),分配指定块数和长度的内存空间,地址也是连续的
       int*p = (int*)malloc(4sizeof(int));  //四块长度为4的内存
               calloc会自动初始化,初始化为0
3、realloc
               可以给已经存在的空间扩充大小
               p = realloc(p,40*sizeof(int) )
//想为已经存在的4个空间的P扩充36个空间,如果4的后面有36个空的内存,则返回4的首地址;如果没有36个,则另外重新申请一个40的空间,先将4里面的内容放进去之后,再返回新空间的首地址。p则指向“”的地址

野指针和内存泄露
野指针:1、未初始化的指针  2、指针所指向的空间已经释放,再去使用指针,该指针就是野指针   
         
内存泄露:防止内存泄露,在指针释放之前,先释放指向的空间,使用free(释放空间的首地址函数,释放完之后,理论上不可以访问已经释放的空间,但是还是可以访问。此时的指针就是野指针,访问的结果是一些以前的垃圾值。所以为了防止这种无理的操作,我们需要:指针初始化—>使用指针结束后—>再次赋值为NULL,以防还可以访问。free(p); p = NULL

指针函数:返回值类型是一个指针(地址)的函数
形式:类型说明符 *函数名(形参表) {
                         函数体
                         返回的是地址
}

函数指针:指向函数(函数的首地址就是函数名)的指针变量
形式:类型说明符 (*变量名)(函数的参数)
初始化:int (*p)(int a , int b) = ∑    //sum是一个求和函数
定义函数指针的时候可以不写形参名,但是类型必须写

构造类型及结构体

构造类型:根据已定义的一个或多个数据类型用构造的方法定义的。一个构造类型的值可以分解为若干元素,每个元素又可以分为基本类型或构造类型。
分类:数组,结构体类型,共用体(联合)类型

结构体structure:存储类型相同或不同的数据
定义结构体:
                   struct 结构名{
                         成员列表;
};
//定义一个学生结构体
   
struct student{
       
//学生学号
       
int ID;
       
//姓名
       
char name[21];
       
//年龄
       
int age;
       
//成绩
       
float score;
    };//分号不能省
结构变量成员访问:结构变量名.成员名    //stu.num
结构体变量的初始化:
1、先定义结构体变量,再初始化
      struct student stu1;
   //给结构体变量初始化
    stu1.
ID = 38;
    stu1.
age = 18;
    stu1.score= 59.9;
    //字符串类型的初始化,如果是数组不可以写成stu1.name = "张三丰";应该用strcpy函数拷贝;如果不是数组则可以。但是定义的同时初始化则也可以
   strcpy(stu1.name,"张三丰");
   //UTF-8国际通用编码,一个汉字占用三个字节
2、定义的同时,进行完全初始化
       struct student stu2 = {8, “凤姐”, 18, 49.99f};    
3、定义的同时,指定元素初始化
       struct student stu3 = {.name = “小三"};

结构体变量的存储原理:结构体占用的内存空间是每个成员占用的字节之和(考虑对齐问题
对齐问题:

计算结构体变量在内存中占用的字节数的方法:
1、先计算对齐模数:(结构体中基本数据类型占用字节最大的那个
2、再计算结构体变量中各个成员占用的字节和

结构体作用域:与局部变量、全局变量相似

结构数组:每一个元素都是具有相同结构类型的下标结构变量
格式:   struct 结构名{
          成员列表
}数组名[数组长度];
1、定义结构体的同时,定义数组:如格式
2、先定义结构体,再定义数组:struct student a[5];

结构数组初始化:


    1、ctrcpy  
    2、scanf

结构体指针定义和初始化
结构指针:指向结构体变量的指针
1、

2、struct Car{
     int lunzi;
     int speed;
}*p;

结构指针间接访问成员的值
形式:(*p).成员名    或      p->成员名     //(*p).lunzi  或  p->lunzi 

结构体嵌套:成员可以使基本数据类型,又可以是一个结构
结构体可以嵌套其他结构的变量,不可以嵌套自己的变量,但是可以嵌套自己的指针

嵌套的结构体初始化
struct Student stuff = {“张丹峰”, 12,33,399f,{1991,9,3}};


结构体变量名及成员,结构体指针作为函数参数
1、成员值做函数的参数:本质上就是一个值传递
2、结构体变量名作为函数的参数:也是值传递(不能修改)
3、结构体指针作为函数的参数:地址传递

枚举

枚举:变量的取值被限定在一个有限的范围内,是一种基本数据类型,不是构造类型
形式:enum 枚举类型名{枚举值表};
在枚举值表中应罗列所有的可用值,这些值也称为枚举元素
系统默认会为枚举元素赋初值,依次从0开始,下一个为上一个加1
手动赋值:可以随便赋值k,但是下一个枚举元素的值为k+1

枚举类型变量:enum 枚举类型名 变量名;

typedef
typedef:为数据类型取别名
形式:typedef 原类型名 新类型名
使用:1、基本数据类型
               typedef int MLXG;
       MLXGqiezi = 3;
     2、用在数组:
               typedef int array[5];
       arraya1;   //a1就是一个长度为5数组
     3、给结构体
            1)  struct Person{
       char *name;
       
int age;
    };
   
typedef struct Person p;
   p p1 = {"xxx",28};

     2)typedef struct Car{
       
int lunzi;
       
int speed;
    }MYCAR;
   MYCAR car1 = {1,200};//此中的Car可以没有,便是给匿名的结构体取个别名
     4、给枚举类型
               和结构体类似,也存在匿名的取别名
     5、给函数指针:指向函数的指针  
 
        int sum(int a, int b) {
   return a + b;
   }
               int (*p)(int,int);
   
//FUN是一个别名
   typedef int (*FUN)(int,int);
     FUNf1;
    f1 =sum;
   printf("%d\n", f1(23,34));


预处理
预处理指令:以#开头的预处理命令,包括#include ,#define
预处理:在编译之前做的一些处理(宏定义,文件包含,条件编译

宏:特殊的标识符,标识符习惯大写。字符串替换(仅仅是替换,并不会自动加括号之类的)的过程叫做宏展开或宏替换

无参宏:#define 标识符 字符串
注意:1、#undef  取消宏
   2、在字符串中出的宏不会被替换
   3、宏可以嵌套定义
   4、宏可以起别名
   5、宏不是一个语句,不需要叫分号
有参宏:#define 宏名(形参表) 字符串
注意:1、宏的参数之间可以出现空格,但是宏名和形参之间不可以出现空格
           2、在带参宏中,形参不分配内存空间
           3、传值时,不要想当然的去代换,要简单的去替换之后,再计算。所以宏的参数最好用括号括起来
          4、可以用宏来定义多个语句

宏定义和typedef的区别:一个仅仅是替换,一个是别名(在指针时,区别最大

条件编译:1、按照不同的条件编译不同的程序,产生不同的目标代码,有利于程序的移植和调试
2、条件编译只编译其中满足要求的程序,目标程序较短
1、#if -#else条件编译指令
#if
#elif
.
.
#elif
#else
#endif
2、#ifdef 用来判断某个宏是否定义  如果定义了,然后执行什么
  #ifndef 用来判断某个宏  如果没有定义,然后执行什么

使用条件编译指令调试BUG
0 0