C语言包含两部分:c语法和c标准库,除了标准库里的函数可以用以外,还可以自定义函数
1. C标准库及库函数
1.1. 库函数使用示例
1.1.1. 随机函数
生成某一范围内的随机数,srand和rand配合使用产生伪随机数序列。
rand函数在产生随机数前,需要系统提供的生成伪随机数序列的种子,rand根据这个种子的值产生随机数。如果种子没有变化,每次调用rand函数生成的伪随机数序列都是一样的。
- rand:
- 声明:
int rand(void)
,产生一组[0, RAND_MAX]的伪随机数, - 返回值:int,所在的声明文件:stdlib.h
- 声明:
- srand:
- 声明:
void srand(unsigned int seed)
,初始化随机数生成器;参数unsigned int,伪随机数生成算法使用的整数值作为种子 - 返回值:void,所在的声明文件:stdlib.h
- 声明:
#include <stdlib.h>
#include <time.h>
// RAND_MAX = 0x7fff // 01111111 11111111 32767 short
int main(void)
{
srand(time(NULL)); // 使用当前时间作为种子,使得每次运行程序时生成的随机数序列不同。
for (int i = 0; i < 10; i++)
{
printf("%d\t", rand() % 100); // 58 73 18 16 39 12 87 73 91 70. 100以内生成10个随机数
}
return 0;
}
1.1.2. 获取时间函数
localtime:
- 声明:
struct tm * localtime(const time_t * timer)
,将 time_t 转换为本地时间的 tm 结构,参数:time_t* - 返回值:struct tm*返回从1990.1.1 00:00:00累计的秒数,所在的声明文件:stdlib.h
int main()
{
time_t rawtime;
rawtime = time(NULL);
struct tm *timeinfo;
timeinfo = localtime(&rawtime);
printf("year: %d\n", timeinfo->tm_year + 1900);
printf("month: %d\n", timeinfo->tm_mon + 1);
printf("day: %d\n", timeinfo->tm_mday);
printf("hour: %d\n", timeinfo->tm_hour);
printf("minute: %d\n", timeinfo->tm_min);
printf("second: %d\n", timeinfo->tm_sec);
return 0;
}
1.2. 常用库函数
double sqrt(double x)
:计算x的平方根double pow(double x, double y)
:计算x的y次幂double cell(double x)
:向上取整double floor(double x)
:向下取整int toupper(int x)
:将x转大写int tolower(int x)
:将x转小写int rand(void)
:产生一个随机数void srand(unsigned int seedS)
:产生一个随机数种子
int main()
{
double data = 9.0;
double root = sqrt(data);
printf("%lf\n", root); // 3.000000
double power = pow(data, 2);
printf("%lf\n", power); // 81.000000
printf("%f\n", ceil(3.14)); // 4.000000
printf("%f\n", floor(3.14)); // 3.000000
for (char ch = 'a'; ch < 'z'; ch++)
{
putchar(toupper(ch)); // ABCDEFGHIJKLMNOPQRSTUVWXY
// putchar(ch - 32);
}
printf("\n");
for (char i = 'A'; i < 'Z'; i++)
{
putchar(tolower(i)); // abcdefghijklmnopqrstuvwxy
// putchar(i + 32);
}
return 0;
}
2. 自定义函数
自顶向下,逐步细化
最上面的main代表了一个函数,下面的abcdefghig都是一个个函数
2.1. 语法格式
自定义函数必须满足以下格式
2.2. 调用方法
2.2.1. 先声明后使用
double myMax(double x, double y)
{
double m;
m = x > y ? x : y;
return m;
}
int main()
{
double a = 5.6;
double b = 7.8;
double maxDouble = myMax(a, b);
printf("两者中的最大值为:%f\n", maxDouble);
return 0;
}
2.2.2. 前向声明
若是定义在前,调用在后,则正常。如果定义在后,调用在前,编译器会complain
// 入参中如果没有参数,可以使用void表示无入参。通常省略
// int *func(void); // int *func();
// 如果没有返回值,即返回类型是void,void不可省略;若省略,系统会默认返回int
// void func2();
// 进程空间
// 函数在被调用之前,其内所有的变量尚未开辟空间。空间的开辟始于函数调用
// 空间的开辟起始于函数调用,空间消失结束于函数调用完毕
// 传值
// void func(int a);
void func(int a);
int main()
{
int a = 10;
// 通过传值的方式,达不到修改变量a的内容的目的,但是地址对于不同的作用域来说,是开放的
func(a);
printf("main a = %d\n", a); // main a = 10
return 0;
}
void func(int a)
{
a++;
printf("func a = %d\n", a); // func a = 11
}
3. 传值与传址
传值与传址,本质上都是传递一个数值
传递地址,可能改变地址所对应的原数值,因为地址对于不同作用域的函数来讲是公开的。
void mySwap(int *pa, int *pb);
int main()
{
int a = 3, b = 5;
printf("&a = %#x, &b = %#x\n", &a, &b); // &a = 0x6b513358, &b = 0x6b513354
printf("a = %d, b = %d\n", a, b); // a = 3, b = 5
mySwap(&a, &b);
printf("a = %d, b = %d\n", a, b); // a = 5, b = 3
return 0;
}
void mySwap(int *pa, int *pb)
{
int temp = *pa;
*pa = *pb;
*pb = temp;
}
4. 函数调用
4.1. 实参与形参
4.1.1.形参
在定义或声明函数中指定的形参,在未出现函数调用时没,不占内存中的存储单元。只有在发生函数调用时,形参才被分配内存空间。在调用结束时,行参所占的内存单元也被释放
4.1.2. 实参
实参可以是常量、变量或表达式,但要求他们有确定的值,在调用时将实参的值赋给行参
4.2. 普通调用
函数间可以互相调用的,常见的有平行调用和嵌套调用
void foo(); // 前向声明
void func()
{
printf("void func()\n");
foo(); // 嵌套调用
}
void foo()
{
printf("void foo()\n");
}
int main()
{
foo();
return 0;
}
5. 函数递归
5.1. 递归定义
函数自身调用自身,或是间接调用自身
5.1.1. 猴子吃桃子问题
猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个。第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第10天早上想再吃时,见只剩下一个桃子了。求第一天共摘了多少?
int peachCount(int day)
{
if(day == 10)
return 1;
else
return (peachCount(++day)+1)*2;
}
5.2. 递归小结
5.2.1. 书写结构
递归返回 func(递归条件)
{
if(递归终止条件)
终止处理;
else
func(趋于递归终结的条件);
}
5.3. 递归与循环论述
递归和循环的共同点:有起点和终点,重复做同样的事情,所以很多时候,两者可以相互转换。
递归内存消耗大,容易导致栈溢出,所以能用迭代解决的问题,不要用递归来完成