C语言学习笔记
输入输出函数
printf 格式输出到屏幕
fprintf 格式输出到磁盘
scanf 从屏幕格式输入
fscanf 从磁盘格式输入
putchar 字符输出到屏幕
puts 字符串输出到屏幕
fputc 字符输出到磁盘
fputs 字符串输出到磁盘
getchar 从屏幕得到一个字符
gets 从屏幕得到一个字符串
fgetc 从磁盘得到一个字符
fgets 从磁盘得到一个字符串
双链表中删除节点
1,首先查找需要删除的节点(Node)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include <time.h>
struct mytime
{
//char name[256];
int hour;//时
int min; //分
int sec; //秒
};
struct stu_data
{
char name[256];//学生名字
int sex;//0女生,非0表男生
struct mytime stuTime;//签到时间
struct stu_data* front; //指向前一个结点
struct stu_data* back; //指向后一个结点
} ;
int main(int argn,char* argv[])// int a[1]//a[0]
{
struct mytime t2;
struct stu_data *stu,*p,*Head,*tail,*Node;
time_t t;// long int
struct tm * timfo;
int i;
//建立Head头结点
Head=p=tail=malloc(sizeof( struct stu_data)); //256+12=268
Head->back=Head->front=Head;//循环链表初始化头结点
memset(p,0,sizeof( struct stu_data));
strcpy(Head->name,"头结点");
do
{
//插入新的结点
stu= malloc(sizeof( struct stu_data)); //
memset(stu,0,sizeof( struct stu_data)); //初始化内存区域
//stu->back=NULL; //新结点尾指针置空
//p->back=stu; //前驱结点back指针指向新结点
//p=stu; //重要移动标志指针
stu->front=p; //新结点指向前驱 2
stu->back=NULL; //新结点尾指针置空
p->back=stu; //前驱结点back指针指向新结点
p=stu; //重要移动标志指针
tail=stu;//可有可无的 2
scanf("%s",&stu->name);
time(&t);
timfo= localtime(&t); //取当前系统时间
stu->stuTime.hour=timfo->tm_hour;//时
stu->stuTime.min=timfo->tm_min;//分
stu->stuTime.sec=timfo->tm_sec;//秒
//构建循环链表
Head->front=stu;
tail->back=Head;
} while(strcmpi(stu->name,"exit")!=0);
Node= FindNode(Head,"s1");
//删除结点
DeleteNode(Node);
//初始指针p 使他头结点Head
stu=Head->back;
do
{
printf("%s,到校时间:%d时%d分%d秒\n",
stu->name,
stu->stuTime.hour,
stu->stuTime.min,
stu->stuTime.sec);
stu=stu->back;
} while (strcmpi(stu->name,"exit"));
return 0;
}
struct stu_data * FindNode(struct stu_data* Head,char* Name)
{
struct stu_data * stu;
stu=Head->back;
do
{
if (strcmpi(stu->name,Name)==0)
{
printf("%s,到校时间:%d时%d分%d秒\n",
stu->name,
stu->stuTime.hour,
stu->stuTime.min,
stu->stuTime.sec);
if (stu->sex)
printf(" 男\n\n");
else
printf(" 女\n\n");
return stu;
}
stu=stu->back;
} while (stu!=Head->back);
return NULL;
}
2,删除节点
struct stu_data* DeleteNode(struct stu_data* Node)
{
if (Node==NULL) return;
Node->front->back=Node->back;
Node->back->front=Node->front;
free(Node);//释放结点空间
}
验证节点是否删除
//初始指针p 使他头结点Head
stu=Head->back;
do
{
printf("%s,到校时间:%d时%d分%d秒\n",
stu->name,
stu->stuTime.hour,
stu->stuTime.min,
stu->stuTime.sec);
stu=stu->back;
} while (strcmpi(stu->name,"exit"));
二叉树
二叉树是一种递归定义。每一个二叉树的结点最多可以指向2个结点(左结点一个,右结点一个)。
满二叉树:除最后一层节点外,其他节点都是两个节点
完全二叉树:非满二叉树,只有左节点
非完全二叉树:非满二叉树,只有右节点
有序二叉树:满足:左节点<根节点<右节点 或者 根节点<左节点<右节点 总之:右节点始终大于左节点的值
创建结点:
struct node
{
long data; //存放数据的一个或者多个项
struct node *pLeft; //左孩子 指向一个二叉树
struct node *pRight; //右孩子 指向一个二叉树
}
//创建节点
struct node *p=malloc(sizeof(struct node));
p->pLeft=p->pRight=NULL;
p->data=0
构建二叉树:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include <time.h>
struct node
{
long data; //存放数据的一个或者多个项
long count;
struct node *pLeft; //左孩子 指向一个二叉树
struct node *pRight; //右孩子 指向一个二叉树
};
struct node * CreateNode(long value)
{
struct node *p=malloc(sizeof(struct node));
p->pLeft=p->pRight=NULL;
p->data=value;
p->count=1;
}
struct node * AddNode(struct node * pNode,long v)
{
//情况一 pNode==NULL
if (pNode==NULL)
{
return CreateNode(v);
}
// pNode->data=v
if (pNode->data==v)
{
pNode->count++;
return pNode;
}
//v大于结点数据
if (v>pNode->data)
{
if (pNode->pRight==NULL)
{
pNode->pRight=CreateNode(v);
return pNode->pRight;
}else return AddNode(pNode->pRight,v); //递归调用
}
//v小于 结点数据
if (v<pNode->data)
{
if (pNode->pLeft==NULL)
{
pNode->pLeft=CreateNode(v);
return pNode->pLeft;
}else return AddNode(pNode->pLeft,v); //递归调用
}
return NULL;
}
int main(void)
{
struct node*root;
long v,i;
printf("请输入二叉树根结点数值:");
scanf("%d",&v);
root=CreateNode(v);
for (i=0;i<=10;i++)
{
AddNode(root,i);
}
return 0;
}
二叉树遍历:
//二叉树遍历
void traversal(struct node* pNode)
{
int i;
if (pNode->pLeft!=NULL)
{
traversal(pNode->pLeft);
}
for (i=1;i<=pNode->count;i++)
{
printf("%d,",pNode->data);
}
if (pNode->pRight!=NULL)
{
traversal(pNode->pRight);
}
}
共享内存--联合(union)
union : 指向同一块内存区域地址
struct:结构的数据是指向不同内存区域地址
Union联合:理解
- 将几个变量放在相同的内存区,但其中只有一个变量在给定时刻有有效值。
- 程序处理许多不同类型的数据,但是一次只处理一种。要处理的类型在执行期间才能确定。
- 在不同的时间访问相同的数据,但在不同的情况下该数据的类型是不同的。
typedef 自定义数据
typedef 类型 别名; typedef struct _stu_data stu_data;
文件操作
1,文件打开:
FILE*fopen(constchar*filename,constchar*mode);
filename是要操作的文件名。
mode |
说明 |
"w" |
打开一个文本文件,进行写入操作。如果文件不存在,则会建立一个新文件. 存在则清空内容。 |
"a" |
打开一个文本文件,进行追加操作。如果文件不存在,则会建立一个新文件. 存在则追加内容。 |
"r" |
打开一个文本文件,进行读取操作。 |
"w"
FILE* f;
f=fopen("test.txt","w");
f=fopen("test.txt","a");
f=fopen("test.txt","r");
2,关闭文件 fclose
int fclose(FILE*stream); //如果成功关闭返回0,不成功则返回EOF,EOF一般定义为-1;
3,写入文本文件fputc
intfputc(intc,FILE*stream);
功能:向文件写入一个字符,且指针下移。
4,读取文本文件fgetc
intfgetc(FILE*stream);
功能:从文件中获取一个字符,且指针下移。
#include <stdio.h>
int main(void)
{
FILE* f;
char i;
f=fopen("test.txt","w");//"w","a"
for (i='A';i<='Z';i++)
{
fputc(i,f);
}
fclose(f);//重要
f= fopen("test.txt","r");
for (i=1;i<=26;i++)
{
printf("%c",fgetc(f));
}
fclose(f);
return 0;
}
5,字符串读写:
写:
int fputs( const char *string, FILE *stream ); //int fputs(char *pstr,FILE *pfile);
string待写入的字符串地址;
stream表示文件指针
读:
char *fgets( char *string, int n, FILE *stream );//char* fgets(char *pstr,int ncahrs,FILE *pfile);
string是用于存放字符串的缓冲区。
n表示要读出字符的个数。
stream表示文件指针
puts(char* s);//把字串输出到屏幕
gets(char* s);//读入字符串
int main(void)
{
FILE* f;
char I;
char s[256];
////写字符串
f=fopen("test.txt","w");//以写入模式打开文件
gets(s);//scanf("%s",s);//从控制台读取字符串
fputs(s,f);//向文件test.txt写入字符串s
fclose(f);//关掉指针与文件的关联
//读字符串
f=fopen("test.txt","r");//以读取模式打开文件test.txt
fgets(s,256,f);//从文件读取字串到s地址开始的缓存区
puts(s);//printf("%s",s); 把字串输出到控制台显示出来 或者理解为向控制台屏幕写入字符串
fclose(f);//关掉指针与文件的关联
return 0;
}
6,格式化文件输出fprintf
int fprintf( FILE *stream, const char *format [, argument ]...);//printf 输出到控制台
eg: fprintf(f,"%d%d%f %s",111,222,444.88,"xxxbbb");
7,格式化文件输出fprintf
int fscanf( FILE *stream, const char *format [, argument ]... );//scanf从控制台获取输入
eg: fscanf(f,"%d%d%f%s",&n1,&n2,&f1,s);
8,文件重命名rename
int rename( const char *oldname, const char *newname );
oldname //需要重新命名的文件名
newname //新的文件名
9,文件删除remove
int remove( const char *path );
path //是全路径名称
10,流的重定向freopen:
区别:stdin和stdout都可以被重定向,stderr不能
FILE *f =freopen("test.txt","r",stdin); printf("%s",fgets(f)); //成功则返回stdin指针
f= freopen("test.txt","r",stdin);//把stdin重定向至test输入
11,清空流缓冲区 fflush:
int fflush( FILE *stream );// 返回0表示成功,非零表示出错
12,错误信息和错误处理 stderr:
//错误信息和错误处理
int main(void)
{
//未重定向的代码
char num[256];
FILE *f;
f=fopen("test.txt","r");
if (f==NULL) //if (!f)
{
fprintf(stderr,"文件打开出错或者是文件不存在 \n");//stdout
perror("test.txt");
}
return 0;
}
13,打开二进制文件
FILE*fopen(constchar*filename,constchar*mode);
filename是要操作的文件名。
mode |
说明 |
"wb" |
打开一个二进制文件,进行写入操作。如果文件不存在,则会建立一个新文件. 存在则清空内容。 |
"ab" |
打开一个二进制文件,进行追加操作。如果文件不存在,则会建立一个新文件. 存在则追加内容。 |
"rb" |
打开一个二进制文件,进行读取操作。 |
14,写二进制文件
size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );
//count*size 是要写入文件的字节数
buffer是缓冲区指针
stream是文件指针(或者流指针)
15,读二进制文件
size_t fread( void *buffer, size_t size, size_t count, FILE *stream );
//count*size 是要读取的字节数
buffer是缓冲区指针
stream是文件指针(或者流指针)
int main(void)
{
//未重定向的代码
char num[256];
FILE *f;
stu_data stu10[10]={0};
int i;
time_t t1;
struct tm * tm1;
f=fopen("test.txt","rb");
//fprintf(f,"ddddd");
if (f==NULL) //if (!f)
{
//fprintf(stderr,"文件打开出错或者是文件不存在 \n");//stdout
perror("test.txt");
}
////获取数据并写入文件
//for (i=0;i<10;i++)
//{
// scanf("%s",&stu10[i].name );
// time(&t1);
// tm1=localtime(&t1);
// //记录签到时间//记录学生 姓名和签到时间
// stu10[i].stuTime.hour=tm1->tm_hour;
// stu10[i].stuTime.min=tm1->tm_min;
// stu10[i].stuTime.sec=tm1->tm_sec;
//}
////写入到二进制文件里
//fwrite(stu10,sizeof(stu_data),10,f);
//读取二进制文件数据至stu10缓冲区
fread(stu10,sizeof(stu_data),10,f);
//显示数据
for (i=0;i<10;i++)
{
printf("name=%s ",stu10[i].name);
printf("时间:%d-%d-%d \n",stu10[i].stuTime.hour,stu10[i].stuTime.min,stu10[i].stuTime.sec);
}
fclose(f);
return 0;
}
16,文件位置相关操作fgetpos和fsetpos
- 找出我们在文件中的位置
int fgetpos(FILE*pfile,fpos_t*position);
fpost_t here=0;//fpos_t 一般是long类型,不同的系统可能有不同的区别所以最好用fpost typedef
fgetpos(pfile,&here);
- 在文件中设定位置
与fgetpos配对的函数是fsetpos
int fsetpos(FILE*pfile,fpos_t*position);
fpost_t here=10;//fpos_t 一般是long类型,不同的系统可能有不同的区别所以最好用fpost typedef
fsetpos(pfile,&here);
- 文件结束判断函数feof
函数名: feof
功 能: 检测流上的文件结束符
用 法: int feof(FILE *stream); feof(fp)有两个返回值: 如果遇到文件结束,函数feof(fp)的值为1,否则为0。
int main(void)
{
int i;
FILE *f;
fpos_t pos; //long pos;
int temp;
// 写入文件二进制数据
//f=fopen("test.txt","wb");
//fgetpos(f,&pos);
// //打印文件位置
//printf("文件位置:%d,\n",pos);
//for(i=1;i<=256;i++)
//{
// fwrite(&i,sizeof(int),1,f);//4*256=1024
//}
//fgetpos(f,&pos);
//printf("文件位置:%d,\n",pos);
//读取文件二进制数据
f=fopen("test.txt","rb");
pos=(256-20)*sizeof(int);//4*20
// fsetpos(f,&pos);
while(!feof(f))
{
fread(&temp,sizeof(int),1,f);
printf("%d,",temp);
}
return 0;
}
17,文件位置相关操作ftell和fseek
- 找出我们在文件中的位置
long ftell (FILE *pfile);
longfpos=ftell(pfile); //fgetpos 通过指针返回fpost_t 位置
- 在文件中设定位置
与ftell配对的函数是fseek
int fseek(FILE*pfile,long offset,int origin);
SEEK_CUR //可正可负
指向当前位置
SEEK_END // - 负
文件结束位置
SEEK_SET // + 正
文件开始
成功,返回0,否则返回其他值。
fseek(f, 20,SEEK_CUR) ;//指针后移20字节的位置
fseek(f,-20,SEEK_END) ;//从文件结束位置前移20字节的位置
fseek(f,-20,SEEK_SET) ;//这样写执行会不成功,对于SEEK_SET应该指定正数向后移指针才行fseek(f,20,SEEK_SET)
int main(void)
{
int i;
FILE *f;
fpos_t pos; //long pos;
int temp;
//写入二进制数据
//f=fopen("t.txt","wb");
//printf("%d,",ftell(f)); //fgetpos 代替
//for (i=101;i<=356;i++)
//{
// fwrite(&i,sizeof(int),1,f);//1024字节
// printf("%d,",ftell(f));
//}
//fseek读取数据
f=fopen("t.txt","rb");
//fseek
//读取末尾的20个int数据
fseek(f,0,SEEK_END);//指向文件尾
pos=ftell(f);//1024
if(fseek(f,pos/2,SEEK_SET)!=0)
{//文件中间 512
printf("fseek 失败 \n");
}
fseek(f,100,SEEK_CUR); //612/4=153+101
while(!feof(f))
{
fread(&temp,sizeof(int),1,f);
printf("%d,",temp);
}
return 0;
}
共享的方式打开文件或者流 安全性比fopen 高
FILE *_fsopen( const char *filename, const char *mode, int shflag );
filename Name of the file to open. //需要打开的文件名
mode Type of access permitted. //可以访问的类型
shflag Type of sharing allowed. //共享访问类型
_SH_COMPAT Sets Compatibility mode for 16-bit applications. //以兼容模式打开16位程序
_SH_DENYNO Permits read and write access. //充许读和写 以此模式打开类似fopen
_SH_DENYRD Denies read access to the file. //拒绝读
_SH_DENYRW Denies read and write access to the file. //拒绝读和写
_SH_DENYWR Denies write access to the file //拒绝写
#include <share.h>
int main(void)
{
FILE *f1,*f2;
char s1[256],s2[256];
//同时打开文件读取
/*f1=fopen("share.txt","r");
f2=fopen("share.txt","r");*/
f1=f2=NULL;
f1=_fsopen("share.txt","r",_SH_DENYRW);//独占文件访问wb
// f2=_fsopen("share.txt","r",_SH_DENYRW);
if (!f1)
{
perror("打开出错");
}else
{
fgets(s1,256,f1);
printf("%s \n",s1);
}
fclose(f1);
f2=fopen("share.txt","r");
if (!f2)
{
perror("打开出错");
}else
{
fgets(s2,256,f2);
printf("%s \n",s2);
}
//关掉指针
fclose(f2);
return 0;
}
预处理
- #include
#include<file.h> //表示在默认路径中搜索file.h 并用file.h里的内容替换#include<file.h>
#include"file.h" //表示在当前目录里搜索file.h 并用file.h里的内容替换#include"file.h"
- #define
#define filenaeme "test.txt" //在预处理时用"test.txt" 替换所有filename 并且删掉此行
#deinfe pi 3.1415926 //在预处时 用3.1415926 替换所有pi 并且删掉此行
- extern
extern可以置于变量或者函数前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。(另外,在C++中extern也可用来进行链接指定。)
如在a.c中定义过全局变量int a要在hello.c中引用全局变量a则要作如下定义:
extern int a;
仅仅是一个变量的声明,其并不是在定义变量a,并未为a分配内存空间。变量a在所有模块中作为一种全局变量只能被定义一次,否则会出现连接错误。
如在a.c中定义过全局函数int add(int a,int b)要在hello.c中引用函数add则要作如下定义:
extern int add(int,int);
仅仅是一个变量的声明,其并不是在定义变量a,并未为a分配内存空间。变量a在所有模块中作为一种全局变量只能被定义一次,否则会出现连接错误。
define 的高级应用
预处理换多行
#define add(a,b) \
a+b
拼接字符
int my2=222;
#define cat(a,b) a##b
printf(%d,cat(my,2)) == printf(%d,my2)
条件编译
一、条件编译#if 条件
#if 表达式
代码段
#endif
//如果表达式成立则,编译代码段
注意:表达式里测试的是预处理指令赋给标识符的指定值
二、条件编译#if defined
#if defined flag
代码段
#endif
//用于测试flag 标识符是否被#define 指令定义过,如果定义过,则编译代码段
#if !defined flag
代码段
#endif
//用于测试flag 标识符是否被#define 指令定义过,如果未被定义过,则编译代码段
三、条件编译#if #else
#if 表达式
代码段1
#else
代码段2
#endif
//如果表达式成立则编译代码段1,不成立则编译代码段2
多线程临界区
创建线程
//创建新线程
HANDLE CreateThread(
LPSECURITY_ATTRIBUTESlpThreadAttributes, // pointer to security attributes
//安全描述符指针
DWORDdwStackSize, // initial thread stack size
//初始化线程堆栈大小
LPTHREAD_START_ROUTINElpStartAddress, // pointer to thread function
//重要 线程回调函数指针
LPVOIDlpParameter, // argument for new thread
//新线程参数
DWORDdwCreationFlags, // creation flags
//创建标志
LPDWORDlpThreadId // pointer to receive thread ID
//返回线程ID
);
DWORD WINAPI ThreadProc(
LPVOID lpParameter //接收CreateThread的第4个参数
)
{
//添加线程代码
return 1;
}
hand=CreateThread(NULL,0,ThreadProc,NULL,0,NULL); //创建线程
三、临界区
临界区在使用时以CRITICAL_SECTION结构对象保护共享资源,并分别用EnterCriticalSection()和LeaveCriticalSection()函数去标识和释放一个临界区
CRITICAL_SECTION
InitializeCriticalSection
EnterCriticalSection
LeaveCriticalSection
EnterCriticalSection
LeaveCriticalSection成对出现可以保护全局共享资源有序访问
//恢复线程
ResumeThread(hThread);
//挂起线程
SuspendThread(hThread);
CRITICAL_SECTION cs;
DWORD WINAPI ThreadProc1(
LPVOID lpParameter // 接收 CreateThread的第4个参数
)
{ int i=0;
while(1)
//添加线程代码
{ i++;
EnterCriticalSection(&cs);
gotoxy(0,1);
printf("i=%d \n",i);
Sleep(100);
LeaveCriticalSection(&cs);
}
return 1;
}
DWORD WINAPI ThreadProc2(
LPVOID lpParameter // 接收 CreateThread的第4个参数
)
{ int i=0;
while(1)
//添加线程代码
{ i++;
EnterCriticalSection(&cs);
gotoxy(0,3);
printf("i=%d \n",i);
Sleep(100);
LeaveCriticalSection(&cs);
}
return 1;
}
//主函数
int main(void)
{
InitializeCriticalSection(&cs);
CreateThread(NULL,0,ThreadProc1,NULL,0,NULL); //创建线程
CreateThread(NULL,0,ThreadProc2,NULL,0,NULL); //创建线程
getch();
printf("22222222222");
getch();
return 0;
}
随机数
随机种子初始化函数srand
srand( (unsigned)time( NULL ) );
rx=rand()%2;
播放声音
//增加库文件 播放自定义音频文件
#pragma comment(lib,"winmm.lib")
//认识几个声音相关的函数
BOOL MessageBeep(
UINTuType // sound type
);
BOOL sndPlaySound(
LPCSTRlpszSound, //*.wav
UINTfuSound
);
BOOL PlaySound
(
LPCSTRpszSound,
HMODULEhmod, //只有播放资源文件里的声音才使用此参数,否则置NULL
DWORDfdwSound
);
//添加声音效果
void playwav()
{
//MessageBeep(MB_ICONQUESTION );
//sndPlaySoundA("turn.wav",SND_ASYNC);
//sndPlaySoundW(L"turn.wav",SND_ASYNC|SND_NODEFAULT);
//sndPlaySound(L"turn.wav",SND_ASYNC|SND_NODEFAULT);
//PlaySoundA("turn.wav",NULL,SND_ASYNC);
PlaySound (L"turn.wav",NULL,SND_ASYNC);
}