本文部分代码可能缺少#include <stdio.h>

请大家在使用时自行加上!!!


一个.c文件中只能有一个main函数;一个工程中可以有多个.c文件。

1.数据类型

char 字符数据类型
short 短整型
int 整型
long 长整型
long long 更长的整型
float 单精度浮点数
double 双精度浮点数

查看数据类型字节数(针对32位或者64位编译器)

int类型可表示的最大整数为2^31-1=65535
说明:明明是32个Bit为什么是31次方,因为最高位是符号位,不参与计算。那为什么要减1呢?因为我们在计算时把0也算进去了。打个比方,假如是8位的编译器,可以存128个,但是正数从零开始,到127,如果要求最大数就要减1。

那么针对16位的编译器

各种类型占多少字节呢?
对于16位的(signed int)整型最大数可表示为2^15-1=32767

计算机中单位:

bit - 比特位 byte - 字节 =8bit kb - 1024byte mb - 1024kb

gb - 1024mb

2.变量、常量

2.1变量

变量分类:局部变量、全局变量

//全局变量 - {}外部定义
int a = 100;
int main()
{//局部变量 - {}内部定义//当局部变量和全局变量名字冲突的情况下,局部优先//不建议把全局变量和局部变量名字写成一样int a = 10;printf("a = %d\n",a);//a = 10return 0;
}

2.1.1作用域和生命周期

除此之外还可以使用两个.c文件,只需声明变量即可

生命周期
变量的生命周期:变量的创建和销毁之间的时间段
局部变量的生命周期:进入局部范围生命开始,出局部范围生命结束
全局变量的生命周期:整个程序的生命周期

2.2 常量

2.2.1 字面常量


无实际含义,但存在。

2.2.2 const修饰的常变量

a为变量,定义a=20后打印出a=20

int main()
{//a为常变量 - 具有常属性(不能改变的属性)const int a = 10;printf("%d\n",a);return 0;
}

被const修饰以后,a不能再修改

**误区:**在创建数组时需要使用常量,此时int n = 10;n为变量,纵使使用const,n为常变量但n的本质还是一个变量,所以报错。

2.2.3 #define定义的标识符常量

#include <stdio.h>
#define min 100
int main()
{int a = min;//min = 2000;//标识符常量不能修改printf("a = %d\n",a);return 0;
}

2.2.4 枚举常量


可以赋初值,当赋2时,就是2,3,4

3. 字符串

定义:双引号引起的一串字符

注:字符串以\0结尾为标志

arr1的字符串是abc\0,打印到c后有\0,不再继续打印
arr2打印abc后后面的数字未知,继续打印
我们手动添加一个\0后充分证明\0是字符串的结束标志

当我们计算字符串长度时,\0不算字符串内容,仅仅只是一个标志。

引入strlen时会报错,只需在前面插入

#include <string.h>


此时arr1的长度为3,arr2的长度为一个随机值

4.转义字符

#include <stdio.h>
int main()
{printf("(are you ok??)"); //??)  -  ]  三字母词//printf("are you ok\?\?)");//使用\转义,这样?就能够被顺利打印出return 0;
}//部分编译器以无法显示出三字母词
printf("%d",100);打印整型
printf("%c",'a');//打印字符
printf("%s","ab");//打印字符串


字符串长度

strlen遇到 \0 停止读取
\t\v\0will\n 字符串长度为3

易错:若八进制为\182 那么就拆分为\1 和82,一共就是三位,\1、8、2

#include <stdio.h>
#include <string.h>
int main()
{printf("%d\n",strlen("c:\tes\\\t\328\\t.c"));return 0;
}

4.1 进制转换

(1)十进制转化为八进制

[1].67D=101Q

67=26+20=1 000 001=1 0 1(先转化为二进制)

1000000 1

[2].10.68D=Q(精确到小数点后2位)

整数部分按照上述[1]的方法,小数部分精确到小数点后几位,小数部分就乘以几次8,然后从上往下的顺序读取。

10D=23+21=1 010=12Q

0.68*8=7.44 取7

0.44*8=3.72 取3

10.68D=12.73Q

(2)八进制转化为十进制

130Q=88D

1 3 0 =1 *8^2+3 *8^1+0 *8^0=88

(3)十六进制转十进制

23daH=D

23da=2 *16^3+3 *16^2+d *16^1+a *16^0=9178D

(4)十进制转为十六进制

27.68H=D(精确到小数点后2位)

27=24+23+2^1=1 1010=19

小数部分0.68*16=10.88 取a

0.88*16=14.08 取e

27.68H=19.aeD

5.数组

5.1 一维数组的创建和初始化

5.1.1 创建

定义:一组相同类型的元素的集合

int arr[10];
//输入数组时,没有&
scanf("%s",arr);//数组名本就是一个地址,没有&



分析:ch5打印到\0停止;而ch6从b开始往后打印时,一直没有遇到\0,便一直打印知道栈溢出

C99 标准中引入一个概念:变成数组
支持在数组创建的时候,用变量指定大小,但数组不能初始化;部分编译器支持此操作。

int n = 10;
int arr[n];

5.1.2 初始化

5.2 一维数组的使用

int sz sizeof(arr) / sizeof(arr[0]);//计算数组个数

5.3 一维数组在内存中的存储


1.一维数组在内存中连续存放
2.随着数组下标的增长,地址由低到高变化

5.4 二维数组的创建和初始化


5.5 二维数组在内存中的存储

二维数组在内存中也是连续存放
一行内部连续,换行也是连续。

5.6 数组作为函数参数

数组传参。调用部分(实参部分)写的数组名,形参部分,既可以 写成数组,也可以写成指针,但本质上都是指针,数组传参时传过去的时数组首元素地址

有两个例外

  1. sizeof(数组名) - 数组名表示整个数组 - 计算整个数组的单位大小(单位是字节)
  2. &数组名 - 数组名表示整个数组 - 取出的是整个数组的地址

    练习:将数组按照升序输出
void bubble_sort(int arr[],int sz)
{int i = 0;//趟数for (i = 0;i < sz - 1;i++){int j = 0;//一个数字一趟交换次数for(j = 0;j < sz - 1 - i;j++){if (arr[j] > arr[j+1]){int tmp = arr[j+1];arr[j+1] = arr[j];arr[j] = tmp;}}}}#include <stdio.h>
int main()
{int arr[] = {10,9,8,7,6,5,4,3,2,1};int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort (arr,sz);//printf("%d",ret);return 0;
}

6.函数

6.1 函数是什么

6.2 函数分类

(1)库函数;(2)自定义函数

6.2.1 库函数

编译器发布的时候携带了一些库函数,在实际操作中,频繁大量出现的一些操作不需要进行实现,提高我们的效率。

https://www.cplusplus.com/
https://msdn.itellyou.cn/


以strcpy库函数为例

#include <string.h> //调用头文件
int main()
{char arr1[20] = {};char arr2[] = {"abc"};strcpy(arr1,arr2);//strcpy作用是copy值printf("%s",arr1);return 0;
}

6.2.2 自定义函数

自定义函数和库函数一样,有函数名,返回值类型和函数参数。但不一样的是这些都是我们可以自己设计的。

比较两数大小
int max(int x,int y)
{if(x>y)return x;elsereturn y;
}
int main()
{int a = 0;int b = 0;scanf("%d %d",&a,&b);int mm = max(a,b);printf("%d\n",mm);return 0;
}

两数进行交换

由此可见并没有交换成功。
分析:在main的主函数中,a = 10,b = 20 都有各自的地址,而在test函数中的x,y也有各自的地址,交换x,y的值与a,b的值并无瓜葛,因此交换失败。这时我们需要使用指针变量解决问题。

使用指针变量

6.3 函数参数

函数定义时使用的参数为形式参数,
a,b真实传给swap1和swap2的参数为实际参数

6.3.1 实际参数

实际参数也可以为表达式,函数等

6.3.2 形式参数

形式参数在函数调用以后就销毁。

6.4 函数调用

分类:传值调用;传址调用
传值调用:形参与实参之间没有什么必然联系,只是把值传过去。
传址调用:函数内部可操作和函数外部,形参与实参之间有联系,这时就为传址调用。

练习1:使用函数打印出100-200之间的素数
int ss (int n)
{int j = 0;//此时上面已经赋n为Int类型,无需再次赋值for (j = 2;j < n;j++){if (n % j == 0){return 0;}}return 1;}int main()
{int i = 0;for(i = 100;i <= 200;i++){if(ss(i) == 1){printf("%d ",i);}}return 0;
}
练习2:打印出1000-2000之间的闰年
int ryear(int y)
{if(((y % 4 == 0) && (y % 100 != 0))||(y % 400 == 0)){return 1;}else{return 0;}
}int main()
{int n = 0;int x = 0;int count = 0;for (n = 1000;n <=2000;n++){if (ryear(n) == 1){count ++;printf("%d ",n);}}printf("count = %d\n",count);return 0;
}

**易错点:**使用下面这种写法,例如1600,能被4整除以后进入下一个if语句,但也可以被100整除,所以直接return 0,但实际上也可以被400整除,是满足闰年条件的。因为在进入一个if语句之后便不会再进行下面的语句。

练习3:实现整型有序数列的二分查找
int test (int a[],int k,int s)
{int left = 0;int right = s - 1;while(left <= right){int mid = (left+right) / 2;if(a[mid] < k){left = mid + 1;}    else if (a[mid] > k){    right = mid - 1;}else{return mid;} }return -1;
}int main()
{int arr[] = {1,2,3,4,5,6,7,8,9,10};int key = 7;int ss = sizeof(arr) / sizeof(arr[0]);int asd = test (arr, key ,ss);//数组,要找的数,数组个数if (asd == -1){printf("找不到\n");}else{printf("找到了,下标为:%d\n",asd);}return 0;
}

练习4:每使用一次函数就加1
void add(int* pa)
{(*pa)++;
}int main()
{int p = 0;add(&p);add(&p);add(&p);//传址调用printf("%d ",p);return 0;
}

6.5 函数的嵌套调用和链式访问

6.5.1 嵌套调用

误区:一个函数内不能定义一个函数

void two()
{printf("hahaha\n");
}int one()
{int i = 0;for(i = 1;i <= 3;i++){two();}
}int main()
{one();//打印三次hahahareturn 0;
}

6.5.2 链式访问

把一个函数的返回值作为另一个函数的参数。

#include <string.h>
#include <stdio.h>
int main()
{int len = strlen("asdf");printf("%d\n",len);//链式访问printf("%d\n",strlen("asdf"));return 0;
}


分析:
我们先了解printf函数

因此,在完成printf(“%d”,43)后,第二个printf(“%d”,2).而最外面的第一个printf(“%d”,2),打印出来为1。

6.6 函数的声明和定义

  • 函数先声明后使用。当函数定义在main函数之前时,不用声明

  • 函数声明一般放在头文件

例如在实际生活中,一个工程由几个工程师同时完成,当某工程师想要汇总所有人的代码时,直接调用头文件。


优点:当我们不想把.c文件直接给其他人但又想让他用到时,用.c和.h文件创建一个静态库,将静态库和,h文件给出去,使用#include <文件名.h>声明,#pragma导入静态库,即可实现函数功能。

6.7 函数递归


练习1:要求输入一个无符号整数,按照输入1234,1 2 3 4的模式输出
分析:1234
1234%10 4
1234/10 =123 % 10 = 3
1234/10 =123/10 = 12 % 10 = 2
1234/10 =123/10 =12 ./ 10 = 1 % 10 = 1

进行递归时函数内部具体操作见下图。

练习2:编写函数求字符串长度,不允许函数创建临时变量

int ss(char* arr)
{if(*arr != '\0'){return 1+ss(arr+1);}else{return 0;}
}int main()
{char arr[] = "two";printf("%d",ss(arr));return 0;
}


递归与迭代
练习1 求阶乘

方法一:递归写法
int jc(int m)
{if(m <=1){return 1;}else{return m * jc(m-1);}
}int main()
{int n = 0;scanf("%d",&n);printf("%d\n",jc(n));return 0;
}方法二:迭代法
int main()
{int n = 0;scanf("%d",&n);int i;int sum = 1;//循环也被称为迭代for (i = 1;i <= n;i++){sum = i * sum;}printf("%d\n",sum);return 0;
}

练习2 斐波那契数列

#include <stdio.h>
int fib(int n)
{if (n <= 2)return 1;elsereturn fib(n-1)+fib(n-2);
}int main()
{int n = 0;scanf("%d",&n);int te = fib(n);printf("%d\n",te);return 0;
}

7. 操作符

7.1 算数操作符


% 操作符两端必须为整数

7.2 移位操作符

左移操作符 <<
右移操作符 >>

7.2.1 左移操作符

#include <stdio.h>
int main()
{int a = 3;//左移操作符针对的是二进制位int b = a << 1;printf("%d\n",b);return 0;
}

a=3是int类型,int类型是4个字节,一个字节8个bit,一共为32bit,a转化为二进制为00000000000000000000000000000011,整体向左移一位,右边空缺用0补充,那么结果为00000000000000000000000000000110,b=6

整体原则就是,左边丢弃,右边补0

7.2.2 右移操作符

1.算术右移
右边丢弃,左边补原符号位
当为正时,符号位本就为0,为负数时为1

整数在内存中存储的是补码

一个整数在二进制中的表示有三种:原码,反码,补码

负数原码,反码,补码的计算,以-1为例:

  • 原码:-1的二进制位
    整数最高位表示符号位,最高位1表示负数
    10000000 00000000 00000000 00000001(原码)

  • 反码:符号位不变,其他位按位取反,1变0,0变1
    11111111 11111111 11111111 11111110(反码)

  • 补码:反码二进制序列加1
    11111111 11111111 11111111 11111111(补码)

对于正整数,原码,反码,补码相同

2.逻辑右移
右边丢弃,左边补0

去判断当前右移操作符为逻辑右移还是算术右移时,只需使用负数,以-1为例,输出为-1是说明32个bit全为1,左边最高位补原符号位,属于算术右移。

7.3 位操作符

位操作符 含义
& 按位与
| 按位或
^ 按位异或
操作数必须为整数
0 ^ a = a;
a ^ a = 0

练习:在不借助第三个变量的情况下,交换a = 3,b = 5的值

方法一:
#include <stdio.h>
int main()
{int a = 3;int b = 5;printf("a = %d,b = %d\n",a,b);a = a + b;b = a - b;a = a - b;printf("a = %d,b = %d",a,b);return 0;
}
缺点:数字太大可能产生溢出方法二:
#include <stdio.h>
int main()
{int a = 3;int b = 5;printf("a = %d,b = %d\n",a,b);a = a ^ b;b = a ^ b;a = a ^ b;printf("a = %d,b = %d",a,b);return 0;
}


分析:
a = a ^ b;
b = a ^ b = a ^ b ^ b = a ^ 0 = a;
a = a ^ b = a ^ b ^ a = b;

7.4 赋值操作符

=  赋值
== 判断

7.5 单目操作符

a + b  //+ 双目操作符,有两个操作数单目操作符:只有一个操作数

7.5.1 !操作符

7.5.2 sizeof操作符讲解



分析:打印第一个值时,a + 2结果放s中,sizeof结果取决于s,不论a类型是否为整型,short字节数为2;
一个.c文件到一个.exe文件需要经历编译>>链接>>运行三个阶段,而s = a + 2在编译阶段已经完成,到运行时只运行了打印2,因此在执行打印s的操作时,s的值并没有改变,还是5。

char c[] ={"china"} //sizeof c = 6  \0也计算在内
char c[] = {'c'.'h','i','n','a'};//sizeof c = 5

7.5.3 ~操作符

int main()
{int a = 0;//~按(二进制)位取反,0变1,1变0printf("%d\n",~a);return 0;
}

对于int a=0,二进制为00000000 00000000 00000000 00000000

~a为 11111111 11111111 11111111 11111111(补码)

而%d输出的是原码,(原码按位取反符号位不变,得到反码,加一得到补码。相反补码减一,按位取反得到原码。)因此需要将~a减1,符号位不变,按位取反,

得到10000000 00000000 00000000 00000001,最终打印出-1
具体原码,反码,补码的介绍请参考7.2.2 右移操作符。

7.5.4 前置++ 后置++

int main()
{int a = 1;int b = a++;//后置++,先使用,后++printf("%d\n",b);//b=1printf("%d\n",a);//a=2int c = 10;int d = ++c;//前置++,先++,后使用printf("%d\n",d);//d=11printf("%d\n",c);//c=11return 0;
}//前置--  后置--  同理

7.5.5 & 和 * 操作符


7.5.6 (类型)


3.14本质上是一个double类型,因此需要强制类型转换。

int main()
{int a =(int)3.14;printf("%d\n",a);return 0;
}

下面这个例子有异曲同工之妙

#include <stdio.h>
int main()
{int a[] = {10000,5000,2000,1000,500,200};int b[] = {100,50,25,10,5,1};double w = 0;int i;scanf("%lf",&w);int n = 100 * w;printf("NOTAS:\n");for (i = 0;i < 6;i++){printf("%d nota(s) de R$ %d.00\n",n / a[i],a[i] /  100);n %= a[i];}printf("MOEDAS:\n");for (i = 0;i < 6;i++){printf("%d moeda(s) de R$ %.2lf\n",n / b[i],b[i] / 100.0);//%lf不对结果本质进行改变,%.2lf出现的意义就是将结果保留两位小数//若想将结果变为浮点数,就需要将被减数进行强制转换,原先是整型,100.0强制变为浮点型,随后输出的结果就是浮点型,随后使用%.2lf保留两位小数n %= b[i];}return 0;
}

7.6 关系操作符

>= 大于等于
<= 小于等于
!= 不等于
== 测试是否等于
两个字符串的比较不能用==

7.7 逻辑操作符

&& 逻辑与
|| 逻辑或
可用于if语句

7.8 条件操作符(三目操作符)

?    :
exp1 ? exp 2 : exp 3

7.9 逗号表达式

int main()
{int a = 1;int b = 2;int c = 3;int d = (b = a + 2,c = b - 1,a = c + 2);//逗号表达式从左向右计算,前面表达式可能影响后面表达式//整个表达式结果是最后一个表达式结果printf("%d\n",d);//d=4return 0;
}

7.10下标引用、函数调用、结构成员

  • 下标引用
  • 函数调用
  • 结构成员

结构体.成员名

7.11 整型提升




7.12 算术转换

精度由高到低排列:
long double
double
float
unsigned long int
long int
unsigned int
int

当操作数属于不同类型时,就需要将其中一个操作数的类型向另一个操作数类型转换,否则无法正常运行,通常是由精度低的向高的转换。

操作符属性
1.优先级
2.结合性
3.控制求值顺序

8. 关键字

c语言的关键字

1.c语言提供,不能自己创建关键字

2.变量名不能是关键字

常见关键字

误区:define 使用 #define

include 使用#include 均为预处理指令

auto 是自动的 - 每个局部变量都是auto修饰的

extern 用来申明外部符号

register 寄存器关键字

#include <stdio.h>
int main()
{//大量频繁被使用的数据,存放在寄存器中,提升效率register int num = 1;//建议num的值存到寄存器中,是否存在寄存器中由编译器决定return 0;
}

8.1 typedef 类型重定义

#include <stdio.h>
typedef unsigned int nu;//unsigned int重定义为nu
int main()
{unsigned int a = 100;nu  b = 200;printf("%d\n",a);//a=100printf("%d",b);//b=200return 0;
}

8.2 static 静态的

  • 修饰局部变量

static修饰局部变量,改变局部变量的生命周期。(本质上改变了变量的存储类型)

void test()
{int a = 1;//第一次走完程序,a=2销毁,第二次重新创建变量a=1a++;printf("%d",a);}
int main()
{int i = 0;while (i<10){test();i++;}return 0;
}//打印出2222222222
void test()
{static int a = 1; //第一次出这个程序,a=2没有销毁,那么这一行代码没有意义了。a++;printf("%d",a);}
int main()
{int i = 0;while (i<10){test();i++;}return 0;
}//打印出 23456789
  • 修饰全局变量

    static修饰全局变量使得全局变量只能在自己所在的源文件(.c)内部使用

    全局变量,在其他源文件内部可以被使用,是因为全局变量具有外部链接属性但是被static修饰之后,就变成了内部链接属性,其他源文件不能链接到这个静态的全局变量。

  • 修饰函数

static修饰函数,使得函数只能在自己所在的源文件内部使用,不能在其他源文件内部使用。

本质上:static将函数的外部链接属性转变为内部链接属性。

和static修饰全局变量一样!!

9.常量和宏

define 是一个预处理指令

1.define 定义符号

#define max 100
int main()
{printf("%d\n",max);return 0;
}

2.define定义宏

#define asd(x,y) x + y
int main()
{int a = 1;int b = 2;printf("%d\n",asd(a,b));//3printf("%d\n",3*asd(a,b));//3*1+2=7return 0;
}

此时若3*asd(a,b)想要得到9,进行如下修改:

#define asd(x,y) ((x)+(y))

10.指针

指针有两层意思,第一,指针是地址;第二,指针存到变量,指针变量也称为指针。

int main()
{char a = 'h';//a在内存中要分配的空间为1个字节printf("%p\n",&a);//%p 打印地址char * pe = &a;//pe用来存放地址,学名为指针变量//* 说明pe为指针变量//char 说明pe所指的对象是char类型//若为int类型,a在内存中分配的空间为4个字节,打印出来的地址为四个字节中的第一个return 0;
}

一个内存单元为一个字节,存在指针中的
指针用来存放地址

指针需要多大空间,取决于地址的存储需要多大空间

指针大小相同!!

32位 32 bit - 4 byte

64位 64bit - 8byte

我这里编译器是64位,所以是8个字节

10.1 指针和指针类型

指针类型意义
1.指针类型决定了指针解引用的权限有多大。
char 1 byte int 4 byte double 8 byte

2.指针类型决定了,指针每走一步的步长

10.2 野指针

指针指向的位置是不正确的。随机的,没有明确限制的

1.指针未初始化

2.越界访问
当i = 10时,p就指向arr[10],p++之后就越界

3.指针指向空间释放

避免野指针应注意:
1.指针初始化

2.小心指针越界
c语言本身不会检查指针越界行为
3.指针指向空间释放及时置null
4.指针使用之前检查有效性
指针变量不知道指向什么时,指向null,指向的空间被释放也指成Null;指向有效空间时,指向有效地址;

10.3 指针运算

10.3.1 指针加减整数

10.3.2 指针-指针



用指针-指针的方法求字符串个数,实现strlen功能

10.3.3 指针的关系运算

vp可以和后一个地址空间比较,不能和第一个之前的元素地址比较。因此更推荐第一种做法

10.4 指针和数组

数组名是数组首元素地址

p是首元素地址,首元素地址+i就是下标为i元素的地址

10.5 二级指针

10.6 指针数组

本质还是数组

11.结构体

11.1 结构体成员访问

11.2 结构体传参

结构体传参时要传结构体地址
每一个函数调用都会在内存栈区上开启一块空间。

12.分支语句

语句定义:由分号(;)隔开的就是一条语句

12.1 if语句

if(表达式)语句;if(表达式)语句1;
else语句2;//多分支
if(表达式1)语句1;
else if(表达式2)语句2;
else if(表达式3)语句3;
else语句4;
//if else后面控制多条语句时,可以带上{},否则默认执行一条语句

误区

(1)表达式两边运算符同时写的问题

age=60>18为真,所以左边为1,此时就是1<26为真,打印出青年。此方法编写有误。

正确写法:

else if (age >= 18 && age < 26)

​ (2)else匹配问题

我们错误的认为结果为haha,但真正的答案确实空,为什么?

乍一看else和第一个if语句对齐,但else只和最近的if语句匹配,编译器默认将代码自动对齐,如下图,因此返回为空。

反思:平时代码书写要规范;可以使用{ },避免引起误会。

书籍:《高质量c/c++编程》

练习1:打印出1-100之间的奇数
int main()
{int i = 0;for(i = 1;i <= 100;i++){if(i % 2 == 1)printf("%d  ",i);}//for (i = 1;i <=100; i+=2)//第一位是1,加2,一次可以产出1,3,5,7,9这样的数字return 0;
}练习2:输入三个数按照从大到小的顺序输出
int main()
{int a = 0;int b = 0;int c = 0;scanf("%d %d %d",&a,&b,&c);if (a < b){int t = a;a = b;b = t;}if (b < c){int t = b;b = c;c = t;}if (a < c){int t = a;a = c;c = t;}printf("%d %d %d\n",a,b,c);return 0;
}
//scanf在搭配%d等使用时切记不要带\n等
//int t只能在if语句中呈现,当在scanf上面出现时,t在进入下一条循环中会携带有上一条语句中的值
//打印时当改变a,b,c的顺序时,if循环语句中应稍作调整练习3:找出两个数中的最大公约数
int main()
{int m = 0;int n = 0;scanf("%d %d",&m,&n);//找出两个数中最小的,然后递减int max = m > n ? m : n;while(1)//目的是让其 一直循环{if (m % max == 0 && n % max == 0){printf("最大公约数为:%d\n",max);break;}max--;}return 0;
}
方法二:辗转相除法
int main()
{int m = 0;int n = 0;int t = 0;scanf("%d %d",&m,&n);while(t = m % n)//当余数为0时,循环截止,最大公约数为n{m = n;n = t;}printf("最大公约数为%d\n",n);return 0;
}练习4:打印出1000-2000中的闰年
判断是否为闰年?
1.能否被4整除,且不能被100整除
2.能被400整除
方法一:
int main()
{int year = 0;for (year = 1000;year <= 2000;year++){if (year % 4 == 0){if (year % 100 != 0){printf("%d ",year);}}if (year % 400 == 0){printf("%d ",year);}}return 0;
}
方法二:
int main()
{int year = 0;for (year = 1000;year <= 2000;year++){if (((year % 4 == 0)&&(year % 100 != 0))||(year % 400 == 0)){printf("%d ",year); }}return 0;
}练习5:打印100-200之间的素数
方法一:
int main()
{int n = 0;for (n = 100;n <= 200;n++){int i = 0;for(i = 2;i < n;i++){if(n % i == 0){break;//若当中有数取模后为0,说明不是素数,跳出当前程序}}//----来到这if(n == i){printf("%d ",n);}}return 0;
}
方法二:
还可以使用另一种思路,例如101,我们只需要去找2 - sqrt(101)之间的数字去尝试
//#include <math.h>
int main()
{int n = 0;for (n = 100;n <= 200;n++){int i = 0;int flag = 1; for(i = 2;i < n;i++)//for(i = 2;i <= sqrt (n);i++){if(n % i == 0){flag = 0;break;}}if(flag == 1){printf("%d ",n);}}return 0;
}

12.2 switch语句

易错点:switch(m) m必须为整型
Int char long均为整型,而float 、double等不是整型。

#include <stdio.h>
int main()
{int day = 0;scanf("%d",&day);//格式化输入,不能随意加\n或者空格switch (day){default://无先后顺序区分printf("error");break;case 1:case 2:case 3:case 4:   case 5:printf("工作日");break;case 6:case 7:printf("休息");break;    }return 0;
}

例题:

结果:m=5 n=3

误区1:在没有break的情况下,case 1,2,3是要依次走下去的;

误区2:当n走在嵌套的switch语句下的case 2时,case 2的break只是跳出嵌套的switch语句,并非整个程序(当在switch语句中),case还是要继续执行。

13.循环语句

while do…while for

13.1 while语句

while(表达式)循环语句;


使用while之后,便会一直打印

break 和 continue

在while循环中,break用于永久的终止循环

并没有结束,陷入死循环。

在while循环中,continue用于跳过本次循环,跳过continue后面的代码(打印,i++),直接去while 的判断部分,看是否进行下一次循环。

13.1.1 getchar


在读取时,遇到一个错误或者是文件结束,返回EOF。正确读取时,返回的是字符的ascii值。ascii值是整型;其二,getchar返回时有可能是EOF,EOF本质上是-1,-1也是整数,getchar返回类型为int类型。

EOF 文件结束标志 本质上是-1

int main()
{int ch = getchar();//获取字符//printf("%c\n",ch);putchar(ch);//Putchar打印字符,与上一条语句有异曲同工之妙return 0;
}
或者
int main()
{int ch = 0;//ctrl+z -- getchar 读取结束while ((ch=getchar()) != EOF)putchar(ch);return 0;
}

使用场景

场景1:在输入123以后,后面的确认密码直接全部输出。

分析:在输入123以后敲入回车。此时编译器在读取时读取到的是123\n,此时scanf只是把123拿走,\n还留在缓冲区,因此getchar默认为是我们的输入,此时打印出输入失败。

场景2:

在场景1的基础上进行改进,只需要将\n解决就可以了。因此在输入密码之后使用getchar()

此时看似成功,但在实际情况中,我们输入的密码还可能存在字母,符号等情况
例如当我们输入123 asds时,同样出现了场景1中的问题,后面的确认密码一起出现。

分析:在输入123 asds后,电脑读取到的信息是123 asds\n,scanf同样只带走123,而getchar只能带走一个字符,缓冲区中还存在很多字符,因此电脑默认我们已经输入ch的值,直接反馈出输入失败。

场景3:

对于上述 的情况进行改进,由于我们在输入密码之后要回车,在读取时默认为\n,因此我们只需要将\n作为密码输入结束的标志。

13.2 for语句

  初始化  判断     调整
for(表达式1;表达式2;表达式3)循环语句;

break、continue在for循环中

//break
int main()
{int i = 0;for(i = 1;i <= 10;i++){if (i == 5)break;printf("%d ",i);}return 0;
}
//1 2 3 4
结果毫无疑问,同样break在for循环中,永久终止循环。//continue
int main()
{int i = 0;for(i = 1;i <= 10;i++){if (i == 5)continue;printf("%d ",i);}return 0;
}
//1 2 3 4 6 7 8 9 10
分析:在for循环中,先执行i=1,随后执行i <=10 ,如果小于,执行打印,打印完以后进行i++,而i=5时,在执行continue之后,其身后的语句不进行执行,不打印5,直接跳到下一步,下一步就是i++

此时for循环三个部分省略,会不段打印。



分析:i=0时,运行j=0,j<3,打印,J++重复3词,随后i++,i=1,i=1<2,同样执行j的for循环,总共打印六次

分析:关键在于i=0时,j由1加加到3,随后i++,此时i=1<2,再去执行j的for循环时,j不再是初始的0,而是上一次留下的3,因此不再打印。

int main()
{int i = 0;int k = 0;for(i = 0,k = 0;k = 0;k++,++i)//k++  ++i无本质区别,都是一样的k++;return 0;
}
问:循环了多少次?答案:0for循环中的判断语句k = 0  此时=并非判断,而是赋值,此时k = 0为假,因此循环0次。
练习:n!
int main()
{int n = 0;int w = 1;int i = 0;scanf("%d",&n);for(i = n;i>0;i--){w *= i;//等同于w = w * i}printf("%d\n",w);return 0;
}
练习:输入一个数n,计算n!+...+1!
int main()
{int w = 1;int i = 0;int j = 0;int sum = 0;int n = 0;scanf("%d",&n);for(j = 1;j <= n;j++){for(i = 1;i <= j;i++){w = w * i;}sum += w;}printf("%d\n",sum);return 0;
}
当n=3时
易错点:并没有给w重新赋值,在执行j=3时,还保留上一次j=2时的值w=2j=1   i=1    w=1*1  sum=0+1=1j=2    i=1    w=1*1i=2  w=1*2=2   sum=1+2*1j=3 i=1 w=2*1 //此时w就有一个初值,为2i=2   w=2*2*1i=3    w=2*3*2*1=12  sum=15(错误答案)
修正:
int main()
{int w = 1;int i = 0;int j = 0;int sum = 0;int n = 0;scanf("%d",&n);for(j = 1;j <= n;j++){w = 1;  //重点!!!计算n的阶乘之前,把w初始为1for(i = 1;i <= j;i++){w = w * i;}sum += w;}printf("%d\n",sum);return 0;
}
//sum=9方法二:
int main()
{int w = 1;int i = 0;int sum = 0;int n = 0;scanf("%d",&n);for(i = 1;i <= n;i++){w = w * i;sum += w;}printf("%d\n",sum);return 0;
}

13.3 do…while

do循环语句;
while(表达式);//先执行再判断。循环体至少要执行一次
  • break

  • Continue

依旧陷入死循环

1.有序数组中查找具体某个数字,二分法
int main()
{int arr[] = {1,2,3,4,5,6,7,8,9,10};int k = 7;int se = sizeof(arr) / sizeof(arr[0]);//求得数组个数int left = 0;int right = se - 1;while (left <= right){int mid = (left + right) / 2;if (arr[mid] < k){left  = mid + 1;}else if (arr[mid] > k){right = mid - 1;}else{printf("下标为:%d\n",mid);break;}}if (left > right){printf("没有找到\n");}return 0;
}2.为展现出             welcome to Chinaw##############awe############na...welcome to China 的效果,编写代码#include <stdio.h>
#include <string.h>
#include <windows.h>
int main()
{char arr1[] = "welcome to China";char arr2[] = "################";int left = 0;int right = strlen(arr1)-1;//使用strlen需要string.h头文件while(left <= right){arr2[left] = arr1[left];arr2[right] = arr1[right];printf("%s\n",arr2);//使用Sleep  需要调用windows.h头文件Sleep(1000);//睡眠时间1000ms=1s  Sleep 首字母大写system("cls");//清空屏幕left++;right--;}//printf("%s\n",arr2);//为使显示效果更好return 0;
}3.编写代码实现,模拟用户登录情景,并且只能登录三次。(只允许输入三次密码,如果密码正确则提示登录成,如果三次均输入错误,则退出程序。
#include <stdio.h>
#include <string.h>//使用stramp时需要调用string.h头文件
int main()
{int i = 0;char password[20] = {0};for  (i = 1;i <=3;i++){printf("请输入密码:");scanf("%s",password);//if(password == "123456") //比较两字符串首字符的地址,并没有比内容   //error!!! 两个字符串比较不能用 == 只能用strcmpif (strcmp(password,"123456") == 0){printf("登陆成功\n");break;}else{printf("密码错误,请重新输入\n");}}if (i == 3){printf("登录次数已超过三次,退出程序");}return 0;
}

13.4 goto语句

goto语句使用场景:for(...){for(...){for(...){if(disaster){goto error;}}}}error:if(...)
直接跳出整个循环,比break更快捷

在输入我是猪之后,电脑取消关机,否则电脑在60秒之后关机
#include <string.h>  //使用strcmp时调用头文件
#include <stdlib.h>  //使用system时使用头文件
int main()
{char arr[20] = {0};//system - 执行系统命令system("shutdown -s -t 60");//调用shutdown,-s 关机  -t  设置在多少秒之后关机
again:printf("请输入:我是猪,否则电脑将在60s以后关机\n");scanf("%s",arr);if (strcmp(arr,"我是猪") == 0){system("shutdown -a");}else{goto again;//给一个机会,重新输入}return 0;
}

c语言学习笔记(持续更新中)相关推荐

  1. JS逆向学习笔记 - 持续更新中

    JS逆向学习笔记 寻找深圳爬虫工作,微信:cjh-18888 文章目录 JS逆向学习笔记 一. JS Hook 1. JS HOOK 原理和作用 原理:替换原来的方法. (好像写了句废话) 作用: 可 ...

  2. typescript-----javascript的超集,typescript学习笔记持续更新中......

    Typescript,冲! Typescript 不是一门全新的语言,Typescript是 JavaScript 的超集,它对 JavaScript进行了一些规范和补充.使代码更加严谨. 一个特别好 ...

  3. 专升本 计算机 公共课学习笔记(持续更新中...)

    计算机公共课学习笔记 第一章 计算机基础知识(30分) 1.计算机概述 计算机(Computer)的起源与发展 计算机(Computer)也称"电脑",是一种具有计算功能.记忆功能 ...

  4. Docker快速入门学习笔记-持续更新中

    Docker安装 #1.卸载旧的版本 yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker ...

  5. Java学习笔记(持续更新中)

    文章目录 项目实战 mall项目(SpringBoot项目) 1. 添加Swagger-UI配置,修改MyBatis Generator注释的生成规则 2. redis基础配置 3. SpringSe ...

  6. 重拾CCNA,学习笔记持续更新ing......(4)

    重拾CCNA,学习笔记持续更新ing......(4) 路由器作用功能的经典解说(笑)(非原创) 假设你的名字叫小不点,你住在一个大院子里,你的邻居有很多小伙伴,在门口传达室还有个看大门的李大爷,李大 ...

  7. SpringCloud学习笔记,课程源自黑马程序员,笔记持续更新中...

    @SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式: 学习内容 1.服务拆分-服务远程调用: 2.搭建eureka服务: 2.1.eureka服务注册-client 2 ...

  8. Admin.NET管理系统(vue3等前后端分离)学习笔记--持续更新

    我的学习笔记 - 9iAdmin.NET 欢迎学习交流 (一)前端笔记 1.1 关于.env的设置 1.2 关于路由模式问题 1.3 关于 vue.config.ts 1.4 关于 打包(pnpm r ...

  9. [Hadoop] Hadoop学习历程 [持续更新中…]

    1. Hadoop FS Shell Hadoop之所以可以实现分布式计算,主要的原因之一是因为其背后的分布式文件系统(HDFS).所以,对于Hadoop的文件操作需要有一套全新的shell指令来完成 ...

  10. opencv-python学习笔记【更新中】

    opencv-python 基于明日科技的<Python OpenCV从入门到实践>的学习笔记 import cv2 1 图像处理的基本操作 (1)读取图像 image = cv2.imr ...

最新文章

  1. 了解下C# 类型转换
  2. LOAM 代码部分的公式推导(前端里程计部分)
  3. 加速、能耗与对抗攻击:5位顶会作者解析2020 AI系统关键挑战
  4. 80后天才程序员,Facebook 第一任 CTO,开挂人生到底多变态?
  5. SqlSugar-事务操作
  6. java jfreechar鱼刺图_java使用jfreechar绘制饼型统计图
  7. 机器学习 可视化_机器学习-可视化
  8. Jquery 寻找父、子、兄弟节点
  9. Perl 6 语言的糟粕
  10. ...为他们的产品痴迷,不是有兴趣,不是了解,而是痴迷
  11. HBASE table导出到文件的方法
  12. 分享12个Python项目教程,看完随便拿!
  13. m6000查看端口状态_M6000日常查看维护命令.doc
  14. 关于UML 画图工具EA 在linux下的安装和界面配置
  15. 如何删除双系统中的ubuntu
  16. python编程快速上手办公自动化_关于疯狂填词(Mad Libs)程序的解答
  17. CapsuleNet的一个小例子
  18. Java 8 新特性|Collectors.joining() 详解
  19. 学计算机女生找不到男朋友,女生找不到男朋友,大多都是这三个原因!
  20. 从入门到放弃表情包 python_Python从入门到放弃(1)

热门文章

  1. 苹果手机账号验证失败连接不上服务器,苹果手机让检查Apple ID 电话号码点击后验证失败,连接服务器失败出错...
  2. 独家 | 利用LSTM实现股价预测
  3. 程序猿的24个段子:栈和队列的区别是啥?
  4. uniapp连接手机端调试
  5. 不谋一时不足以谋一域_不谋万世者不足谋一时,不谋全局者不足谋一域是什么意思...
  6. 第一周 1.17-1.19
  7. 【PyG】简介 - 图神经网络
  8. 三端稳压管反向击穿情况及分析与防护措施
  9. 自动弹出 微信授权登录窗口
  10. Word在试图打开文件时错误,如何解决