C 语言编程 — 变量与常量
2020-04-02 20:16 云物互联 阅读(621) 评论(0) 编辑 收藏 举报目录
文章目录
前文列表
《程序编译流程与 GCC 编译器》
《C 语言编程 — 基本基本语法》
《C 语言编程 — 数据类型》
变量与常量
C 中有两种类型的表达式:
- 左值(lvalue):指向内存位置的表达式被称为左值(lvalue)表达式,即:命名标识符。左值可以出现在赋值号的左边或右边。
- 右值(rvalue):指的是存储在内存中某些地址的数值,即:实际的数据值。右值是不能对其进行赋值的表达式,也就是说,右值可以出现在赋值号的右边,但不能出现在赋值号的左边。
变量和常量是左值,因此可以出现在赋值号的左边。数值型的字面值是右值,因此不能被赋值,不能出现在赋值号的左边。
变量
变量其实就是一个有名字的数据项。或者说,变量表示一个具有名字的,具有特定属性的存储空间。在程序运行过程中该存储空间内的数据值可以被改变。习惯上,变量名使用小写字母表示,而大写字母常用于字符常量或自定义数据类型名。
变量具有下列三个要素:
- 变量名:内存入口地址
- 数据类型:决定了内存空间的大小
- 数据值:储存在内存空间中的数值
变量的类型
变量名由字母、数字和下划线字符组成。它必须以字母或下划线开头,大小写敏感。有以下几种基本的变量类型:
此外,C 语言也允许定义各种其他类型的变量,比如枚举、指针、数组、结构、共用体等等。
变量的声明
变量声明,向编译器保证变量以指定的类型和名称存在,这样编译器在不需要知道变量完整细节的情况下也能继续进一步的编译。变量声明只在编译时有它的意义,在程序连接时编译器需要实际的变量声明,当程序使用了一个没有被声明的变量名时候就会出现编译错误。
若只是声明定义了变量的数据类型,但却没有为变量赋予数据值,则变量所分配的内存单元中还保留着自上一次内存单元存储的旧数据。
变量的声明有两种情况:
- 一种是需要建立存储空间的。例如:
int a
在声明的时候就已经建立了存储空间。 - 另一种是不需要建立存储空间的,通过使用 extern 关键字声明了变量名而不定义它。 例如:
extern int a
其中变量 a 可以在别的文件中进行定义的。
extern int i; //声明,但不定义
int i; //声明,也是定义
变量的定义
变量定义,就是告诉编译器在何处创建变量的存储,以及如何创建变量的存储。本质就是确定了内存地址的起始地址和内存空间大小。
不带初始化的定义:带有静态存储持续时间的变量会被隐式初始化为 NULL(所有字节的值都是 0),其他所有变量的初始值是未定义的。
int i, j, k;
char c, ch;
float f, salary;
double d;
变量的初始化与赋值
同时完成变量的定义和初始化:把数值填充到内存空间。
extern int d = 3, f = 5; // d 和 f 的声明与初始化
int d = 3, f = 5; // 定义并初始化 d 和 f
byte z = 22; // 定义并初始化 z
char x = 'x'; // 变量 x 的值为 'x'
变量的初始化本质就是与变量定义同时进行的第一次赋值,此外的都称之为赋值。
#include <stdio.h>
// 定义全局变量
int x;
int y;
int addtwonum(){
// 声明使用全局变量
extern int x;
extern int y;
// 为全局变量进行赋值
x = 1;
y = 1;
return x + y;
}
int main(){
int result;
result = addtwonum();
printf("result : %d", result);
return 0;
}
常量
常量就像是常规的变量,常量是固定值,在程序执行期间不会改变,通常使用大写字母标识。
- 整型常量:即整数
- 浮点型常量:又称为实型常量,即小数,可使用小数或指数形式来表示
- 字符型常量:即符号,用单引号括起来的一个字符,具有一般字符和转义字符两种类型
- 字符串常量:即用双引号括起来的多个字符组成的字符序列
- 符号常量:即对常量进行命名,所以字符常量即是常量名,使用 const 关键字或 #define 预处理器指令来定义
整型常量
整数常量可以是十进制、八进制或十六进制的常量,通过前缀来确定。整数常量也可以带一个后缀,后缀是 U 和 L 的组合,U 表示无符号整数(unsigned),L 表示长整数(long)。后缀可以是大写,也可以是小写,U 和 L 的顺序任意。
85 /* 十进制 */
0213 /* 八进制 */
0x4b /* 十六进制 */
30 /* 整数 */
30u /* 无符号整数 */
30l /* 长整数 */
30ul /* 无符号长整数 */
浮点型常量
浮点型常量,又称实型常量,具有以下两种表示方式:
- 小数形式:带小数点的常数。例如:21.290000。
- 指数形式(科学计数法):由数符尾数(整数或小数)、阶码标志(E 或 e)、阶符和整数阶码组成的常数。例如:2.129000e+001。
NOTE:
- 浮点型常量默认为双精度 double 类型。
- 若浮点型常量以 F 或 f 结尾,表示该浮点型常量为单精度 float 类型常量。
- 若浮点型常量以 l 或 L 结尾,表示该浮点型常量为 long double 类型常量。
3.14159 /* 合法的 */
314159E-5L /* 合法的 */
510E /* 非法的:不完整的指数 */
210f /* 非法的:没有小数或指数 */
.e55 /* 非法的:缺少整数或分数 */
字符型场景
字符型(Char)常量:用单引号括起来,具有两种类型:
- 一般字符常量:一个用单引号 ’ ’ 括起来的字符。
- 转义字符常量:一个用单引号 ’ ’ 括起的,并以反斜杠 ‘’ 开头的字符,用于表示具有特殊功能的字符。
字符型常量占用 1Byte 内存空间,以 ASCII 码形式(即整型)存储。 因此,字符型数据对象和整型数据对象是可相互转换的。
下表列出了转义序列表:
字符串常量
字符串(String)常量:一个用双引号 " " 括起来的字符串,可以保存多个字符(e.g. “abc”)。
字符串常量中的字符序列在内存中连续存储,并在最后面加上转义字符 ‘\0’ 作为字符串结束标志,字符 ‘\0’ 占用 1Byte 内存空间。 例如:字符串 “HELLO” 在内存实际占据 6Bytes,实际字符存放顺序为 ‘H’、‘E’、‘L’、‘L’、‘O’、’\0’。
下面的实例显示了一些字符串常量。下面这三种形式所显示的字符串是相同的。
"hello, dear"
"hello, \
dear"
"hello, " "d" "ear"
符号常量
使用符号来作为常量的标识,所以也叫常量名。
符号常量具有两种定义方式:
- 使用
#define
预处理器定指令义常量的形式,不需要声明常量的数据类型:#define 符号常量名 常量表达式;
。
#include <stdio.h>
#define LENGTH 10
#define WIDTH 5
#define NEWLINE '\n'
int main()
{
int area;
area = LENGTH * WIDTH;
printf("value of area : %d", area);
printf("%c", NEWLINE);
return 0;
}
- 使用
const
关键字声明指定类型的常量:const 数据类型 符号常量名 = 常量表达式;
。
#include <stdio.h>
int main()
{
const int LENGTH = 10;
const int WIDTH = 5;
const char NEWLINE = '\n';
int area;
area = LENGTH * WIDTH;
printf("value of area : %d", area);
printf("%c", NEWLINE);
return 0;
}
NOTE:
- 使用 const 方式定义符号常量时,必须初始化符号常量
- 在程序运行过程中,不能修改符号常量的数据值
- 符号常量名一般用大写字母表示
- 符号常量定义中的常量表达式不能含有变量或函数
作用域
所谓作用域(Scope),就是变量的有效范围。C 语言中所有的变量都有自己的作用域,决定变量作用域的是变量的定义位置。
- 局部变量(Local Variable):定义在函数内部的变量称为局部变量,包括函数形参变量。实参给形参传值的过程也就是给局部变量赋值的过程。
- 全局变量(Global Variable):它的作用域默认是整个程序,也就是所有的源文件,包括 .c 和 .h 文件。
注:局部变量和全局变量的名称可以相同,但是在函数内,如果两个名字相同,会优先使用局部变量值,全局变量不会被使用。
全局变量与局部变量在内存中的区别:
- 全局变量保存在内存的全局存储区中,占用静态的存储单元;
- 局部变量保存在栈中,只有在所在函数被调用时才动态地为变量分配存储单元。
- 当局部变量被定义时,系统不会对其初始化,您必须自行对其初始化。定义全局变量时,系统会自动对其初始化。
注:正确地初始化变量是一个良好的编程习惯。
如果需要在一个源文件中引用另外一个源文件中定义的变量,我们只需在引用的源文件中将变量加上 extern 关键字的声明即可。如下例:
- addtwonum.c
#include <stdio.h>
extern int x;
extern int y;
int addtwonum(){
return x + y;
}
- main.c
#include <stdio.h>
int x=1;
int y=1;
int addtwonum();
int main(){
int result;
result = addtwonum();
printf("result : %d", result);
return 0;
}
运行:
$ gcc -Wall main.c addtwonum.c -o main
$ ./main
result : 2
注意,因为 C 语言代码是从前往后依次执行的,所以全局变量通常位于源文件的顶部。
int a, b; //全局变量
void func1(){
//TODO:
}
float x,y; //全局变量
int func2(){
//TODO:
}
int main(){
//TODO:
return 0;
}
a、b、x、y 都是在函数外部定义的全局变量。由于 x、y 定义在函数 func1() 之后,所以在 func1() 内无效;而 a、b 定义在源程序的开头,所以在 func1()、func2() 和 main() 内都有效。
存储类
存储类定义了 C 程序中的变量/函数的作用域和生命周期,通过特殊的修饰符(关键字)来进行修饰不同的变量/函数,使得他们具有灵活的作用域。下面列出 C 程序中可用的存储类:
- auto
- register
- static
- extern
auto 修饰符
auto 存储类是所有局部变量默认的存储类。auto 只能用在函数内,即 auto 只能用于修饰局部变量。
下述实例定义了两个具有相同存储类的变量:
{
int mount;
auto int month;
}
register 修饰符
register 存储类用于定义存储在寄存器中而不是内存中的局部变量。这意味着变量的最大 Size 等于寄存器的 Size,且不能对它应用一元运算符 ‘&’ (因为它没有内存空间)。
寄存器只用于需要快速访问的变量,比如:计数器。还应注意的是,使用了 register 修饰符进行修复并不意味着变量将被存储在寄存器中,它意味着变量可能存储在寄存器中,这取决于硬件和实现的限制。
{
register int miles;
}
static 修饰符
被 static 修饰的变量和常量被称为 静态变量 或 静态常量。
-
用于修饰局部变量时:编译器在程序的生命周期内保持局部变量的存在,不会在每次进入和离开函数的时候对局部变量进行重新的创建和销毁(重新赋值)。因此,使用 static 修饰局部变量可以在函数调用之间保持住局部变量的值不被重置。
-
用于修饰全局变量时:会使变量的作用域限制在声明它的源文件内,而不再是整个程序。
#include <stdio.h>
/* 函数声明 */
void func1(void);
static int count=10; /* 全局变量 - static 是默认的 */
int main()
{
while (count--) {
func1();
}
return 0;
}
void func1(void)
{
/* 'thingy' 是 'func1' 的局部变量 - 只初始化一次
* 每次调用函数 'func1' 'thingy' 值不会被重置。
*/
static int thingy=5;
thingy++;
printf(" thingy 为 %d , count 为 %d\n", thingy, count);
}
运行:
$ ./main
thingy 6, count 9
thingy 7, count 8
thingy 8, count 7
thingy 9, count 6
thingy 10, count 5
thingy 11, count 4
thingy 12, count 3
thingy 13, count 2
thingy 14, count 1
thingy 15, count 0
可见,静态变量 thingy 不会在每次函数调用时重置。
extern 修饰符
extern 存储类用于提供一个全局变量/函数的引用。全局变量/函数的作用域是整个程序,当你在一个源文件中想引用另外一些源文件中定义的全局变量/函数时,你可以在该源文件中使用 extern 修饰符,修饰一个全局变量/函数,而且不需要进行初始化(因为该全局变量实际上在另一个源文件中已经完成了初始化)。
简而言之,extern 修饰符就是用与在一个文件中,对另外一个完全中的全局变量或函数进行声明(但不定义),向编译器保证该变量/函数的存在(即便变量/函数的定义并不在本文件中)。
- main.c
#include <stdio.h>
int count ;
extern void write_extern();
int main()
{
count = 5;
write_extern();
}
- support.c
#include <stdio.h>
extern int count;
void write_extern(void)
{
printf("count is %d\n", count);
}