- 常量: 在程序运行过程中, 其值不能改变的量.
- 直接常量/字面常量 Literal:
A literal is a value that is expressed as itself.- 整型常量(-10,0-12,-0xa), 实型常量(1.2), 字符常量('c'), 字符串常量("str")
- 直接常量 不需要实现定义, 使用时直接写出即可, 其类型由系统根据书写方法自动默认.
- 符号常量 Constant:
A constant is a data type that substitutes a literal.- 用宏定义一个标识符来带代表一个常量
#define PI 3.14159265
- 用宏定义一个标识符来带代表一个常量
- 直接常量/字面常量 Literal:
- 变量: 在程序运行过程中, 其值可以改变的量.
- 变量是一段有名字的连续存储空间
- 变量赋初值: C语言允许在定义变量的同时使用变量 初始化.
变量初始化不是在编译阶段完成的, 而是在程序运行时执行本函数时赋予初始的, 相当于有一个赋值语句;
(int a = 5; 等价于 int a; a=5;)
(int * p = &a; 等价于 int * p; p=&a;) - 常变量:
const int PI = 3.14
类型说明符 Type Specifier
short & long
If you need to use a small/large number, you can use a type specifier short/long.
The long keyword cannot be used with float and char types.
类型修饰符 Type Modifier
signed & unsigned
You can alter the data storage of a data type by using them.
1. 基本数据类型 Basic types
数据类型 Data type | 格式字符 Format character |
---|---|
char | c |
int | d 或 i (十进制) |
unsigned int | u(十进制), o(八进制), x(十六进制) |
float | f(小数输出), e(指数输出) |
double | lf |
long double | llf |
short int | hd 或 hi |
long int | ld 或 li |
long long int | lld 或 lli |
unsigned short int | hu |
unsigned long int | lu |
unsigned long long int | llu |
string | s |
1.1 整型 Integer
Integers are whole numbers that can have both zero, positive and negative values but no decimal values.
- 默认为 10进制, 10 ,20
- 以 0 开头为 8进制, 045,021
- 以 0b 开头为 2进制, 0b11101101
- 以 0x 开头为 16进制, 0x21458adf
- 整型常量后可以用:
- u 或 U 明确说明为无符号整型数;(unsigned int ui = 5U;)
- l 或 L 明确说明为长整型数.(long int li = 5L;)
- 整型数据在内存中的存放形式:
二进制补码 形式存放(0正 1负).
#include<stdio.h> int main() { int decimal = 10; int binary = 0b1010; int octal = 012; int hexadecimal = 0xa; // C 不支持直接通过格式控制输出内存中的二进制数. printf("变量名\t\t 十进制\t 八进制\t 十六进制\n"); printf("decimal\t\t %d\t 0%o\t 0x%x\n", decimal, decimal, decimal); printf("binary\t\t %d\t 0%o\t 0x%x\n", binary, binary, binary); printf("octal\t\t %d\t 0%o\t 0x%x\n", octal, octal, octal); printf("hecadecimal\t %d\t 0%o\t 0x%x\n", hexadecimal, hexadecimal, hexadecimal); /* 计算机中数据以二进制补码的形式进行存储 无符号数, 所有位都用来存储数据, 都是数据位; 有符号书, 第一位用来存储符号 ( 0正 1负), 是符号位. 其余所有位用来存储数据, 是数据位. -1 二进制原码: 0b 10000000 00000000 00000000 00000001 -1 的二进制补码形式(实际存储形式) 0b 11111111 11111111 11111111 11111111 0x ff ff ff ff */ short int hInt = -0x1; unsigned int uInt = -0x1; unsigned long int ulInt = -0x1; //unsigned long long int ullInt = -0x1; // 转换说明符 %u, %o, %x 都是按无符号数据的格式输出的. printf("\n变量名\t 有符号十进制\t 无符号十进制\t\t 八进制\t\t\t 十六进制\n"); printf("hInt\t %d\t\t %hu\t\t\t %ho\t\t\t %hx\n", (int)hInt, hInt, hInt, hInt); printf("uInt\t %d\t\t %u\t\t %o\t\t %x\n", (int)uInt, uInt, uInt, uInt); printf("ulInt\t %d\t\t %lu\t %lo\t %lx\n", (int)ulInt, ulInt, ulInt, ulInt); //printf("ullInt\t %d\t\t %llu\t %llo\t %llx\n", (int)ullInt, ullInt, ullInt, ullInt); return 0; }
1.2 浮点型 Floating-point
float and double are used to hold real numbers.
- 十进制小数形式(.123, 0.123, 0.0)
- 单精度浮点型, float , single precision float data type
- 双精度浮点型, double, double precision float data type
- 指数形式 exponential
- e/E 之前必须有数字, e/E 后面的指数必须为 整数;(12e3, 12E3)
- 规范化的指数形式: 小数点左边应当有且只有一位非 0 数字;
- 浮点型数据默认为 双精度, 如果要指定它为 单精度, 可以加后缀 f.
- 一般浮点数 默认为双精度类型
- 浮点型数据在内存中的存放形式:
- 符号(0正 1负) + 小数部分(有效位) + 指数部分
- 有效位以外的数字将被舍去.(舍入误差)
- 由于实数存在舍入误差, 使用时要注意:
- 不要试图用一个实数精确表示一个大正数, 记住: 浮点数是不精确的;
- 避免直接将一个很大的实数与一个很小的实数相加减, 否则会"丢失"小的数;
- 符号(0正 1负) + 小数部分(有效位) + 指数部分
1.3 字符型 Character
- 计算机无法直接存储字符, 而是将字符转化为对应的 ASCII 码, 以其 ASCII 码的 补码 的形式存储.(因为 ASCII 码第一位都是 0, 所以都是正数, 而正数的补码和原码相同)
- 所有字符常量(包括可以显示的, 不可显示的)均可以使用字符的转义表示法(ASCII码表示):
- '\ddd' 或 '\0dd' ===> ddd表示 ASCII 码对应的的八进制表示('\n' == '\12' == '\012' == '\xa')
- '\xhh' ==========> hh 表示 ASCII 码对应的十六进制表示
- C 语言中的 NULL 字符:
- NULL 是 ASCII 码表中的第一个字符, 编码为 0000 0000;
- NULL 可用作字符串结束的标志 '\0';
- NULL 可用作 空指针, 表示该指针未指向任何有效地址.
- 字符型数据在内存中的存放形式:
以字符 ASCII 码对应的 二进制补码 形式存放, 占用一个字节.
#include<stdio.h> int main() { char c = 'A'; char cd = 65; char co = 0101; char cx = 0x41; // 通过字符对应的 ASCII 码也可输出控制/打印字符 printf("变量名\t 字符\t 十进制\t 八进制\t 十六进制\n"); printf("c\t %c\t %d\t 0%o\t 0x%x\n", c, c, c, c); // 通过 \n 输出换行 printf("cd\t %c\t %d\t 0%o\t 0x%x\12", cd, cd, cd, cd); // 通过 \ddd 格式输出换行 printf("co\t %c\t %d\t 0%o\t 0x%x\012", co, co, co, co); // 通过 \0dd 格式输出换行 printf("cx\t %c\t %d\t 0%o\t 0x%x\xa", cx, cx, cx, cx); // 通过 \xhh 格式输出换行 /* 计算机中 char 型数据和 int 型数据一样, 都以二进制补码的形式进行存储 所以, char 型数据在存储上也是整数, 可以通过格式字符 c 或 d 决定 到底是作为字符还是整数 来输入输出; 同理, int 型数据在一定范围内, 也可以通过格式字符 c 作为字符输出. 通过 ASCII 码表, 可以知道整数&字符的对应关系 */ unsigned short int huInt = 0b01000001; printf("\n变量名\t 符号\t 有符号十进制\t 无符号十进制\t 八进制\t 十六进制\n"); printf("hInt\t %c\t %d\t\t %hu\t\t 0%ho\t 0x%hx\n", (char)huInt, (int)huInt, huInt, huInt, huInt); return 0; }
2. 派生数据类型 Derived Data Types
- 数组类型 Arrays
- 指针类型 Pointers
- 函数类型 Function types
- 结构体类型 Structures
- 共用体类型 Union
3. 枚举类型 Enumerated Type
4. 不完整类型/空类型 Incomplete type
- void is an incomplete type. It means "nothing" or "no type".
Note that, you cannot create variables of void type.
Other Types
- bool type
- C89 没有定义布尔类型, 使用整型 int 来表示真假:
- 输入时: 非 0 值表示 true; 0 表示 false.
- 输出时: true == 1; false == 0.
- C99 提供了 _Bool 型, 所以布尔型可以声明为 _Bool identifierName;
- C99 还提供了一个头文件 <stdbool.h> 定义了 bool 代表 _Bool, true==1, false==0.
- C89 没有定义布尔类型, 使用整型 int 来表示真假:
- Enumerated type
- Complex types
输入/输出 Input/Output (I/O)
C 语言没有设计专门的输入输出语句:
实现数据的输入输出(I/O)操作, 是通过调用 C 语言提供的 I/O 库函数.(须包含头文件 stdio.h)
0. sizeof 运算符
- sizeof 是一个单目运算符.
- 作用是: 得到操作数在存储空间中 实际占用的存储空间 大小(单位: Byte), 即使未使用所有空间.
- sizeof 不能用于函数类型.
- 优先级: 2 级(比 3 级 的算数运算符要高)
#include<stdio.h> int main() { int a; /* 错误用法示例 size_t 类型, 在头文件 <stddef.h> 中 typedef 为 unsigned int 类型. 该类型保证能容纳实现所建立的最大对象的字节大小.(PS: 此处为 unsigned long int 类型) size_t t = sizeof int; */ // 正确用法 printf("sizeof(int) = %lu \nsizeo***sizeo***", sizeof(int), sizeof(a), sizeof a); return 0; }
1. 输出函数
1.1 printf();
printf(格式控制, 输出项1, 输出项2,...); 实现在标准终端输出设备(显示器)上按指定的格式进行数据输出.
- 格式控制的完整格式:
%-0m.nl(或h)格式字符- % 区别于普通字符的标志, 格式字符的起始符号;
- - 左对齐 输出, 如省略则表示右对齐输出;
- 0 指定空位填 0, 如省略则不填充;
- m.n
- m 指 域宽, 即对应的输出项在输出设备上所占的列数
- n 指 精度, 用于说明输出的实型数的小数位, 如省略则默认为 6
- l 或 h
- l 将 int 修正为 long int;将 float 修正为 double
- h 将 int 修正为 short int
- 格式字符 指定输出类型: c, d, u, o, x, f, s
- f float(默认整数部分全部输出, 小数部分输出 6 位小数)
- 输出实数时, 其数据的实际精度并不完全取决于格式控制中的域宽&小数的域宽, 而是取决于数据在计算机内的存储精度.
- 因此, 若程序中指定的域宽&小数域宽超过相应类型数据的有效数字, 输出的多余数字是没有意义的, 只是系统用来填充域宽而已.
- "格式字符" 必须和 "输出项" 一一对应:
- 格式说明 > 输出项 ---> 多出部分输出无意义的数字乱码
- 格式说明 < 输出项 ---> 缺少部分不予输出
1.2 putchar();
putchar(字符变量or常量); // 向显示器输出一个字符;
2. 输入函数
2.1 scanf();
scanf(格式控制, 地址列表); 实现从终端键盘上读入数据
scanf("%d%d%d", &a, &b, &c); // 1 2 3
scanf("a=%d,b=%d,c=%d", &a, &b, &c); // a=1,b=2,c=3
scanf("%d%d%*d%d", &a, &b, &c); // 跳过输入的第三个数据 ?
- scanf函数的格式字符前可以加入一个正整数指定输入数据所占的宽度, 但不可以对实数指定小数位的宽度.
- 用 scanf 函数从键盘输入数据时, 每行数据在 未按下回车键之前, 可以任意修改. 但按下回车键之后, scanf 函数即接受了这一行数据, 不能再修改.
- 空白符间隔输入数据(空格, tab, enter 等)
2.2 getchar();
- 从终端键盘读字符到 键盘缓冲区, 直到用户按回车(回车也被读入键盘缓冲区);
- 回车之后, getchar 函数从缓冲区 每次读入一个字符;
- getchar 返回值, 是用户输入的第一个字符的 ASCII 码; 如出错返回 -1, 且将用户输入字符回显到屏幕.
- 如用户在按回车之前输入了多余一个的字符, 其他字符会保留在键盘缓存区, 等待后续 getchar 调用读取. 直到缓冲区中的字符读完后, 才等待用户按键输入.
#include<stdio.h> int main() { int a, b, c; //scanf("%d%d%d", &a, &b, &c); // 65 66 67 不规定特定的输入格式, 空白符号表示间隔, 回车后数据放到缓冲区, 不可修改 //scanf("a=%d,b=%d,c=%d", &a, &b, &c); // 严格规定输入格式为: a=65,b=66,c=67 scanf("%d%d%*d%d", &a, &b, &c); // 要求输入的 4 个数据, 但第三个数据为无效数据 printf("%-5d%-5d%-5d\n", a, b, c); // putchar 输出字符 putchar(a); putchar(b); putchar(c); /* getchar 接收字符 调用 getchar 函数时, 如果缓冲区有字符则直接从缓冲区取出一个字符; 如果缓冲区为空, 则开始等待用户输入, 可以输入多个字符, 回车表示输入完毕. 同时返回第一个输入的字符的 ASCII 码. 注意, 上面使用 scanf 函数接收输入时, 输入的数据全部被保存在 缓冲区 中; scanf 函数结束后, 最后输入的 '\n' 仍然保留在缓冲区中没有取出.(使用 scanf 函数时需要注意) 这里先调用 getchar 函数取出最后一个 换行符; 再调用 putchar 函数将取出的换行符直接打印打屏幕上. */ putchar(getchar()); // 若不先取出残余的 '\n', 后面 a 将被赋值为 '\n' // 此时, 缓冲区为空, 调用 getchar 函数等待用户输入, 并取出第一个字符赋值给 a.(其他字符仍保留在缓冲区, 等待下次读取) a = getchar(); putchar(a); b = getchar(); putchar(b); c = getchar(); putchar(c); return 0; }
Others
变量名 & 地址 的关联:
- 编译阶段, 编译器会将合法的变量名放到一个叫 符号表 Symbol Table 的表中.
- 每个符号对应一个地址。当你调用此变量时,就会根据此符号表找到对应的地址,然后进行操作.
- 通过 Symbol Table 可以实现"变量名 - 地址"的关联.: 变量名 ---> 地址 ---> 存储空间中的值.
- 编译完成后, 所有变量名都在 Symbol Table 中有一个对应的地址.(目标文件中也全部是对地址的操作)
- 取地址运算符&, 可以取出变量所对应的地址
- 变量名, 可以直接取出地址所对应的值(变量值)
- 取值运算符*, 可以取出指针值所对应的地址上存放的值
- 运行阶段, 内存中并不存在变量名, 只有相应的地址, 直接对进行地址操作.
- 运行阶段, 变量名所标识的内存空间, 只有 首地址 & 大小 的概念了,
- 在运行期间动态申请的空间,是需要额外的代码维护,以确保不同变量不会混用内存。
- 内存申请和释放时机很重要,过早会丢失数据,过迟会耗费内存。