程序员的知乎:http://stackoverflow.com/
笔记6
#include <stdio.h>
#define _CRT_SECURE_NO_WARNINGS
#include <math.h>
#include <string.h>
#include <time.h>//调用时间函数时引用;返回的是函数值是time_t类型(长整型)
#include <stdlib.h>//使用rand时需引用的头文件;
函数:库函数和自定义函数
1.库函数:C语言本身提供给我们的函数;
库函数网站:http://www.cplusplus.com
文献网站:zh(中文版)http://en.cppreference.com;
//常用:(分类)
IO函数 (输入输出函数 网站中stdio.h往下的都是)
字符串操作函数(strlen....)
字符操作函数(大写转小写,小写转大写)
内存操作日期(memset....)
时间/日期函数(time....)
数学函数 (sqrt.....)
其他库函数
rg
1.strlen---string length--字符串求长度
2.strcpy---string copy---字符串拷贝
//拷贝的时候连最后的'\0'都被拷贝了;
//char * strcpy(char * destination , const char * sorce);---最后返回的是个地址
eg:
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[]="xswlnzym";
char arr2[]="##########";
strcpy(arr2,arr1);//strcpy(放到哪里,拷贝谁);
printf("%s\n",arr2);
return 0;
}//因为strcpy拷贝的时候,最后的'\0'(字符串结束标志),所以拷贝到arr2后,结果只有arr1的字符串;
//如果源头的字符串比目的地的字符串还要长,一定会溢出,但是这是个bug,所以要保证目的地比源头的字符串要长
3.memset---memory set--内存设置
//void * memset (void * ptr,int value,size_t num);
//把ptr所指向的那块空间之前的内容设置成我们指定的value值;
int main()
{
char arr[]="hello world!";
memset(arr , '*' , 5);
printf("%s\n",arr);
return 0;
}//将arr所指的那块空间(hello world!)的前5个字符改成*;所以 最后打印的结果应为***** world!;
//存储的时候存储的是ASCII码值,所以输入'*'于返回的int值不冲突;
2.自定义函数:自定义函数和库函数一样,有返回类型、函数名和函数参数;但是不一样的是,这些都是我们自己来设计。
函数的组成:
ret_type fun_name(part1,part2,....)
{
statement;//函数体:交代函数的实现
}
返回类型+函数名+(函数参数)
eg:获取两个数的最大值
int get_max(int x,int y)
{
if(x>y)
return x;
else
return y;
}//定义函数
int main()
{
int a=2;
int b=5;
int max=0;
max=get_max(a,b);
printf("max=%d\n",max);
return 0;
}
用函数交换两个数的值//swap--交换
//当实参传给形参的时候,形参其实是实参的一份临时拷贝,对形参的修改是不会影响实参的。
void Swap(int x,int y)
{
int temp;
temp=x;
x=y;
y=temp;
}
void Swap2(int *pa,int *pb)
{
int temp=0;
temp=*pa;//相当于把*pa所指的那个数据a放到temp中;
*pa=*pb;//然后把*pb所指的那个数据放到*pa所指的那个地方的空间去;
*pb=temp;//然后再把放到temp中的数据放到*pb所指的那个地方中;
}
int main()
{
int a=2;
int b=5;
Swap(a,b);
printf("a=%d,b=%d\n",a,b);//交换前的a,b;
Swap2(&a,&b);//由于函数里面x和y的地址与此处a,b的地址不一样,但是有了和a,b一样的内容,单单只用swap函数无法实现我们想要的结果,所以要把地址传出去,进行远程调用
printf("a=%d,b=%d\n",a,b);//交换后的a,b;
return 0;
}
函数参数
1.实际参数:真实传递给函数的参数
eg:在int main()函数中的Swap(a,b);和Swap2(&a,&b);--a,b,&a,&b均为实参
//实参可以是:变量、常量、表达式、函数等
2.形式参数:函数名后括号中的变量
eg:在int main()函数之外的int get_max(int x,int y);void Swap(int x,int y);void Swap2(int *pa,int *pb)等等括号里面的都是形参
//只有在函数被调用的过程中才实例化(分配存储单元),形式参数当函数调用完成之后就自动销毁了,因此形式参数只在函数中有效。
函数的调用:
传值调用:函数的形参和实参分别占用不同的内存块,对形参的修改不会影响实参;
传址调用:
//传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用方式;
//这种传参方式可以让函数和函数外面的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量;
什么时候传值,什么时候传址?
//当你在函数内部不改变传进来的值时,就用传值;
//如果你要对传进函数内的值进行改变,那么就要传址;
练习:
1.打印100-200之间的素数
is_prime()---专门用来判断素数
#include <math.h>
#include <stdio.h>
//是素数返回1,不是返回0;
int is_prime(int x)
{ int j=0;
for(j=2;j<=sqrt(x);j++)
{if(x%j==0)
return 0;}
return 1;//当j==x时,自动跳出循环;
}
//return 0一执行,整个函数就结束,比break的功能强;
int main()
{
int i=0;
for(i=100;i<=200;i++)
{
if(is_prime(i)==1)
printf("%d ",i);
}
return 0;
}
写一个函数判断一年是否为闰年
int is_leap_year(int x)
{ if(x%4==0 && x%100!=0||x%400==0)
return 1;
else return 0;
}
int main()
{
int n=0;
int count=0;
for(n=1000;n<=2000;n++)
{if(is_leap_year(n)==1)//判断是否为闰年
printf("%d ",n);
count++;}//打印一次,count+1;
printf(" count= %d\n",count);
return 0;
}
写一个函数实现一个整形有序数组的二分查找
//二分查找,如果找到了返回这个数的下标,找不到的返回-1;
int binary_search(int brr[],int n,int sz)
{
int left=0;
int right=sz-1;
int mid=0;
while(left<=right)//注意一定要有=;
{
mid=(left+right)/2;//必须在循环内部,因为每进一次循环,都要求;
if(n>brr[mid])
{left=mid+1;}
else if(n<brr[mid])
{
right= mid-1;}
else
return mid;
}
return -1;
}
int main()
{
int k=6;
int arr[]={1,2,3,4,5,6,7,8,9,10};
int m=sizeof(arr)/sizeof(arr[0]);
int ret=binary_search(arr,k,m);
if(ret==-1)
printf("找不到指定的数字\n");
else printf("找到了,下标是: %d\n",ret);
return 0;
}
//int ret=binary_search(arr,k);数组传过去的是数组首个元素的地址
// sz=sizeof(brr)/sizeof(brr[0]);在函数内计算的sz是指针的大小,即sz=4字节/4字节;
//所以就需要把求sz的大小在函数外计算;
写一个函数,没调用一次这个函数,就会讲num的值加1;
void fun(int* p)
{
(*p)++;//由于++的优先级比*高,所以*p++这样的写法是错误的;
}
int main()
{
int num=0;
fun(&num);//由于需要在函数内部改num的值,所以需要传地址;
printf("num=%d\n",num);
fun(&num);
printf("num=%d\n",num);
fun(&num);
printf("num=%d\n",num);
return 0;
}
函数的嵌套调用和链式访问
1.函数的嵌套调用
eg:
我想打印三次hehe
#include <stdio.h>
void new1()
{
printf("hehe\n");
}//定义函数1
void new2()
{
int i=0;
for(i=1;i<=3;i++)
{ new1();}
}//定义函数2,并调用new1
int main()
{
new2();
return 0;
}//调用mew2;
2.链式访问:把一个函数的返回值作为另外一个函数的参数;
eg:打印一个字符串的长度;
写法1:int main()
{
int len=0;
len = strlen("abc");
printf("%d\n",len);
return 0;
}
写法2:
int main()
{
printf("%d\n",strlen("abc"));
return 0;
}//链式访问;把strlen()的值打印出来;
eg:
int main()
{
printf("%d",printf("%d",printf("%d",43)));
return 0;
}结果为432
//printf先打印内部的数,而它返回打印的字符串的个数;
函数的声明与定义:如果把定义的函数放在主函数的后面,那么在主函数的前面需要进行函数声明;
eg:
int add(int ,int );//由于程序是从前往后进行的,在主函数中进行到add时,它会自动往前找;
int main()
{
int a=20;
int b=30;
int sum=0;
sum=add(a,b);
printf("%d\n",sum);
return 0;
}
int add(int x,int y)
{
int z=x+y;
}
函数的声明与定义的真正用法:
将加法程序生成一个模块:那么就要在同一个工程中创建一个add.h的头文件,创建一个add.c的源文件;
在add.h中放置函数声明int add(int ,int );
在add.c中放置函数的实现(函数定义):
int add(int x,int y)
{int z=x+y;}
那么当你在同一个工程中调用add模块时,在主函数的前面声明
#include "add.h"就可以直接调用加法模块
在add.h头文件创建函数声明的格式如下:
#ifndef _ADD_H_ //if no define _ADD_H_;如果没有定义这个_ADD_H_
#define _ADD_H_ //那么我现在定义一下_ADD_H_
int add(int x,int y);
#endif
//#include "add"调用调用的是,从定义开始到定义结束;
//如果再次被调用 就会在第一行判断是不是被定义过,若果定义过,就直接使用;
//防止一个头文件被引用多次;
函数递归:一个函数自己调用自己叫递归;
//它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归只需少量的程序就可以描述出解题过程所需要的多次重复计算。
递归的两个必要条件:
1.存在限制条件,当满足这个限制条件的时候,递归便不再继续;
2.每次递归调用之后越来越接近这个限制条件;
eg:
int main()
{
printf("hhhhhh\n");
main();
return 0;
}//对于main函数的调用一次,就要在栈区中申请一个空间,最后栈空间被用完了,就会出现栈溢出stack overflow;
//会一直打印hhhhh,但是最后会出现栈溢出;
C语言期间内存大概分为:
栈区:局部变量,函数形参
堆区:动态开辟的内存(malloc,calloc)
静态区:全局变量,static修饰的变量
练习1:接收一个整型数,按照顺序打印它的每一位。例如:1234, 输出1 2 3 4.
void print(int n)
{
if(n>9)//递归不可缺少的必要条件;
{print(n/10);}
printf("%d ",n%10);
}
int main()
{
unsigned int num=0;
scanf("%d",&num);//1234;
print(num);
return 0;
}
练习2:编写函数不允许创建临时变量,求字符串的长度;
1.当在函数中创建临时变量时:
int my_strlen(char* str)
{
int count=0;//count--临时变量
while(*str !='\0')
{
count++;
str++;
}
return count;
}//由于传参的是一个数组的首地址,所以定义类型为char* 的指针变量str(代表首地址);
//去判断*str(指str所指的那个地址里面的字符)是不是等于'\0'(字符串结束标志),不是就计数1次,然后指针后移一位;
int main()
{
char arr[]="abcd";
int len= my_strlen(arr);
printf("len = %d\n",len);
return 0;
}
2.当不在函数中创建临时变量时;
int my_strlen(char* str)
{
if(*str !='\0')//必要条件;
{
return 1+my_strlen(str+1);}
else
return 0;
}
//把大事化小
//my_strlen("abcd");
//1+my_strlen("bcd");
//1+1+my_strlen("cd");
//1+1+1+my_strlen("d");
//1+1+1+1+my_strlen("");
//4;
int main()
{
char arr[]="abcd";
int len= my_strlen(arr);
printf("len = %d\n",len);
return 0;
}
迭代:重复的去做一件事情,和循环很类似;可以叫迭代,但是不能叫循环;
练习3:求n的阶乘;
法1:迭代来求
int fac(int x)
{
int i=0;
int m=1;
for(i=1;i<=x;i++);
{return m= m*i;}
}
int main()
{
int i=0;
int n=0;
int sum=1;
scanf("%d",&n);
int sum = fac(n);
printf("sum = %d\n",sum);
}
法2:递归来求
int fac1(int x)
{
if(x==1)
return 1;
else if(x==0)
return 0;
else
return x*fac1(x-1);
}
int main()
{
int i=0;
int n=0;
int sum=0;
scanf("%d",&n);
sum = fac1(n);
printf("sum = %d\n",sum);
return 0;
}
练习4:求第n个斐波那契数(迭代法比递归法更好)
//斐波那契数列:1 1 2 3 5 8 13 21 34 55.....(前两个数之和等于第三个数)
//公式:fib(n)=1(n<2) fib(n)=fib(n-1)+fib(n-2);
1.递归法:(只要知道公式,递归就可以很简单的写出来)
int fib(int x)
{
if(x<=2)
return 1;
else
return fib(x-1)+fib(x-2);
}
int main()
{
int ret=0;
int n=0;
scanf("%d",&n);
ret=fib(n);
printf("%d\n",ret);
return 0;
}
//但是效率很低,你可以试试电脑要多久才可以求出来,第50个斐波那契数
//由于进行了大量的重复计算,所以效率很低
2.迭代法:
int fib(int x)
{
int a=1;
int b=1;
int c=1;//由于当x<2时,值都为1,无法进入循环,所以设置c=1最合适
while(x>2)
{
c=a+b;//求出第三个值
a=b;//然后将第二个值赋给a
b=c;//将第三个值赋给b,以此类推;
x--;//表示我们算出第x个值执行的次数
}
return c;
}
int main()
{
int ret=0;
int n=0;
scanf("%d",&n);
ret=fib(n);
printf("%d\n",ret);
return 0;
}