vlambda博客
学习文章列表

[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 存储属性

变量的存储属性是指存储变量值的内存类型。它可以是普通内存、运行时堆栈、硬件寄存器。


这是更深一层的知识,后面章节会深入探讨,作为初步了解,此处不做展开。