C 中的数据
数据定义相关关键字
输入输出函数
sizeof()
运算符scanf()
printf()
字符串,创建和存储
strlen()
关键字
const
#define
和const
数据类型关键字
最初的 K&R 关键字
int
long
short
unsigned
char
float
double
C90 标准新增的关键字
signed
void
C99 标准新增的关键字
_Bool
_Complex
_Imaginary
C99 新增的关键字有些不太一样,在使用 bool 类型时,一般会 #include <stdbool.h>
,这里对 bool 类型做了更多的封装
数据类型
关键字中,long
、short
、unsigned
用来休书数据类型。一些需要注意的点
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 类型被设计用来存储字符,但是从技术层面看,这是一个整数类型。char 类型在内存里实际上还是用一个整数来表示。
标准 ASCII 码的范围为 0~127,但是许多字符集都超过了127,可以查询相关资料如 Unicode 编码、UTF-8 编码。
有些编译器把 char
实现为有符号类型,有些则实现为无符号类型。这需要查编译器手册。C90 标准可以使用关键字指定,因此为了写出可移植的程序,要指定char的类型,但是如果只是用 char 处理字符,那么无需指定类型。
可移植的类型 stdint.h
和 inttypes.h
stdint.h
和 inttypes.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
中。
宏定义处理机器相关的类型,因此语句等价于 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>
常量和预处理
预处理提供了一个常量表示的更好方案
编译时程序中所有的 PI
都会被替换为,这一过程称为编译时替换(compile-time substitution)。
对于常量的定义,C90 还提供了 const
关键字,限定一个变量为只读。
C 头文件 limits.h
和 float.h
提供了与整数类型和浮点类型大小限制相关的详细信息。如
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,这是很有用的。
最后更新于