标准C基础知识五
函数的形参和局部变量在调用时与上次调用时的值无关;形参每次都会被覆盖,局部变量每次调用完都会被释放。
静态局部变量(在多次调用时并不释放以前的分配空间)与上次调用时的值有关。
值传递
vi byvalue.c
#include <stdio.h>
void swap(int a,int b)//交换两个变量的值;也可以使用异或来进行交换,但要考虑溢出
{
printf("in:m=%d,n=%d\n",m,n);
printf("a:%p,b:%p\n",&a,&b);
int t=a;
a=b;
b=t;
printf("out:m=%d,n=%d\n",m,n);
}
int main()
{
int m=10,n=20;
printf("m:%p,n:%p\n",&m,&n);
swap(m,n);
printf("m=%d,n=%d\n",m,n);
return 0;
}
//程序从main主入口运行后,m=19,n=20;
//调用swap(m,n)函数;把m=19值复制给a;n=20的值复制给b;
//通过swap后,a和b的值互换,a=20;b=19;但m,n的值不变
//也可以通过获取变量的地址来进行比较看是否同一个变量或对象;&a
编译gcc byvalue.c
运行a.out
结果:
in:a=10,b=20
out:a=20,b=10
m=10,n=20
结构作为函数的形参
#include <stdio.h>
typedef struct date{
int year;
int month;
int day;
}date;
date input ()//void input (date d)--->理解形参实参在调用的时候是否同一
//要确认形参是需要的
//在C语言中,形参为空时,表示形参个数或类型不定。
//如何表示不传参数呢?(void)
{
date d;
printf("d of function input:%p\n",&d);
printf("请输入年月日");
scanf("%d%d%d",&d.year,&d.month,&d.day);
return d;
}
void print(date d)
{
printf("%d-%d-%d\n",d.year,d.month,d.day);
}
int date2int(date d)//把日期转化称整数如:2012-04-27 ---> 20120427
{
return (d.year*10000+d.month*100+d.day);
}
date int2date(int n)//把整数转换成日期格式如:20120427---->2012-04-27
{
date d;
d.day=n%100;
d.year=n/10000;
d.month=n/100%100;
return d;
}
int main()
{
date d;
printf("d of function main:%p\n",&d);
//input(d);//input(date d);这种写法是错误的。
d=input();
printf(d);
int n=date2int(d);
printf("%d\n",n);
printf(int2date(n));
return 0;
}
//形参和实参,可能名字相同,数值相同,但不在同一个地方,是两个不同的变量;
日期格式转换,比如把日期转换成整数,把整数转换成日期格式;2012-04-27《----》20120427
数组作为值来传递
#include <stdio.h>
void print(int a[],int n)
{
int i;
for (i=0;i<n;i++){
printf("%d ",a[i]);
}
printf("\n");
}
void add(int x[],int n)//元素数值是原来的2倍;1、可以2;也可以左移
{
int i;
for (i=0;i<n;i++){
x[i]<<=1;//左移+赋值
}
}
void reverse(int x[],int n)//元素倒序,交换位置
{
int i;
for (i=0;i<n/2;i++)
{
int t=x[i];
x[i]=x[n-1-i];
x[n-1-i]=t;
}
}
int main()
{
int a[5]={11,22,33,44,55};
int b[6]={1,2,3,4,5,6};
print(a,5);
print(b,6);
add(a,5);
print(a,5);
reverse(a,5);
print(a,5);
return 0;
}
//向函数传递数组时,我们一般会传递两个参数:1\首地址;2\元素个数
形参数组和实参数组实际上是同一组数组,在内存中同一个地方。---》这样的话,也就没必要再return返回数组了。
如何才能传入更多的参数,传多少参数都可以;比如printf和scanf
#include <stdio.h>
int main()
{
printf("hello\n");
printf("%d,%c\n",123,65);
printf("%f\n",4.5);
printf("%f,%s\n",123,65);//报错
return 0;
}
man -s3 printf
(,...) //不定长
va_start va_arg va_end va_list函数用来处理不定长参数表;靠占位符来识别有几个数据
如max(5,18,55,93,25,67) max("%d%d%d%d%d",18,55,93,25,67)
max(2,83,69) max("%d%d",83,69)
函数可以定义为:int max(int n,...)
如何来用呢?
vi variable_arguments.c
//求任一个整数的最大值
#include <stdio.h>
#include <stdarg.h>
int max(int cnt,...)
{
va_list v;//va_list类型,v保存可变长参数表;v实际上是一个地址类型
va_start(v,cnt);//不定长参数表之前至少有一个固定形参;用v保存参数cnt之后的那些参数
int i;
int maxvalue=va_arg(v,int);//从参数表中取出一个int类型的参数
for (i=1;i<cnt;i++){
int data=va_arg(v,int);//从参数表中取出一个int类型的参数
if (data>maxvalue){
maxvalue=data;
}
}
va_end(v);//释放可变长参数表v
return maxvalue;
}
void printstring(int cnt,...)
{
va_list v;
va_start(v,cnt);
int i;
for (i=0;i<cnt;i++){
puts(va_arg(v,char*));
}
va_end(v);
}
int main()
{
printf("%d\n",max(2,88,69));
printf("%d\n",max(5,91,25,86,97,89));
printf("%d\n",max(4,93,65,76,87));
printstring(3,"hello","my","dear");
return 0;
}
不定长参数类型会提升:
char,short===》int
float===》double
#include <stdio.h>
int f(int x)
{
printf("call f(%d),&x=%p\n",x,&x);
if(x<=0)
return 5;
else
return 2*f(x+1)+3;
}
int main()
{
printf("%d\n",f(3));
return 0;
}
//该程序在运行的时候会出现段错误;怎么解决呢?必须有终止条件
//if控制的内容如果以return结束,else可以省略
ABC3个位置上,每个位置上放了N个盘子,将盘子从A放到B位置,只能是从最上面开始移动,一次一个
vi hano.c
#include <stdio.h>
void hano(char from,int n,char to ,char spare)
{
if(n>0)
{
hano(from,n-1,spare,to);//把上面n-1个盘子移到空位
printf("move %d %c==>%c\n",n,from,to);//移动到第n个
hano(spare,n-1,to,from);//把空位上n-1个盘子移动到目的地
}
}
int main()
{
hano('a',5,'b','c');
return 0;
}
用递归:
1、找递推关系
2、找终止条件
递归必须有终点,要不一会儿就会把栈占用完
每个函数在执行完毕后都会回到开始执行的地方;即使没有返回值(传递值)也会用到栈。每次调函数的时候,程序都会返回地址存到栈里面
每次调用,都会新分配内存空间,都会用到栈;也就利于返回和复原;
vi decbit.c
#include <stdio.h>
void decbit(int n)
{
if (n>9){
decbit(n/10);
}
printf(" %d",n%10);
}
int main()
{
decbit(53926);
printf("\n");
return 0;
}
1 1 2 3 5 8 13 21 34 55 89 .... fibonacci;数列
函数尽量先声明后使用
特殊函数 宏函数 macro
vi macro.c
#include <stdio.h>
#define PI 3.14159 //常量
#define P print(
#define H "hello\n");
#define I int n;printf("请输入一个整数:");scanf("%d,&n");\
printf("您输入的是%d的一半\n",n+n);
// \为续行符
#define MA int main(){
#define END return 0;}
#define W "wahaha"
#ifndef H
#define H "你好!\n");
#else
#define _H H
#undef H //取消
#define H "****\n");
#endif
int main()
{
P H //编译的时候做个替换
I
return 0;
}
头文件
vi head.h
//定义一个结构类型
struct s{};
typdef struct s s;
s input();
void print(s,a);
vi head.c
#include <stdio.h>
//#include "head.h"
//#include "func.h" //理解:head.h和func.h中重复定义了s
//int x=2;
//int x=3; //编译也会报错
#ifndef VX
#define VX 1 //习惯上定义为1
int x=2;
#endif
#ifndex VX
#define VX 1 //习惯上定义为1
int x=3;
#endif
int main()
{
s a;
return 0;
}
vi func.h
s input();
void print(s,a);
一般头文件都是采用以下结构:
#ifndef VX //VX命名一般按照头文件名来命名如head.h在这里命名为HEAD_H
#define VX 1
......
#endif
vi heard.c
#ifndef HEAD_H
#define HEAD_H 1
struct s;
typedef struct s s;
#endif
C语言本身有几个已经预定义好的宏
vi predef.c
#include <stdio.h>
int main()
{
__FILE__ //字符串
__LINE__ //整数
__DATE__ //字符串
__TIME__ //字符串
//——STDC—— //字符串
return 0;
}
带参数的宏;宏函数;处理的时候还是原样替换
vi mfunc.c
#include <stdio.h>
#define SWAP(T,x,y){T t=x;x=y;y=t;} //交换2个数值
#define MAX(x,y){x<y?y:x} //x,y谁大
#define PI 3.14159
#define AREA(r) PI*(r)*(r) //带参数的宏,后面一定要有();可以通过编译时加-E来看效果
#define STR(X) puts(#x) //puts("hello")
void welcomestudent(){printf("欢迎各位同学");}
void welcometeacher(){printf("欢迎各位老师");}
#define welcome(who) welcome##who()
int main()
{
int a=10,b=20;
double c=12.3,d=45.6;
SWAP(int,a,b)
SWAP(double,c,d)
printf("a=%d,b=%d\n",a,b);
printf("c=%g,d=%g\n",c,d);
printf("%d\n",MAX(a,b));
printf("%g\n",AREA(10));
STR(hello);
return 0;
}
//调用宏函数的时候,尽量不要用++,--,赋值的式子
//宏的优势:不存在传递参数等;直接把需要的数据放在相应的位置,速度快。
##把参数和其他的东西拼接在一起
#include <stdio.h>
#define ISLEAP(y)((y)%4==0&&(y)%100!=0||(y)%400==0)
#define ISSMALL(m)((m)==4||(m)==6||(m)==9(m)==11)
#define NORMAL(y,m)(ISSMALL(m)?30:31)
#define DAYS(y,m)((m)==2?28+ISLEAP(y):NORMAL(m))
#define IN(x,from,to)((x)>=(from)&&(x)<=(to))
#define ISVALID(y,m,d)((y)>1600&&IN(m,1,12)&&IN(d,1,DAYS(y,m)))
int main()
{
printf("%d,%d,%d\n",DAYS(2012,2),DAYS(2010,8),DAYS(2012,4));
return 0;
}
C语言的话,宏使用比较多,而在C++相对来说比较少
指针
char*地址,每个变量在内存中总有个地址;所谓的地址就是开始地址
1个指针保存的是地址=====》类似于硬盘中的lba地址(定位扇区用)
地址不是孤立的,总是带着类型;即通过寻址后,取那种类型的变量
vi pointer.c
#include <stdio.h>
int main()
{
char x[8]={'a','b','c','d','e','f','g','h'};//不是字符串,通过puts输出时可能会有乱码
//char x[9]={'a','b','c','d','e','f','g','h'};//一定不会有乱码;不够的时候,会用0来补;而0使用'\0'来表示
puts(x);
char* p1;
p1=&x[0];//表示取x[0]的地址;char*
char* p2=&x[2];//初始化;//char*,表示的char*类型
printf("%c\n",*p1);
*p1='A';
*p2='C';
puts(x);
int * p3=&x[3];//char * 在编译时会报不兼容的指针类型初始化警告
printf("%X\n",*p3);
return 0;
}
//在定义变量的时候,*不是运算符,不表示运算