[C语言]2.0 C语言的数据类型
程序代码离不开数据处理。这篇文章将集中讲解C语言中的数据类型。
先来直观感受下:
int add(int a, int b)
{
int result = 0; /* 创建一个有符号的整型变量result,并初始化为0 */
result = a + b; /* 进行求和运算,并把结果赋值给变量result */
return result; /* 返回result的值 */
}
在C语言中,仅有4中基本数据类型,分别是:整型、浮点型、指针和聚合类型(如数组和结构等)。在此基础上,会派生出各种各样的组合。对于一个变量而言,它包含三个属性:作用域、链接属性和存储属性。
1 数据类型
变量的声明或定义的基本形式是:
数据类型 变量名;
下面将逐个数据类型进行讲解。
1.1 整型
字符(char)、短整型(short)、整型(int)和长整型(long)都称为整型数据,它们又各自分为有符号(signed)和无符号(unsigned)两种。
char a = 'a'; /* 字符型,有无符号,取决于编译器 */
unsigned char b = 'b'; /* 无符号字符型 */
short int c = 100; /* 有符号短整型 */
unsigned short int d = 100; /* 无符号短整型 */
int e = 100; /* 有符号整型 */
unsigned int f = 100; /* 无符号整型 */
long int g = 100; /* 有符号长整型 */
unsigned long int h = 100; /* 无符号长整型 */
C标准中规定了每种整型数据的最小范围,归纳为:长整型至少应该和整型一样长,而整型至少应该和短整型一样长。简单表示成long >= int >= short int。
头文件limits.h中有相应的宏定义说明了各种整型数据的范围。详见下面的表格。
变量的范围
类型 |
最小范围 |
最小值 |
最大值 |
char |
0~127 |
0 |
SCHAR_MAX |
signed char |
-127~127 |
SCHAR_MIN |
SCHAR_MAX |
unsigned char |
0~255 |
0 |
UCHAR_MAX |
short int |
-32767~32767 |
SHRT_MIN |
SHRT_MAX |
unsigned short int |
0~65535 |
0 |
USHRT_MAX |
int |
-32767~32767 |
INT_MIN |
INT_MAX |
unsigned int |
0~65535 |
0 |
UINT_MAX |
long int |
-2147483647~2147483647 |
LONG_MIN |
LONG_MAX |
unsigned long int |
0~4294967295 |
0 |
LONG_MAX |
短整型即可表示为short,也可以表示为short int;长整型即可表示为long,也可以表示为long int。
特别的,signed关键字一般只用在char类型,而其他数据缺省情况等同于使用signed。即short、short int、signed short int等同于signed short;int、signed等同于signed int;long、long int、signed long int等同于signed long。
char具体指signed char还是unsigned char则因编译器而异。因此使用char时,最好使用其signed char与unsigned char的交集,这样便于代码的移植和复用。
1.2 浮点型
浮点数据类型包括float、double和long double。
float a = 1.1;
double b = 1.1;
long double c = 1.1;
头文件float.h中定义了float、double和long double的最大值和最小值,详见下表。
浮点数据类型的范围
类型 |
最小值 |
最大值 |
float |
FLT_MIN |
FLT_MAX |
double |
DBL_MIN |
DBL_MAX |
long double |
LDBL_MAX |
LDBL_MAX |
1.3 指针
指针是C语言中的一大特点。C语言并未对指针操作做出过多的限制,用得好会出现很灵活高效的代码,用得不好,容易造成数据被错改,甚至内存溢出等问题导致系统崩溃。
指针可以指向各种数据类型变量,定义跟其他变量相似,类型后多增加一个*号。
int* a = NULL; /* 指向有符号整型数据的指针 */
float* b = NULL; /* 指向浮点型数据的指针 */
1.4 常量
常量跟变量完全一样,只是常量初始化完后不允许修改,并且使用const关键字进行修饰。如果对一个常量进行修改或重新赋值得操作,通常编译器都会发出错误提醒。
const int a = 10; /* 整型常量 */
cosnt float b = 1.1;/* 浮点型常量 */
Tips:
C语言中支持一种叫做typedef的机制,它允许我们对各种数据类型定义新的名字。例如如下定义:
typedef signed int u32; /* u32等同于signed int */
2 作用域
变量被创建后,它在内存中是有存活时间的。这里涉及到一个概念叫作用域,一个变量只能存活在它的作用域里。一旦程序离开该作用域,变量就会被销毁。
标识符声明的位置决定了它的作用域。作用域分为文件作用域、函数作用域、代码块作用域和原型作用域。
Tips:
标识符就是变量、函数、数据类型等的名字。它们由大小写字母、数字和下划线组成,但不能以数字开头。
2.1 文件作用域
当一个变量在函数外进行定义时,从该文件的定义到该文件结束的代码都可以访问该变量。
test.c文件
int a = 10; /* 从该处定义开始,直到本文件结束都可以调用变量a。变量a一直存活,直到整个程序结束。*/
void fun1(void)
{
... /* 此处可以调用变量a */
}
void fun2(void)
{
... /* 此处可以调用变量a */
}
特别的,当一个文件中一个函数外定义且不带static关键字的变量(又称为全局变量),被另一个文件通过extern声明时,这个文件也可以调用该变量。
test2.c文件
extern int a; /* 通过关键字extern声明变量a */
void fun3(void)
{
... /* 此处可以调用test.c文件中的变量a */
}
2.2 函数作用域
函数作用域仅适用于语句标签,语句标签用于goto语句。一个函数中的所有语句标签必须唯一。
void example(void)
{
...
goto EXIT_FLAG; /* 使用goto语句让程序跳转到EXIT_FLAG标签处 */
...
EXIT_FLAG: /* 调用goto语句后,程序从此处继续执行 */
...
}
2.3 代码块作用域
一对花括号之间的所有语句称为一个代码块。在代码块起始位置声明的标识符都具有代码块作用域。函数的形参也具有代码块作用域。
int example(int a) /* 形参a的作用域在最外层花括号内 */
{ /* 最外层花括号 */
int b; /* 变量b的作用域在最外层花括号内 */
...
{ /* 内部花括号1 */
int b; / 与外面的变量b是两个不同的变量,他的作用域在内部花括号1中 */
int c;
}
...
{ /* 内部花括号2 */
int c; /* 与前一个花括号内的变量c是两个不同的变量,它的作用域在内部花括号2中 */
}
}
不推荐在嵌套代码中使用相同的变量名,虽然可以这么使用,但会造成不必要的麻烦。
2.4 原型作用域
在函数原型中声明的参数名,它的作用域只在该函数内部。
int add(int a, int b) /* 函数中声明的形参(参数名)仅在函数内部有效 */
{
...
}
3 链接属性
链接属性一共有3种——external(外部)、internal(内部)和none(无)。
none:每个被none关键字修饰的变量或函数都是独立的个体。
internal:在同一个文件内部所有声明相同名字的变量或函数都是同一个实体。不同文件中,则是不同实体。
external:几个文件间,声明的都是同一个变量或函数都是同一个实体。
这是更深一层的知识,后面章节会深入探讨,作为初步了解,此处不做展开。
4 存储属性
变量的存储属性是指存储变量值的内存类型。它可以是普通内存、运行时堆栈、硬件寄存器。
这是更深一层的知识,后面章节会深入探讨,作为初步了解,此处不做展开。