C关键字用法.docx
- 文档编号:8666267
- 上传时间:2023-02-01
- 格式:DOCX
- 页数:30
- 大小:44.20KB
C关键字用法.docx
《C关键字用法.docx》由会员分享,可在线阅读,更多相关《C关键字用法.docx(30页珍藏版)》请在冰豆网上搜索。
C关键字用法
关键字extern
1用extern修饰外部变量
(1)在一个文件内声明外部变量
如果外部变量不在文件的开头定义,其有效的作用范围只限于定义处到文件结束。
如果在定义点之前的函数想引用该外部变量,则应该在引用之前用关键字extern对该变量做“外部变量声明”,表示该变量是一个已经定义的外部变量。
作用域扩展到从声明处开始,到本文件结束。
#include"stdio.h"
voidmain()
{
intmax(int,int);
externA,B;/*外部变量声明*/
printf("%d\n",max(A,B));
}
intA=13,B=-8;/*定义外部变量*/
intmax(intx,inty)
{
intz;
z=x>y?
x:
y;
returnz;
}
一般的做法是外部变量的定义放在引用它的所有函数之前,这样可以避免在函数中多加一个extern声明。
用extern声明外部变量时,类型名可以写也可以省写。
(2)在多文件的程序中声明外部变量
如果整个工程由多个文件组成,在一个文件中想引用另一个文件中已经定义的外部变量时,则只需要在引用的文件中用extern关键字加以声明即可。
可见,其作用域从一个文件扩展到多个文件了。
文件file1.c中内容为:
#include"stdio.h"
intBASE=2;//外部变量定义
intexe(int);//外部函数声明
intmain()
{
inta=10;
printf("%d^%d=%d\n",BASE,a,exe(a));
return0;
}
文件file2.c中内容为:
#include"stdio.h"
externBASE;//外部变量声明
intexe(intx)
{
inti;
intret=1;
for(i=0;i ret*=BASE; returnret; } 2用extern声明外部函数 (1)在定义函数时,如果在函数首部的最左端加关键字extern,则表示此函数是外部函数,可供其他文件调用。 如函数首部可以写成externintfun(inta,intb);这样,函数fun就可以为其他函数调用。 C语言规定,如果在定义函数时省略extern,则隐含为外部函数。 (2)在需要调用此函数的文件中,用extern对函数作声明,表示该函数是在其他文件中定义的外部函数。 file1.c(文件1) #include"stdio.h" voidmain() { /*以下3行声明在本函数中将要调用的在其他文件中定义的3个函数*/ externvoidenter_string(charstr[]); externvoiddelete_string(charstr[],charch); externvoidprint_string(charstr[]); charch; charstr[80]; printf("pleaseinputthestring: \n"); enter_string(str); printf("inputthedeletedcharacter: \n"); scanf("%c",&ch); delete_string(str,ch); print_string(str); } file2.c(文件2) #include"stdio.h" voidenter_string(charstr[])/*定义外部函数enter_string*/ { gets(str);/*向字符数组输入字符串*/ } file3.c(文件3) #include"stdio.h" voiddelete_string(charstr[],charch)/*定义外部函数delete_string*/ { inti,j; for(i=j=0;str[i]! ='\0';i++) if(str[i]! =ch) str[j++]=str[i]; str[j]='\0'; } file4.c(文件4) #include"stdio.h" voidprint_string(charstr[])/*定义外部函数print_string*/ { printf("%s\n",str); } 关键字static 1用static声明局部变量 在局部变量之前加上关键字static,局部变量就被定义成为一个局部静态变量。 1)内存中的位置: 静态存储区。 2)对静态局部变量是在编译时赋初值的,即只赋初值一次,在程序运行时它已有初值。 若定义静态局部变量时不赋初值,编译时系统自动赋初值0(对数值型变量)或空字符(对字符变量)。 以后每次调用函数时不再重新赋初值而是保留上次函数调用结束时的值。 3)作用域: 作用域仍为局部作用域,当定义它的函数或语句块结束的时候,作用域随之结束。 注: 当static用来修饰局部变量的时候,它就改变了局部变量的存储位置,从原来的栈中存放改为静态存储区。 但是局部静态变量在离开作用域之后,并没有被销毁,而是仍然驻留在内存中当中,直到程序结束,只不过我们不能再对它进行访问。 #include"stdio.h" voidmain() { intfun(int); inta=2,i; for(i=0;i<3;i++) printf("%d",fun(a)); } intfun(inta) { intb=0; staticintc=3;/*定义静态局部变量*/ b++; c++; return(a+b+c); } 2用static声明外部变量 在全局变量之前加上关键字static,全局变量就被定义成为一个全局静态变量。 1)内存中的位置: 静态存储区 2)初始化: 未经初始化的全局静态变量会被程序自动初始化为0 3)作用域: 作用域仅限于变量定义的文件中,其他文件即使使用extern声明也没有办法使用它。 准确地说作用域是在定义之处开始,到文件结尾处结束,在定义之处前面的那些代码也不能使用它。 要想使用就得在前面加上extern**。 //test.c(文件1) #include"stdio.h" voiddisplay(); externintn; intmain() { n=20; printf("%d\n",n); display(); return0; } //test1.c(文件2) #include"stdio.h" staticintn;//定义全局静态变量,自动初始化为0,仅在本文件中可见 voiddisplay() { n++; printf("%d\n",n); } 文件分别编译通过,但link的时候test.c中的变量n找不到定义,产生错误。 3用static声明内部函数 在定义内部函数时,在函数名和函数类型的前面加static,即staticintfun(inta,intb)。 使用内部函数的作用域只局限于所在文件,在不同的文件中有同名的内部函数,互不干扰。 //test.c(文件1) #include"stdio.h" voiddisplay(); voidstaticdis(); intmain() { display(); staticdis(); return0; } //test1.c(文件2) #include"stdio.h" voiddisplay() { voidstaticdis(); printf("display()hasbeencalled\n"); } staticvoidstaticdis()//定义内部函数 { printf("staticdis()hasbeencalled\n"); } 文件分别编译通过,但是连接的时候找不到函数staticdis()的定义,产生错误。 关键字const 1const修饰的是只读变量 定义const只读变量,具有不可变性。 例如: constintMax=100; intArray[Max]; 在VC下分别创建.c和.cpp文件测试一下。 可以发现在.c文件中,编译器会提示出错,而在.cpp文件中则顺利进行。 我们知道定义一个数组必须指定其元素的个数。 这从侧面说明在C语言中,const修饰的Max仍然是变量,只不过是只读性罢了;而在C++里,则扩展了const的含义,这里不讨论。 2修饰一般变量 一般常量是指简单类型的只读变量。 这种只读变量在定义时,修饰符const可以用在类型说明符前,也可以用在类型说明符后。 例如: intconsti=2;或constinti=2; #include intmain() { intconsta; a=5; printf("a=%d\n",a); return0; } 如果编译这个c文件,就会报错。 显而易见,这是const在搞鬼,因为声明了const的变量是不能修改的! 如果将源代码修改为如下这样,就没有问题了! #include intmain() { intconsta=5; printf("a=%d\n",a); return0; } 总结: const声明的变量必须要进行初始化赋值,如果错过这个机会,以后再想给const的变量赋值,可就没门了! 切记 3修饰数组 定义或说明一个只读数组可采用如下格式: intconsta[5]={1,2,3,4,5};或constinta[5]={1,2,3,4,5}; 4修饰指针 constint*A;//A可变,A指向的对象不可变 intconst*A;//A可变,A指向的对象不可变 int*constA;//A不可变,A指向的对象可变 constint*constA;//指针A和A指向的对象都不可变 下面分别对这几种情形讨论一下: 情景一: constint*A与intconst*A等价,这里只讨论constint*A #include intmain() { intnum=12; constint*A=# printf("result=%d\n",*A); return0; } 编译执行结果为: result=12 接下来,我们动动手脚,在代码中加入了(*A)++;这条语句: #include intmain() { intnum=12; constint*A=# (*A)++; printf("result=%d\n",*A); return0; } 编译时报错: errorC2166: l-valuespecifiesconstobject 可以看到,报错的内容表示”*A”是只读的,不能修改。 我们再修改一下源代码为下面这样: #include intmain() { intnum=12; inttmp=100; constint*A=# A=&tmp; printf("result=%d\n",*A); return0; } 编译执行结果为: result=100 结论: 如果声明了constint*A,那么A值是可以修改的,而*A是不可以修改的。 更通俗的说,A指针可以随便指向一个整型,但只要被A盯上了的整型变量在使用*A引用时就不能修改了。 情景二: int*constA #include intmain() { intnum=12; int*constA=# printf("result=%d\n",*A); return0; } 编译执行结果为: result=12 我们稍微修改下源代码: #include intmain() { intnum=12; inttmp=100; int*constA=# A=&tmp; printf("result=%d\n",*A); return0; } 编译时报错: errorC2166: l-valuespecifiesconstobject 可见A本身的值已经不能再改变了。 继续修改源代码如下: #include intmain() { intnum=12; int*constA=# (*A)=100; printf("result=%d\n",*A); return0; } 编译执行结果: result=100 可以看出,(*A)是可以改变的。 结论: int*constA; //const修饰指针A,A不可变,A指向的对象可变 情景三: constint*constA;//指针A和A指向的对象都不可变 #include intmain() { intnum=12; intconst*constA=# (*A)=100; printf("result=%d\n",*A); return0; } 编译报错: errorC2166: l-valuespecifiesconstobject 可见A指向的对象不可修改。 修改源代码: #include intmain() { intnum=12; inttmp=100; intconst*constA=# A=&tmp; printf("result=%d\n",*A); return0; } 编译报错: errorC2166: l-valuespecifiesconstobject 可见A的值也不可以修改。 结论: constint*constA;//指针A和A指向的对象都不可变。 当然constint*constA;和intconst*constA=#是等价的! 5修饰函数形参 const修饰符也可以修饰函数的参数,当不希望这个参数值被函数体内意外改变时使用。 例如: voidFun(constinti);告诉编译器i在函数体中的不能改变,从而防止了使用者的一些无意的或错误的修改。 #include intaddto(constintnum,inta,intb) { if(num==1){ returna+b; }else{ return0; } } intmain(){ intnum=100; inta=12,b=22; intres; num=1; res=addto(num,a,b); printf("res=%d\n",res); return0; } 编译执行结果为: res=34 如果我修改一下,编译就会出错: #include intaddto(constintnum,inta,intb) { if(num==1) { num=3; returna+b; } else return0; } intmain() { intnum=100; inta=12,b=22; intres; num=1; res=addto(num,a,b); printf("res=%d\n",res); return0; } 编译报错: errorC2166: l-valuespecifiesconstobject 可见在函数里形参被声明为const的变量也是不能修改的。 6用const修饰函数的返回值 1)如果给以“指针传递”方式的函数返回值加const,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const修饰的同类型指针。 如对于: constchar*GetString(void); 如下语句将出现编译错误: char*str=GetString();//cannotconvertfrom‘constchar*’to‘char*’; 正确的做法是: constchar*str=GetString(); 2)如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const修饰符没有任何价值。 如不要把函数intGetlnt(void)写成constintGetlnt(void). inline用法 C语言的内联函数是在最新的C99标准里才加入的,在以前的C89标准里是没有的。 我们现在使用的编译器很多都还是基于C89标准的,对于C99标准的支持度各个编译器都不同,所以能不能在C语言里使用内联函数要看具体编译器支不支持了。 经过测试,VC6.0在.c文件中是不支持inline函数的。 不过在.cpp文件中是可以通过的,因为inline本身就是C++的关键字。 选择inline类型的函数是有条件的 ·函数足够简单,并且非常频繁被调用,只有一条程序就可以完成任务 ·不能包含复杂的结构控制语句例如whileswitch 下面讨论在C++情况下,inline函数的使用方法: 考虑下列min()函数 intmin(intv1,intv2) { return(v1 v1: v2); } inline内联函数给出了一种解决方案若一个函数被指定为inline函数则它将在程序中每个调用点上被内联地展开例如: intminVal2=min(i,j);在编译时被展开为intminVal2=i i: j;把min()写成函数的额外执行开销从而被消除了。 在函数声明或定义中的函数返回类型前加上关键字inline即把min()指定成为inline inlineintmin(intv1,intv2){/*...*/} 但是,注意inline指示对编译器来说只是一个建议。 编译器可以选择忽略该建议,因为把一个函数声明为inline函数,并不见得真的适合在调用点上展开。 例如一个递归函数,并不能在调用点完全展开。 一个1200行的函数也不太可能在调用点展开。 一般地inline机制用来优化小的只有几行的经常被调用的函数。 inline函数对编译器而言必须是可见的,以便它能够在调用点内联展开该函数。 与非inline函数不同的是,inline函数必须在调用该函数的每个文本文件中定义。 当然,对于同一程序的不同文件,如果inline函数出现的话,其定义必须相同。 对于由两个文件compute.C和draw.C构成的程序来说,程序员不能定义这样的min()函数,它在compute.C中指一件事情而在draw.C中指另外一件事情。 如果两个定义不相同,程序将会有未定义的行为: 编译器最终会使用这些不同定义中的哪一个作为非inline函数调用的定义是不确定的,因而程序的行为可能并不像你所期望的。 为保证不会发生这样的事情,建议把inline函数的定义放到头文件中。 在每个调用该inline函数的文件中包含该头文件。 这种方法保证对每个inline函数只有一个定义,且程序员无需复制代码,并且不可能在程序的生命期中引起无意的不匹配的事情。 测试代码: #include #include usingnamespacestd; inlinestringdbtest(inta);//函数原形声明为inline即: 内联涵数 voidmain() { for(inti=1;i<=10;i++) cout< "< } stringdbtest(inta)//这里不用再次inline,当然加上inline也是不会出错的 { return(a%2>0)? "奇": "偶"; } 上面的例子就是标准的内联涵数的用法,使用inline修饰带来的好处我们表面看不出来,其实在内部的工作就是在每个for循环的内部所有调用dbtest(i)的地方都换成了(i%2>0)? "奇": "偶"这样就避免了频繁调用函数对栈内存重复开辟所带来的消耗。 函数重载 C语言中不支持函数重载。 函数重载时C++的一大特性,下面就来讨论一下C++函数重载的内容。 1怎样重载一个函数名 在C++中可以为两个或多个函数提供相同的名字只要它们的每个参数表惟一就行: 或者是参数的个数不同,或者是参数类型不同。 下面是重载函数max()的声明: intmax(int,int); intmax(constvector intmax(constmatrix&); 参数集惟一的每个重载声明都要求一个独立的max()定义。 当一个函数名在一个特殊的域中被声明多次时编译器按如下步骤解释第二个以及后续的的声明。 1)如果两个函数的参数表中参数的个数或类型不同则认为这两个函数是重载的,例如: //重载函数 voidprint(conststring&); voidprint(vector 2)如果两个函数的返回类型和参数表精确匹配则第二个声明被视为第一个的重复声明。 例如: //声明同一个函数 voidprint(conststring&str); voidprint(conststring&); 参数表的比较过程与参数名无关。 4)如果两个函数的参数表相同但是返回类型不同,则第二个声明被视为第一个的错误重复声明会被标记为编译错误,例如: unsignedintmax(inti1,inti2); intmax(int,int);//错误: 只有返回类型不同 函数的返回类型不足以区分两个重载函数。 5)如果在两个函数的参数表中只有缺省实参不同则第二个声明被视为第一个的重复声明。 例如: //声明同一函数 intmax(int*ia,intsz); intmax(int*,intsz=10); 当一个参数类型是const或volatile时,在识别函数声明是否相同时,并不考虑const和volatile修饰符。 例如下列两个声明声明了同一个函数: //声明同一函数 voidf(int); voidf(constint); 但是,如果把const或volatile应用在指针或引用参数指向的类型上,则在判断函数声明是否相同时,就要考虑const和volatile修饰符。 //声明了不同的函数 voidf(int*); v
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 关键字 用法