《C编程.开始C》5.变量

像大多数编程语言一样,C能够使用和处理命名变量及其内容。变量只是用于指代内存中某个位置的名称 – 一个包含我们正在使用的值的位置。

将变量视为值的占位符可能会有所帮助。您可以将变量视为等于其指定值。因此,如果你有一个可变初始化(设定为等于)到4,那么它遵循i + 1的将等于5

由于C是一种相对低级的编程语言,因此在C程序可以利用内存来存储变量之前,它必须声明存储变量值所需的内存。这是通过声明变量来完成的。声明变量是C程序显示所需变量数量,它们将被命名的内容以及它们需要多少内存的方式。

在C编程语言中,在管理和处理变量时,了解变量的类型和这些类型的大小非常重要。类型的大小是存储此类型的一个值所需的计算机内存量。由于C是一种相当低级的编程语言,因此类型的大小可以特定于所使用的硬件和编译器 – 也就是说,在一种类型的机器上使用该语言的方式可能与它的工作方式不同另外一个。

C中的所有变量都是键入的。也就是说,声明的每个变量都必须指定为某种类型的变量。

声明,初始化和分配变量

这是一个声明一个整数的例子,我们称之为some_number。(注意行末尾的分号;这是编译器将一个程序语句与另一个程序语句分开的方式。)

int  some_number ;

这个语句意味着我们为一个名为some_number的变量声明了一些空间,该变量将用于存储整数数据。请注意,我们必须指定变量将存储的数据类型。有特定的关键字可以执行此操作 – 我们将在下一节中查看它们。

可以使用一个语句声明多个变量,如下所示:

诠释 aNumber的, anothernumber , yetanothernumber ;

我们还可以同时声明为变量分配一些内容。

int  some_number  =  3 ;

这称为初始化

在C的早期版本中,变量必须在块的开头声明。在C99中允许任意混合声明和语句 – 但这样做并不常见,因为它很少需要,一些编译器仍然不支持C99(可移植性),并且它可能因为它不常见而激怒其他程序员(可维护性)。

在声明变量之后,您可以稍后使用如下语句为变量赋值:

some_number  =  3 ;

您还可以为变量分配另一个变量的值,如下所示:

  
aNumber的 =  anothernumber ;

或者使用一个语句为多个变量分配相同的值:

aNumber的 =  anothernumber  =  yetanothernumber  =  3 ;

这是因为赋值x = y返回赋值的值。x = y = z实际上是x =(y = z)的简写。

命名变量

C中的变量名由字母(大写和小写)和数字组成。下划线字符(“_”)也是允许的。名称不得以数字开头。与某些语言(如Perl和某些BASIC方言)不同,C不对变量名使用任何特殊的前缀字符。

有效(但不是很具描述性)的C变量名称的一些示例:

foo 
Bar 
BAZ 
foo_bar 
_foo42 
_ 
QuUx

无效C变量名称的一些示例:

2F OO     (一定 不能 开始 与 一个 数字)
我 富  (空格 不是 允许 在 名)
$ FOO     ($  不是 允许 -  只有 字母, 和 _ )
,而   (语言 关键字 不能 被 用来 作为 名称)

如最后一个示例所示,某些单词在语言中被保留为关键字,并且这些单词不能用作变量名。

不允许在同一范围内对多个变量使用相同的名称。因此,在与其他开发人员合作时,应采取措施避免对全局变量或函数名使用相同的名称。一些大型项目遵循命名准则[以避免重复名称和一致性。

此外,还有一些名称,虽然不是语言关键字,但由于某种原因而保留。例如,C编译器可能会“在幕后”使用某些名称,这可能会导致尝试使用它们的程序出现问题。此外,还保留了一些名称,以备将来在C标准库中使用。准确确定保留名称的规则(以及它们保留在哪些上下文中)太复杂,无法在此处描述[,作为初学者,无论如何都不需要担心它们。现在,请避免使用以下划线字符开头的名称。

C变量的命名规则也适用于命名其他语言结构,如函数名,结构标记和宏,所有这些都将在后面介绍。

文字

在程序中,您明确指定值而不是引用变量或某种其他形式的数据,该值称为文字。在上面的初始化示例中,3是文字。文字可以采用由其类型定义的形式(很快就会更多),或者可以使用十六进制(十六进制)表示法直接将数据插入变量,而不管其类型如何。[十六进制数字总是以0x开头。但是现在,你可能不应该太担心十六进制。

四种基本数据类型

在标准C中,有四种基本数据类型。他们是intcharfloat,和double

int类型

该INT类型“整数”的形式存储整数。整数通常是一个机器字的大小,在大多数现代家用PC上是32位(4个八位字节)。文字的例子是整数(整数),例如1,2,3,10,100 ……当int是32位(4个八位字节)时,它可以存储-2147483648和2147483647之间的任何整数(整数)。位字(数字)有可能表示4294967296种可能性中的任何一个(2到32的幂)。


如果要声明新的int变量,请使用int关键字。例如:

int  numberOfStudents , i , j = 5 ;

在这个声明中我们声明了3个变量,numberOfStudents,i和j,j这里分配了文字5。

char类型

char类型能够保存执行字符集的任何成员。它存储与int(即整数)相同类型的数据,但通常具有一个字节的大小。宏CHAR_BIT指定一个字节的大小,它指定char(字节)中的位数。在标准C中,它永远不会少于8位。类型的变量char最常用于存储字符数据,因此它的名称。大多数实现使用ASCII字符集作为执行字符集,但除非实际值很重要,否则最好不要知道或关心它。

字符文字的例子是’a’,’b’,’1’等,以及一些特殊字符,例如’ \0‘(空字符)和’ \n‘(换行符,回忆“Hello,World”)。请注意,该char值必须包含在单引号中。

当我们初始化一个字符变量时,我们可以采用两种方式。一个是首选,另一个是糟糕的编程实践。

第一种方式是写

char  letter1  =  'a' ;

这是一个很好的编程习惯,因为它允许阅读代码的人理解letter1正在用字母’a’初始化以开始。

第二种方式是编写字母字符时应该使用的 方法

char  letter2  =  97 ;  / *在ASCII中,97 ='a'* /

一些人认为这是非常糟糕的做法,如果我们用它来存储一个字符,而不是一个小数字,如果有人读你的代码,大多数读者被迫查找哪个字符对应于数字97编码方案。最后,letter1letter2存储相同的东西 – 字母’a’,但第一种方法更清晰,更容易调试,更直接。

值得一提的是,数字的字符与其对应的数字表示不同,即“1”不等于1.简而言之,任何单个条目都包含在“单引号”中。

还有一种类型的文字需要与字符连接来解释:字符串文字。字符串是一系列字符,通常用于显示。它们被双引号包围(“”,而不是”)。字符串文字的一个例子是“Hello,World”示例中的“Hello,World!\ n”。

字符串文字分配给字符数组,稍后将描述数组。例:

const  char  MY_CONSTANT_PEDANTIC_ITCH []  =  “学习使用情境。\ n ” ; 
printf (“变量名后的方括号表示它是指向数组元素类型大小的内存块字符串的指针。\ n ” );

float类型

float浮点数的缩写。它存储实数的不精确表示,包括整数和非整数值。它可以用于远大于最大值的数字intfloat文字必须以F或f为后缀。例子是:3.1415926f,4.0f,6.022e + 23f。

重要的是要注意浮点数是不精确的。像0.1f这样的数字不能完全表示为floats,但会有一个小错误。非常大且非常小的数字将具有较低的精度,并且算术运算有时由于缺乏精度而不是关联的或分配的。尽管如此,浮点数最常用于逼近实数,并且对它们的操作在现代微处理器上是有效的。[2] 在维基百科上更详细地解释了浮点运算

float变量可以使用float关键字声明。A float只是一个机器字大小。因此,当需要精度低于双倍精度时使用。

所述双型

在双和浮动类型非常相似。该浮式允许你存储的单精度浮点数,而双关键字可以存储双精度浮点数-实数,换句话说。它的大小通常是两个机器字,或大多数机器上的8字节。双文字的例子是3.1415926535897932,4.0,6.022e + 23(科学记数法)。如果使用4而不是4.0,则4将被解释为int。

浮动和双打之间的区别是因为这两种类型的大小不同。当C首次使用时,空间是最小的,因此明智地使用float而不是double可以节省一些内存。如今,随着内存越来越自由,您很少需要像这样保存内存 – 一致地使用双打可能更好。实际上,当您声明一个float变量时,一些C实现使用双精度而不是浮点数。

如果要使用double变量,请使用double关键字。

sizeof 

如果您对任何变量实际使用的内存量有任何疑问(这也适用于我们稍后将讨论的类型),您可以使用sizeof运算符来确定。(为了完整起见,重要的是要提到sizeof是一元运算符,而不是函数。)它的语法是:

sizeof  对象
sizeof (类型)

上面的两个表达式返回指定对象和类型的大小(以字节为单位)。返回类型是size_t(在标头<stddef.h>中定义),它是无符号值。这是一个示例用法:

size_t  大小; 
int  i ; 
size  =  sizeof (i );

尺寸将被设置为4,假设CHAR_BIT被定义为8,和一个整数是32个位宽。sizeof的结果值是字节数。

请注意,当sizeof应用于char时,结果为1; 那是:

sizeof (char )

总是返回1。

数据类型修饰符

可以通过在某些修改器前面添加任何数据类型来更改数据存储。

long short是修饰符,使数据类型可以使用更多或更少的内存。该 INT关键字不必遵循短期和长期的关键词。这种情况最常见。甲短,可以使用其中的值落入范围较小比一的内INT,通常-32768到32767长可以用于包含值的扩展范围。不保证 short使用的内存少于 int,也不保证 long占用的内存多于 int。只保证sizeof(short)<= sizeof(int)<= sizeof(long)。通常,short是2个字节,int是4个字节,long是4或8个字节。现代C编译器还提供long long,通常是8字节整数。

在上述所有类型中,一位用于指示值的符号(正或负)。如果您确定变量永远不会保持负值,则可以使用unsigned修饰符将该位用于存储其他数据,从而有效地将值的范围加倍,同时强制这些值为正值。的无符号的说明符还可以没有尾随使用INT,在这种情况下,大小默认为一个的INT。还有一个签名修饰符,它是相反的,但它没有必要,除了char的某些用法,并且很少使用,因为所有类型(除了char)都是默认签名的。

该长调节剂也可与采用双创建一个长双类型。这种浮点类型可以(但不是必须)具有比double类型更高的精度。

要使用修饰符,只需使用数据类型和相关修饰符声明变量:

unsigned  short  int  usi ;   / *完全限定 -  unsigned short int * / 
short  si ;                 / * short int * / 
unsigned  long  uli ;        / * unsigned long int * /

const限定符

使用const限定符时,必须在声明时初始化声明的变量。然后不允许更改它。

虽然永远不会改变的变量的想法似乎没有用,但是有充分的理由使用const。首先,许多编译器在知道数据永远不会改变时,可以对数据执行一些小的优化。例如,如果在计算中需要π的值,则可以声明pi的const变量,因此程序或其他人编写的其他函数不能更改pi的值。

请注意,如果尝试更改const变量,则符合标准的编译器必须发出警告- 但在这样做之后,编译器可以自由地忽略const限定符。

魔术数字

编写C程序时,您可能会想要编写依赖于某些数字的代码。例如,您可能正在为杂货店编写程序。这个复杂的程序有成千上万行代码。程序员决定代表一罐玉米的成本,目前为99美分,作为整个代码的字面值。现在,假设一罐玉米的成本变为89美分。程序员现在必须进入并手动将每个99美分的条目更改为89.虽然这不是一个大问题,考虑到许多文本编辑器的“全局查找 – 替换”功能,考虑另一个问题:罐头的成本绿豆最初也是99美分。要可靠地更改价格,您必须查看数字99的每次出现。

C具有某些功能以避免这种情况。此功能大致相同,但一种方法在一种情况下可用于另一种情况。

使用const关键字

该常量关键字有助于消除幻数。通过在块的开头声明变量const corn,程序员可以简单地更改该const,而不必担心在其他地方设置值。

还有另一种避免幻数的方法。它比const更灵活,在许多方面也存在更多问题。它还涉及预处理器,而不是编译器。看哪…

#define 

编写程序时,可以创建所谓的,因此当计算机读取代码时,它将用指定的表达式替换单词的所有实例。

这是一个例子。如果你写

#define PRICE_OF_CORN 0.99

例如,当您想要打印玉米的价格时,您使用的是单词PRICE_OF_CORN而不是数字0.99 – 预处理器将替换所有PRICE_OF_CORN0.99的实例,编译器将其解释为double0.99 的字面值。预处理器执行替换,即PRICE_OF_CORN替换为0.99,这意味着不需要分号。

值得注意的是,它#define具有与许多文本编辑器/文字处理器中的“查找和替换”功能基本相同的功能。

出于某些目的,#define可以有害地使用,并且通常优选使用,const如果#define不必要的话。例如,可以#define将宏DOG视为数字3,但如果您尝试打印宏,认为DOG代表可以在屏幕上显示的字符串,则程序将出错。#define也不考虑类型。它无视程序的结构,在任何地方替换文本(实际上,无视范围),这在某些情况下可能是有利的,但可能是有问题的错误的根源。

您将#define在本文后面看到该指令的更多实例。#define在所有大写中写d个单词是一个很好的约定,所以程序员会知道这不是你声明的变量而是#defined宏。没有必要结束预处理器指令,例如#define用分号; 事实上,如果你这样做,一些编译器可能会警告你代码中不必要的令牌。

范围

在“基本概念”部分中,介绍了范围的概念。重新审视本地类型和全局类型之间的区别以及如何声明每个类型的变量非常重要。要声明局部变量,请将声明放在变量被视为本地的块的开头(即在任何非声明性语句之前)。要声明全局变量,请在任何块之外声明变量。如果变量是全局变量,则可以从程序中的任何位置读取和写入变量。

全局变量不被认为是良好的编程实践,应尽可能避免。它们会抑制代码可读性,创建命名冲突,浪费内存,并且可能会产生难以跟踪的错误。过度使用全局变量通常是懒惰或设计不佳的标志。但是,如果存在局部变量可能会产生更多钝的和不可读的代码的情况,那么使用全局变量并不可耻。

其他修饰符

为了完整起见,此处包含更多标准C提供的修饰符。对于初级程序员,staticextern可能很有用。volatile对高级程序员更感兴趣。registerauto在很大程度上已被弃用,对初级或高级程序员来说通常不感兴趣。

静态

static有时是一个有用的关键字。这是一个常见的误解,唯一的目的是使变量留在记忆中。

将函数或全局变量声明为static时,无法通过项目中其他文件的extern(参见下文)关键字访问函数或变量。这称为静态链接

当您将局部变量声明为静态时,它就像任何其他变量一样创建。但是,当变量超出范围(即它本地的块已完成)时,变量将保留在内存中,保留其值。变量保留在内存中,直到程序结束。虽然此行为类似于全局变量,但静态变量仍遵循范围规则,因此无法在其范围之外访问。这称为静态存储持续时间

声明为static的变量默认情况下初始化为零(或指针,NULL [无效<ref>标记;无效名称,例如太多)。它们可以在声明时显式初始化为任何量值。初始化只在编译时进行一次。

您可以(至少)以两种不同的方式使用静态。考虑一下这段代码,并想象它位于一个名为jfile.c的文件中:

#include  <stdio.h>
 
static  int  j  =  0 ;
 
void  up (void )
{ 
   / * k在程序启动时设置为0。然后
    ,对于程序的其余部分,
该行被“忽略” *(即,每次    调用
up()* 时k都不设置为0     * / 
   static  int  k  =  0 ; 
   j ++ ; 
   k ++ ; 
   printf (“up()调用.k =%2d,j =%2d \ n ” , k  , j ); 
}
 
void  down (void )
{ 
   static  int  k  =  0 ; 
   j - ; 
   k - ; 
   printf (“down()调用.k =%2d,j =%2d \ n ” , k  , j ); 
}
 
int  main (void )
{ 
   int  i ;
     
   / *调用up函数3次,然后down函数2次* / 
   for  (i  =  0 ;  i  <  3 ;  i ++ )
      up (); 
   for  (i  =  0 ;  i  <  2 ;  i ++ )
      down ();
    
   返回 0 ; 
}

j变量是由两个向上访问和向下,保持它的价值。该k变量也保留自己的价值,但它们是两个不同的变量,一个在他们的每一个作用域。静态变量是实现封装的好方法,这是一种来自面向对象思维方式的术语,它有效地意味着不允许对变量进行更改,除非通过函数调用。

运行上面的程序将产生以下输出:

up () 调用。   k =   1 , j =   1 
up () 调用。   k =   2 , j =   2 
up () 调用。   k =   3 , j =   3 
down () 调用。 k =  - 1 , j =   2 
down () 调用。 ķ =  - 2 , Ĵ =   1

特点static变量:

    1.使用的关键字 - 静态
    2.存储 - 内存
    3.默认值 - 零
    4.范围 - 声明它的块的本地
    5.生命周期 - 不同函数调用之间的值仍然存在
    6.关键字可选性 - 使用关键字必须提供

extern 

当文件需要访问另一个文件中的变量时,可以使用 extern,它可能没有 #include d。因此, extern实际上并没有为新变量创造空间,它只是为编译器提供了足够的信息来访问远程变量。

extern变量的特点:

    1.使用的关键字 - 外部
    2.存储 - 内存
    3.默认值 - 零
    4.范围 - 全球(整个计划)
    5.生命周期 - 值一直持续到程序执行结束
    6.关键字选项 - 如果在所有函数之外声明,则为可选

易变的

volatile是一种特殊类型的修饰符,它通知编译器变量的值可以由程序本身以外的外部实体更改。这对于使用优化编译的某些程序是必要的 – 如果变量未定义为 volatile,则编译器可能会认为涉及该变量的某些操作可以安全地进行优化,而事实上它们并非如此。当使用嵌入式系统(程序可能无法完全控制变量)和多线程应用程序时, volatile特别相关。

自动

auto是一个修饰符,它指定一个“自动”变量,该变量在范围内时自动创建,在超出范围时销毁。如果您认为这听起来就像您在声明变量时一直在做的那样,那么您就是对的:块中所有声明的项都是隐式“自动”的。出于这个原因, auto关键字更像是一个琐事问题的答案,而不是一个有用的修饰符,并且有许多非常称职的程序员不知道它的存在。

特点automatic变量:

    1.使用的关键字 - 汽车
    2.存储 - 内存
    3.默认值 - 垃圾值(随机值)
    4.范围 - 定义它的块的本地
    5.生命周期 - 当控件保持在块内时,值仍然存在
    6.关键字选项 - 可选

注册

register是一个提示,编译器在程序运行时通过将其存储在计算机CPU的寄存器中来尝试优化给定变量的存储。大多数优化编译器无论如何都会这样做,因此通常不需要使用此关键字。事实上,ANSI C声明编译器可以忽略这个关键字,如果它需要的话 – 很多人都这么做。Microsoft Visual C ++是一个完全忽略 register关键字的实现示例。

特点register变量:

    1.使用的关键字 - 注册
    2.存储 -  CPU寄存器(可以比从内存中检索更快的值)
    3.默认值 - 垃圾值
    4.范围 - 定义它的块的本地
    5.生命周期 - 当控件保持在块内时,值仍然存在
    6.关键字可选性 - 使用关键字必须提供

猜你想读:《C编程.开始C》6.简单的输出和输入

THE END
分享