在编程语言中,最基本的、不可再分的数据类型称为基本类型(Primitive Type),例如整型、浮点型;根据语法规则由基本类型组合而成的类型称为复合类型(Compound Type),例如字符串是由很多字符组成的。有些场合下要把复合类型当作一个整体来用,而另外一些场合下需要分解组成这个复合类型的各种基本类型,复合类型的这种两面性为数据抽象(Data Abstraction)奠定了基础。

基本数据类型

C语言只提供了下列几种基本数据类型:

类型 说明
char 字符型,占用一个字节,可以存放本地字符集中的一个字符
int 整型,通常反映了所用机器中整数的最自然长度
float 单精度浮点型
double 双精度浮点型

此外,还可以在这些基本数据类型的前面加上一些限定符。

short 和 long

short与long两个限定符用于限定整型;

1
2
short int sh;
long int counter;

在上述这种类型的声明中,关键字int可以省略 。通常很多人也习惯这么做。

short与long两个限定符的引入可以为我们提供满足实际需要的不同长度的整型数。int通常代表特定机器中整数的自然长度。short类型通常为16位,long类型通常为32位,int类型可以为16位或32位。各编译器可以根据硬件特性自主选择合适的类型长度,但要遵循下列限制:short与int类型至少为16位,而long类型至少为32位,并且short类型不得长于int类型,而int类型不得长于long类型。

signed 和 unsigned

类型限定符signed与unsigned可用于限定char类型或任何整型。unsigned类型的数总是正值或0,并遵守算数模 2n 定律,其中n是该类型占用的位数。例如,如果char对象占用8位,那么unsigned char类型变量的取值范围为0~255,而signed char类型变量的取值范围则为 -128~127(在采用对二的补码的机器上)。不带限定符的char类型对象是否带符号则取决于具体机器,但可打印字符总是正值。

float, double 和 long double

long double类型表示高精度的浮点数。同整型一样,浮点型的长度也取决于具体的实现。float、double与long double类型可表示相同的长度,也可以表示两种或三种不同的长度。

有关这些类型长度定义的符号常量以及其他与机器和编译器有关的属性可以在标准头文件limits.hfloat.h中找到。

类型转换

自动转换

自动转换:把“比较窄的”操作数转换为“比较宽的”操作数,并且不丢失信息的转换。针对可能导致信息丢失的表达式,编译器可能会给出警告信息,比如把较长的整型值赋给较短的整型变量,把浮点型值赋值给整型变量,等等,但这些表达式并不非法。

char类型和整型

由于char类型就是较小的整型,因此在算术表达式中可以自由使用char类型的变量,这就为实现某些字符转换提供了很大的灵活性,比如,下面的函数atoi就是一例,它将一串数字转换为相应的数值:

1
2
3
4
5
6
7
8
9
/* atoi: convert s to integer */
int atoi(char s[])
{
int i, n;
n = 0;
for (i = 0; s[i] >= '0' && s[i] <= '9'; ++i)
n = 10 * n + (s[i] - '0');
return n;
}

函数lower是将char类型转换为int类型的另一个例子,它将ASCII字符集中的字符映射到对应的小写字母。如果待转换的字符不是大写字母,lower函数将返回字符本身。

1
2
3
4
5
6
7
8
/* lower: convert c to lower case; ASCII only */
int lower(int c)
{
if (c >= 'A' && c <= 'Z')
return c + 'a' - 'A';
else
return c;
}

标准头文件ctype.h定义了一组与字符集无关的测试和转换函数。例如,tolower(c)函数将c转换为小写形式(如果c为大写形式的话),可以使用tolower替代上述lower函数。类似的,测试语句 c >= '0' && c <= '9' 可以用该标准库中的函数 isdigit(c) 替代。

当关系表达式(如i>j)以及由&&||连接的逻辑表达式的判定结果为真时,表达式的值为1;当判定结果为假时,表达式的值为0。

隐式算术类型转换

一般来说,如果二元运算符(具有两个操作数的运算符称为二元运算符,比如+*)的两个操作数具有不同的类型,那么在进行运算之前先要把“较低”的类型提升为“较高”的类型,运算的结果为较高的类型。

强制类型转换

在任何表达式中都可以使用一个称为强制类型转换的一元运算符强制进行显式类型转换。在下列语句中,表达式将按照上述转换规则被转换为类型名指定的类型:

1
(类型名) 表达式

我们可以这样来理解强制类型转换的准确含义:在上述语句中,表达式首先被赋值给类型名指定的类型的某个变量,然后再用该变量替换上述整条语句。强制类型转换与其他一元运算符具有相同的优先级。

自动强制转换 :在通常情况下,参数是通过函数原型声明的。这样,当函数被调用时,声明将对参数进行自动强制转换。例如,对于sqrt的函数原型

1
double sqrt(double);

下列函数调用:

1
root2 = sqrt(2);

不需要使用强制类型转换运算符就可以自动将整数2强制转换为double类型的值2.0。

标准库中包含一个可移植的实现伪随机数发生器的函数rand以及一个初始化种子数的函数srand。前一个函数rand使用了强制类型转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
unsigned long int next = 1;

/* rand: return pseudo-random integer on 0..32767 */
int rand(void)
{
next = next * 1103515245 + 12345;
return (unsigned int)(next/65536) % 32768;
}
/* srand: set seed for rand() */
void srand(unsigned int seed)
{
next = seed;
}

sizeof

sizeof 函数可以查看一个变量/类型所占用的字节空间。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <stdio.h>

int main(int argc, char *argv[])
{
int areas[] = {10, 12, 13, 14, 20};
char name[] = "Zed";
char full_name[] = {
'Z', 'e', 'd',
' ', 'A', '.', ' ',
'S', 'h', 'a', 'w', '\0'
};

// WARNING: On some systems you may have to change the
// %ld in this code to a %u since it will use unsigned ints
printf("The size of an int: %ld\n", sizeof(int));
printf("The size of areas (int[]): %ld\n",
sizeof(areas));
printf("The number of ints in areas: %ld\n",
sizeof(areas) / sizeof(int));
printf("The first area is %d, the 2nd %d.\n",
areas[0], areas[1]);

printf("The size of a char: %ld\n", sizeof(char));
printf("The size of name (char[]): %ld\n",
sizeof(name));
printf("The number of chars: %ld\n",
sizeof(name) / sizeof(char));

printf("The size of full_name (char[]): %ld\n",
sizeof(full_name));
printf("The number of chars: %ld\n",
sizeof(full_name) / sizeof(char));

printf("name=\"%s\" and full_name=\"%s\"\n",
name, full_name);

return 0;
}
尽可能用 sizeof(varname) 代替 sizeof(type)。使用 sizeof(varname) 是因为当代码中变量类型改变时会自动更新。某些情况下 sizeof(type) 或许有意义,但还是要尽量避免,因为它会导致变量类型改变后不能同步。

typedef

typedef可以用来定义类型的同义词:

1
2
3
typedef double wages;    // wages is a synonym for double
typedef int exam_score; // exam_score is a synonym for int
typedef wages salary; // indirect synonym for double

typedef通常被用于以下三种目的。

  • 为了隐藏特定类型的实现,强调使用类型的目的;
  • 简化复杂的类型定义,使其更易理解;
  • 允许一种类型用于多个目的,同时使的每次使用该类型的目的明确。

深入阅读

  1. 更高级的数据类型

Comments