结构体变量之间的传递直接把结构体赋值给另一个结构体变量#include<stdio.h>structDate{intyear;intmonth;intday;}date={.year=2021,.month=1,.day=5};intmain(void){structDated;d=date;printf("d的值:%d-%d-%d",d.year,d.month,d.day);return0;}结构体作为函数参数传递把结构体变量作为函数的参数传递给函数#include<stdio.h>//定义结构体structDate{intyear;intmonth;intday;};//定义函数structDategetDate(structDatedate){//给结构体赋值date.year=2021;date.month=1;date.day=5;//返回结构体returndate;}intmain(void){structDatedate,d;d=getDate(date);printf("日期为:%d-%d-%d",d.year,d.month,d.day);return0;}结构体指针作为函数参数传递函数直接传入结构体会降低性能,传入结构体指针能提高程序效率#include<stdio.h>structDate{intyear;intmonth;intday;};//传入指针不需要返回值voidgetDate(structDate*date){//给结构体赋值date->year=2021;date->month=1;date->day=5;}intmain(void){structDatedate;getDate(&date);//传入地址printf("日期为:%d-%d-%d",date.year,date.month,date.day);return0;}动态申请结构体空间动态申请,存储在堆空间,用完记得释放#include<stdio.h>#include<stdlib.h>structDate{intyear;intmonth;intday;};//传入指针不需要返回值voidgetDate(structDate*date){//给结构体赋值date->year=2021;date->month=1;date->day=5;}intmain(void){structDate*date;//申请空间date=(structDate*)malloc(sizeof(structDate));getDate(date);//传入地址printf("日期为:%d-%d-%d",date->year,date->month,date->day);//释放空间free(date);return0;}
结构体定义方式关键字:structstruct结构体名称{基本类型变量名;基本类型变量名;...};//注意这有分号structBoy{charname[20];chargender;floatage;};structBoy{charname[20];chargender;floatage;}boy;//这样定义不用初始化boy为全局变量structBoy{charname[20];chargender;floatage;}boy={"大数据男孩",//可以不指定但顺序要对.gender='A',.age=18.5,};//初始化并设置默认值初始化结构体//基本初始化struct结构体名称变量名;structBoyboy;//初始化全部值值的类型需要对应struct结构体名称变量名={内容,""}structBoyboy={"大数据男孩","A",18.2}//初始化部分值struct结构体名称变量名={.定义结构=值,...}structBoyboy={.name="大数据男孩",.gender="A",}结构体使用结构体赋值结构体变量名.值=变量;boy.name="大数据男孩";获取结构体的值变量=结构体变量名.值floatage=boy.age;结构体的内存占用原因:C语言的内存对齐结构体使用#include<stdio.h>//定义结构体structBoy{charname[20];chargender;floatage;};//注意这里有分号intmain(){structBoyboy;//初始化结构体printf("输入姓名:");scanf("%s",boy.name);//数组不用取址符printf("输入性别:");scanf("%s",&boy.gender);printf("输入年龄:");scanf("%f",&boy.age);printf("--------信息录入完毕---------\n\n");printf("基本信息:\n");printf("姓名:%s\n",boy.name);printf("性别:%s\n",&boy.gender);printf("年龄:%.2f\n",boy.age);return0;}结构体嵌套//日期结构体structDate{intyear;intmonth;intday;};//男孩结构体structBoy{charname[20];chargender;structDatedate;//嵌套的日期结构体}boy={.name="大数据男孩",.gender='A',.date={2020,12,15},};访问嵌套结构体#include<stdio.h>//日期结构体structDate{intyear;intmonth;intday;};//男孩结构体structBoy{charname[20];chargender;structDatedate;//嵌套的日期结构体}boy={.name="大数据男孩",.gender='A',.date={2020,12,15},};intmain(){printf("姓名:%s\n",boy.name);printf("性别:%c\n",boy.gender);//嵌套的结构体一层一层的访问printf("日期:%d-%d-%d",boy.date.year,boy.date.month,boy.date.day);return0;}结构体数组structDate{intyear;intmonth;intday;}date[2]={{2020,12,15},{2020,12,16},};访问结构体数组#include<stdio.h>structDate{intyear;intmonth;intday;}date[2]={{2020,12,15},{2020,12,16},};intmain(){//结构体数组//structDatedate[2]={//{2020,12,15},//{2020,12,16},//};//访问结构体数组的内容printf("date[0]:%d-%d-%d\n",date[0].year,date[0].month,date[0].day);printf("date[1]:%d-%d-%d\n",date[1].year,date[1].month,date[1].day);return0;}结构体指针结构体指针本质是指针,作用是指向一个结构体structDatedate={2020,12,15};//定义一个结构体structDate*pt;//定义结构体指针pt=&date;//指针指向结构体通过结构体指针访问结构体#include<stdio.h>structDate{intyear;intmonth;intday;};intmain(){//先定义该结构体structDatedate={2020,12,15};//定义该结构体指针structDate*pt;//结构体指针指向结构体pt=&date;//获取结构体值printf("year:%d",date.year);//结构体直接访问printf("month:%d",(*pt).month);//通过结构体指针访问,括号必须的(优先级问题)printf("day:%d\n",pt->day);//结构体指针箭头访问return0;}
宏定义的实质再牛逼的宏定义也是机械的替换宏定义结束不需要;(分号)#definePI3.14终止宏定义生效范围#definePI3.14代码块..#undefPI不带参数的宏定义#include<stdio.h>#definePI3.14intmain(){intr=6;printf("%d半径圆的面积%.5f",r,PI*r*r);return0;}带参数的宏定义比较两数字大小#include<stdio.h>#defineMAX(x,y)((x)>(y))?(x):(y)//这些括号是为了保证判断正确intmain(){intx=6,y=10;printf("%d%d-max->%d",x,y,MAX(x,y));return0;}求平方(有BUG)宏定义只是机械性替换,所以不能代替函数#include<stdio.h>#defineSQUARE(x)x*xintmain(){intx=6;printf("%d-平方->%d\n",x,SQUARE(x));printf("%d+1-平方->%d\n",x,SQUARE(x+1));return0;}
更灵活的内存管理方式在C语言中不存在其他高级语言的垃圾回收机制,在平常方式直接的声明变量,C语言不知道什么时候回收它,所以会一直占用内存空间.而更灵活的管理就是我们能在不使用该变量时就主动释放它。申请的空间不在使用,一定要释放(free(),虽然不是一定释放,但这是一个好习惯使用的库需要引入#include<stdlib.h>void*malloc(size_tsize);申请动态内存空间voidfree(void*ptr);释放动态内存空间void*calloc(size_tnmemb,size_tsize);申请并初始化内存空间为0void*realloc(void*ptr,size_tsize);重新分配内存空间申请空间&释放空间void*malloc(size_tsize);申请成功:返回一个指向申请的内存空间的指针,返回值是void所以可以转化成其他类型申请失败:返回NULL如果size参数设置为0,返回值也可能是NULL,但这并不意味着函数调用失败。voidfree(void*ptr);只能释放由malloc、calloc、realloc函数申请的空间,释放后该地址变为非法地址。参数指向将要释放的内存空间的地址没有返回值#include<stdio.h>#include<stdlib.h>//引入intmain(){//申明变量int*ptr,num=520;//申请内存空间ptr=(int*)malloc(sizeof(int));if(ptr==NULL){exit(1);}//内存申请失败//内存空间存入变量ptr=#//输出值printf("prt->%d",*ptr);//释放内存空间free(ptr);return0;}内存泄漏内存泄漏是程序一直占用内存,不释放,造成内存达到很高的使用量,严重造成计算机卡死,所以在不使用该空间时尽量释放掉。有两种情况会导致:隐式内存泄漏(即用完内存块没有及时使用free函数释放)丢失内存块地址申请并初始化&重新分配申请并初始化void*calloc(size_tnmemb,size_tsize);nmemb:指定分配的内存块个数size:指定每个内存块的大小,以字节为单位。#include<stdio.h>#include<stdlib.h>intmain(){//初始化变量int*ptr=NULL;//申请并初始化变量ptr=(int*)calloc(10,sizeof(int));if(ptr==NULL){exit(1);}//查看内容for(inti=0;i<10;++i){printf("%d",ptr[i]);};printf("\n");return0;}重新分配内存空间只能重新分配malloc()函数申请的空间void*realloc(void*ptr,size_tsize);重新分配内存空间参数*ptr:指向由先前调用malloc、calloc或realloc函数返回的内存空间如果传入NULL,相当于调用malloc(size)函数参数size:指定新的内存块空间大小,以字节为单位该函数将移动内存空间的数据并返回新的指针如果新分配的内存空间比原来的大,则旧内存块的数据不会发生改变;如果新的内存空间大小小于旧的内存空间,可能会导致数据丢失,慎用!#include<stdio.h>#include<stdlib.h>intmain(){//初始化变量int*ptr=NULL;//申请并初始化变量ptr=(int*)malloc(10*sizeof(int));if(ptr==NULL){exit(1);}//重新分配内存空间ptr=(int*)realloc(ptr,20*sizeof(int));printf("%llu",sizeof(ptr));ptr[12]=66666666;//赋值//查看内容for(inti=0;i<20;++i){printf("%d",ptr[i]);}return0;}
递归递归是一种编程思想,但是在实际的开发中除特定场景外,尽量不要使用递归使用递归求解阶乘#include<stdio.h>intfact(int);intfact(inti){longres=1L;//计算结果if(i>0){res=i*fact(i-1);}returnres;}intmain(void){longres=fact(30);printf("res=%ld",res);//输出long类型return0;}汉罗塔汉罗塔的C语言实现#include<stdio.h>voidhanoi(intn,charx,chary,charz);voidhanoi(intn,charx,chary,charz){if(n==1){printf("%c-->%c\n",x,z);}else{hanoi(n-1,x,z,y);printf("%c-->%c\n",x,z);hanoi(n-1,y,x,z);}}intmain(){intn;printf("请输入层数:");scanf("%d",&n);hanoi(n,'X','Y','Z');return0;快速排序#include<stdio.h>voidquick_sort(intarray[],intleft,intright);voidquick_sort(intarray[],intleft,intright){inti=left,j=right;//左右索引intpivot;//中间基准变量inttemp;//替换元素的临时变量pivot=array[(right+left)/2];while(i<=j){//遍历右边的小while(array[i]<pivot){i++;};//遍历左边的大while(array[j]>pivot){j--;};//左右大小交换if(i<=j){temp=array[i];array[i]=array[j];array[j]=temp;i++;j--;};}if(left<j){quick_sort(array,left,j);}if(i<right){quick_sort(array,i,right);}}intmain(void){intarray[]={33,12,4,8,45,56,78,9,56};//待排序数组intlength;length=sizeof(array)/sizeof(array[0]);//数组长度数组占用长度/其中一个元素长度quick_sort(array,0,length-1);for(intk=0;k<length;++k){printf("%d",array[k]);}putchar("\n");return0;}快速排序原理
局部变量局部变量只在那一个代码块能访问。#include<stdio.h>intmain(){inti=520;for(inti=0;i<3;i++){printf("for内部i:%d\n",i);};printf("外部的i%d",i);return0;}全局变量全局变量在程序启动就会创建,直到程序结束才会被销毁,所以会一直占用内存。在实际使用中,尽量避免使用全局变量,在维护时,会改一点而牵全身#include<stdio.h>intcount=0;//这就是全局变量inta(){returncount++;}intb(){returncount++;}intc(){returncount++;}intmain(){a();b();c();printf("count=%d",count);return0;}extern关键字当全局变量没有在开头定义,但在前面又需要使用,这时就需要使用extern了。#include<stdio.h>inta(){externcount;returncount++;}intcount=0;//这就是全局变量intb(){returncount++;}intc(){returncount++;}intmain(){a();b();c();printf("count=%d",count);return0;}
指针函数格式:返回类型*函数名(参数列表);不要返回局部变量的指针举个例子#include<stdio.h>char*get_Word(chars);char*get_Word(chars){charstr[]="Apple";//函数内的局部变量switch(s){case'A':returnstr;//无法返回这个strcase'B':return"Bad";case'C':return"Cat";default:return"None";}}intmain(){chari;printf("请输入一个字母:");scanf("%s",&i);printf("%s\n",get_Word(i));return0;}函数指针定义格式:返回值(*名称)(参数类型);#include<stdio.h>intsquare(intnum);intsquare(intnum){returnnum*num;}intmain(){intnum;int(*fp)(int);//定义函数指针printf("输入一个数字:");scanf("%d",&num);fp=square;printf("%d*%d=%d\n",num,num,(*fp)(num));printf("%d*%d=%d\n",num,num,*fp(num));return0;}头脑风暴涉及知识&实现功能涉及知识把函数指针作为函数的返回值。实现功能用户输入一个式子(1+5),自动判断符号,进行相应的运算。代码说明声明函数intadd(int,int);//加法函数intsub(int,int);//减法函数int(*select(char))(int,int);//符号选择,返回相应的计算函数intcalc(int(*)(int,int),int,int);//计算函数,传入一个函数指针,以及两个数字函数方法//加法函数intadd(intnum1,intnum2){returnnum1+num2;}//减法函数intsub(intnum1,intnum2){returnnum1-num2;}//符号选择int(*select(charop))(int,int){switch(op){case'+':returnadd;case'-':returnsub;}}//计算函数intcalc(int(*fp)(int,int),intnum1,intnum2){return(*fp)(num1,num2);}使用#include<stdio.h>intmain(){intnum1=0,num2=0;charop;int(*fp)(int,int);//接收函数指针printf("输入一个式子(1+1):");scanf("%d%c%d",&num1,&op,&num2);fp=select(op);//符号选择,返回函数指针printf("%d%c%d=%d",num1,op,num2,calc(fp,num1,num2));return0;}
爬虫目标爬虫字段:职位名称、职位URL、职位要求(逗号分隔csv文件)完整代码:https://github.com/bigdataboy2020/job51_spider爬虫结构分层结构模块化结构,减少耦合使用模块:gevent、lxml、typing、requests项目说明操作类(job51.py)主要包含发送请求的方法defget_data():请求获取一页所有的职位名称和职位的URLdefget_job_explain():获取一个职业的要求classJob51(object):#总页数。第一次获取会设置真实页数all_page=9999#正在获取的页数page_num=1#Job_name和url列表job_url_data:List[Tuple[str,str]]=list()#职位详情页列表[name,url,说明]job_explain:Tuple[str,str,List[str]]=Nonesession=requests.session()defget_data(self,page_num:str,city:List[str],search:str)->bool:"""#获取该关键词所有的职位名称与链接:parampage_num:当前页数:paramcity:城市列表:paramsearch:搜索词:return:"""returnTruedefget_job_explain(self,job_name:str,url:str)->bool:"""#获取一个职业的要求:paramjob_name:职位名称:paramurl:职位的详情页:return:"""returnTrue中间处理函数(handle.py)主要包含对请求的数据进行处理的函数及相应错误提示defget_data():获取所有页数的职位名称及职位URLdefget_job_explain():获取单个职位的详细信息msg={1:"获取成功",-1:"获取职位链接获取失败",-2:"获取职位详情失败"}classHandle(object):job=Job51()job_num=0#job总个数job_explain:Tuple[str,str,List[str]]=None#Job_name和url列表job_url_data:List[Tuple[str,str]]=list()def__init__(self,city:List[str],search:str):self.get_data(city,search)defget_data(self,city:List[str],search:str):"""#获取所有页数的职位名称及URL:paramcity:城市列表:paramsearch:搜索词:return:"""#获取职位详情链接whileself.job.page_num<=self.job.all_page:ifnotself.job.get_data(str(self.job.page_num),city,search):return-1self.job.page_num+=1#总Job个数self.job_num=self.job.job_url_data.__len__()self.job_url_data=self.job.job_url_datadefget_job_explain(self,job_name:str,job_url:str):"""#获取单个职位的详细信息:paramjob_name:城市名称:paramjob_url:城市url:return:"""job_url_data=self.job.job_url_dataself.job_len=job_url_data.__len__()ifnotself.job.get_job_explain(job_name,job_url):return-2self.job_explain=self.job.job_explainreturn1工具类模块(utils.py)只有一个写入结果的函数#文件写入deflog(msg:Tuple[str,str,List[str]]):withopen("msg.csv","a",encoding="utf-8")asf:f.write(f"{msg[0]},{msg[1]},{msg[2]}\n")主文件(main.py)主要启动文件,协程在这支持
函数传值如果函数只是传入变量值(不是数组),那么传入变量值的作用域只有该函数内部。#include<stdio.h>voidswap(intx,inty);voidswap(intx,inty){inttmp;//存储临时变量printf("swap传入值交换前x=%d,y=%d\n",x,y);tmp=x;x=y;y=tmp;printf("swap传入值交换后x=%d,y=%d\n",x,y);}intmain(){intx=111,y=222;printf("main传入指针互换前x=%d,y=%d\n",x,y);swap(x,y);printf("main传入指针互换后x=%d,y=%d\n",x,y);return0;}函数传址(指针)函数如果传入指针(地址),在函数内部改变指针,外部会跟着改变。#include<stdio.h>voidswapzz(int*x,int*y);/**交换数字*/voidswapzz(int*x,int*y){printf("swap传入指针交换前x=%d,y=%d\n",*x,*y);inttmp;tmp=*x;*x=*y;*y=tmp;printf("swap传入指针交换后x=%d,y=%d\n",*x,*y);}intmain(){intx=111,y=222;printf("main传入指针互换前x=%d,y=%d\n",x,y);swapzz(&x,&y);printf("main传入指针互换后x=%d,y=%d\n",x,y);return0;}函数传入数组函数传入数组,本质是传入一个指针。#include<stdio.h>voidget_arr(intarr[3]);voidget_arr(intarr[3]){inti;arr[1]=520;printf("get_arr方法内");for(i=0;i<3;i++){printf("%d",arr[i]);};}intmain(){intarr[3]={1,2,3};get_arr(arr);inti;printf("\nmain方法内");for(i=0;i<3;i++){printf("%d",arr[i]);}return0;}
函数定义格式返回类型函数名(参数类型参数变量){函数方法体}//定义函数intsum_num(intnum){intres=1;returnres;}函数使用#include<stdio.h>//首先声明函数(有些版本C语言,可以不用)intsum_num(intnum);//定义函数intsum_num(intnum){intres=1;//循环阶乘for(num;num>1;num--)res=num*res;returnres;}intmain(){intnum=10;//使用函数intres=sum_num(num);printf("%d的阶乘结果:%d\n",num,res);return0;}函数的可变参数使用可变参数需要引入:#include<stdarg.h>#需要#include<stdarg.h>头文件里面的这几个东西-va_list-va_start-va_arg-va_end定义可变参数之前的intnum,是可变参数的总长度一般这是实现原理#include<stdio.h>#include<stdarg.h>//声明函数intsum_num(intnum,...);//定义可变参数的函数intsum_num(intnum,...){/*该函数实现可变函数的值相加。*/inti,sum=0;va_listvap;//定义接收可变参数的列表va_start(vap,num);//开辟接收可变参数的空间num是可变参数的个数//利用循环取出可变参数for(i=0;i<num;i++){sum+=va_arg(vap,int);//判断参数逻辑}//关闭空间va_end(vap);returnsum;}intmain(){intres=sum_num(5,1,2,3,4,5);printf("相加结果:%d\n",res);}
常量常量一般这样定义://不加分号#defineLOVE520#defineA'a'#definePI3.14const修饰的指针变量const靠近谁就锁定谁,靠近*p,就锁定*p。intnum=520;intconst*p=##//锁定*p为只读,p能变int*constp=##//锁定p为只读,*p能变const修饰的指针变量有三种常见情况地址值能变,值不能变#include<stdio.h>intmain(){intnum=520;intconst*p=##//锁定*p为只读,p可变printf("p=%d\n",*p);//解引用指针变量,获取值p=(constint*)1314;//等价于p=1314;重新赋值printf("p=%d\n",p);return0;}地址值不能变,值能变#include<stdio.h>intmain(){intnum=520;int*constp=##//锁定p为只读,*p可变printf("p=%d%p\n",*p,p);//解引用指针变量,获取值*p=1314;//重新赋值printf("p=%d%p\n",*p,p);return0;}地址值&值都不能变实际中很少用到intmain(){intnum=520;constint*constp=##//锁定p、*p为只读return0;}
void指针viod指针称为通用指针,就是可以指向任意类型的数据,就是任意类型的指针都可以赋值给void指针。#include<stdio.h>intmain(){//声明不同类型的变量inti=123;char*c="ABC";//声明void指针void*v;v=i;//把int类型数据赋值给void类型数据printf("%d\n",(int*)v);v=c;//把char类型数据赋值给void类型数据printf("%s\n",(char*)v);return0;}NULL指针NULL用于给指针和对象,指向一个不被使用的地址,避免出现野指针(没有初始化的指针,而出现的随机值);而\0表示字符串的结尾。#include<stdio.h>intmain(){int*a;//野指针int*b=NULL;//作为指针变量初始化printf("%d\n",a);printf("%d\n",b);return0;}