标准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;
    
   }
   
   //在定义变量的时候,*不是运算符,不表示运算