标准C基础知识五

12年前


 函数的形参和局部变量在调用时与上次调用时的值无关;形参每次都会被覆盖,局部变量每次调用完都会被释放。
 静态局部变量(在多次调用时并不释放以前的分配空间)与上次调用时的值有关。
 
 
 
 
 
 值传递
 
 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;
  
 }
 
 //在定义变量的时候,*不是运算符,不表示运算