C语言——定义&&声明

1.变量的定义&声明

变量的声明有两种情况:

本文是小结了一下C语言的函数语法,详细介绍了C语言函数语法的概述、函数的定义、函数的返回值、函数调用等7个方面的内容,非常详细,这里推荐给小伙伴们。

<1>一种是需要建立存储空间的。例如:int a
在声明的时候就已经建立了存储空间。

1、概述

<2>另一种是不需要建立存储空间的。 例如:extern int a
其中变量a是在别的文件中定义的。

在C语言中,函数是来完成子程序或者某个模块的功能。有主程序或者其他函数调用,其他函数之间可以相互调用。同一函数可以被一个或多个函数任意次调用。

前者是“定义性声明(defining
declaration)”或者称为“定义(definition)”,而后者是“引用性声明(referncing
declaration)”。

注意:

从广义的角度来讲声明中包含着定义,即定义是声明的一个特例,所以并非所有的声明都是定义,例如:int
a 它既是声明,同时又是定义。然而对于 extern a 来讲它只是声明不是定义。

a、一个C程序有一个或多个程序模块组成,每一个程序模块作为一个源程序文件。一个源文件可以为多个C程序共用。

一般的情况下我们常常这样叙述,把建立空间的声明称之为“定义”,而把不需要建立存储空间的声明称之为“声明”。很明显我们在这里指的声明是范围比较窄的,即狭义上的声明,也就是说非定义性质的声明,例如:在主函数中:

b、程序在编译时是以源文件为单位进行编译,而不是以函数为单位进行编译。一个源文件就是一个编译单元

int main() {  extern int A;  //这是个声明而不是定义,声明A是一个已经定义了的外部变量  //注意:声明外部变量时可以把变量类型去掉如:extern A;  dosth(); //执行函数  }  

c、C程序的执行是从main函数开始,也是在main函数中结束整个程序

int A; //是定义,定义了A为整型的外部变量

d、所有函数是平行的,即在定义函数时是分别进行的,是互相独立的。一个函数并不从属于另一个函数,即函数不能嵌套定义。函数之间可以相互调用,但不能调用main函数。main函数是系统调用的。

外部变量的“定义”与外部变量的“声明”是不相同的,外部变量的定义只能有一次,它的位置是在所有函数之外,而同一个文件中的外部变量声明可以是多次的,它可以在函数之内(哪个函数要用就在那个函数中声明)也可以在函数之外(在外部变量的定义点之前)。系统会根据外部变量的定义(而不是根据外部变量的声明)分配存储空间的。对于外部变量来讲,初始化只能是在“定义”中进行,而不是在“声明”中。所谓的“声明”,其作用,是声明该变量是一个已在后面定义过的外部变量,仅仅是为了“提前”引用该变量而作的“声明”而已。extern
只作声明,不作任何定义。

e、从用户使用角度看,函数分为两种:

(我们声明的最终目的是为了提前使用,即在定义之前使用,如果不需要提前使用就没有单独声明的必要,变量是如此,函数也是如此,所以声明不会分配存储空间,只有定义时才会分配存储空间。)

(1)标准函数。标准函数即函数库,它由系统提供。不同的C语言编译系统提供的标准函数的数量和功能或有不同,但是基本函数是一样的。

注:如果声明有初始化式,那么它可以被当作是定义,即使声明标记为extern:

(2)用户自定义函数。

  extern double pi=3.1416; //定义

f、从函数形式看,函数分为两种:

  虽然使用了extern,但这条语句还是定义了pi,分配并初始化了存储空间。只有当extern声明位于函数外部时,才可以含有初始化式。

(1)有参函数。

任何在多个文件中使用的变量都需要有与定义分离的声明。在这种情况下,一个文件含有变量的定义,使用该变量的其他文件则包含该变量的声明(而不是定义)。

(2)无参函数。在调用函数时,主调函数通过参数向被调函数传递数据

用static来声明一个变量的作用有二:

2、函数的定义

(1)对于局部变量用static声明,则是为该变量分配的空间在整个程序的执行期内都始终存在。

定义无参函数的一般形式:

(2)外部变量用static来声明,则该变量的作用只限于本文件模块。

类型表示符 函数名()

2.函数的定义&声明

{

声明:一般在头文件里,对编译器说:这里我有一个函数叫function()
让编译器知道这个函数的存在。声明是告诉编译器有这么个变量,但并不实现。

声明部分

定义:就是实现这个变量,真正在内存(堆或栈中)为此变量分配空间。定义一般在源文件里,具体就是函数的实现过程,写明函数体。

语句部分

对函数的“定义”和“声明”不是一回事。“定义”是指对函数功能的确立,包括指定函数名,函数值类型、形参类型、函数体等,它是一个完整的、独立的函数单位。而“声明”
的作用则是把函数的名字、函数类型以及形参类型、个数和顺序通知编译系统,以便在调用该函数时系统按此进行对照检查(例如函数名是否正确,实参与形参的类型和个数是否一致)。

}

从程序中可以看到对函数的声明与函数定义中的函数首部基本上是相同的。因此可以简单地照写已定义的函数的首部,再加一个分号,就成为了对函数的“声明”。在函数声明中也可以不写形参名,而只写形参的类型。

ca88,【实】定义无参函数,输出“Hello World”字符串

在C语言中,函数声明称为函数原型(function
prototype)。使用函数原型是ANSI
C的一个重要特点。它的作用主要是利用它在程序的编译阶段对调用函数的合法性进行全面检查。

复制代码代码如下:

说明:

void show()

<1>
以前的C版本的函数声明方式不是采用函数原型,而只是声明函数名和函数类型。
如:float add();
不包括参数类型和参数个数。系统不检查参数类型和参数个数。新版本也兼容这种用法,但不提倡这种用法,因为它未进行全面的检查。

{

<2>
实际上,如果在函数调用前,没有对函数作声明,则编译系统会把第一次遇到的该函数形式(函数定义或函数调用)作为函数的声明,并将函数类型默认为int
型。如一个max函数,调用之前没有进行函数声明,编译时首先遇到的函数形式是函数调用”max(a,
b)”,由于对原型的处理是不考虑参数名的,因此系统将max()加上int作为函数声明,即int
max();
因此不少教材说,如果函数类型为整型,可以在函数调用前不必作函数声明。但是使用这种方法时,系统无法对参数的类型做检查。或调用函数时参数使用不当,在编译时也不会报错。因此,为了程序清晰和安全,建议都加以声明为好。

printf(“Hello World”);

<3>
如果被调用函数的定义出现在主调函数之前,可以不必加以声明。因为编译系统已经先知道了已定义的函数类型,会根据函数首部提供的信息对函数的调用作正确性检查。

}

<4>
如果已在所有函数定义之前,在函数的外部已做了函数声明,则在各个主调用函数中不必对所调用的函数再作声明。

定义有参函数的一般形式:

3.C语言中头文件和源文件写法有什么区别?

类型标识符 函数名(形式参数列表)

相同点:
头文件与源代码都是源文件,都用纯文本文件存储,都需要遵照C/C++语言规范来写,都可以使用类型定义和常、变量定义,都可以包含函数的声明和实现,都能包含宏定义,都使用相同的调用规范。

{

不同点:
C/C++的编译器规定源代码文件必须包含函数入口,即main函数,或者winmain函数。而头文件不得包含函数入口,也就是说,头文件不可以单独编译成一个程序,仅仅包含程序片断或者仅仅定义常、变量。

声明部分

关联:
头文件是专为源代码调用而写的静态包含文件,可被源代码文件中的#include编译预处理指令解释,而将头文件完整拷贝到源代码的指令处,从而在编译时相当于在源代码中插入了函数声明或者实现。

语句部分

某些头文件包含了静态库中的函数调用声明,包含了LIB文件的调用信息,编译时静态链接进程序;而某些头文件是专为方便API调用而写的,里面包含了动态链接库的调用信息和规范,只有在运行时将DLL载入内存提取函数执行。

}

也就是说,其实你将头文件里的信息完全写到源代码里,是可以实现完全相同的功能的。如果你想要学写头文件,可以参照标准C/C++库的h文件。

【例】传递两个int的参数a和b,然后计算a加b的和,然后返回a加b的和。

 

复制代码代码如下:

 

int add(int a, int b)

 

{

 

int t = 0; // 声明部分


t = a + b;

return t;

}

空函数就是没有函数体的函数,如:

void test(){ … }

调用空函数时,什么工作也不做,没有任何实际作用。在程序设计中往往程序初期只实现基本功能,对于高级功能我们可以提供一空函数,以后实现该空函数。

定义函数时指定的参数称为“形参”,调用函数时传递的参数称为“实参”。

a、在定义函数中指定的形参,在未出现函数调用时,它们并不占内存中的存储单元。只有在发生函数调用时,函数中的形参才被分配内存单元。在调用结束后,形参所占用的内存单元也被释放。

b、实参可以是常量、变量或表达式,如:max(3, a+b);

c、在被定义的函数中,必须指定形参的类型

d、实参和形参的类型应相同或赋值兼容。

e、在C语言中,实参向形参数据传递是“值传递”,单向传递,只由实参传递给形参,而形参不能传递给实参。在内存中形参和实参是不同的内存单元。因此,改变形参的值不会影响实参的值。

3、函数的返回值

通常,希望通过函数调用是主调函数能得到一个确定的值,这就是函数的返回值。

a、函数的返回值是通过函数中的return语句获得的。return语句将被调用函数中的一个确定值带回主调函数中去。如果需要从被调用函数带回一个函数值,被调函数中必须包含return语句。如果不需要从被调函数带回函数值可以不需要return语句。一个函数可以有多个return语句,执行到哪个return语句,哪个语句就起作用。

b、函数值的类型。既然函数有返回值,这个值当然应属于某一个确定的类型,应当在定义函数时指定函数值的类型。C语言中,凡是不加类型说明的函数,自动按整型处理。在Turbo
C 2.0中不写返回类型可以通过,Turbo C++
3.0编译时不能通过。所以建议读者在定义时对所有函数都指定函数类型。

c、在定义函数是指定的类型应该与return返回类型一致。如果函数值的类型和return语句中表达式值类型不一致,则按照函数类型为准。

d、对于不带回值的函数,应当用“void”定义函数为“无类型”或称“空类型”。此时函数体中不得出现return语句。

4、函数调用

函数调用的一般形式为:函数名(实参列表);如:

复制代码代码如下:

int a = max(2, 4);

按函数在程序中出现的位置来分,可以有以下3种函数调用方式:

a、函数语句。把函数调用作为一个语句。此时不许要函数返回值。如: show();

b、函数表达式。函数出现在一个表达式中,这种表达式称为函数表达式,需要函数返回一个确定的值。如:a
= 2*max(a, b);

c、函数参数。函数调用作为一个函数的参数。如:m = max(a, max(b, c));

5、对被调函数的声明和函数原型

在一个函数中调用另一个函数需要具备以下条件:

a、被调函数必须是已经存在的函数

b、如果使用函数库,还应该在文本文件开头用#include命令将调用有关库函数时所需用到的信息“包含”到本文件中来。如:#include

其中“stdio.h”是一个“头文件”。在stdio.h文件中包含了输入输出库函数所用到的一些宏定义信息。如果不包含stdio.h文件的信息,就无法使用输入输出库中的函数。

c、如果使用用户自己定义的函数,而该函数的位置在调用它的函数的后面(在同一个文件中),应该在主调函数中对被调用的函数进行声明。如:

复制代码代码如下:

#include

void main()

{

float add(float x, float y); // 或者: float add(float, float);

float a, b, c;

scanf(“%f%f”, &a, &b);

c = add(a, b);

printf(“sum is %f\n”, c);

}

float add(float x, float y)

{

return x + y;

}

6、局部变量和全局变量

a、局部变量

在一个函数内部定义的变量是内部变量,它只能在函数范围内才有效,也就是说只在本函数内才能使用它们,在此函数外部不能使用这些变量。如:

复制代码代码如下:

float f1(int a)

{

int b, c;     // b、c变量只在f1函数内部有效

}

char f2(int x, int y)

{

int i, j;     // i、j变量在f2函数内部有效

}

void main()

{

int m, n;  // m、n变量在main函数中有效

}

注意:

1)主函数中定义的变量(m、n)也只在主函数中有效,而不因为在主函数中定义而在整个文件或程序中有效。主函数也不能使用其他函数中定义的变量。

2)不同函数中可以使用相同名字的变量,它们代表不同的对象,互不干扰。

发表评论

电子邮件地址不会被公开。 必填项已用*标注