C语言中声明和定义详解

变量声明和变量定义


  • 变量定义:用于为变量分配存储空间,还可为变量指定初始值。程序中,变量有且仅有一个定义。

  • 变量声明:用于向程序表明变量的类型和名字。

  • 定义也是声明,extern声明不是定义

  • 定义也是声明:当定义变量时我们声明了它的类型和名字。
  • extern声明不是定义:通过使用extern关键字声明变量名而不定义它。
    [注意]
    变量在使用前就要被定义或者声明。
    在一个程序中,变量只能定义一次,却可以声明多次。
    定义分配存储空间,而声明不会。
   C++程序通常由许多文件组成,为了让多个文件访问相同的变量,C++区分了声明和定义。

    变量的定义(definition)用于为变量分配存储空间,还可以为变量指定初始值。在程序中,变量有且仅有一个定义。

    声明(declaration)用于向程序表明变量的类型和名字。定义也是声明:当定义变量的时候我们声明了它的类型和名字。可以通过使用extern声明变量名而不定义它。不定义变量的声明包括对象名、对象类型和对象类型前的关键字extern。

    extern声明不是定义,也不分配存储空间。事实上它只是说明变量定义在程序的其他地方。程序中变量可以声明多次,但只能定义一次。

    只有当声明也是定义时,声明才可以有初始化式,因为只有定义才分配存储空间。初始化式必须要有存储空间来进行初始化。如果声明有初始化式,那么它可被当作是定义,即使声明标记为extern。

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

如何清晰的区分变量声明和定义


extern通知编译器变量在其他地方被定义


1.extern告诉编译器变量在其他地方定义了。

例如:

extern int i;       //声明,不是定义
int i;              //声明,也是定义,未初始化

带有初始化式的声明必定式定义


2.如果声明有初始化式,就被当作定义,即使前面加了extern。
只有当extern声明位于函数外部时,才可以被初始化。

例如:

extern double pi=3.141592654;  //定义

函数的声明和定义


3.函数的声明和定义区别比较简单,带有{ }的就是定义,否则就是声明。

例如:

extern double max(double d1,double d2);  //声明

除非有extern关键字,否则都是变量的定义。


4.除非有extern关键字,否则都是变量的定义。

例如:

extern int i; //声明
int i; //定义          

程序模块化设计风格


概要


1. 不要把变量定义放入.h文件,这样容易导致重复定义错误。

永远不要在.h文件中定义变量。定义变量和声明变量的区别在于定义会产生内存分配的操作,是汇编阶段的概念;而声明则只是告诉包含该声明的模块在连接阶段从其它模块寻找外部函数和变量

2. 尽量使用static关键字把变量定义限制于该源文件作用域,除非变量被设计成全局的。

3. 可以在头文件中声明一个变量,在用的时候包含这个头文件就声明了这个变量。

模块化要点


 (1) 模块即是一个.c文件和一个.h文件的结合,头文件(.h)中是对于该模块接口的声明;

(2) 某模块提供给其它模块调用的外部函数及数据需在.h中文件中冠以extern关键字声明;

(3) 模块内的函数和全局变量需在.c文件开头冠以static关键字声明;

(4) 永远不要在.h文件中定义变量!定义变量和声明变量的区别在于定义会产生内存分配的操作,是汇编阶段的概念;而声明则只是告诉包含该声明的模块在连接阶段从其它模块寻找外部函数和变量。

一般情况下头文件中只放变量的声明,因为头文件要被其他文件包含(即#include),如果把定义放到头文件的话,就不能避免多次定义变量,C++不允许多次定义变量,一个程序中对指定变量的定义只有一次,声明可以无数次。

不过有三个例外,一下三中实体的定义也可放到头文件中。

1.值在编译时就已知的const 变量的定义可以放到头文件中
如:const int num(10);
2.类的定义可以放到头文件中
3.inline 函数

这三个实体可以定义在多个源文件中,只要在每个源文件中的定义相同。

示例程序


#include <stdio.h>
#include <stdlib.h>

//  是定义,定义了A为整型的外部变量
//  C中定义的变量默认就是extern的,
//  因此一般来说int a = 10 <==> extern int a = 10;
/*extern */int a = 10;
//如果声明有初始化式,就被当作定义,即使前面加了extern。
//只有当extern声明位于函数外部时,才可以被初始化。

int main(void)
{
    extern int  a;           //  声明一个外部extern的int型变量a
    //  这是个声明而不是定义,声明A是一个已经定义了的外部变量
    //  注意:声明外部变量时可以把变量类型去掉如:extern a;
    printf("a = %d\n", a);



    return EXIT_SUCCESS;
}

这里写图片描述

在这个程序中,我们再函数外部定义了一个变量
注extern int a = 10;只有当extern声明位于函数外部时,才可以被初始化。
我们后面还会提到这个问题

#include <stdio.h>
#include <stdlib.h>



int main(void)
{
    int a;                  //  定义一个变量, 不初始化
    int b = 10;             //  定义一个变量, 同时进行初始化
    extern int  c;          //  声明一个外部extern的int型变量a

    printf("a = %d\n", a);
    printf("b = %d\n", b);
    printf("c = %d\n", c);

    extern int d = 10;


    return EXIT_SUCCESS;
}

在这个程序中,

int a;是个定义,但是未初始化,打印他的值式不确定的,因此编译时会报未初始化的异常。

int b = 10; 是个定义,并且被正确初始化,打印b的值没有问题。

这里写图片描述

但是是个声明,如果要对c进行读写操作,而我们并没有对c进行定义,因此语法检查没有问题,但是在链接时,连接器会找不到c的地址。

这里写图片描述

对于d再明显不过了,前面我们提到过如果声明有初始化式,就被当作定义,即使前面加了extern。但是只有当extern声明位于函数外部时,才可以被初始化。

现在这个定义很明显被gcc编译器认为是错误的。

这里写图片描述

C语言的指针函数是指返回值为指针类型的函数,它们在函数的定义和调用上与普通函数有所不同。 指针函数的定义需要在函数名前加上返回类型为指针的声明,并在函数体内返回一个指针类型的值。例如,我们可以定义一个返回整型指针的函数如下: ```c int* allocateIntArray(int size) { int* array = (int*)malloc(size * sizeof(int)); return array; } ``` 在调用指针函数时,需要用一个与返回类型相匹配的指针变量接收返回值。例如,调用上述函数并将返回值存储在指针变量: ```c int* myArray = allocateIntArray(5); ``` 上述代码,myArray是一个整型指针变量,它用于存储allocateIntArray函数的返回值。这个返回值是一个指向分配的整型数组的指针。 指针函数的调用过程,我们还需要注意以下几点: 1. 为了避免内存泄漏,我们通常需要在不使用指针的时候手动释放内存,可以使用free函数来释放通过malloc分配的内存。 2. 在函数内部分配的内存空间返回给调用者后,我们应该确保在使用这块内存之前不会被其他操作修改或释放掉,否则可能导致程序运行时错误。 3. 指针函数也可以作为参数传递给其他函数,使得我们可以更方便地使用和操作指针类型的数据。 总结来说,指针函数是具有返回值为指针类型的函数,其定义和调用方式与普通函数有所区别。使用指针函数可以更灵活地操作和传递指针类型的数据,但在使用时需要注意内存的释放和确保指针的有效性。
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值