c语言函数隐式声明 c语言隐式声明怎么解决
大家好,今天小编来为大家解答以下的问题,关于c语言函数隐式声明,c语言隐式声明怎么解决这个很多人还不知道,现在让我们一起来看看吧!
C语言中函数声明的位置有几种
对函数的“定义”和“声明”不是一回事。函数的定义是指对函数功能的确立,包括指定函数名,函数值类型、形参及其类型以及函数体等,它是一个完整的、独立的函数单位。而函数的声明的作用则是把函数的名字,函数类型以及形参的类型、个数和顺序通知编译系统,以便在调用该函数时进行对照检查(例如,函数名是否正确,实参与形参的类型和个数是否一致),它不包括函数体。——谭浩强,《C程序设计》(第四版),清华大学出版社,2010年6月,p182
这段论述包含了许多概念性错误,这些概念错误在许多C语言书中都同样普遍存在。为了说明这些错误,首先来回顾一下C语言演变和发展的一些情况。
最早,C语言的代码可以这样写:
main(){printf("hello,world!
");}注意,这段代码对标识符printf没有进行任何说明。这是因为printf()函数的返回值为int类型。当时的C语言规定,对于没有任何说明的函数名,编译器会默认为返回值为int类型,因此对这样的函数名可以不做任何说明。那个时期的C语言,很多情况下int可以不写。例如main()函数返回值的类型为int就可以不写。
但是需要特别说明的是,这种“省劲”的写法已经过时,从C90标准起,这种写法就步入了被逐步抛弃的过程(尽管当时还没有完全立即废止)。C99废除了隐式函数声明法则(remove implicit function declaration),另外,省略main()前面的int也已经不再容许了。
在C语言早期,尽管有时不需要对函数名进行说明,但有些情况下对函数名进行说明还是必须的,比如:
?
12345
double sqrt();int main(){printf("%f
", sqrt(9.));}
这是因为函数sqrt()返回值的类型不是int类型而是double类型,编译器编译时需要知道sqrt(9.)这个表达式的类型。
不难注意到这种对函数名的说明非常简单,这是最早期的一种函数类型说明的形式。这种说明只着重说明函数名是一个函数及其返回值类型,如果程序员在调用函数时存在参数类型或个数方面的错误编译器是无法察觉的,因为函数类型说明中“()”内没有任何信息。
这种办法只说明了函数名与()进行运算的结果也就是函数返回值的数据类型,无法进一步检查参数方面的错误是这种写法的不足之处。
如果不写函数类型说明,也可以把函数定义写在函数调用之前:
?
123456789
double square( double x){return x* x;}int main(void){printf("%f
", square(3.));return 0;}
这表明函数定义也具有对函数名的类型加以说明的效果,因此从这个意义上来说,函数定义也是一种对函数类型的说明。这种办法可以检查出函数调用时在参数个数和类型方面的错误。
但是,用这种办法说明函数名并不好,因为这样做在编程时还需要考虑应该把哪个函数定义写在前面,哪个写在后面的问题。假如函数A调用函数B,函数B调用函数C,函数C又调用函数A,究竟如何安排函数定义的顺序就会让人感到无所适从。此外这种办法也不利于代码的组织,在由多个源文件组成的源程序时,这种写法就更会捉襟见肘、漏洞百出。因此,在1990年,C标准借鉴C++语言规定了一种新的说明函数名的方法,这就是函数原型(Function Propotype)式说明函数类型的方法:
?
12345678910
double square( double);//或 double square( double x)int main(void){printf("%f
", square(3.));return 0;}double square( double x){return x* x;}
使用这种办法,不但可以检查函数调用时参数类型和个数方面的错误,同时解决了源代码的组织问题,因为程序员不必再考虑该把哪个函数写在前面、哪个写在后面这种无聊的问题了。这种办法全面地说明了函数名的数据类型。此外要说明的是,把形参及其数据类型写在“()”内形式的函数定义也属于函数原型(Function Propotype)的范畴。
由此可见,古老的、不对参数进行任何说明的函数类型说明方式、函数定义以及函数原型式的函数类型说明方式都具有说明函数名意义的效用。从这个意义上讲它们都是函数声明。在C语言中,声明(Declaration)这个词的本义就是指定标识符的意义和性质(A declaration specifies the interpretation and attributes of a set of identifiers.),某个标识符的定义(Definition)同时也是这个标志符的“声明”(Declaration)。函数定义(Function definition)则意指包括函数体。(A definition of an identifier is a declaration for that identifier that:……for a function, includes the function body;)。函数原型则特指包括说明参数类型的函数声明,它同样包含用这种方式写出的函数定义。
现在回过头来看样本中的第一句话:“对函数的“定义”和“声明”不是一回事”。由于函数定义本身就是一种函数声明,怎么可以说它们不是一回事呢?这句话的逻辑就如同说“男人”和“人”不是一回事。你可以说男人和女人不是一回事,因为他们没有交集。但没法说男人和人不是一回事,因为男人是人的子集,男人就是人的一种,怎么可以说男人和人不是一回事呢?
那么,不带函数体的函数声明应该如何称呼呢?在C语言中,它们叫被做“函数类型声明”(Function type declaration)。函数类型声明最主要的特点是声明了函数名是一个函数及其返回值的类型,如果也声明了参数的类型,则是函数原型式的函数类型声明。
样本中的“而函数的声明的作用则是把函数的名字,函数类型以及形参的类型、个数和顺序通知编译系统,以便在调用该函数时进行对照检查(例如,函数名是否正确,实参与形参的类型和个数是否一致),它不包括函数体”这句话同样不通。其主要错误是它混淆了“函数原型式类型声明”与“函数声明”这两个概念,前一个概念只是后一个概念的子集。函数声明中不但包含“函数类型声明”,也包含“函数定义”和老式的“函数类型声明”。由于函数定义本身就是一种函数声明,所以无法断定函数的声明是否包括函数体;而且老式的函数类型声明(例如double sqrt();)也属于函数声明,这种函数声明并不检查参数类型及个数方面的错误。此外函数声明也并没有检查“函数名”正确与否的功能。
这段文字中的“函数类型”这个概念也有错误,函数类型所描述的不但包括函数返回值类型,也可能一并描述参数的个数和类型(如果是函数原型),因此不能与“形参的类型、个数”相提并论。
现代的C语言的函数定义和函数类型声明都采用函数原型式的风格,C99把旧的非原型形式视为过时,这意味着非原型形式以后可能被禁止。
main()函数
在各种C语言书上,能看到各式各样main()函数的写法,简直令人无所适从,这是这么回事?原因主要有两个:一个是随着C语言的发展和演化,main()函数的写法也在不断变化;另外,某些书籍写法不规范或误导的现象也同时存在。
最初main()函数的写法非常简洁,那个时候的C程序员哪怕一个字符似乎都不肯多写。不知道是因为当时键盘质量不好还是因为编辑器太糟糕的缘故,那个时代的C程序员似乎惊人地一致崇尚“简约”——甚至可以说是“至简”。
?
1234
main(){printf("hello,world
");}
这就是main()函数最古老的写法,K&R在他们的经典名著《The C Programming Language》中的第一个C语言源程序(1978)。这种写法是那个时代的主流。
简直和裸体差不多,连#include<stdio.h>也没有么?在《The C Programming Language》的第一版中确实没有。那个时代的C语言,返回值类型为int的函数不用声明。不过在该书的第二版(1988)中这个程序被改成了:
?
12345
#include<stdio.h>main(){printf("hello,world
");}
返回值类型为int的函数不用声明的规则改变了吗?规则没有改变。改变了的是观念,人们已经不再倾向于代码的“至简”,而开始倾向于在代码中交代清楚每一个标识符的来龙去脉。从C89开始倡导在函数调用之前一定要有函数声明,但并没有强求,而在C99这已经是强制性的要求了。由于《The C Programming Language》第二版正值ANSI C标准颁布(1989)前夕出版,所以这种变化也应该视为ANSI C标准的倾向性以及K&R对新标准的认同。尽管这个例子没有完全反映出来这种认同。
为什么说没有完全反映出来这种认同呢?因为这个main()的定义并没有按照函数原型(Function prototype)的方式来写,C90中规定不带参数的main()函数应该这样写:
?
1
int main(void){/*...*/}
但同时规定那个int可以省略。C90把()内不写任何内容视为过时的写法,尽管C90无奈地容忍了它(The use of function declarators with empty parentheses(not prototype-format parameter type declarators) is an obsolescent feature.)。
为什么要容忍?因为有许多老式的代码还在用。
如果以C99的标准看这个main()写得如何呢?C99不容许省略int。但同样只把()内不写任何内容视为过时,而没有完全禁止,可见习惯力量的顽固。
那又为什么说K&R对新标准的认同呢?《The C Programming Language》第二版中的其他函数定义和函数类型声明基本上都改成了函数原型风格。比如,在讲解main()函数的参数时,K&R把原来的main()函数
?
1234567
#include<stdio.h>main(argc,argv)int argc;char*argv[];{/*……*/return 0;}
改成了:
?
123456
#include<stdio.h>main(int argc, char*argv[]){/*……*/return 0;}
前一个写法今天已经差不多绝迹,后一个main()以今天的眼光来看有些奇怪,main()的参数是用函数原型风格写的,但却没有写main()返回值的类型,给人有点半新半旧的感觉。尽管不能说它违背C90(因为C90容许不写main()前面的int),但如果写上了返回值的类型int,就同时满足现代C99标准的要求了。
这里出现的“return 0;”是怎么回事?这在现代C语言中已经是司空见惯了,它返回给操作系统一个值以表明程序是在何种状态下结束的。但在另一段代码中,K&R似乎又走得太远:
?
1234567
#include<stdio.h>main(int argc,char*argv[]){int found= 0;/*……计算found的值*/return found;}
这个实在有些“标新立异”,居然把计算结果返回给了操作系统,颇有突破常规之嫌。
那前面几个没有“return 0;”的main()函数会怎么样?按照C90标准,会返回一个不确定的int类型的值,如果确实不关心这个返回值是多少,不写确实可以。但C99却要求编译器在编译的时候帮忙给补上这个“return 0;”,C99在必须写int这个问题上没有迁就懒人,但在这里却对偷懒的做法给予了迁就。问:如果确实不关心main()函数的返回值,把main()的返回值定义为void类型如何?我看到许多书上都这样写的。
?
12345
#include<stdio.h>void main(){printf("This is a C program.
");}
这在C99之前是一种野路子写法,究竟从哪里冒出来的,无据可考。但前几年的主流教科书中这种写法很常见。K&R(C语言的发明者)没有这样写过,C90国际标准也不承认这种写法。Bjarne Stroustrup(C++语言的创始人)在他的关于C++的FAQ中,在回答是否可以写“void main()”时愤怒地回答说这种写法在C++和C中都不曾有过。事实上,很多C语言专家都认为“void main()”非常邪恶。
因此,在C99之前,这是不符合标准的写法。尽管这段代码的功能似乎是输出“This is a C program.”,但其实却不是一个“C program”。
但是有时这样写并没有产生错误啊?首先,C语言的错误不一定反应在编译、链接或运行过程中。你输出一个垃圾值也可能一路通过编译、链接或运行,但这不说明你的代码没有错误,更不能说明这样的代码正确、有意义。其次,这样的写法在有些编译器下程序会产生崩溃或得到警告。这说明这种写法至少不普遍性适用的。可以说,如果不是C99标准,这种写法根本没有立锥之地。
C99给了这种写法以立足之地么?从某种意义上也许可以这样理解。因为K&R没承认过这种写法,C90根本不承认这种写法,C99虽然没有正式承认这种写法,但为这种写法留了一个后门:“It shall be defined……or in some other implementation-defined manner”。这意思就是说,如果编译器明确声称允许void main()这种写法的话,那么C99不再象C90那样简单认为这种写法违背C标准。
但是不管怎么说,这种写法最多是某些编译器的一种“方言土语”,如果没有特殊理由,比如仅仅是工作在某个特殊环境,且仅仅使用特定的编译器而根本不考虑程序的可移植性,为什么不写普遍适用的形式呢?
既然很多C语言专家都认为“void main()”非常邪恶,C99为什么包容这种写法呢?很难确定C99是否就是打算专门想把这种写法也“收容”在标准之列。因为除了void main(),还有另外一些main()函数的写法被C90排除在标准之外了。而现在,这些写法在理论上也具备了符合C99标准的可能性。
还有什么样的main()函数?很多编译器都支持下面的main()的写法:
?
12345
int main(int argc, char*argv[], char*env[]){/**/return 0;}
居然有3个形参,那个env是做什么用的?那个参数可以使程序获得环境变量。
什么叫环境变量?简单地讲可以理解为操作系统记录的一些数据,比如计算机的名字,操作系统放在哪里等等。应用程序在运行时可能要用到这些信息,这时可以通过env这个参数来获得。
如果编译器不支持main()的第三个参数怎么办?标准库函数也可以达到同样的目的。
?
12
#include<stdlib.h>char*getenv(const char*name);
是否可以说void main()和int main(int argc, char*argv[], char*env[])也符合C99标准呢?恐怕还不能这么说,现在只是不能说这两种写法一定不符合C99标准。但这两种写法不符合C90标准是确定的,这两种写法的可移植性很差也是确定无疑的。C99标准在这里写的很模糊,没有进一步界定“implementation-defined manner”的含义。除非编译器声明遵守C99标准,且容许这两种写法,否则断言这两种写法符合C99标准属于空穴来风。
有人说“C99建议把main函数指定为int型”,这种说法对吗?显然不对。因为C99并非绝对不包容返回值非int类型的main()。正确的说法是,C90要求main()函数的返回值一定得是int型。但C90容许不写那个int,而C99则要求必须写上这个“int”。
下面这种风格如何?
?
123456
#include<stdio.h>int main(){printf("This is a C program.
");return 0;}
这个写法有点不伦不类。返回值的类型int写了,这个和C89的倡导或C99的要求一致,但是()里面什么都不写,又与标准的所倡导的风格不符,所以说不伦不类。这种写法目前的标准依然容许,但属于标准目前尚能容忍的但即将过时的(obsolescent)写法,被抛弃只是早晚的问题。这种写法就如同古代的函数形参的写法一样:
?
123456
main(argc,argv)int argc;char*argv[];{/*……*/return 0;}
都属于历史的垃圾。
见过在main()的函数体的“}”之前前写一句getch();,这个是怎么回事?这个是时代的产物。在PC从DOS时代转变为Windows时代的过程中,DOS时代开发的IDE(主要是TC)无法在运行程序后显示输出结果,为了在运行后从容仔细地观察一下运行结果再返回IED界面,加上了这么一句,人为地延长程序运行时间(因为getch()会等待用户输入一个字符)。但这与main()本身的结构无关。这条语句不具备普遍意义,只是将就过时的IDE的一种权宜之计而已。所谓不具备普遍意义是指,第一,真正的程序往往不需要这条语句,就是说这条语句与程序功能无关;第二,getch()这个函数并不是标准函数,只有个别的编译器才支持它,在其他编译器上写这条语句,很可能行不通。
为什么不用getchar()这个标准库函数呢?getchar()的功能和getch()有点区别,前者会在标准输出设备上显示用户键入的字符,这显得很不利索,而后者则不会显示用户所键入的字符,更接近“Press Any Key to continue……”的效果。
有的代码在main()函数结束前写system("PAUSE");,是否也是这个意思?是的。这也是一种人工制造的“请按任意键继续...”,与程序功能结构无关,只是为了方便地观察输出结果。但是这种写法比调用getch()要好,因为system()函数是标准库函数,各个编译器都提供支持。
有一种说法,“在最新的C99标准中,只有以下两种定义方式是正确的:”
?
12345
int main( void){/**/return 0;}
和
?
12345
int main( int argc, char*argv[]){/**/return 0;}
这种说法对吗?
这种说法显然不对。但可以确认的是这两种定义方式一定正确。不但在C99来说是正确的,以C89来说也是正确的。
还有一种写法:
?
1234
int main( void){return EXIT_SUCCESS;}
那个EXIT_SUCCESS是怎么回事?
return EXIT_SUCCESS;是与return 0;等价的一种文雅的写法。EXIT_SUCCESS是在stdlib.h中定义了的符号常量,返回这个值表示程序任务完成后程序退出。在stdlib.h定义的另一个符号常量EXIT_FAILURE,通常用于程序无法完成任务而退出。
实在太眼花缭乱了,需要记住这么多吗?显然没必要。很多东西都是历史原因遗留下的垃圾。
如果学习C语言,应该记住或使用哪种呢?显然是:
?
12345
int main( void){/**/return 0;}
和
?
12345
int main( int argc, char*argv[]){/**/return 0;}
第一,他们普遍适用,不存在可移植性的问题;
第二,就目前看,他们不存在任何过时或即将过时的成分。当然,如果喜欢文雅,不写return 0;而写EXIT_SUCCESS也可以。顺便说一句,有的学习者记不住带参数main()函数两个形参的名字。其实这两个形参的名字也可以自己取,不一定用那两个名字,只要记住类型就可以了。第二个参数的类型也可以是char**,这和原来的是等价的。
C语言中请问函数名中的括号里可以写表达式吗
C语言中请问函数名中的括号里可以写表达式吗调用函数时,参数部分,可以使用表达式
定义函数时,不可以写表达式
c语言中可否用中文写函数名不可以,为什么要用中文呢?
C语言中的算数表达式答案是2.500000。那个int(x+y)强制类型转换成整型,结果是7;前面a%3求余为1,即1*7%2/4;7与2求余是1,与4整除为0;则答案是x的值。程序如下
#include<stdio.h>
int main()
{
float x=2.5;
i nt a=7;
float sum;
float y=4.7;
sum= x+a%3*(int)(x+y)%2/4;
printf("%f
", sum);
return 0;
}
怎么理解C语言中函数:switch(表达式)case break;就是一个判断语句 switch(A)判断A case 1:...;如果A等于1则做一些操作,然后break跳出,不进行判断,如果没有break;他会继续判断是否等于2的; break; case 2:...;.. default:...;这里如果不等于1也不等于2,就执行default里面的语句;最后跳出循环
麻烦采纳,谢谢!
关于c语言中的数学表达式简单的话可以使用pow(a,1.0/n),需加头文件#include<math.h>
a为被开方数,n为开方数
注意是1.0,不是1
1/n不等于1.0/n的(n是整型数据)
当然也可以自己写个函数,计算一下
函数调用是表达式的一种吗在c语言中 c语言中,函数调用是函数调用,表达式是表达式。两个概念。
在某些语言中(如:lisp),函数调用就是表达式。C语言不是这样的。
c语言,return(),括号里可以是表达式,数值,函数地址还有什么呢?或者说括号里不能是什么呢?说点夸张的,你想让它返回什么,它就能返回什么,当然,你想让它,把你返回了,它是不能哈~~~
c中,return可以返回,任何是值(value)的东西,
return 1; return 1+ 2; return"abc";
实际上,return也可以什么都不返回,
return;表示,函数到此结束,控制权已经交给了调用它的人了,后面的代码,无视~~
因为吧,c中有指针类型一说,而指针是无所不指的,可以指向char, int, double,
可以指向指针*,可以指向数组int[12],可以指向函数,指向结构体,等等、
而指针本来就是一种类型,一个值(value),所以说,只要你懂,return什么东西都能返回,
补充一句,他不能返回,非值的东西(value),比如
return+;这什么东东??返回运算符?、god、、你会这样吗?、
C语言中的函数名字该怎么读?一般取名都是单词省略后得到函数名,也有的人用拼音命名,怎么读并不重要,关键是明白函数是干什么的.你可以参考一下匈牙利命名法
在C语言中有那些函数名?仅仅为了获取函数名,就在函数体中嵌入硬编码的字符串,这种方法单调乏味还易导致错误,不如看一下怎样使用新的C99特性,在程序运行时获取函数名吧.对象反射库、调试工具及代码分析器,经常会需要在运行时访问函数的名称,直到不久前,唯一能完成此项任务并且可移植的方法,是手工在函数体内嵌入一个带有该函数名的硬编码字符串,不必说,这种方法非常单调无奇,并且容易导致错误。本文将要演示怎样使用新的C99特性,在运行时获取函数名。
那么怎样以编程的方式从当前运行的函数中得到函数名呢?
答案是:使用__FUNCTION__及相关宏。
引出问题
通常,在调试中最让人心烦的阶段,是不断地检查是否已调用了特定的函数。对此问题的解决方法,一般是添加一个cout或printf()——如果你使用C语言,如下所示:
void myfunc()
{
cout<<"myfunc()"<<endl;
其他代码
}
通常在一个典型的工程中,会包含有数千个函数,要在每个函数中都加入一条这样的输出语句,无疑难过上“蜀山”啊,因此,需要有一种机制,可以自动地完成这项操作。
获取函数名
作为一个C++程序员,可能经常遇到 __TIME__、__FILE__、__DATE__这样的宏,它们会在编译时,分别转换为包含编译时间、处理的转换单元名称及当前时间的字符串。
在最新的ISO C标准中,如大家所知的C99,加入了另一个有用的、类似宏的表达式__func__,其会报告未修饰过的(也就是未裁剪过的)、正在被访问的函数名。请注意,__func__不是一个宏,因为预处理器对此函数一无所知;相反,它是作为一个隐式声明的常量字符数组实现的:
static const char __func__[]="function-name";
在function-name处,为实际的函数名。为激活此特性,某些编译器需要使用特定的编译标志,请查看相应的编译器文档,以获取具体的资料。
有了它,我们可免去大多数通过手工修改,来显示函数名的苦差事,以上的例子可如下所示进行重写:
void myfunc()
{
cout<<"__FUNCTION__"<<endl;
}
官方C99标准为此目的定义的__func__标识符,确实值得大家关注,然而,ISO C++却不完全支持所有的C99扩展,因此,大多数的编译器提供商都使用 __FUNCTION__取而代之,而 __FUNCTION__通常是一个定义为 __func__的宏,之所以使用这个名字,是因为它已受到了大多数的广泛支持。
在Visual Studio 2005中,默认情况下,此特性是激活的,但不能与/EP和/P编译选项同时使用。请注意在IDE环境中,不能识别__func__,而要用__FUNCTION__代替。
Comeau的用户也应使用 __FUNCTION__,而不是 __func__。
C++ BuilderX的用户则应使用稍稍不同的名字:__FUNC__。
GCC 3.0及更高的版本同时支持 __func__和__FUNCTION__。
一旦可自动获取当前函数名,你可以定义一个如下所示显示任何函数名的函数:
void show_name(const char* name)
{
cout<<name<<endl;
}
void myfunc()
{
show_name(__FUNCTION__);输出:myfunc
}
void foo()
{
show_name(__FUNCTION__);输出:foo
}
因为 __FUNCTION__会在函数大括号开始之后就立即初始化,所以,foo()及myfunc()函数可在参数列表中安全地使用它,而不用担心重载。
签名与修饰名
__FUNCTION__特性最初是为C语言设计的,然而,C++程序员也会经常需要有关他们函数的额外信息,在Visual Studio 2005中,还支持另外两种非标准的扩展特性:__FUNCDNAME__与 __FUNCSIG__,其分别转译为一个函数的修饰名与签名。函数的修饰名非常有用,例如,在你想要检查两个编译器是否共享同样的ABI时,就可派得上用场,另外,它还能帮助你破解那些含义模糊的链接错误,甚至还可用它从一个DLL中调用另一个用C++链接的函数。在下例中,show_name()报告了函数的修饰名:
void myfunc()
{
show_name(__FUNCDNAME__);输出:?myfunc@@YAXXZ
}
一个函数的签名由函数名、参数列表、返回类型、内含的命名空间组成。如果它是一个成员函数,它的类名和const/volatile限定符也将是签名的一部分。以下的代码演示了一个独立的函数与一个const成员函数签名间的不同之处,两个函数的名称、返回类型、参数完全相同:
void myfunc()
{
show_name(__FUNCSIG__); void __cdecl myfunc(void)
}
struct S
{
void myfunc() const
{
show_name(__FUNCSIG__); void __thiscall S::myfunc(void) const
}
};
C语言中!x的等价表达式 x==0
C语言学习笔记(四)extern函数
C语言学习笔记(四):extern函数详解extern关键字在C语言中主要用于声明在其他文件中定义的变量或函数,使得当前文件能够访问这些外部实体。在函数声明场景下,extern可显式表明函数定义存在于其他编译单元,但实际使用时通常可省略(因函数默认具有外部链接性)。
一、extern函数的核心作用跨文件函数调用:当函数定义在另一个源文件(.c文件)中时,需在当前文件中通过extern声明该函数原型,才能合法调用。显式声明外部链接性:虽函数默认具有外部链接性(无需显式写extern),但显式声明可增强代码可读性,明确函数来源。二、extern函数的使用方法1.基本语法extern返回类型函数名(参数列表);示例:若sub71.c中定义了void print(int x[], int n),在main.c中调用前需声明:extern void print(int x[], int n);//显式声明//或直接写(隐式extern,更常见)void print(int x[], int n);2.实际案例分析参考提供的Matlab与C混合编程示例:
文件结构:main.c(主函数文件):
#include<mex.h>extern void print(int x[], int n);//声明外部函数extern void output();//声明外部函数void mexFunction(){ int x[10]={1,2,3,4,5}; print(x, 5);//调用外部函数 output();}sub71.c(外部函数定义文件):
#include<mex.h>void print(int x[], int n){//函数定义 for(int i=0; i<n; i++){ printf("x[%d]=%-3dn", i, x[i]);}}void output(){ printf("Xiamen Universityn");}3.编译与链接关键命令:需同时编译所有相关文件,确保链接器能找到所有函数定义:mex main.c sub71.c# Matlab的mex命令编译多个C文件若使用GCC等传统编译器:
gcc main.c sub71.c-o program三、extern函数的注意事项函数默认外部链接性:C语言中,函数定义默认具有外部链接性(可在其他文件中调用),因此extern声明通常可省略。显式声明多用于强调或明确依赖关系。
声明与定义的区别:
声明:告诉编译器函数存在(如extern void func();),不分配内存。
定义:提供函数实现(如void func(){...}),分配内存。
头文件的使用:实际项目中,通常将函数声明放在头文件(.h)中,通过#include引入,避免手动重复声明:
myfuncs.h:
#ifndef MYFUNCS_H#define MYFUNCS_Hvoid print(int x[], int n);void output();#endifmain.c和sub71.c均包含该头文件。
避免重复定义:确保函数定义仅出现在一个源文件中,否则链接时会报错(重复定义)。
四、extern与变量的区别虽同为外部链接性控制,但extern在变量和函数中的用法有差异:
变量:需显式用extern声明(如extern int x;),且定义时需初始化(如int x= 10;)。函数:默认外部链接,extern可省略,定义时直接写函数体。五、常见错误与解决未声明的标识符:
错误:调用未声明的外部函数。
解决:确保在调用前声明函数原型,或包含对应头文件。
未定义的引用:
错误:链接时找不到函数定义。
解决:检查是否漏编译定义该函数的源文件。
重复定义:
错误:多个源文件定义同名函数。
解决:保留一个定义,其余文件仅声明或调用。
六、总结extern函数的核心:通过声明实现跨文件函数调用,增强代码模块化。最佳实践:将函数声明集中管理在头文件中。
编译时确保所有相关文件参与链接。
避免显式extern过度使用(函数默认已具备外部链接性)。
通过合理使用extern,可以构建结构清晰、易于维护的多文件C语言项目。
关于本次c语言函数隐式声明和c语言隐式声明怎么解决的问题分享到这里就结束了,如果解决了您的问题,我们非常高兴。