C语言程序设计的源码和书籍, 所有源码都是可执行的,整理不易家人们
https://kdocs.cn/join/gi76b0d?f=101 ➕绿泡泡:jianyuePros, 备注:C 程序设计;五个元子下载使用;
「《C 程序设计》(第五版)谭浩强主编,清华大学出版社,2017 年和源码」
文章目录
3.1 C语言的数据类型和3.2 常量与变量
- 数据类型
- 基本类型
- 整型
- 字符型
- 实型(浮点型)
- 单精度型
- 双精度型
- 枚举类型
- 构造类型
- 数组类型
- 结构体类型
- 共用体类型
- 指针类型
- 空类型
- 基本类型
#include <stdio.h>
#include <string.h>
#include <stdbool.h> // 包含布尔类型定义
// 常量和符号常量
#define PRICE 30
// 函数返回空类型
void greet()
{
printf("你好!\n");
}
// 定义一个枚举类型,表示一周中的几天
enum Weekday
{
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
};
// 定义一个结构体类型,表示学生信息
struct Student
{
char name[50];
int age;
float score;
};
// 定义一个共同体类型,表示既可以存储整数也可以存储浮点数的变量
union Data
{
int intValue;
float floatValue;
};
int main()
{
int age = 30; // 整型变量
printf("年龄: %d\n", age);
char initial = 'A'; // 字符型变量
printf("首字母: %c\n", initial);
float height = 170.5f; // 单精度浮点型
double pi = 3.14159; // 双精度浮点型
printf("身高: %.1f 米\n", height);
printf("π值: %lf\n", pi);
bool isMale = true; // 布尔型变量
printf("性别: %s\n", isMale ? "男" : "女");
greet(); // 调用无返回值的函数
int numbers[5] = {1, 2, 3, 4, 5}; // 整型数组
for (int i = 0; i < 5; i++)
{
printf("numbers[%d]: %d\n", i, numbers[i]);
}
int num = 100;
int *ptr = # // 整型指针
printf("数值: %d\n", num);
printf("通过指针访问数值: %d\n", *ptr);
enum Weekday today = Wednesday; // 声明并初始化一个枚举变量
// 打印今天是周几
switch (today)
{
case Monday:
printf("今天是星期一。\n");
break;
case Tuesday:
printf("今天是星期二。\n");
break;
case Wednesday:
printf("今天是星期三。\n");
break;
case Thursday:
printf("今天是星期四。\n");
break;
case Friday:
printf("今天是星期五。\n");
break;
case Saturday:
printf("今天是星期六。\n");
break;
case Sunday:
printf("今天是星期日。\n");
break;
}
// 注意:枚举值默认从0开始,但可以指定起始值
enum
{
Red = 100, // 从100开始
Green,
Blue
};
enum
{ // 也可以不命名枚举类型,直接使用
Jan = 1,
Feb,
Mar
};
printf("Green 的值是:%d\n", Green); // 输出 Green 的值,基于前面的定义应为 101
struct Student student1; // 声明一个结构体变量
// 初始化结构体变量
strcpy(student1.name, "张三");
student1.age = 20;
student1.score = 88.5;
// 打印学生信息
printf("姓名: %s, 年龄: %d, 成绩: %.1f\n", student1.name, student1.age, student1.score);
union Data data;
// 存储整数
data.intValue = 100;
printf("作为整数时的值: %d\n", data.intValue);
// 同一块内存现在存储浮点数
data.floatValue = 3.14;
// 注意:这里打印的是之前整数值转换成浮点数的结果,因为共用体在同一时间只能有效存储一种类型的数据
printf("转换为浮点数后的值(可能不准确): %.2f\n", data.floatValue);
int num1, total;
num1 = 10;
total = num1 * PRICE;
printf("total = % d", total);
return 0;
}
3.3.2 整型变量(原码,反码,补码)
在计算机科学中,补码、原码和反码是用来表示带符号整数的二进制编码方法,特别是在计算机内存中存储和处理整数时。这些编码方式帮助计算机区分正数和负数,并支持算术运算。以下是它们的具体含义:
原码(True Form or Sign-and-Magnitude)
- 定义:原码是最直接的表示方法,用最高位来表示符号(0表示正数,1表示负数),其余位表示数值的绝对值。例如,+3的原码为0011,而-3的原码为1011。
- 特点:简单直观,但直接使用原码进行加法运算时,正数与负数的加法会出现问题,因为符号位会直接相加。
反码(Ones’ Complement)
- 定义:反码是为了解决原码加法问题而引入的,对于正数,反码与原码相同;对于负数,除了符号位保持不变,数值位上的每一位都取反(0变成1,1变成0)。例如,+3的反码依然是0011,而-3的反码则是1100。
- 特点:使用反码进行加法运算可以正确处理正数加负数的情况,但是0有两种表示(+0和-0),且减法运算依然不直接。
补码(Two’s Complement)
- 定义:补码是在反码的基础上,对负数的反码再加1得到的。正数的补码与原码、反码相同。例如,+3的补码为0011,而-3的补码是1101(先取反得到1100,再加1)。
- 特点:
- 唯一性:所有数(包括正数、负数和零)都有唯一的补码表示,消除了+0和-0的问题。
- 方便计算:使用补码可以直接进行加法和减法运算,无需区分操作,因为减一个数等同于加上它的补码。
- 溢出处理:补码还能自然处理溢出问题,使得计算机在处理算术运算时可以忽略溢出情况,简化硬件设计。
总结:在现代计算机系统中,补码是最常用的表示方法,因为它简化了硬件设计,并且使得加法、减法运算以及溢出的处理变得统一和高效。原码和反码在教学中主要用于解释补码的概念,以及理解计算机系统的历史发展。
了解您想看关于原码、反码、补码的实际应用案例,下面我将通过一个简单的C语言示例来演示如何在实际编程中使用这些概念来处理有符号整数的运算。这个例子将会展示如何手动计算一个负数的原码、反码和补码,并用补码形式执行加法运算。
实战案例:计算负数的补码并进行加法运算
#include <stdio.h>
// 手动计算负数的补码
unsigned int calculateTwosComplement(int number) {
if (number >= 0) return number; // 正数的补码等于其原码
unsigned int absNumber = -(unsigned int)number; // 获取绝对值的无符号表示
return ~(absNumber) + 1; // 取反后加1得到补码
}
int main() {
int a = -5; // 示例中的负数
int b = 3; // 示例中的正数
// 计算a的补码
unsigned int a_twos_complement = calculateTwosComplement(a);
printf("数字 %d 的补码是:%u\n", a, a_twos_complement);
// 将a的补码转换回原码表示,仅为了展示原码和反码的计算过程
int a_original = (int)a_twos_complement;
if (a < 0) a_original = -((int)~a_twos_complement + 1);
printf("数字 %d 的原码是:%d\n", a, a_original);
// 注意:在实际计算机内存中,a直接以补码形式存储,不会手动转换回原码
// 使用补码进行加法运算
// 注意:直接使用C语言的加法运算符即可,因为编译器内部会处理补码运算
int sum = a + b;
printf("-%d 加上 %d 的结果(使用补码计算)是:%d\n", a, b, sum);
return 0;
}
解释
- 上述代码首先定义了一个函数
calculateTwosComplement
,用于计算一个整数的补码。特别地,如果输入是正数或零,函数直接返回原值(因为正数的补码等于其原码);如果是负数,则先取绝对值转换为无符号整数,然后对每位取反,最后加1得到补码。 - 在
main
函数中,我们定义了两个整数变量a
和b
,分别代表一个负数和一个正数。我们通过调用calculateTwosComplement
函数计算a
的补码,并打印出来。 - 接着,为了教学目的,我们手动演示了如何从补码恢复到原码的过程,但实际上在计算机中我们不需要做这个转换,因为计算机会直接使用补码进行运算。
- 最后,我们通过简单的加法运算
a + b
展示了在C语言中,即使是负数和正数的加法,也能直接进行,这是因为编译器底层处理的是补码形式的数值。
这个例子展示了补码在实际编程中的应用,尤其是在处理有符号整数运算时,补码提供了便利和效率。
【例3.2 】整型变量的定义与使用。
main()
{
int a, b, c, d;
unsigned u;
a = 12;
b = -24;
u = 10;
c = a + u;
d = b + u;
printf(“a + u = % d, b + u = % d\n”, c, d);
}
【例 3.3】整型数据的溢出。
main()
{
a=32767
b=a+1;
printf("%
}
【例 3.4】
main()
{
long x, y;
int a, b, c, d;
x = 5;
y = 6;
a = 7;
b = 8;
c = x + a;
d = y + b;
printf("c=x+a=%d,d=y+b=%d\n", c, d);
}
3.4 实型数据
#include <stdio.h>
main()
{
printf("%f\n ", 356.);
// printf("%f\n ",356);
// printf("%f\n ",356f);
float a, b;
a = 123456.789e5;
b = a + 20;
printf("%f \n", a); //自动类型转换,精度缺失
float a1;
double b1;
a1 = 33333.33333;
b1 = 33333.3333333333;
printf("%f \n %f \n", a1, b1);// b1存在精度缺失,有效位只有七
return 0;
}
3.5 字符型数据
3.5.2 转义字符
#include <stdio.h>
main()
{
int a, b, c;
a = 5;
b = 6;
c = 7;
printf("ab c\tde\rf\n");
printf("hijk\tL\bM\n");
return 0;
}
3.6 变量赋初值
#include <stdio.h>
main()
{
/* 【例 3.9】向字符变量赋以整数。 */
// char a, b;
// a = 120;
// b = 121;
// printf("%c,%c\n", a, b);
// printf("%d,%d\n", a, b);
/* 【例 3.10】 字符串变量参与运算, 大小写字母的 ASCII 码相差 32*/
// char a, b;
// a = 'a';
// b = 'b';
// a = a - 32;
// b = b - 32;
// printf("%c,%c\n%d,%d\n", a, b, a, b);
/* 【例 3.11】 应注意,在定义中不允许连续赋值,如 a=b=c=5 是不合法的。*/
// int a = 3, b, c = 5;
// b = a + c;
// printf("a=%d,b=%d,c=%d\n", a, b, c);
/* 【例 3.12】 本例程序中,PI 为实型;s,r 为整型。在执行 s=r*r*PI 语句时,r 和 PI 都转换成 double
型计算,结果也为 double 型。但由于 s 为整型,故赋值结果仍为整型,舍去了小数部分。*/
float PI = 3.14159;
int s, r = 5;
s = r * r * PI;
printf("s=%d\n", s);
/* 【例 3.13】本例表明,f 虽强制转为 int 型,但只在运算中起作用,是临时的,而 f 本身的类型并不
改变。因此,(int)f 的值为 5(删去了小数)而 f 的值仍为 5.75。 */
float f = 5.75;
printf("(int)f=%d,f=%f\n", (int)f, f);
return 0;
}
3.7 各类数值型数据之间的混合运算
#include <stdio.h>
main()
{
/* 【例 3.12】本例程序中,PI 为实型;s,r 为整型。在执行 s=r*r*PI 语句时,r 和 PI 都转换成 double
型计算,结果也为 double 型。但由于 s 为整型,故赋值结果仍为整型,舍去了小数部分。 */
float PI = 3.14159;
int s, r = 5;
s = r * r * PI;
printf("s=%d\n", s);
/* 【例 3.13】本例表明,f 虽强制转为 int 型,但只在运算中起作用,是临时的,而 f 本身的类型并不
改变。因此,(int)f 的值为 5(删去了小数)而 f 的值仍为 5.75。 */
float f = 5.75;
printf("(int)f=%d,f=%f\n", (int)f, f);
return 0;
}
3.8 算术运算符和算术表达式
C语言的运算符可分为以下几类:
- 算术运算符:用于各类数值运算。包括加(+)、减(-)、乘(*)、除(/)、求余(或称模运算,%)、
自增(++)、自减(–)共七种。 - 关系运算符:用于比较运算。包括大于(>)、小于(<)、等于(==)、 大于等于(>=)、小于等于(<=)和不等于(!=)六种。
- 逻辑运算符:用于逻辑运算。包括与(&&)、或(||)、非(!)三种。
- 位操作运算符:参与运算的量,按二进制位进行运算。包括位与(&)、位或(|)、位非(~)、
位异或(^)、左移(<<)、右移(>>)六种。 - 赋值运算符:用于赋值运算,分为简单赋值(=)、复合算术赋值(+=,-=,*=,/=,%=)和复合位
运算赋值(&=,|=,^=,>>=,<<=)三类共十一种。 - 条件运算符:这是一个三目运算符,用于条件求值(?😃。
- 逗号运算符:用于把若干表达式组合成一个表达式(,)。
- 指针运算符:用于取内容(*)和取地址(&)二种运算。
- 求字节数运算符:用于计算数据类型所占的字节数(sizeof)。
- 特殊运算符:有括号(),下标[],成员(→,.)等几种。
代码案例
#include <stdio.h>
main()
{
/* 〖例 3.14〗 本例中,20/7,-20/7 的结果均为整型,小数全部舍去。而 20.0/7 和-20.0/7 由于有实数参与运算,因此结果也为实型。
求余运算符(模运算符)“%”:双目运算,具有左结合性。要求参与运算的量均为整型。
求余运算的结果等于两数相除后的余数。
*/
printf("\n\n%d,%d\n", 20 / 7, -20 / 7);
printf("%f,%f\n", 20.0 / 7, -20.0 / 7);
/* 【例 3.15】本例输出 100 除以 3 所得的余数 1。 */
printf("%d\n", 100 % 3);
/* 【例 3.16】 i 的初值为 8,第 2 行 i 加 1 后输出故为 9;第 3 行减 1 后输出故为 8;第 4 行输出 i 为
8 之后再加 1(为 9);第 5 行输出 i 为 9 之后再减 1(为 8) ;第 6 行输出-8 之后再加 1(为 9),
第 7 行输出-9 之后再减 1(为 8)。 */
// int i = 8;
// printf("%d\n", ++i);
// printf("%d\n", --i);
// printf("%d\n", i++);
// printf("%d\n", i--);
// printf("%d\n", -i++);
// printf("%d\n", -i--);
/* 【例 3.17】 这个程序中,对 P=(i++)+(i++)+(i++)应理解为三个 i 相加,故 P 值为 15。然后 i 再自增 1
三次相当于加 3 故 i 的最后值为 8。而对于 q 的值则不然,q=(++j)+(++j)+(++j)应理解为 q
先自增 1,再参与运算,由于 q 自增 1 三次后值为 8,三个 8 相加的和为 24,j 的最后值仍
为 8。 */
int i = 5, j = 5, p, q;
p = (i++) + (i++) + (i++);
q = (++j) + (++j) + (++j);
printf("%d,%d,%d,%d", p, q, i, j);
return 0;
}
3.9 赋值运算符和赋值表达式和3.10 逗号运算符和逗号表达式和3.11 小结
#include <stdio.h>
main()
{
/* 【例 3.18】 本例表明了上述赋值运算中类型转换的规则。a 为整型,赋予实型量 y 值 8.88 后只取整
数 8。x 为实型,赋予整型量 b 值 322, 后增加了小数部分。字符型量 c1 赋予 a 变为整型,
整型量 b 赋予 c2 后取其低八位成为字符型(b 的低八位为 01000010,即十进制 66,按 ASCII
码对应于字符 B)。*/
// int a, b = 322;
// float x, y = 8.88;
// char c1 = 'k', c2;
// a = y;
// x = b;
// a = c1;
// c2 = b;
// printf("%d,%f,%d,%c", a, x, a, c2);
/* 【例 3.19】 本例中,y 等于整个逗号表达式的值,也就是表达式 2 的值,x 是第一个表达式的值。
对于逗号表达式还要说明两点:
1) 逗号表达式一般形式中的表达式 1 和表达式 2 也可以又是逗号表达式。
例如:
表达式 1,(表达式 2,表达式 3)
形成了嵌套情形。因此可以把逗号表达式扩展为以下形式:
表达式 1,表达式 2,…表达式 n
整个逗号表达式的值等于表达式 n 的值。
2) 程序中使用逗号表达式,通常是要分别求逗号表达式内各表达式的值,并不一定
要求整个逗号表达式的值。
并不是在所有出现逗号的地方都组成逗号表达式,如在变量说明中,函数参数表中逗号
只是用作各变量之间的间隔符。*/
int a = 2, b = 4, c = 6, x, y;
y = (x = a + b), (b + c);
printf("y=%d,x=%d", y, x);
return 0;
}