结构体与共同体:
typedef说明一个新类型:作用是为一种数据类型定义新的名字,不可以创建新的类型;
- 与#define不同,typedef仅限于数据类型,而不是能是表达式或具体的值
- #define发生在预处理,typedef发生在编译阶段
结构体变量的定义和初始化:
//结构体定义格式:
结构体 结构体名称
{
结构体成员列表 (可以是不同数据类型 基本类型 构造类型 指针类型)
};
三种方式:
1、先声明结构体类型再定义变量名 struct stu{}; struct stu Mike
2、在声明类型的同时定义变量 struct stu{}Mike;
3、直接定义结构体类型变量(无类型名)struct {}Mike;
结构体成员的使用:
struct stu s1; strcpy(s1.name,"abc"); s1.age = 18;printf("s1.name = %s, s1.age = %d\n", s1.name, s1.age);
//指针变量的方式:
struct stu s1; strcpy((&s1)->name,"test"); (&s1)->age = 22;printf("s1.name = %s, s1.age = %d\n", (&s1)->name, (&s1)->age);
结构体数组:
struct stu boy[5] = { 。。。结构体成员内容}; //结构体数组的初始化
//结构体数组成员的打印输出
for (i = 0; i < 5; i++)
{
printf(" name=%s, score=%f\n", boy[i].name, boy[i].score);
// printf(" name=%s, score=%f\n", (boy+i)->name, (boy+i)->score);
}
void BubbleSort(student * ss,int len)
{
int i , j =0;
student temp;
for(i = 0;i < len -1;i++)
{ for(j = 0;j < len - i -1;j++)
{
temp = ss[j];
ss[j] = ss[j+1];
ss[j+1] =temp;
}
}
}
结构体的嵌套使用:
// struct 技能 (格式)
{名称 伤害 范围 效果 等待时间}
// struct 人物信息
{ 等级 经验 MP HP struct 技能 skill[4] ;}
struct person
{
char name[20];
char sex;
};
struct stu
{
int id;
struct person info;
};
共用体(union):
- 联合union是一个能在同一个存储空间存储不同类型数据的类型;
- 联合体所占的内存长度等于其最长成员的长度倍数,也有叫做共用体;
- 同一内存段可以用来存放几种不同类型的成员,但每一瞬时只有一种起作用;
- 共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员的值会被覆盖;
- 共用体变量的地址和它的各成员的地址都是同一地址
union TEST{ int a,float b, char c};//定义共用体变量 union TEST tmp;
printf("%p, %p, %p\n", &(tmp.a), &(tmp.b), &(tmp.c));
printf("%lu\n", sizeof(union Test));//共用体大小为最大成员类型的大小
//一个成员赋值会影响带动其它成员的值
链表:
链表无需一次性分配一块连续的存储空间,只需分配N块节点的存储区域,通过两个指针建立关系;(便于插入和删除某个元素)
typedef strcut _LINKNODE{ int id ;//数据域struct _LINKNODE* next;//指针域}linknode;
链表的初始化:
link_node* init_linklist(){
//创建头结点指针
link_node* head = NULL;
//给头结点分配内存
head = (link_node*)malloc(sizeof(link_node));
if (head == NULL){
return NULL;
}
head->data = -1;
head->next = NULL;
//保存当前节点
link_node* p_current = head;
int data = -1;
//循环向链表中插入节点
while (1){
printf("please input data:\n");
scanf("%d",&data);
//如果输入-1,则退出循环
if (data == -1){
break;
}
//给新节点分配内存
link_node* newnode = (link_node*)malloc(sizeof(link_node));
if (newnode == NULL){
break;
}
//给节点赋值
newnode->data = data;
newnode->next = NULL;
//新节点入链表,也就是将节点插入到最后一个节点的下一个位置
p_current->next = newnode;
//更新辅助指针p_current
p_current = newnode;
}
遍历链表:
顺序输出单向链表各项结点数据域中的内容:
if(head == NULL)
{
return;
}
link_node*p_current1 = head->next;
while(p_current1 != NULL)
{
printf("%d ",p_current1->data);
p_current1 = p_current1->next;
}
printf("\n");
插入节点:
void insert_linklist(link_node* head,int val,int data)
if (head == NULL){
return;
}
//两个辅助指针
link_node* p_prev = head;
link_node* p_current = p_prev->next;
while (p_current != NULL){
if (p_current->data == val){
break;
}
p_prev = p_current;
p_current = p_prev->next;
}
//如果p_current为NULL,说明不存在值为val的节点
if (p_current == NULL){
printf("不存在值为%d的节点!\n",val);
return;
}
//创建新的节点
link_node* newnode = (link_node*)malloc(sizeof(link_node));
newnode->data = data;
newnode->next = NULL;
//新节点入链表
newnode->next = p_current;
p_prev->next = newnode;
删除节点:
void remove_link(link_node*head,int val)
if(head == NULL)
{
return ;
}
link_node* p_pre = head;
link_node* p_cur = p_pre->next;
while(p_cur != NULL)
{
if(p_cur->data == val)
{
break;
}
p_pre = p_cur;
p_cur = p_pre->next;
}
if(p_cur == NULL)
{
return;
}
p_pre->next = p_cur->next;
free(p_cur);
位运算:
-
与运算(&)(AND):对两个操作数的每一位进行与操作,只有当两位都为1时,结果才为1,否则为0。
-
或运算(|)(OR):对两个操作数的每一位进行或操作,只有当两位都为0时,结果才为0,否则为1。
-
异或运算(^)(XOR):对两个操作数的每一位进行异或操作,当两位相同时,结果为0;当两位不同时,结果为1。
-
取反运算(~)(NOT):对操作数的每一位进行取反操作,即将0变为1,将1变为0。
-
左移运算(<<):将操作数的二进制数向左移动指定的位数,右侧空出的位置用0填充。
-
右移运算(>>):将操作数的二进制数向右移动指定的位数,左侧空出的位置用符号位填充(如果是有符号数),或者用0填充(如果是无符号数)。
位移运算符:
左移运算符:相当于函数去乘以2来运算 例如:从二进制来看 10 << 1 0000 1010 —— 00010100
右移运算符:相当于函数去除以2来运算;
位运算的应用:通过异或来改变交换a和b的值例如:a=10,b=20
传统方式:temp = a; a = b;b = temp;
位运算:a = a^b; b = a^b; a=a^b;
位运算赋值运算符:
位运算和赋值运算符可以组合成复合赋值运算符,如:&=,|=,>>=,<<=,^=等等,a>>=2相当于a = a>>2.
文件操作:
在C语言中用一个指针变量指向一个文件,这个指针称为文件指针。
FILE是系统使用typedef定义出来的有关文件信息的一种结构体类型,结构中含有文件名、文件状态和文件当前位置等信息。
文件的打开与关闭:
第一个参数的相对路径:文件名称加扩展名;fopen(“passwd.txt”,“r”);
//打开当前目录(test)下的文件: fopen(“./test/passwd.txt”,“r”);
//打开当前目录上一级目录(相对当前目录)passwd.txt文件 fopen(“../passwd.txt”,"r");
//绝对路径:打开C盘test目录下一个叫passwd.txt文件 fopen(“C:/test/passwd.txt”,“r”);
打开模式 | 含义 |
r或rb | 以只读方式打开一个文本文件(不创建文件,若文件不存在则报错) |
w或wb | 以写方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件) |
a或ab | 以追加方式打开文件,在末尾添加内容,若文件不存在则创建文件 |
r+或rb+ | 以可读、可写的方式打开文件(不创建新文件) |
w+或wb+ | 以可读、可写的方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件) |
a+或ab+ | 以添加方式打开文件,打开文件并在末尾更改文件,若文件不存在则创建文件 |
文件的关闭: fclose(FILE *fp);关闭先前fopen()打开的文件。此动作让缓冲区的数据写入文件中,并释放系统所提供的文件资源。
FILE * fp = NULL;
fp = fopen("abc.txt", "r");
fclose(fp);
文件的读写:
写文件:int fputc(int ch,FILE*stream);将ch转换为unsigned char后写入stream指定的文件中
FILE *fp;
fp = fopen("abc.txt","w");
if(fp == NULL)
{
printf("open test fail!\n");
return EOF;
}
char buf[]="this is a Test";
int i = 0;
int n = strlen(buf);
for(i=0;i<n;i++)
{
int ch = fputc(buf[i],fp);
printf("ch=%c\n",ch);
}
fclose(fp);
return 0;
读文件: int fgetc(FILE *fp);
char ch;
while ((ch = fgetc(fp)) != EOF)
{
printf("%c", ch);
}
printf("\n");
fclose(fp);
int fputs(const char*str,FILE*stream)将str所指定的字符串写入到stream指定的文件中,字符串结束符 '\0' 不写入文件。
char*fgets(char*str,int size,FILE*stream);从stream指定的文件内读入字符,保存到str所指定的内存空间,直到出现换行字符、读到文件结尾或是已读了size - 1个字符为止,最后会自动加上字符 '\0' 作为字符串结束。
char buf[100] = 0;char *p = fgets(buf, sizeof(buf), fp);
文件结尾:
EOF表示文件结束符(end of file)。在while循环中以EOF作为文件结束标志,这种以EOF作为文件结束标志的文件,必须是文本文件。在文本文件中,数据都是以字符的ASCII代码值的形式存放。我们知道,ASCII代码值的范围是0~127,不可能出现-1,因此可以用EOF作为文件结束标志
按照格式化文件 fprintf fscanf
fprintf(fp,"%d %d %d\n",1,2,3);根据参数format字符串来转换并格式化数据,然后将结果输出到stream指定的文件中,指定出现字符串结束符 '\0' 为止。
fscanf(fp,"%d %d %d\n",&a,&b,&c);从stream指定的文件读取字符串,并根据参数format字符串来转换并格式化数据。
按照块读写文件fread、fwrite
fwrite:unsigned int fwrite(const void*ptr,unsigned int size,unsigned int nmemb,FILE *fp);
ptr:准备写入文件数据的地址
size: 此参数指定写入文件内容的块数据大小
nmemb:写入文件的块数,写入文件数据总大小为:size * nmemb
stream:已经打开的文件指针
fread:unsigned int fread(const void*ptr,unsigned int size,unsigned int nmemb,FILE *fp);
ptr:存放读取出来数据的内存空间
size: 此参数指定读取文件内容的块数据大小
nmemb:读取文件的块数,读取文件数据总大小为:size * nmemb
stream:已经打开的文件指针
返回值:
成功:实际成功读取到内容的块数,如果此值比nmemb小,但大于0,说明读到文件的结尾。
文件的定位:
int fseek(FILE *stream, long offset, int whence);移动文件流(文件光标)的读写位置;
stream:已经打开的文件指针
offset:根据whence来移动的位移数(偏移量),可以是正数,也可以负数,如果正数,则相对于whence往右移动,如果是负数,则相对于whence往左移动。如果向前移动的字节数超过了文件开头则出错返回,如果向后移动的字节数超过了文件末尾,再次写入时将增大文件尺寸。
whence:其取值如下:
SEEK_SET:从文件开头移动offset个字节
SEEK_CUR:从当前位置移动offset个字节
SEEK_END:从文件末尾移动offset个字节
long ftell(FILE *stream);获取文件流(文件光标)的读写位置。
void rewind(FILE *stream);把文件流(文件光标)的读写位置移动到文件开头