C 变量

预计时间: 13分钟

变量是内存中已命名的位置,其中放置程序可修改的值。所有C变量在使用前必需先定义。

C变量类型

变量只不过是我们的程序可以操作的存储区域的名称。C中的每个变量都有一个特定的类型,它决定了变量内存的大小和布局;可以存储在该内存中的值的范围;以及可以应用于变量的一组操作。

标识符

C标准称变量、函数、和各种用户定义的对象的名字为标识符(identifier)。

  • 第一个字符必须是字母或下划线,随后只能取字母、数字或下划线。
  • C标准规定标识符长度任意,但是编译器有限制
  • 外部链接限制6个字符,其它地方限制31个字符,超长部分被编译器忽略。

C变量命名

变量的名称可以由字母、数字和下划线字符组成。它必须以字母或下划线开头。大写和小写字母是不同的,因为 C 区分大小写。

声明和定义

  • 定义:为对象分配内存,只能出现在一个地方。
  • 声明:描述其他地方创建的对象,可多次出现。

变量的声明用于说明变量的属性(主要是变量的类型),而变量的定义除此以外还将引起存储器的分配。

C变量定义

变量定义告诉编译器为变量创建的存储位置和存储空间大小。 变量定义指定数据类型并包含该类型的一个或多个变量的列表,如下所示:

type variable_list;
  • type 必须是有效的 C 数据类型,包括 char、w_char、int、float、double、bool 或任何用户定义的对象;
  • variable_list 可以由一个或多个用逗号分隔的标识符名称组成。

示例:

int    i, j, k;
char   c, ch;
float  f, salary;
double d;
  • int i, j, k; 定义并且声明变量,编译器创建名为 i、j 和 k 的 int 类型变量。

C变量初始化

变量可以在它们的声明中被初始化(分配一个初始值)。初始化程序由一个等号和一个常量表达式组成,如下所示:

type variable_name = value;

示例:

extern int d = 3, f = 5;    // declaration of d and f. 
int d = 3, f = 5;           // definition and initializing d and f. 
byte z = 22;                // definition and initializes z. 
char x = 'x';               // the variable x has the value 'x'.

如果没有指明初始化,那么具有静态存储持续时间的变量被隐式初始化为 NULL(所有字节的值都为 0);所有其他变量的初始值未定义。

C变量声明

变量声明向编译器保证存在具有给定类型和名称的变量,以便编译器可以继续进行进一步编译,而不需要有关变量的完整详细信息。 变量定义只有在编译时才有意义,编译器在链接程序时需要实际的变量定义。

使用多个文件的程序中,可在其中一个文件定义变量,并在其它文件中使用关键字extern声明变量。可以在 C 程序中多次声明一个变量,但它只能在文件、函数或代码块中定义一次。

下面的例子,变量已在顶部声明,但它们已在主函数内部定义和初始化。

#include <stdio.h>

// Variable declaration:
extern int a, b;
extern int c;
extern float f;

int main () {

   /* variable definition: */
   int a, b;
   int c;
   float f;
 
   /* actual initialization */
   a = 10;
   b = 20;
  
   c = a + b;
   printf("value of c : %d \n", c);

   f = 70.0/3.0;
   printf("value of f : %f \n", f);
 
   return 0;
}

编译并执行上述代码时,会产生以下结果 -

value of c : 30
value of f : 23.333334

相同的概念适用于函数声明,您在声明时提供函数名称,并且可以在其他任何地方给出其实际定义。例如 -

// function declaration
int func();

int main() {

   // function call
   int i = func();
}

// function definition
int func() {
   return 0;
}

C中的左值和右值

C中有两种表达式

  • lvalue 『左值』表达式是引用内存位置的表达式。左值可能出现在赋值的左侧或右侧。
  • rvalue 『右值』是指存储在内存中某个地址的数据值。右值是一个不能赋值给它的表达式,这意味着右值可能出现在赋值的右侧而不是左侧。
  • 变量是左值,因此它们可能出现在赋值的左侧。
  • 数字文字是右值,因此它们可能不会被分配并且不能出现在左侧。

看看以下有效和无效的陈述 -

int g = 20; // 有效

10 = 20; // 无效,会产生编译错误

C定义变量的位置

局部变量

函数内定义的变量称为局部变量(local variable)。局部变量仅由定义它的代码块(block)中的语句访问,在被定义的代码块之外是不可见的。

关键字 auto 定义局部变量,因为非全局变量缺省值为 auto,所以一般都省略,几乎不用。

全局变量

函数外定义的变量称为全局变量(global variable)。与局部变量不同,全局变量在程序中都是可见的,可以被任何代码片段使用。在程序的执行期内,全局变量可以保持其值。

形式参数

使用变元的函数,必须定义接受变元值的变量,这种变量称为形式参数(formal parameter)。

形参的作用与函数内的任何局部变量相同。

形式参数和实际参数(形参、实参)

  • 形式参数:用来表示函数定义或函数声明中的输入对象(或标识符)
  • 实际参数:表示传递给函数调用的表达式。

C变量修饰符

C定义了两个修饰符 const 和 volatile 来控制对变量的访问或修改。

const

const 变量是只读的,只能取初始值。例如

const int a = 10;

volatile

volatile 说明变量的值可能以未在程序中明确表达的方法改变。(例如被另一个进程修改)

volatile 防止编译器认为变量的值不变而自动优化表达式。

C变量存储类

存储类定义了C变量或函数的范围(可见性)和生命周期。

C支持4种存储类型说明符

  • extern
  • static
  • register
  • auto

存储类说明符放在变量声明之前

storage_specifier type var_name;

extern

extern存储类用于提供对所有程序文件可见的全局变量的引用。 当您使用“extern”时,无法初始化变量,但是它将变量名称指向先前定义的存储位置。

当你有多个文件并且你定义了一个全局变量或函数, 它也将在其他文件中使用时, 那么在另一个文件中将使用extern来提供定义的变量或函数的引用。 只是为了理解,extern用于在另一个文件中声明一个全局变量或函数。

当有两个或多个文件共享相同的全局变量或函数时,最常使用 extern 修饰符,如下所述。

第一个文件:main.c

#include <stdio.h>
 
int count ;
extern void write_extern();
 
main() {
   count = 5;
   write_extern();
}

第二个文件:support.c

#include <stdio.h>
 
extern int count;
 
void write_extern(void) {
   printf("count is %d\n", count);
}

这里,extern用于在第二个文件中声明count,因为它在第一个文件 main.c 中有定义。现在,编译这两个文件如下 -

gcc main.c support.c

它将生成可执行程序a.out。当这个程序被执行时,它会产生以下结果 -

count is 5

static

C静态局部变量

static存储类指示编译器在程序的生命周期内保持局部变量的存在,而不是在每次进入和离开范围时创建和销毁它。 因此,将局部变量设为静态允许它们在函数调用之间保持其值。

C静态全局变量

static修饰符也可以应用于全局变量。完成后,它会导致该变量的范围被限制在声明它的文件中。

静态全局变量仅在定义该变量的文件中是可见的。 因此,虽然变量是全局的,但其它文件中的子程序无法感知其存在,无法修改之,有效的消除了副作用。

在局部变量无法满足要求的情况下,可以建立一个小文件,其中只放需要的静态全局变量,然后独立编译该文件, 以后使用时不必担心副作用。

static 示例

#include <stdio.h>
 
/* function declaration */
void func(void);
 
static int count = 5; /* global variable */
 
main() {

   while(count--) {
      func();
   }
	
   return 0;
}

/* function definition */
void func( void ) {

   static int i = 5; /* local static variable */
   i++;

   printf("i is %d and count is %d\n", i, count);
}

编译并执行上述代码时,会产生以下结果 -

i is 6 and count is 4
i is 7 and count is 3
i is 8 and count is 2
i is 9 and count is 1
i is 10 and count is 0

register

register寄存器存储类用于定义应存储在寄存器而不是内存中的局部变量。 这意味着该变量的最大大小等于寄存器大小(通常是一个字),并且不能对其应用一元“&”运算符(因为它没有内存位置)。

{
   register int  miles;
}

该寄存器应仅用于需要快速访问的变量,例如计数器。 还应注意,定义“register”并不意味着变量将存储在寄存器中。 这意味着它可能会根据硬件和实现限制存储在寄存器中。

auto

auto存储类是所有局部变量的默认存储类。

{
   int mount;
   auto int month;
}

上面的示例在同一个存储类中定义了两个变量。'auto' 只能在函数内使用,即局部变量。

更新于2022年04月09日