7.1 整数类型
两种类型:
- 有符号数: 使用补码表示, 需要最高位作为符号位.
- 无符号数: 使用原码表示, 只能表示正整数.
特性 | 说明 | 代码 |
---|---|---|
类型实际长度是不确定的 | 由机器和编译器决定, 但是通常: 1. int: 32位 2. short: 16位 3. long: 64位 可以通过 <limits.h> 头查看长度定义的宏 | |
整型 | int unsigned int | |
短整型 | short short int unsigned short int | |
长整型 | long long int unsigned long int | |
超长型 #C99 | long long unsigned long long 长度: 至少64位 | |
整数常量数制 (立即数) | 三个类型: 1. 十进制: 一定不能以0开头 2. 八进制: 以0开头 3. 十六进制: 以0x开头 (字母可以随意大小写) | |
整数常量存储 | 后缀: 1. 21L 表示作为长整数存储2. 15U 表示作为无符号常量存储3. 455UL 表示作为无符号长整数存储4. 345LL 表示超长整数 #C99整数常量自动类型匹配(编译器): 5. int (如果是8或者16进制多匹配unsigned int) 6. long int 7. unsigned long int | |
整数溢出 | 两种情况: 1. 操作数有符号: 未定义行为 2. 操作数无符号: 对$2^n$取模, $n$为类型位长 | unsigned short情况下: 65535 + 1 = 0 |
7.1.5 读写整数
读写对应的整数需要补充转换说明符
类型 | 说明符 |
---|---|
无符号整数 | 1. %u 表示十进制无符号整型2. %o 表示八进制无符号整型3. %x 表示十六进制无符号整型 |
短整型修饰符 | h : %hd %hu %ho %hx |
长整型修饰符 | l : %ld %lu %lo %lx |
(程序) 数列求和
将数列元素换成长度更长的类型, 防止溢出, 但是需要修改相应的scanf转换说明符
sum2.c
#include <stdio.h>
int main(void) {
long n, sum = 0;
printf("This program sums a series of integers.\n");
printf("Enter integers (0 to terminate): ");
scanf("%ld", &n);
while (n != 0) {
sum += n;
scanf("%ld", &n);
}
printf("The sum is: %ld\n", sum);
return 0;
}
运行:
This program sums a series of integers.
Enter integers (0 to terminate): 10000 20000 30000 0
The sum is: 60000
7.2 浮点类型
特性 | 说明 | 代码 |
---|---|---|
浮点类型 | 1. float: 单精度浮点数 (一般32位) 2. double: 双精度浮点数 (默认类型, 一般64位) 3. long double: 扩展精度浮点数 (80或128) | |
浮点常量 (立即数) | 小数计数法: 57.0 57. 科学计数法: 57e0 5.7e1 570.e-1 后缀: 1. float: f 或者F 2. long double: l 或者L |
7.2.2 读写浮点数
特性 | 说明 |
---|---|
float | %e %f %g |
double | %le %lf %lg |
long double | %Le %Lf %Lg |
7.3 字符类型
特性 | 说明 | 代码 |
---|---|---|
字符集 | 使用ASCII | |
类型 | char默认有无符号是不确定的, 根据编译器定义. 所以最好显式定义有无符号: signed char : unsigned char : | |
字符常量 (立即数) | 整数型: char a = 1; 字符型: char b = 'B' | |
转义序列 | 常用的: 换行: \n 水平制表符: \t 问号: \? 单双引号: ' " | wiki链接: [(C语言程序设计-现代方法) Chapter3-格式化输入和输出#3 1 2 转义序列]((C语言程序设计-现代方法) Chapter3-格式化输入和输出#3 1 2 转义序列 "wikilink") |
数字转义序列 | 八进制转义序列: '\EE' 十六进制转义序列: '\xHH' | |
常用函数 | 小写->大写: ch = toupper(ch) | 所在库: <ctype.h> |
7.3.3 算数类型
整数类型和浮点类型通常为算数类型, 以下是对比C89和C99之间的算数类型范围的差别
C89 | C99 |
---|---|
1. 整数类型: 1. 字符类型: char 2. 有符号整型: signed char, short, int, long 3. 无符号整型: unsigned char, unsigned shor, unsigned int, unsigned long 4. 枚举类型 | 1. 整数类型: 1. 字符类型: char 2. 有符号整型: signed char, short, int, long, long long 3. 无符号整型: unsigned char, unsigned shor, unsigned int, unsigned long, unsigned long long, _Bool 4. 枚举类型 |
2. 浮点类型: float, double, long double | 1. 浮点类型: 1. 实数浮点类型: float, double long double 2. 复数类型: float_Complex, double_Complex, long double_Complex |
7.3.6 scanf和printf读写字符
scanf("%c", &ch);
printf("%c", ch);
特殊点: 读取char数据时不会跳过空白符(空格, 换行), 如果缓冲区中保留了空白符 (比如: 上次调用了scanf读取一个整数, 缓冲区中残留了换行符), 那么ch会读取到一个换行符.
解决方法:
// 方法一: 前方加入空格, 表示跳过空白符
scanf(" %c", &ch);
// 方法二: 使用getchar()读取缓冲区, 直到无残留
do {
scanf("%c", &ch);
} while (ch != '\n');
// 或者
do {
ch = getchar();
} while (ch != '\n');
// 惯用法
while (getchar() != '\n');
7.3.7 getchar和putchar读写字符
特性:
- 返回值类型: int
- 不会跳过空白符
- 由于是宏实现, 开销较低
putchar(ch);
ch = getchar();
(程序) 获取字符串长度
length.c
#include <stdio.h>
int main(void) {
char ch;
int len = 0;
printf("Enter a message: ");
ch = getchar();
while (ch != '\n') {
len++;
ch = getchar();
}
printf("Your message was %d chracter(s) long.\n", len);
return 0;
}
精简写法 length2.c
#include <stdio.h>
int main(void) {
int len = 0;
printf("Enter a message: ");
while (getchar() != '\n')
len++;
printf("Your message was %d character(s) long.\n", len);
return 0;
}
7.4 类型转换
C89中的转换
特性 | 说明 | 代码 |
---|---|---|
隐式转换 | 发生情况: 1. 算数(逻辑)表达式类型不同时 2. 赋值运算符两侧类型不同时 3. 函数调用实参形参不同时 4. return类型不同时 | |
显式转换 | ||
算数类型转换原则 | 原则: 转换成可以安全使用两个数值的"最狭小的"类型 1. 所有操作数均为整值的提升: int -> unsigned int -> long -> unsigned long (char和short发生提升时无条件转换为int) 2. 任意操作数为浮点的提升: float -> double -> long double | |
赋值类型转换原则 | 原则: 转换为左侧类型 1. 等宽类型: 没有问题 2. 非等宽类型: float->int 丢失精度3. 下降转换: int->char 无意义结果 | |
类型不匹配的危害 | 1. 比较错误: -10 (int) < 10 (unsigned int), 其中int会被转换为unsigned int, 未模式不发生改变, 导致转化为一个非常大的数, 导致最终结果为0 |
7.4.3 C99中的隐式转换
整数转换登记表
等级 | 类型 |
---|---|
1 | long long, unsigned long long |
2 | long, unsigned long |
3 | int, unsigned int |
4 | short, unsigned short |
5 | char, signed char, unsigned char |
6 | _Bool |
特性 | 说明 | 代码 |
---|---|---|
整数提升 | 任何等级低于int和unsigned int的类型均转换为int | |
全整数情况 | 1.整数提升 2.如果结果是类型相同, 则结束; 如果类型不同, 则继续 1.均有符号 或者 均无符号: 向上提升 2.无符号等级 大于 有符号等级: 转换为等级最高的类型 3.有符号可以表示无符号: 转换为有符号 4.其他情况: 均转换为有符号数类型对应的无符号类型 | |
存在浮点情况 | 与C89相同 |
7.4.4 强制类型转换
强制类型转换的两个功能:
- 强制转化类型
- 显式的表示隐式转换的目标类型
7.5 类型定义
使用场景:
- 可移植性: 针对不同的环境, 修改类型定义 (局限: 无法改变使用方式, 后续仍然需要修改代码)
#define BOOL int // 注意顺序与typedef相反
typedef int Bool; // 更优
7.6 sizeof运算符
使用建议: sizeof返回类型为size_t, 建议强制类型转换为已知的无符号整数类型
sizeof(类型名/常量/变量/表达式)
sizeof 变量 // 可以不加括号, 但是需要考虑优先级, 所以推荐使用括号
sizeof(int)
sizeof(a)
sizeof(1)
sizeof int
练习题
答案链接:
- http://knking.com/books/c2/answers/c7.html
- https://github.com/williamgherman/c-solutions/tree/master/07
记号说明:
- 加粗(错误): 表示错误的答案或者遗漏的知识点
- 在代码块中
(错误)
: 表示错误的答案
Practice1
给出下列整数常量的十进制值。
- 077
- 0x77
- 0XABC
回答:
7 * 8 + 7 = 63
7 * 16 + 7 = 119
10 * 256 + 11 * 16 + 12 = 2748
Practice2
下列哪些常量在C语言中不是合法的?区分每一个合法的常量是整数还是浮点数。
- 010E2
- 32.1E+5
- 0790
- 100_000
- 3.978e-2
回答:
1. 不确定: 八进制是否存在科学计数法形式 (订正: 合法)
2. 合法, 浮点数
3. 合法, 整数 (不合法: 八进制没有9)
4. 不确定: (只记得rust里面是合法的) (不合法: c中不能有下划线)
5. 合法, 浮点数
Practice3
下列哪些类型在C语言中不是合法的?
- short unsigned int
- short float
- long double
- unsigned long
回答:
1.合法
2.不合法
3.合法
4.合法
Practice4
如果变量c 是char 类型,那么下列哪条语句是非法的?
- i += c; /* i has type int */
- c = 2 * c - 1;
- putchar(c);
- printf(c);
回答:
1. 合法
2. 合法
3. 合法
4. 非法: 需要格式串
Practice5
下列哪条不是书写数65的合法方式?(假设字符集是ASCII。)
- 'A'
- 0b1000001
- 0101
- 0x41
回答:
1.合法
2.不确定: 好像没有二进制立即数 (排除法确定错误)
3.合法
4.合法
Practice6
对于下面的数据项,指明char 、short 、int 、long 类型中哪种类型是足以存储数据的最小类型。
- 一个月的天数
- 一年的天数
- 一天的分钟数
- 一天的秒数
回答:
char
short
short
int (错误: long)
Practice7
对于下面的字符转义,给出等价的八进制转义。(假定字符集是 ASCII。)可以参考附录E,其中列出了ASCII字符的数值码。
- \b
- \n
- \r
- \t
回答:
010
012
015
011
Practice8
重复练习题7,给出等价的十六进制转义。
回答:
0x08
0x0A
0x0D
0x09
Practice9
假设变量i 和变量j 都是int 类型,那么表达式i / j + 'a' 是什么类型?
回答:
(i / j) + 'a'
左操作数(i / j)运算后仍为int, 右操作数'a'为char
整数提升: 操作数为int
Practice10
假设变量i 是int 类型,变量j 是long int 类型,并且变量k 是unsigned int 类型,那么表达式i + (int)j * k 是什么类型?
回答:
强制类型转换为一元运算符, 优先级最高
i + ((int)j * k)
int + (int * unsigned int)
全为整数的情况, 且同级: 全部转换为无符号类型
最终: unsigned int
Practice11
假设变量i 是int 类型,变量f 是float 类型,变量d 是 double 类型,那么表达式i * f / d 是什么类型?
回答:
左结合性可得:
(i * f) / d
(int * float) / double
最终: double
Practice12
假设变量i 是int 类型,变量f 是float 类型,变量d 是 double 类型,请解释在执行下列语句时发生了什么转换?
d = i + f
回答:
右侧表达式: i + f含有浮点数, 转换为float类型
赋值运算符: 类型转换为左侧类型, double
Practice13
假设程序包含下列声明:
char c = '\1';
short s = 2;
int i = -3;
long m = 5;
float f = 6.5f;
double d = 7.5;
给出下列每个表达式的值和类型:
- c * i = -3, int
- s + m = 7, long
- f / c = 6.5f, float
- d / s = 3.75, double
- f - d = -1.0, double
- (int) f = 6, int
回答:
Practice14
下列语句是否总是可以正确地计算出f 的小数部分(假设f 和 frac_part 都是float 类型的变量)?
如果不是, 那出了什么问题?
frac_part = f - (int) f;
回答:
可以 (错误: float比int的值域更大, 可能会溢出)
Practice15
使用typedef 创建名为Int8 、Int16 和Int32 的类型。定义这些类型以便它们可以在你的机器上分别表示8位、16位和32位的整数。
回答:
typedef char Int8;
typedef short Int16;
typedef int Int32;
代码题
code1-max
如果i * i 超出了int 类型的最大取值,那么6.3节的程序 square2.c 将失败(通常会显示奇怪的答案)。运行该程序,并确定导致失败的n 的最小值。尝试把变量i 的类型改成short 并再次运行该程序。(不要忘记更新printf 函数调用中的转换说明!)然后尝试改成long 。从这些实验中,你能总结出在你的机器上用于存储整数类型的位数是多少吗?
代码如下
#include <stdio.h>
int main(void) {
short i, n;
printf("This program prints a table of squares.\n");
printf("Enter number of entries in table: ");
scanf("%hd", &n);
for (i = 1; i <= n; i++) {
printf("%10hd%10hd\n", i, i * i);
}
return 0;
}
编译并运行
This program prints a table of squares.
Enter number of entries in table: 1000
1 1
2 4
3 9
4 16
5 25
....
178 31684
179 32041
180 32400
181 32761
182 -32412
code2-square3
修改6.3节的程序square2.c ,每24次平方后暂停并显示下列信息:
Press Enter to continue...
代码如下
#include <stdio.h>
int main(void) {
short i, n;
char counter;
printf("This program prints a table of squares.\n");
printf("Enter number of entries in table: ");
scanf("%hd", &n);
while (getchar() != '\n');
for (i = 1, counter = 0; i <= n; i++) {
printf("%10hd%10hd\n", i, i * i);
counter++;
if (counter == 24) {
printf("Press Enter to continue...\n");
getchar();
counter = 0;
}
}
return 0;
}
编译并运行
code3-sum3
修改7.1节的程序sum2.c ,对double 型值组成的数列求和。
代码如下
#include <stdio.h>
int main(void) {
double n, sum = 0;
printf("This program sums a series of double.\n");
printf("Enter integers (0 to terminate): ");
scanf("%lf", &n);
while (n != 0) {
sum += n;
scanf("%lf", &n);
}
printf("The sum is: %f\n", sum);
return 0;
}
编译并运行
This program sums a series of double.
Enter integers (0 to terminate): 11.2 22.1 33.3 0
The sum is: 66.600000
code4-transform
编写程序可以把字母格式的电话号码翻译成数值格式:
(如果没有电话在身边,参考这里给出的字母在键盘上的对应关系:2=ABC ,3=DEF ,4=GHI ,5=JKL ,6=MNO ,7=PQRS , 8=TUV ,9=WXYZ 。)原始电话号码中的非字母字符(例如数字或标点符号)保持不变:
可以假设任何用户输入的字母都是大写字母。
实例一
Enter phone number: CALLATT
2255288
实例二
Enter phone number: 1-800-COL-LECT
1-800-265-5328
代码如下
#include <stdio.h>
int main(void) {
char ch;
printf("Enter phone number: ");
ch = getchar();
while (ch != '\n') {
switch(ch) {
case 'A': case 'B': case 'C':
ch = '2'; break;
case 'D': case 'E': case 'F':
ch = '3'; break;
case 'G': case 'H': case 'I':
ch = '4'; break;
case 'J': case 'K': case 'L':
ch = '5'; break;
case 'M': case 'N': case 'O':
ch = '6'; break;
case 'P': case 'Q': case 'R': case 'S':
ch = '7'; break;
case 'T': case 'U': case 'V':
ch = '8'; break;
case 'W': case 'X': case 'Y': case 'Z':
ch = '9'; break;
}
printf("%c", ch);
ch = getchar();
}
printf("\n");
return 0;
}
编译并运行
╭─lamecrow@Lam3Cr0w ~/TRY/code/c/Retest/C-Chapter7/code4-transform
╰─$ cd "/Users/lamecrow/TRY/code/c/Retest/C-Chapter7/code4-transform/" && gcc transfo
rm.c -o transform && "/Users/lamecrow/TRY/code/c/Retest/C-Chapter7/code4-transform/"t
ransform
Enter phone number: CALLATT
2255288
╭─lamecrow@Lam3Cr0w ~/TRY/code/c/Retest/C-Chapter7/code4-transform
╰─$ cd "/Users/lamecrow/TRY/code/c/Retest/C-Chapter7/code4-transform/" && gcc transfo
rm.c -o transform && "/Users/lamecrow/TRY/code/c/Retest/C-Chapter7/code4-transform/"t
ransform
Enter phone number: 1-800-COL-LECT
1-800-265-5328
code5-character
在十字拼字游戏中,玩家利用小卡片组成单词,每个卡片包含字母和面值。面值根据字母稀缺程度的不同而不同。(面值有:1------ AEILNORSTU,2------DG,3------BCMP,4------FHVWY,5------K,8------JX, 10------QZ。)编写程序通过对单词中字母的面值求和来计算单词的值:
编写的程序应该允许单词中混合出现大小写字母。提示 :使用 toupper 库函数。
Enter a word: pitfall
Scrabble value: 12
代码如下
#include <stdio.h>
#include <ctype.h>
int main(void) {
char ch;
int sum = 0;
printf("Enter a word: ");
ch = getchar();
while (ch != '\n') {
ch = toupper(ch);
switch(ch) {
case 'A': case 'E': case 'I': case 'L': case 'N':
case 'O': case 'R': case 'S': case 'T': case 'U':
sum += 1; break;
case 'D': case 'G':
sum += 2; break;
case 'B': case 'C': case 'M': case 'P':
sum += 3; break;
case 'F': case 'H': case 'V': case 'W': case 'Y':
sum += 4; break;
case 'K':
sum += 5; break;
case 'J': case 'X':
sum += 8;
case 'Q': case 'Z':
sum += 10;
}
ch = getchar();
}
printf("\nScrabble value: %d\n", sum);
return 0;
}
编译并运行
Enter a word: pitfall
Scrabble value: 12
code6-sizeof
编写程序显示sizeof(int) 、sizeof(short) 、 sizeof(long) 、sizeof(float) 、sizeof(double) 和 sizeof(long double) 的值。
代码如下
#include <stdio.h>
int main(void) {
printf("int : %lu\n", sizeof(int));
printf("short: %lu\n", sizeof (short));
printf("long: %lu\n", sizeof(long));
printf("float: %lu\n", sizeof(float));
printf("double: %lu\n", sizeof(double));
printf("long double: %lu\n", sizeof(long double));
return 0;
}
编译并运行
int : 4
short: 2
long: 8
float: 4
double: 8
long double: 8
code7-compute
修改第3章的编程题6,使得用户可以对两个分数进行加、减、乘、除运算(在两个分数之间输入+ 、- 、* 或/ 符号)。
代码如下
#include <stdio.h>
int main(void) {
int m1, s1, m2, s2, rm, rs;
char operator;
printf("Enter two fractions separated by a plus sign: ");
scanf("%d/%d %c %d/%d", &s1, &m1, &operator, &s2, &m2);
switch(operator) {
case '+':
rs = s1 * m2 + s2 * m1;
rm = m1 * m2;
break;
case '-':
rs = s1 * m2 - s2 * m1;
rm = m1 * m2;
break;
case '*':
rs = s1 * s2;
rm = m1 * m2;
break;
case '/':
rs = s1 * m2;
rm = m1 * s2;
break;
}
printf("\nThe result is %d/%d\n", rs, rm);
return 0;
}
编译并运行
Enter two fractions separated by a plus sign: 3/1 - 2/1
The result is 1/1