C 中的数据

  • 数据定义相关关键字

  • 输入输出函数

  • sizeof() 运算符

  • scanf()

  • printf()

  • 字符串,创建和存储

  • strlen()

  • 关键字 const

  • #defineconst

数据类型关键字

最初的 K&R 关键字

  • int

  • long

  • short

  • unsigned

  • char

  • float

  • double

C90 标准新增的关键字

  • signed

  • void

C99 标准新增的关键字

  • _Bool

  • _Complex

  • _Imaginary

C99 新增的关键字有些不太一样,在使用 bool 类型时,一般会 #include <stdbool.h> ,这里对 bool 类型做了更多的封装

数据类型

关键字中,longshortunsigned用来休书数据类型。一些需要注意的点

  • short int 占用的存储空间可能比 int

    • 或者简写为 short

  • long int 占用的存储空间可能比 int

  • long long int 占用的存储空间可能比 long

    • C99 标准下可以写为 long long,至少占 64 bit

“可能”的解释:C 语言只规定了占用存储空间的大小,此规定为了适应不同字长的机器,这也是 C 语言移植性比较好的来源。

因此在一些跨平台的项目里,会定义一些使用存储 bit 的长度来描述的类型,避免移植性上的问题。

常量的类型

通常程序中的数字都被视为 int 类型。

如果需要指定类型,如以 long 类型存储一个小数字(显示表示一个地址值),可以在数字末尾加上 L

  • 020L

  • 0x10L

  • 6ULL

    • unsigned long long

printf 输出

  • unsigned int , %u

  • long, %ld

  • %lo: 以八进制格式输出 long 类型整数

  • %lx

char 类型

char 类型被设计用来存储字符,但是从技术层面看,这是一个整数类型。char 类型在内存里实际上还是用一个整数来表示。

标准 ASCII 码的范围为 0~127,但是许多字符集都超过了127,可以查询相关资料如 Unicode 编码、UTF-8 编码。

有些编译器把 char 实现为有符号类型,有些则实现为无符号类型。这需要查编译器手册。C90 标准可以使用关键字指定,因此为了写出可移植的程序,要指定char的类型,但是如果只是用 char 处理字符,那么无需指定类型。

可移植的类型 stdint.hinttypes.h

前面遇到了不同类型在不同机器中实现不同的问题,因此 C99 新增了两个头文件,确保类型在各系统中的实现相同。

C99 标准为现有类型创建了更多的类型名,定义在 stdint.h 中,例如 int32_t 直接指定了整数占用的 bit 数,本质上还是类型关键字,只不过做了机器相关的关联,在编译期做处理。此即精确宽度整数类型(exact-width integer type)

如果机器不支持精确宽度整数类型,也有一些类型名保证所表示的类型至少有指定宽度的类型,即最小宽度类型(minimum width type)。如一个机器最小宽度只有 16bit,此机器没有 int8_t 类型,但是可以使用 int_least8_t

如果一些程序员更关心速度而非存储空间,C99 和 C11 定义了一组可以让计算达到最快的类型,即最快最小宽度类型(fastst minimum width type)。比如 int_fast8_t 是对于 8bit 而言计算最快的类型。

此外,C99 还定义了最大整数类型 intmax_t ,此为当前机器支持的最大宽度的类型。

有了可移植类型名,当要输入输出数据时,也有数据类型的问题。C99 和 C11 针对这种情况,提供了一些字符串宏来显示可移植类型。定义在 inttypes.h 中。

#include <stdio.h>
#include <inttypes.h>

int main(void)
{
    int32_t num32 = 12341235;
    printf("nums = %" PRId32 "\n", num32);
    return 0
}

宏定义处理机器相关的类型,因此语句等价于 printf("nums = %" "d" "\n", num32);

类型大小

sizeof() 为 C 语言内置运算符。

printf 的缓冲区

printf 的内容不会立刻输出到屏幕上,因为读写 IO 需要使用系统调用,会降低运行效率,所以输出内容会暂存到缓冲区中。

C 标准规定了何时把缓冲区内容输出到屏幕

  • 缓冲区满,处理文件时较为常见

  • 遇到换行字符 标准输出(stdout)是行缓冲模式

  • 需要输入,scanf() 会迫使缓冲区刷新

此外还可以使用 fflush() 方法直接主动去刷新缓冲区。

fflush(FILE *stream) 手动刷新缓冲区

字符串

C 语言中并没有专门的字符串变量,字符串存储在 char 类型数组中。数组末尾位置的字符为 \0 ,这是空字符(null character),C 中用来标记字符串的结束。字符串看起来挺复杂,需要先创建一个数组,然后把字符串中的每个字符逐个放入,还要在末尾加 \0 ,好在计算机可以自己完成这些操作。

sizeof 运算符类似,strlen() 函数给出字符串中的字符长度,需要引入 #include <string.h>

关于 sizeof 的用法,当运算对象为类型时,需要() 当运算对象为特定量时,可以不需要括号。如 sizeof val

strlen() 函数用来计算字符串长度,使用此函数需要 #include <string.h>

常量和预处理

预处理提供了一个常量表示的更好方案

#define PI 3.14159

编译时程序中所有的 PI 都会被替换为,这一过程称为编译时替换(compile-time substitution)

对于常量的定义,C90 还提供了 const 关键字,限定一个变量为只读。

C 头文件 limits.hfloat.h 提供了与整数类型和浮点类型大小限制相关的详细信息。如

#define  INT_MAX    +32767
#define  INT_MAX    -32767

printf 和 scanf

过去,这些函数和 C 库其他函数一样,并不是 C 语言定义的一部分。最初 C 把输入输出的实现交给了编译器作者,因为编译器作者最了解机器的,这样可以针对特殊机器更好的匹配输入输出。各编译器都提供不同版本的输入输出函数,C90 和 C99 标准规定了这些函数的标准版本。

printf

% 开头的这些符号称为转换说明(conversion specification),指明如何把数据转换为可显示的形式。

显示在屏幕上的东西都是 ASCII 码,变量中存储的整数,因此这些转换工作就实现在了此函数中,可以找一些 libc 的源码看看。

还有转换说明修饰符,可以做更多的事情。常用的标记有 - + # 0,有些是 C99 新增的。

sizeof 返回的应该是个无符号整数,但并未强制规定是什么如 d ld lld,C 提供了移植性更好的类型,在 stddef.h 中(stdio.h中包含) 有一个类型 size_t ,sizeof 的返回类型定义为此,称为底层类型(underlying type)

在 printf 时的类型为 %zd

scanf

scanf 接收输入,大多数是来自键盘。要注意的是键盘进来的全都是字符。也即 scanf 也做了许多计算工作,把字符串处理成想要的类型。printf 正好做了相反的操作。

scanf 会跳过空白(除了 %c)

比如读取一个整数,每次读取一个字符,跳过空白,直到遇到+-或数字,然后遇到非数字停止,计算输入并赋值到指定变量。程序下一次读取时,读到的是后面的内容。

对于 %s 读取,会读除空格之外的所有内容,跳过开头空白,然后读非空白字符并保存,直到遇到空白。也就是说,%s 只能读一个单词。

常用的读字符的函数 getchar()fgets() 来读单个字符,或读包含空格的字符串。

格式字符串中的普通字符要和用户输入的匹配。

scanf 是有返回值的,检测到文件结尾会返回 EOF,这是很有用的。

最后更新于