欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C语言学习笔记

程序员文章站 2022-05-12 15:24:38
...

输入输出函数

printf 格式输出到屏幕
fprintf 格式输出到磁盘

scanf 从屏幕格式输入
fscanf 从磁盘格式输入

putchar 字符输出到屏幕
puts 字符串输出到屏幕
fputc 字符输出到磁盘
fputs 字符串输出到磁盘

getchar 从屏幕得到一个字符
gets 从屏幕得到一个字符串
fgetc 从磁盘得到一个字符
fgets 从磁盘得到一个字符串

双链表中删除节点

C语言学习笔记

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联合:理解

  1. 将几个变量放在相同的内存区,但其中只有一个变量在给定时刻有有效值。
  2. 程序处理许多不同类型的数据,但是一次只处理一种。要处理的类型在执行期间才能确定。
  3. 在不同的时间访问相同的数据,但在不同的情况下该数据的类型是不同的。

 

typedef 自定义数据

typedef  类型   别名; typedef struct  _stu_data  stu_data;

 

文件操作

1,文件打开:

FILE*fopen(constchar*filename,constchar*mode);

filename是要操作的文件名。

mode

说明

"w"

打开一个文本文件,进行写入操作。如果文件不存在,则会建立一个新文件. 存在则清空内容。

"a"

打开一个文本文件,进行追加操作。如果文件不存在,则会建立一个新文件. 存在则追加内容。

"r"

打开一个文本文件,进行读取操作。

"w"

 FILEf;

 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指针

ffreopen("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);
}