標準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;
}
//在定義變量的時候,*不是運算符,不表示運算