C语言基础(六)复杂数据类型与文件操作

宏定义

宏有两种定义方法:一般定义在代码开头,有无参数宏定义和有参数宏定义。接下来我们依次讲解

无参数宏定义

定义形式:#define 标识符 字符序列

举例:#define MAX_VALUE 256 

当我们在写代码时使用标识符,当程序开始编译前,程序中出现的MAX_VALUE,会替换成256

注:

1.无参数宏定义只能作为字符序列的替换工作,不作任何语法的检查

2.如果宏定义不当,定义错误要到预处理(替换)之后的编译阶段才能被发现

带参数宏定义

定义形式:#define 标识符(参数表) 字符序列

举例:

#define ADD(a) a + 5 

程序中出现ADD()时,空号中的参数代入后面的表达式中计算所得值返回

#define MAX(a,b) a > b ? a : b

程序中出现MAX(a,b)时如果a>b的结果为真,那么表达式的结果会是a,表达式的结果为假,那么

表达式的结果就会是b。

#define MAX(a,b) a > b ? a : b; printf("hello")

同上个宏定义一样,但是在执行完毕后,会额外打印hello

优点:宏定义相比使用函数来完成MAX的功能时,不用额外给MAX开辟一块内存,而是在编译前直接用字符序列代替MAX部分

注意:

1.定义宏的标识符与左圆括号之间不允许有空白符,应紧接在一起

2.宏与函数的区别:函数分配额外的堆栈空间,而宏只是替换

3.为了避免出错,宏定义中给形参加上括号

4.末尾不需要分号

5.define可以替代多行的代码,需要换行时在换行前的语句后面加\

举例:

#define MALLOC(n,type)\

((type*)malloc((n)*sizeof(type)))

条件编译

#if _WIN32 操作系统

int g_OS = 0

#elif __linux__

int g_OS = 1

#endif 

#define FALG 1

#if FALG == 1

int nFalg = 1;

#else

int nFalg = 1561

常见预定义符号

__ FILE __    进行编译的文件路径

__ LINE __    此文件内当前行号

__ DATE __   编译时日期

__ TIME __    编译时时间

__ STDC __   如果编译器遵循ANSI C ,值为1,否则未定义

这些预定义符号可以直接进行使用

如以下代码

#include <stdio.h>
int main()
{
    printf("当前文件路径:%s\n",__FILE__);    
    printf("当前行号:%d\n",__LINE__);
}

结构体 

结构体基本概念

结构体属于用户自定义的数据类型,允许用户存储不同的数据类型

结构体定义和使用

定义方式: struct 结构体名 { 结构体成员列表 };

通过结构体创建变量的方式有三种:

1.struct 结构体名 变量名

举例如下:struct _PlayerInfo Playobj;

2.struct 结构体名 变量名 = { 成员1值 , 成员2值...}

struct _PlayerInfo
{
    char szName[50];
    int nHP;
    float PosX;
    float PosY;
};

3.定义结构体时顺便创建变量

struct _PlayerInfo
{
    char szName[50];
    int nHP;
    float PosX;
    float PosY;
}Playobj;

注意:

1:定义结构体时的关键字是struct,不可省略 

2:创建结构体变量时,关键字struct可以省略

3:结构体变量利用操作符 ''.'' 访问成员

结构体起别名

typedef struct _PlayerInfo 
{
    char szName[50];
    int nHP;
    float PosX;
    float PosY;
}PlayerInfo;

也可*PPlayerInfo用作后续程序用指针时的别名

此时PlayerInfo等于struct _PlayerInfo

其内存为其包含的所有成员所占内存总和决定

结构体数组

结构体数组就是将自定义的结构体放入到数组中

定义形式为:struct  结构体名 数组名[元素个数] = {   {} , {} , ... {} }

如下一个程序进行讲解

此处先定义一个结构体:

struct student

{

    string name;

    int age;

    int score;   

}

此时在主程序中定义结构体数组

int main()

{

    struct student arr[3]={{"张三",18,80 },{"李四",19,60 },{"王五",20,70 }

};

结构体指针

结构体指针就是通过指针访问结构体中的成员,利用操作符 -> 通过结构体指针访问结构体属性

如下一个例子进行讲解:

此时我们定义一个结构体:

struct student

{

    string name; 

    int age;

    int score; 

};

此时我们开始在主程序中应用:

int main()

{

    struct student stu = { "张三",18,100, };

    struct student * p = &stu;

    p->score = 80;  此时指针通过 -> 操作符可以访问成员

    return 0;

}

结构体做函数参数

结构体做函数参数就是将结构体作为参数向函数中传递,传递方式有两种:

1.值传递

2.地址传递

下面举例讲解:

此时有一个已经定义好的结构体

struct student

{

    int age;

    int score;  

};

此时有一个要用到结构体值传递的函数:

void printStudent1(student stu )

{

    stu.age = 28;  此处如此调用结构体成员

    printf("%d,%d",age,score);

}

此时有一个要用到结构体地址传递的函数:

void printStudent2(student *stu)

{

    stu->age = 28; 此处如此调用结构体成员

    printf("%d,%d",age,score);

}

此时我们要做主函数调用这两个函数了

int main()

{

    student stu = {18,100};

    printStudent1(stu); 

    printStudent2(&stu); 

    return 0;

};

注意:如果不想修改主函数中的数据,用值传递,反之用地址传递(详解可看函数章节)

结构体在程序中的使用

typedef struct _PlayerInfo 
{

   char szName[50];

   int nHP;

   float PosX;

   float PosY;

}PlayerInfo;

int main()

{

    struct _PlayerInfo Playobj =  { "rkvir",100,50.32f,100.09f };定义一个结构体

    printf("%s\r\n",Playobj.szName) 打印结构体的属性

    Playobj.nHP = 500; 在结构体外可修改属性

    struct _PlayerInfo* pPlayobj; 定义一个结构体指针

    pPlayobj = &Playobj; 此时该指针指向结构体

    pPlayobj->PosX = 200.05f; 此时利用指针访问属性不再用.而是-> 这叫间接访问

    printf("%f\r\n",pPlayobj->Posx);  以下两个利用指针访问结构体属性的方式

    printf("%f\r\n",(&Playobj)->Posx); 

    struct _PlayerInfo* pPlayobj2 = malloc(sizeof( struct _PlayerInfo));

    利用动态申请内存malloc函数申请一个同原结构体大小的内存空间并创建一个结构体,malloc应

    用时需要包含头文件malloc.h

    sizeof( struct _PlayerInfo)申请内存的长度

    struct _PlayerInfo的长度就是他所包含的属性长度综合

    malloc应用时需要包含头文件malloc.h

    pPlayobj2->PosY = 100.5f;

    printf("%f\r\n",  pPlayobj2->PosY);

  }

联合体

联合体的优点是在同一份的内存空间中定义多个不同变量。比如我们设置两个局部变量,int类型

0x12345678和char类型0x78,当我们单独定义时,他们都需要8字节的空间。但当我们使用联合

体时,就只需要分配int类型的0x12345678的数据内存大小,但该联合体却可以存储了int和char两

个数据类型变量

联合体的有两种定义方式:

方式一:

union MyUnion

{

    char szName[50];

    int nHP;

    float PosX;

    float PosY;

};

方式二:

union

{

    char szName[50];

    int nHP;

    float PosX;

    float PosY;

}MyUnion;

方式一中的MyUnion表示一种联合体的数据类型

方式二中的MyUnion表示此联合体类型的变量

联合体的内存长度由最长成员所占内存决定,每当使用下一个成员时,上一个所使用的成员所占内

存会被覆盖,这是因为联合体的成员是共享内存空间的

枚举类型

enum MyEnum

    zero,    打印0

    one = 10 打印10    

    two,  打印11

    three = 12138  打印12138

};

struct

{

    int a : 8;

    int b : 8;

    int c : 8;

    int d : 8;

}rk; 命名结构体rk

rk.a = 0x78;

rk.b = 0x56;

rk.c = 0x34;

rk.d = 0x12;

printf("0x%X\r\n",rk)

打印结果0x12345678

作用:按照八位来拆分int

a b c d 作为索引

每八位都可以触发每个索引对应的一个功能

此时int不再代表一个数,而是作为功能开关

注:每个int都有32位,在极限情况下,可以有32位分别对应32个功能

例如

if (rk.a == 1)

{

    开始影子列表

}

else if(rk.b == 1)

{

    开启拓展页表   

}

文件

文件分为二进制文件和文本文件,其具体结构如下:

typedef struct

{    

    short level;                      缓冲区“满”或“空”的程度

    unsigned flags                文件状态标志

    char fd;                           文件描述符

    unsigned char hold;        如缓冲区无内容不读取字符

    short bsize;                     缓冲区的大小

    unsigned char*buffer;     数据缓冲区的位置

    unsigned char*curp;       文件位置标记指针当前的指向

    unsigned istemp;            临时文件指示器

    short token;                    用于有效性检查

}FILE,*FILE;

文件操作流程

C语言来操作文件流程:

1.将文件以数据流的形式打开,这是因为一个程序与数据的交互是以流的形式进行的

2.将文件数据读入内存,可以通指向文件数据的文件指针对文件中的数据进行操作

3.关闭数据流

文件操作的函数

fopen()

fopen():该函数用于打开函数:如果正常打开,则返回被打开文件的文件指针;如果打开异常,

则返回NULL

格式:FILE* fp = fopen(char *filename, *type);

char *filename:文件名

文件名有两种填写方式:

1.文件的绝对地址,即文件的具体路径,格式为:D:\\father\\son.exe,或者D:/father/son.exe;

2.文件的相对地址,即相对于当前文件的位置去查找我们要应用的文件地址

*type:文件打开模式

打开模式有以下几种:

r:只能从文件中读数据,该文件必须先存在,否则打开失败

w:只能向文件写数据,若指定的文件不存在则创建它,如果存在则先删除它再重建一个新文件

a:向文件增加新数据(不删除原数据),若文件不存在则打开失败,打开时位置指针移到文件末尾

rb、wb、ab:表示以二进制形式打开的文件

举例:

FILE* fp = fopen("e:\zjj\a.txt","r"); 此处是以文件的绝对地址作为文件名,读的方式打开文件

fclose()

fclose():该函数用于关闭文件:如果正常关闭则返回0;如果异常则返回EOF

格式:int n = fclose(fp);

fp表示一个已经被打开文件的文件指针

举例:

FILE* fp = fopen("d:\a.txt","w");

int n = fclose(fp);

fread()

fread():二进制文件读函数,但它也可以操作文本文件。用于将文件中数据读取到缓冲区

格式:size_t fread( void *buffer, size_t size, size_t count, FILE *stream );

void *buffer :用于接收读取数据的缓冲区的指针

size_t size :要读取的数据的单位大小

size_t count:要读取的数据的单元个数

FILE *stream:要读取的文件的指针

使用该函数后,可以在缓冲区中观察到读取的文件的数据

fwrite()

fwrite():二进制文件写函数,但它也可以操作文本文件。用于将缓冲区数据写入文件中

格式:fwrite(void *buffer, size_t  size,  size_t count , FILE *stream)

void *buffer :用于存储要写入文件数据的缓冲区的指针

size_t size :要写入的数据的单位大小

size_t count:要写入的数据的单元个数

FILE *stream:被写入数据的文件的指针

返回值:实际写入数据的基本单元个数

使用该函数后,打开被写入文件,可以发现写入文件的数据

fseek()

fseek():定位指针在文件中位置函数,定位成功,返回0,否则返回其他值。

格式:int i = fseek(FILE *stream, long offset, int origin);

stream:要操作的文件指针

offset:指针的偏移量,整数表示正向偏移,负数表示负向偏移

origin:表示指针从文件的哪里开始偏移,可能取值为:SEEK_CUR,SEEK_END 或 SEEK_SET

SEEK_SET: 文件开头,值为0

SEEK_CUR: 当前位置,值为1

SEEK_END: 文件结尾,值为2

举例:int i = fseek(fp,sizeof(char)*2,0); 指针指向从文件开头向后偏移2字节

ftell()

ftell():获取当前指针位置相对于文件首地址偏移字节数 ,当指针在起始位置时值为1

该函数一般用在fseek函数后面,用于确定指针移动后的位置

格式:long或int lenth = ftell(FILE *stream);

FILE *stream:操作的文件的指针

举例:

FILE* fp = fopen("d:\a.txt","rb");  打开a.txt文件,假设文件大小3字节

int start = ftell(fp);   确定指针最开始指向的位置,即文件初始位置,此处为1

fseek(fp,0,2);   将指针移到文件尾

int size = ftell(fp);  获取此时指针的位置为3,而不是2,因而可以用此方法算文件大小

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言与数据结构 1.基本概念和术语 1)数据:是对客观事物的符号表示。在计算机科学中其含义是指所有能够输入到计算机中并被计算机程序处理的符号集合。 2)数据元素:是数据集合中的一个实体,是计算机程序中加工处理的基本单位。 数据元素按其组成可分为简单型数据元素和复杂型数据元素。简单型数据元素由一个数据项组成,所谓数据项就是数据中不可再分割的最小单位;复杂型数据元素由多个数据项组成,它通常携带着一个概念的多方面信息。 3)数据对象:是性质相同的数据元素的集合,是数据的一个子集。 4)数据结构:是一门研究非数值计算的程序设计问题中计算机的操作对象以及它们之间关系和操作等的学科。或者说,数据结构是相互之间存在一种或多种特定逻辑关系的数据元素的集合。数据元素之间的相互关系称为结构(Structure)。 一、考点: 了解C语言的特点 掌握C语言程序的构成和书写风格。 二、知识点: 1、C语言的特点: 1)C语言简洁紧凑,压缩了一切不必要的成分。 2)C语言运算丰富,将括号、赋值、强制类型转换、取变量地址等都以运算实现。 3)C语言数据类型丰富,具有现代语言的各种数据类型,用户还能扩充它,实现各种复杂的数据结构,完成各种问题的数据描述。 4)C语言是一种结构化程序设计语言,层次清晰,便于按模块化方式组织程序,易于调试和维护。C程序由若干程序文件组成,一个程序文件由若干函数构成。 5)C语言可以直接访问物理地址,进行位(bit)一级的操作,能实现汇编语言的大部分功能。 6)C语言提供了预处理机制,有利于大型程序的编写和调试。 7)C语言生成的目标代码质量很高,程序执行效率很高。 8)用C语言写的程序可移植性好。 9)C语言语法限制不太严格,程序员设计自由度较大

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值