目录
一.数据类型转换
1. 隐式转换+强制转换+隐式规则
概念:不一致但相互兼容的数据类型,在同一表达式中将会发生类型转换。
隐式转换:系统按照隐式规则自动进行的转换
强制转换:用户显式自定义进行的转换
隐式规则:从小类型向大类型转换,目的是保证不丢失表达式中数据的精度
隐式转换示例代码
#include <stdio.h>
int main(void){
char a = 'a';
int b = 12;
float c = 3.14;
float x = a + b - c; // 在该表达式中将发生隐式转换,所有操作数被提升为float
printf("a + b - c = %f",x);
}
强制转换:用户强行将某类型的数据转换为另一种类型,此过程可能丢失精度
char a = 'a';
int b = 12;
float c = 3.14;
float x = a + b - (int)c;
在该表达式中a隐式自动转换为int,c被强制转为int,不管是隐式转换,还是强制转换,变换的都是操作数在运算过程中的类型,是临时的,操作数本身的类型不会改变,也无法改变。
2.数据类型的本质
概念:各种不同的数据类型,本质上是用户与系统对某一块内存数据的解释方式的约定。
推论:类型转换,实际上是对先前定义时候的约定,做了一个临时的打破。 理论上,可以对任意的数据做任意的类型转换,但转换之后的数据解释不一定有意义。
3.可移植性整型 typedef
概念:不管放到什么系统,尺寸保持不变的整型数据,称为可移植性整型
关键:typedef
typedef int int32_t; // 将类型 int 取个别名,称为 int32_t
typedef long int64_t; // 将类型 long 取个别名,称为 int64_t
思路:
为所有的系统提供一组固定的、能反应数据尺寸的、统一的可移植性整型名称 在不同的系统中,为这些可移植性整型提供对应的 typedef 语句
系统预定义的可移植性整型:
int8_t
int16_t
int32_t
int64_tuint8_t
uint16_t
uint32_t
uint64_tpid_t
time_t
size_t … …
二 .符号位
1.算术运算符
注意:
减号也是负号,比如 -a 是取变量 a 的相反数。 取模运算要求左右两边操作数必须是整型数据。
自加和自减运算不仅可以对整型操作,也可以对浮点数、指针操作。 前后缀运算:
- 前缀自加自减运算:先进行自加自减,再参与表达式运算
- 后缀自加自减运算:先参与表达式运算,在进行自加自减
int a = 100;
int c = ++a; // a先自加1,变成101;然后再赋值给c,因此c等于101
int d = a++; // a先赋值给d,因此d等于100;然后a自加1,变成101
2.关系运算符
注意: 1. 关系运算符用于判断运算符两边的表达式是否满足给定的大小条件。
2.由关系运算符组成的表达式称为关系表达式,其值为布尔型。
3.判断是否相等是双等号==,而不是一个等号。
3.逻辑运算符
运算规则:
逻辑反:将逻辑真、假翻转,即真变假,假变真。
逻辑与:将两个关系表达式串联起来,当且仅当左右两个表达式都为真时,结果为真。
逻辑或:将两个关系表达式并联起来,当且仅当左右两个表达式都为假时,结果为假。
&& | 0 | 1 | || | 0 | 1 |
0 | 0 | 0 | 0 | 0 | 1 |
1 | 0 | 1 | 1 | 1 | 1 |
注意:
在逻辑与运算中,如果左边表达式的值为假,那么右边表达式将不被执行。
在逻辑或运算中,如果左边表达式的值为真,那么右边表达式将不被执行。
4.位运算符
位运算符操作的对象是数据中的每一位
运算规则:
位逻辑反、位逻辑与、位逻辑或拥有与逻辑运算相似的规则和一样的真值表
异或运算:相同为0,不同为1
移位运算:移出去的不要,空出来的补零。移位运算都是针对无符号数的运算。
左移:
右移:
5.特殊运算符
赋值运算符:不能对常量赋值,只能对变量赋值 不能对数组赋值 可以连续赋值,顺序从右到左
int a, b;
int x[5];
a = 100; // 对变量 a 赋值,正确
3 = 100; // 对常量 3 赋值,错误!
x = 123; // 对数组 b 赋值,错误!
// 连续赋值
a = b = 50; // 先将 50 赋给 b,再将 b 的值赋给 a,正确
复合赋值符:当左右两边有相同的操作数时,采用复合赋值符不仅直观,且能提高运算效率 除了下述10个复合运算符之外,生造别的复合运算符是非法的
// 加减乘除:
a += n; // 等价于 a = a+n;
a -= n; // 等价于 a = a-n;
a *= n; // 等价于 a = a*n;
a /= n; // 等价于 a = a/n;
// 求余:
a %= n; // 等价于 a = a%n;
// 位运算:
a &= n; // 等价于 a = a&n;
a |= n; // 等价于 a = a|n;
a ^= n; // 等价于 a = a^n;
a >>= n; // 等价于 a = a>>n;
a <<= n; // 等价于 a = a<<n;
6.条件运算符
哒哒:唯一需要三个操作数的运算符
语法:表达式1 ? 表达式2 : 表达式3
释义:当表达式1为真时,取表达式2,否则取表达式3
int a = 100;
int b = 200;
int m = (a>b) ? a : b; // 如果 a>b 为真,则 m 取 a 的值,否则取 b 的值
7.sizeof 运算符
含义:计算指定数据类型或者变量所占据内存的字节数
语法:sizeof(类型) 、sizeof(变量) ,计算变量的字节数时圆括号可以省略
eg:
#include <stdio.h>
//2023.2.2 gyp
int main(void){
printf("%d\n", sizeof(int));
printf("%d\n", sizeof(long double));
int a[5];
printf("%d\n", sizeof(a));
printf("%d\n", sizeof a);
}
8.return运算符
含义:退出某个函数(如果退出的是主函数main,那么整个程序也就退出了) 语法:必须出现在函数体内,可以带函数对应类型的数据
int main()
{
return 0;
}
9.优先级与结合性+优美表格
当表达式中出现不同的运算符时,根据优先级来决定谁先执行,比如先乘除,后加减
当表达式中出现多个相同优先级的运算符时,根据结合性来决定谁先运行,比如从左到右
三.控制流
1.二路分支 if-else
逻辑:程序中某段代码需要在满足某个条件时才能运行
形式:if 语句:表达一种 如果-则 的条件执行关系
if-else 语句:表达一种 如果-否则 的互斥分支关系
eg:
#include <stdio.h>
//2023.2.2 gyp
int main(void){
int a;
printf("请输入一个正整数:");
scanf("%d",&a);
// if 语句
if(a%2 == 0){
printf("a是偶数\n");
}// of if
//if-else 语句
if(a%2 == 0){
printf("a是偶数\n");
}
else{
printf("a是奇数\n");
}//of else
return 0;
}//of main
注意:if 语句可以单独使用,else 语句不可以,else 语句必须跟if语句配套使用 不管是 if 语句还是 else语句,代码块都必须使用大括号{}括起来,否则只有首句有效
2. 多路分支 switch
定义:根据不同的条件执行不同的代码片段
eg:
switch(a){
case 1:
printf("one\n");
break;
case 2:
printf("two\n");
break;
case 3:
printf("three\n");
break;
default:
printf("其他数字\n");
}//of switch
要点分析:
1.switch(n) 语句中的 n 必须是一个整型表达式,即 switch 判断的数据必须是整型
2.case语句只能带整型常量,包括普通整型或字符,不包括 const 型数据。
3.break 语句的作用是跳出整个 swtich 结构,没有 break程序会略过case往下执行 default 语句不是必须的,一般放在最后面(因此不需要 break)
3.while与 do…while循环
逻辑:使得程序中每一段代码可以重复循环地运行
形式:while 循环:先判断,再循环
do-while 循环:先循环,再判断
while循环:入口判断
// 循环输出一系列整数,直到100为止
int a;
scanf("%d", &a);
while(a <= 100){
printf("%d\n", a);
a++;
}
do-while循环:出口判断
int a;
scanf("%d", &a);
do{
printf("%d\n", a);
a++;
}while(a <= 100);
注意:
while 循环先进行判断,条件为真后才执行循环体,因此循环体可能一遍也不执行。
do-while循环先执行循环体,再进行判断,因此循环体至少会执行一遍。
do-while 循环中的 while 语句后面有分号;
4.for循环
逻辑:与 while 循环类似,但更加紧凑,for 循环将控制循环的变量集中在一行
int a;
for(a=1; a<=100; a++){
printf("%d\n", a);
}
语法点:
循环头的标准语法是: for(表达式1 ; 表达式2 ; 表达式3)
表达式1一般用来初始化循环控制变量
表达式2一般用来作为循环判定条件,为真则进入循环,为假则跳出循环
表达式3一般用来更新循环控制变量 三个表达式均可以省略,但分号不能省略
5. break与continue
逻辑:
break:① 跳出 switch 语句; ② 跳出当层循环体
continue:结束当次循环,进入下次循环
switch(n){
case 1:
printf("one\n");
break; // 跳出 switch 语句
case 2:
printf("two\n");
break;
}
while(1){
int n = scanf("%d", &a);
if(n == 0)
break; // 跳出当层 while 循环体
}
for(int i=1; i<=100; i++){
if(i%7 == 0)
continue; // 跳过所有能被7整除的数
printf("%d\n", i);
}
6.goto语句
逻辑:无条件跳转
int main(){
printf("%d\n", __LINE__); // 打印第3行
// 无条件跳转到label处
goto label;
printf("%d\n", __LINE__); // 打印第7行,此处被略过
label:
printf("%d\n", __LINE__); // 打印第9行
}
语法:goto语句直接跳转到本代码块中的标签处 标签指的是以冒号结尾的标识符
四.练习3day
1.问题一
假设有一个无符号32位整型数据
unsigned int data=0x12ff0045
请编写一个程序用位运算把data的第14、15位置位(即赋值为1),把22、23位置零(即赋值为0), 并且输出修改后的数据。
参考答案:
#include <stdio.h>
int main(){
unsigned int data=0x12ff0045;//0001 0010 1111 1111 0000 0000 0100 0101
unsigned int num1=0x3<<14;//0110 0000 0000 0000
data=data|num1;//或运算data的第14、15位置1
unsigned int num2=0x3<<22;//1100 0000 0000 0000 0000 0000
data=data^num2;//异或 22、23位置零
printf("%x",data);
}
2.问题二(基本数据类型)
每一种数据类型都是有一定的表达范围的,编程实现一种算法,在不假定数据类型长度的条件下,用最简便的方式算出当前系统的整型 int所能表达的最大值。
要求:
不能使用系统宏定义 INT_MAX。
参考答案:
#include <stdio.h>
int main(){
int a,max;
a=1;
while (1){
max=a;
a=a<<1;
a|=1;
if(a < 0)
break;
}
printf("int的最大值为%d\n", max);
// 方式二:
int max1 = ~(0x1 << sizeof(int)*8-1);
printf("int的最大值为%d\n", max1);
}
3.问题三
编写一个程序,计算N升纯净水所包含的分子个数,结果用科学计数法表示
( 每个水分子的质量是3.02*10 ^(-23)千克)
参考答案:
#include <stdio.h>
#define WaterMolecule 3.02e-23
int main(){
double MolecularNumber,WaterCapacity;
printf("请输入水的容量(升):\n");
while(1){
// 输入数字
if(scanf("%lf", &WaterCapacity) == 1)
break;
// 输入非数字,清空缓冲区
while(getchar()!='\n');
printf("请输入数字:\n");
}//of while
MolecularNumber=WaterCapacity/WaterMolecule;
printf("你拥有%e个水分子!",MolecularNumber);
}
4.问题四
【4】编写一个程序,计算 1+2+3+4+…+n 的值。
解析:
一般初学者看到本题,大多会直接使用循环来解决,但是求解1到n的求和问题是有公式的,在程序中恰当地运用数学公式,可以极大提高程序效率,数学是程序开发者不可以遗忘的关键一环。
参考答案 :
//参考代码:
#include <stdio.h>
int main(){
int n;
scanf("%d", &n);
// 直接利用数学公式,求解前n为自然数之和
printf("1+...+n = %d\n", n*(1+n)/2);
return 0;
}
5.问题五
编写一个程序,将用户输入的天数转化为等价的星期数和天数。 例如:
输入:18
输出:2星期零4天解析: 本题主要考察求余(取模)运算符。
#include <stdio.h>
#define week 7
int main(void){
int days;
printf("请输入天数:\n");
while(1){
// 输入数字
if(scanf("%d", &days) == 1)
break;
// 输入非数字,清空缓冲区
while(getchar()!='\n');
printf("请输入数字\n");
}
printf("%d星期零%d天\n", days/7, days%7);
return 0;
}
6.问题六(基本程序路基、单目增减运算符)
【6】分析并解释以下程序的执行结果。
#include <stdio.h>
int main(void)
{
int x = 10;
int a, b, c;
a = x++;
b = ++x;
c = 10*x++;
printf("x=%d,a=%d,b=%d,c=%d\n",x,a,b,c);
return 0;
}
参考答案:
7. 问题七(基本输入输出,运算)
编写一个程序,用户输入华氏温度F,程序输出摄氏温度C,结果保留2位小数。
提示: 华氏温度F转化为摄氏温度C的公式为: C = 5×(F - 32)÷ 9
参考答案:
#include <stdio.h>
int main(void){
float fah;
float cel;
printf("请输入华氏温度:\n");
while(1)
{
// 输入数字
if(scanf("%f", &fah) == 1)
break;
// 输入非数字,清空缓冲区
while(getchar()!='\n');
printf("请输入数字\n");
}
cel = (5*(fah-32)) / 9;
printf("华氏%.2fF° = 摄氏%.2fC°\n", fah, cel);
return 0;
}
8.问题八(位运算)
现有一个嵌入式设备终端传输过来的数据包是一个32位无符号的整型数,这个数据包里面各位含义如下:
00-07位:表示温度数据
08-15位:表示湿度数据
16-19位:表示4扇门的状态(门编号0-3)
20-23位:表示4盏灯的状态(灯编号0-3)
24-31位:预留备用下面是最近一段时间接受的数据包:
0x12344520, 0xff004B1C, 0x00553C1E
请编写一个程序接收这3个数据包,并解析出对应的数据【温度,湿度,门0-3,灯0-3】(门和灯的状态0-关,1-开)。
参考答案:
C语言中define的用法_in_nocence的博客-CSDN博客_define函数的用法
#include <stdio.h>
#define TEMP_MASK 0x000000FF
#define HUMI_MASK 0x0000FF00
#define temperature(a) ( (a&TEMP_MASK) >> 0 )//define定义函数
#define humidity(a) ( (a&HUMI_MASK) >> 8 )
#define DOOR0_MASK 0x00010000
#define DOOR1_MASK 0x00020000
#define DOOR2_MASK 0x00040000
#define DOOR3_MASK 0x00080000
#define LIGHT0_MASK 0x00100000
#define LIGHT1_MASK 0x00200000
#define LIGHT2_MASK 0x00400000
#define LIGHT3_MASK 0x00800000
int main(void)
{
unsigned int data;
printf("请输入接收的数据:\n");
while(1){
scanf("%x",&data);
printf("温度:%d\n", temperature(data));
printf("湿度:%d\n", humidity(data));
printf("门0: %s\t", (data & DOOR0_MASK) ? "开":"关");
printf("门1: %s\t", (data & DOOR1_MASK) ? "开":"关");
printf("门2: %s\t", (data & DOOR2_MASK) ? "开":"关");
printf("门3: %s\n", (data & DOOR3_MASK) ? "开":"关");
printf("灯0: %s\t", (data & LIGHT0_MASK) ? "亮":"灭");
printf("灯1: %s\t", (data & LIGHT1_MASK) ? "亮":"灭");
printf("灯2: %s\t", (data & LIGHT2_MASK) ? "亮":"灭");
printf("灯3: %s\n", (data & LIGHT3_MASK) ? "亮":"灭");
}
}
9.问题九 (循环控制)
编写一个程序,输出26个小写字母。
解析: 本题考点两个:
字符的整数本质,可以对字符直接进行加减法运算 基本循环结构
参考答案:
#include<stdio.h>
int main(){
char alphabet;
for(alphabet='a';alphabet<'z';alphabet++){
printf("%c ",alphabet);
}
printf("\n");
return 0;
}
10.问题十(循环控制)
编写一个程序,在用户输入某个大写字母后,产生一个金字塔图案。 例如输入字母E,则产生如下图案:
A ABA ABCBA ABCDCBA ABCDEDCBA
解析: 每一行都是由:“空格、升序字母、降序字母” 序列构成,只要分析出第ii行中有多少个空格、升序字母和降序字母即可。
参考代码:
#include <stdio.h>
#include <stdlib.h>
int main(void){
// 输入一个字母
char ch;
scanf("%c", &ch);
if((ch<'A') || (ch>'Z')){
printf("只接受大写字母\n");
exit(0);//程序正常退出
}
// 总行数
int line = ch - 'A' + 1;
int i, j;
for(i=1; i<=line; i++){
// 输出若干个空格
for(j=0; j<line-i; j++){
printf(" ");
}
// 输出若干个升序字母
for(j=0; j<i; j++){
printf("%c", 'A'+j);
}
// 输出若干个降序字母
for(j-=2; j>=0; j--){
printf("%c", 'A'+j);
}
printf("\n");
}
return 0;
}
11.问题十一(循环控制、多路分支)
编写一个程序,接收用户的输入信息,当用户输入完成后(Ctrl+D代表输入完成),自动统计用户输入的空格数、大小写字母数和其他字符数。
参考代码:
#include<stdio.h>
int main()
{
char cc;
int a=0,b=0,c=0,d=0,e=0;
printf("请输入文本(井号#结束):\n");
while((cc=getchar()) != '#')
{
if(cc==32)
{
a++;
continue;
}
if(cc>='0'&&cc<='9')
{
b++;
continue;
}
if(cc>='A'&&cc<='Z')
{
c++;
continue;
}
if(cc>='a'&&cc<='z')
{
d++;
continue;
}
e++;
}
printf("空格数为:%d\n",a);
printf("数字个数为:%d\n",b);
printf("大写字母数为:%d\n",c);
printf("小写的字母数为:%d\n",d);
printf("其他字符数:%d\n",e);
}
12.问题十二(分支控制、循环控制)
编写一个程序,接受一个整数输入,然后显示所有小于或等于该数的素数。
提示: 素数指只能被1和自身整除的大于等于2的整数,例如2、3、5、7、11……
解答: 本题需要将判断素数的代码封装成一个函数,这可以用来让学生预习C语言中关于函数的语法。参考代码如下:
#include <stdio.h>
#include <limits.h>
#include <stdbool.h>
bool prime(int num){//函数prime
int i;
for(i=2; i*i<(num+1); i++){
if(num%i == 0)
return false;
}
return true;
}
int main(void){
int n;
scanf("%d", &n);
for(int i=2; i<n+1; i++){
if(prime(i))
printf("%d\t", i);
}
printf("\n");
return 0;
}
13.问题十三(分支控制、循环控制)
1976年的一天,《华盛顿邮报》于头版头条刊登了一条数学新闻,文中叙述了这样一则故事:70年代中期,美国个所名牌大学校园内,人们都像发疯一般,日以继夜废寝忘食地玩弄一种数学游戏,这个游戏十分简单,任意写出一个(非零)自然数N,并且按照以下规律进行变换:
如果是个奇数,则下一步变成 3N+1 如果是个偶数,则下一步变成 N/2
一时间学生、教师、研究员、教授,甚至是一等一的数学大拿、天才都纷纷加入这个看似简单的数学游戏,人们取了各种各样的数字N去检验这个算法,最终都无一例外地坠入自然数序列4-2-1,于是就自然萌生出这样的猜想:对于任意非零自然数N,经上述变换最终都将落入4-2-1序列的宿命。这就是著名的角谷猜想,或称冰雹猜想。冰雹猜想最大的魅力,在于其不可预知性,数字N的转化过程变幻莫测,有些平缓温和,有些剧烈沉浮,但却都无一例外地会坠入4-2-1的谷底,这好比是一个数学黑洞,将所有的自然数牢牢吸住。有人把冰雹路径比喻一个参天大树,下面的树根是连理枝4-2-1,而上面的枝枝叶叶则构成了一个奥妙的通路,把一切(非零)自然数统统都覆盖了,这个小学生都看得懂的问题,迄今为止却没有任何数学手段和超级计算机可以证明。
冰雹猜想跟蝴蝶效应恰好相悖,蝴蝶效应蕴含的原理是:初始值的极小误差,会造成结果的巨大不同,而冰雹猜想恰好相反:无论刚开始存在多大的误差,最后都会自行修复,直到坠入谷底。使用你所学习的C语言知识,编程实现冰雹猜想的算法,并测算各个自然数到达4-2-1谷底所经过的变换次数吧。
#include <stdio.h>
int main(void)
{
unsigned long long n;
int num, max=0;
for(n=2; n<999; n++){
int count = 0, a=n, b=n;
while(a > 1){
if(a%2 == 0)
a /= 2;
else
a = 3*a + 1;
count++;
}//of while
if(count > max){
max = count;
num = b;
}//of if
printf("数字[%llu]经过%5d次变换到达黑洞中心【变换次数最高纪录:%d】\n", n, count, max);
}//of for
return 0;
}//of main