函数:
一段具有某项功能的代码,是C语言中管理代码的单位。
把代码封装成一个个函数,可以方便的管理和调用代码。
函数分类:
标准库函数:C语言标准为委员会为C语言以函数形式提供的一些基础功能,被封装在libc.so库中,使用时需要包含头文件,函数名(参数)即可以调用。int isalnum(int c);功能:当以是数字、字母字符返回真int isalpha(int c);功能:当c是字母时返回真int isdigit(int c);功能:当c是数字字符时返回真int islower(int c);功能:当c是小写字母时返回真int isupper(int c);功能:当c是大写字母时返回真int abs(int num);功能:返回num的绝对值以下函数被封装在libm.so库文件中。double sqrt(double x);功能:返回x的平方根double pow(double x, double y);功能:返回x的y次方double floor(double arg);功能:返回小于等于arg的最大整数double ceil( double num );功能:返回大于等于num的最小整数double fabs(double arg);功能:返回arg的给对值void srand(unsigned seed);功能:设置随机种子int rand(void);功能:返回一个随机数rand()%(b-a)+a time_t time( time_t *time);功能:返回自19701月1日 00:00:00 到当前时间过了多少秒。int system(const char *command);功能:调用系统命令练习4:获取10个[100,1001)的随机数。for(int i=0;i<10;i++){srand(time(NULL));rand()%(1001-100)+100; //相当于[100,1000]}练习5:随机出一组双色球彩票号。6红:1~331蓝:1~16
#include <stdio.h>
#include <stdlib.h>
#include <time.h>int main(int argc,const char* argv[])
{time_t t;time(&t); /*获取time_t类型的当前时间*/srand(time(NULL));printf("红:");for(int i=0; i<6; i++){printf("%d ",rand()%(33-1+1)+1);}printf("\n蓝:");printf("%d ",rand()%(16-1+1)+1);/*用gmtime将time_t类型的时间转换为struct tm类型的时间按,//没有经过时区转换的UTC时间然后再用asctime转换为我们常见的格式 Fri Jan 11 17:25:24 2008*/printf("\n---- %s\n",asctime(gmtime(&t)));//转化成常见字符串printf("---- %s\n",ctime(&t));return 0;
}
系统函数:是操作系统以函数接口形式提供的一功能,这些功能包括:内存管理、信号处理、文件IO、文件管理、 进程管理、进程通信、线程管理、线程同步、网络通信。第三库函数:一些开源或收费的第三代码。glog 日志记录JSON 序列化反序列化MD5 验证XML 配置文件解析 自定义函数:为了更好的管理代码、减少冗余把代码封装成函数。函数声明:函数声明的目的是为了告诉其它代码函数的调用格式。返回值类型 函数名(类型1 变量名1,类型2 变量名2,...);1、C语言中函数名一般全部小写,用下划线分隔。2、如果不需要参数建议写void,不要空着。3、如果不需要返回值就写void。隐式声明:当调用函数时没有定义,编译器会猜测函数的格式,参数列表会根据调用时提供的数据(实参)猜测,返回值会猜成int类型。函数定义:返回值类型 函数名(类型1 变量名1,类型2 变量名2,...){函数休;return val;}函数调用:函数名(实参);返回值会放在调用的位置,可以立即显示,也可以用变量记录下来。
函数传参:
1、形参变量属于它所在的函数,出了该函数就不能再用。
2、实参与形参之间是以赋值的形式传递数据的(值传递)。
3、return 其实是把数据放置到一个公共区域(函数和函数调用者),如果不写return语句,该区域中就是一个随机的垃圾数据。
4、数组作为函数的参数时,长度会丢失,需要额外增加一个变量把数组的长度也传递过去。
5、数组的传递是"址传递",函数和函数调用者可以共享数组。
练习1:实现函数,找出数组中的最大值。
#include <stdio.h>
#include <stdlib.h>int max_arr(int arr[],int len)
{int max = arr[0];for(int i=1; i<len; i++){if(arr[i] > max){max = arr[i];}}return max;
}int main(int argc,const char* argv[])
{int arr[10] = {};for(int i=0; i<10; i++){arr[i] = rand() % 1000;printf("%d ",arr[i]);}printf("\n%d\n",max_arr(arr,sizeof(arr)/sizeof(arr[0])));
}
练习2:实现一个函数,对数组进行排序。
练习3:实现一个函数,查找数组是否存在某个值,如果存在返回该值在数组中的下标。
int find_arr(int arr[],int len,int val);
#include <stdio.h>
#include <stdlib.h>void sort(int arr[],int len)
{for(int i=0; i<len-1; i++){for(int j=i+1; j<len; j++){if(arr[i] > arr[j]){int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;}}}
}int find(int arr[],int len,int val)
{/*for(int i=0; i<len; i++){if(arr[i] == val){return i;}}*/int l = 0 , r = len-1;while(l<=r){int p = (l+r)/2;if(arr[p] == val){return p;}if(arr[p] > val){r = p-1;}else{l = p+1;}}return -1;
}
int main(int argc,const char* argv[])
{int arr[10] = {};for(int i=0; i<10; i++){arr[i] = rand() % 100;printf("%d ",arr[i]);}printf("\n");sort(arr,10);for(int i=0; i<10; i++){printf("%d ",arr[i]);}printf("\n%d\n",find(arr,10,49));
}
设计函数的准则:
1、一个函数最好只解决一个问题,这样可以降低出错率,提高可读性。
2、最好不依赖其它函数(降低耦合度)。
3、数据由调用者提供,结果也样返回给调用者(通用性)。
4、要考虑调用者提供的非法数据,可以通过返回值方式告诉调用者,或者把可能出现的情况在注释中写明。
进程映像:
程序:存储磁盘上的可执行文件(二进制文件、脚本文件)。
进程:正在系统中运行的程序。
进程映像指的就是进程的分布情况:
text 代码段 存储是二进制指令,常量数据,权限是只读,强制修改会产生段错误。
data 数据段 初始化过的全局变量
bss 静态数据段 未初始化的全局变量,程序运行时会被清理为0。
stack 栈 局部变量、块变量 会随着程序运行不断申请、释放 由系统管理
heap 堆 由程序员手动管理 特点就是够大
局部变量和全局变量:
局部变量:定义在函数内stack 函数调用开始到函数执行结束只能在函数内使用
全局变量:定义在函数外data(初始化)或bss(未初始化) main运行前定义完成程序结束才释放程序的任何位置都可以使用
局部变量可以和全局变量同名,但会屏蔽同名的全局变量,建议全局变量首字母大写。
存储介质:
按数据读取速度由慢到快:
硬盘-》内存-》高级缓存-》寄存器
速度上,寄存器>cache>RAM>ROM
类型限定符:
auto 用于定义自动分配、释放内存的变量(局部变量),不加就代表加。注意:全局变量不能用它修饰。C11 标准中用于自动类型识别auto num = 3.14;
extern声明变量,意思是此变量在别处已经定义,请放心使用。但只临时满过编译时间,链时如果找不到依然会报错。
static改变存储位置:改变局部变量的存储位置,由stack改为data或bss。被它修饰过程的局部变量也叫静态局部变量。延长生命周期:延长局部变量的生命周期。限制作用域:限制全局变量、函数只能在本文件内使用。可以全局变量、函数命名冲突,也可以防止被别人调用。
const"保护"变量不被显式修改。但是,如果补初始化过的全局变量、静态局部变量被const修饰,存储位置就会变成text。volatile如果变量值没有显式的修改,在使用这个变量时不从内存中读取,而继续使用上次读取的结果。变量被volatile修饰后,每次使用到这个变量时,都会从内存中读取。一般硬件编程时或多线程编程时使用。 volatile int num = 10;if(num == num){}register申请把变量的存储介质由内存改为寄存器,但由于寄存器有限不一定百分百成功。注意:寄存器变量不取地址。typedef类型重定义,定义变量时如果前面加上typedef变量名就变了这种类型(注意不是替换关系)。
函数递归:
函数自己调用自己的分行叫递归,会产生死循环。
递归可以实现分治这种算法,就是把一个复杂的大问题,分解成若干个相同的小分问题,直到问题全部解决。1、出口
2、解决一个小问题
3、调用自己如果计算第N项斐波那切数列。递归函数每调用一次都会在栈内存产生一份自己的拷贝,直到达到出口,才一层释放,因此使用递归非常耗费内存,与循环相比速度非常慢,能用循环解决的问题不要使用递归。递归优缺点:1、耗费内存、速度慢2、就是好理解、思路清晰。3、可以解决非线性的执行过程。
小练:
1、使用递归模拟汉诺塔的移动过程。2、全排列0~9。3、输入一个整数,计算0~9每个数字出现的次数。0 1 2
0 2 1
1 0 2
1 2 0
2 0 1
2 1 0