编程之基 --- C语言基础大全 I

这一系列的C语言笔记是取材于慕课翁恺老师的课程《C语言程序设计》

First Week 程序设计与C语言


1.1 计算机和编程语言

1.1.1计算最大公约数:
  1. 如果v等于0,计算结束,u就是最大公约数;
  2. 如果v不等于0,那么计算u除以v的余数,让u等于v,而v等于那个余数;
  3. 回到第一步。
#include <stdio.h>

int main(int argc, char const *argv[]){
	int u = 32;
	int v = 26;
	while (v!=0) {
		int temp = u%v;
		u = v;
		v = temp;
	}
	printf("%d", u);

	return 0;
}
1.1.2 程序的执行
  • 解释:借助一个程序,那个程序能试图理解你的程序,然后按照你的要求执行。
  • 编译:借助一个程序,就像一个翻译,把你的程序翻译成计算机真正能懂得语言——机器语言——写的程序,然后,这个机器语言写的程序就能直接执行了。

对于计算机编程语言来说,语言本身是没有解释和编译得区分的,任何语言都可以解释执行或编译执行,只是具体执行方式不同。

好处:解释型方式或许可以在程序运行过程中更改源代码。编译型方式是有一个确定的运算性能。

两种执行方式没有非常大的本质的区别,只是传统和喜好的问题。

1.2 C语言

  • C语言是从B语言发展而来的,B语言是从BCPL发展而来的,BCPL是从FORTRAN发展而来的
  • BCPL和B都支持指针间接方式,所以C也支持了
  • C语言还受到了PL/I的影响,还和PDP-11的机器语言有很大的关系
  • 1973年3月,第三版的Unix上出现了C语言的编译器
  • 1973年11月,第四版的Unix发布了,这个版本时完全用C语言重新写的

  • C语言的创造者,最开始发布的C版本是K&R标准
  • 1989年ANSI发布了一个标准——ANSI C
  • 1990年ISO接受了ANSI的标准——C89
  • C的标准在1995和1999年两次更新——C95和C99

C语言是一种工业语言

  • 操作系统
  • 嵌入式系统
  • 驱动程序
  • 底层驱动
  • 图形引擎、图像处理、声音效果

  • 开发效率>>学习过程
  • 开发效率>>开发乐趣
  • 日常应用很少直接用c语言编写
  • 学习c的过程主要是写练习代码
  • 而非真是软件

1.3 第一个程序

四则运算C符号意义
++
--
x*
÷/
%取余(两个数相除后的余数)
()()括号


Second Week 计算


1. 变量

所有的变量在第一次被使用之前应该有过初始化。

ANSI C标准只能在代码开头的地方定义变量。C99则是只要在使用前定义就可以。

可以有别的办法知道C语言的输入是否为整数

scanf 函数是有返回值的,它的返回值可以分成三种情况:

  1. 正整数,表示正确输入参数的个数。例如执行 scanf(“%d %d”, &a, &b);
    如果用户输入"3 4",可以正确输入,返回2(正确输入了两个变量);
    如果用户输入"3,4",可以正确输入a,无法输入b,返回1(正确输入了一个变量)。
  2. 返回0,表示用户的输入不匹配,无法正确输入任何值。如上例,用户如果输入",3 4",返回0。
  3. EOF,这是在stdio.h里面定义的常量(通常值为-1),表示输入流已经结束。在Windows下,用户按下CTRL+Z(会看到一个^Z字符)再按下回车(可能需要重复2次),就表示输入结束;Linux/Unix下使用CTRL+D表示输入结束。

C99才可以const常量,一般用全大写

1.1 英制计量单位转换
#include <stdio.h>

int main(int argc, char const *argv[]){
	printf("请分别输入身高的英尺和英寸,"
			"如输入\"5 7\"表示5英尺7英寸:");
	int foot;
	int inch;
	scanf("%d %d", &foot, &inch);
	printf("身高是%f米。\n", ((foot + inch/12.0) * 0.3048)); 
    
	return 0;
} 

当浮点数和整数一起运算时,整数会自动改为浮点数。

整数在计算机里运算比浮点数快。

2. 表达式

C语言的整个一行都是表达式,由运算符和算子构成,故C的赋值左侧算子也可以是一个复杂的运算。

2.1 计算时间差
#include <stdio.h>

int main(int argc, char const *argv[]){
	int hour1, minute1;
	int hour2, minute2;
	scanf("%d %d", &hour1, &minute1);
	scanf("%d %d", &hour2, &minute2);
	
	int t1 = hour1*60 + minute1;
	int t2 = hour2*60 + minute2;
	int t = t2 - t1;
	
	printf("时间差是%d小时%d分。\n", t/60, t%60);
	
	return 0;
} 
2.2 运算符优先级
优先级运算符运算结合关系举例
1+单目不变自右向左a*+b
1-单目取负自右向左a*-b
2*自左向右a*b
2/自左向右a/b
2%取余自左向右a%b
3+自左向右a+b
3-自左向右a-b
4=赋值自右向左a=b
2.3 赋值运算符
  • 赋值也是运算,也有结果
  • a=6的结果是a被赋予的值,也就是6
  • a=b=6 ——> a=(b=6)
2.4 “嵌入式赋值”(不推荐使用)
int a = 6;
int b;c
int c= 1+(b=a);
  • 不利于阅读
  • 容易产生错误
2.5 交换两个变量(a和b)的值(小套路之一)
int a=5, b=6, t;
t = a;
a = b;
b = t;
2.6 复合赋值

“+=”、“-=”、“*=”、“/=”、“%=”

两个运算符中间不要由空格。

total /= 12 + 6;
total = total / (12+6);  // 两者等效
2.7 递增递减运算符

它们的算子必须是变量。

其前缀后缀:

表达式运算表达式的值
count++给count加1count原来的值
++count给count加1count+1以后的值
count–给count减1count原来的值
–count给count减1count-1以后的值

历史来源:

  • 当年在PDP11上有两条特殊的指令 INC=递增,DEC=递减

  • 有了++、–,则C语言编译器可方便的将其编译为这两条特殊指令,运算可以加快。

  • 可是如今,这个的意义已不太大了,因为现在的编译器即便你不写的是递增递减运算符,而只是a=a+1它也会产生相同的指令,并且有些CPU或许并没有INC、DEC指令,没法加快。

  • 如今还在使用仅是因为习惯和方便。


Third Week 判断


1. 判断

1.1关系运算:
运算符意义
==相等
!=不相等
>大于
>=大于或等于
<小于
<=小于或等于
  • 运算结果为0或1

  • 所有的关系运算符的优先级比算术运算的低,但是比赋值运算的高

  • ==和!=的优先级比其他的关系运算低,连续的关系运算是从左到右的

1.2 注释
/* */
  • 其内包含的都是注释

  • 而注释方式//仅在C99有用,代表行注释。

  • 注释在编译中会被替换为一个空格

1.3 IF语句
  • 一个基本的if语句由一个关键字if开头,跟上在括号里的一个表示条件的逻辑表达式,然后是一对大括号“{}”之间的若干条语句。如果表达条件的逻辑表达式的结果不是零,那么就执行后面跟着的这对大括号中的语句,否则就跳过这些语句不执行,而继续下面的其他语句。
  • if语句这一行结束的时候并没有表示语句结束的“;”,而后面的赋值语句写在if的下一行,并且缩进了,在这一行结束的时候有一个表示语句结束的“;”。这表明这条赋值语句是if语句的一部分,if语句拥有和控制这条赋值语句,决定它是否要被执行。
if(total>amount)
	total += amount + 10;
  • 为了方便阅读,一般都带{}

2. 分支

// 非单一出口
if(x<0){
	printf("%d", -1);
}else if(x==0){
	printf("%d", 0);
}else{
	printf("%d", 2*x);
}
// 单一出口
int f;
if(x<0){
	f = -1;
}else if(x==0){
	f = 0;
}else{
	f = 2*x;
}
printf("%d", f);
  • 更支持单一出口方式

// 错误使用==和=
// if只要求()里的值是零或非零
if(a=b){
	printf("A=B");
}

2.1 switch-case
  • 控制表达式只能是整数型的结果
  • 常量可以是常数,也可以是常数计算的表达式(c99)
switch (控制表达式) {
case 常量:
	语句
	...
case 常量:
	语句
	...
default:
	语句
	...
}
  • break:switch语句可以看作是一种基于计算的跳转,计算控制表达式的值后,程序会跳转到相匹配的case(分支标号)处。分支标号只是说明switch内部位置的路标,在执行完分支中的最后一条语句后,如果后面没有break,就会顺序执行到下面的case里去,直到遇到一个break,或者switch结束为止。
switch (type) {
case 1:
case 2:
	printf("你好\n");
	break;
case 3:
	printf("晚上好\n");
case 4:
	printf("再见\n");
	break;
default:
	printf("啊,什么啊?\n");
	break;
}
  • C99支持case后是const常量

用switch-case来实现分段函数: y = { − 1 x < 0 0 x = 0 2 ∗ x x > 0 y= \begin{cases} -1 & x<0 \\ 0 & x=0 \\ 2*x & x>0 \end{cases} y= 102xx<0x=0x>0

#include <stdio.h>

int main(int argc, char const *argv[]){
	int x;
	int y;
	scanf("%d", &x);
	switch (x<0) {
		case 1: y=-1;break;
		case 0: y=2*x;break;
	}
	printf("y=%d", y);
    
	return 0;
} 
  • 虽然可以实现,但是这种情况尽量使用IF判断


Fourth Week 循环


1. 循环

1.1 数位数
#include <stdio.h>

int main(int argc, char const *argv[]){
	int x;
	scanf("%d", &x);
	int n = 0;
	do {
		x /= 10;
		n++;
	} while (x >0);
	printf("%d", n);
    
	return 0;
} 
1.2 猜大小
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(int argc, char const *argv[]){
	srand(time(0));
	int number = rand()%100 + 1;
	int count = 0;
	int a = 0;
	printf("我已经想好了一个1到100之间的数");
	do {
		printf("请猜测:");
		scanf("%d", &a);
		count++;
		if (a > number) {
			printf("你猜的数大了。");
		} else if (a < number) {
			printf("你猜的数小了。");
		} 
	}  while (a != number);
	printf("太好了!你用了%d次猜到了答案。\n", count); 
    
	return 0;
}
1.3 求平均数
#include <stdio.h>

int main(){
	int number;
	int sum = 0;
	int count = 0;
	
	scanf("%d", &number);
	while (number!=-1) {
		sum += number;
		count++;
		scanf("%d", &number);
	}
	
	/*do {
		scanf("%d", &number);
		if (number!=-1) {
			sum += number;
            count++;
		}
	} while (number!=-1);*/
	
	printf("%f\n", 1.0*sum/count);
    
	return 0;
}
  • 这个程序的while比do-while好,因为少了一次IF判断。
1.4 任意位数的逆序

整数的分解

  • 一个整数是由I至多位数字组成的,如何分解出整数的各个位上的数字,然后加以计算
  • 对一个整数做%10的操作,就得到它的个位数
  • 对一个整数做/10的操作,就去掉了它的个位数
  • 然后再对2的结果做%10,就得到原来数的十位数了
  • 依此类推
#include <stdio.h>

int main(){
	int x;
	// scanf("%d", &x);
	x = 700;
	int digit;
	int ret = 0;
	
	while (x>0) {
		digit = x%10;
		// printf("%d\n", digit);
		ret = ret*10 + digit;
		printf("x=%d,digit=%d,ret=%d\n", x, digit, ret);
		x /= 10;
	}
	printf("%d", ret);
    
	return 0;
}

//结果
//x=700,digit=0,ret=0
//x=70,digit=0,ret=0
//x=7,digit=7,ret=7
//7


Fifth Week 循环控制


1. 第三种循环

1.1 For循环
for (count=10;count>0;count--)
// 对于一开始的count=10,当count>0时,重复做循环体,每一轮循环在做完循环体内语句后,使得count--。
// 循环控制变量i只在循环里被使用了,在循环外面它没有任何用处。因此我们可以把变量i的定义写到for语句里面去
// 仅支持C99
int n;
scanf("%d", &n);
int fact = 1;
for (int i=1; i<=n; i++) {
	fact *= i;
}
printf("%d!=%d\n", n, fact);

阶乘:

#include <stdio.h> 

int main(int argc, char const *argv[]){
	int n;
	scanf("%d", &n);
	int fact = 1;
	int i = n;
	for ( ; n>1; n--) {
		fact *= n;
	}
	printf("%d!=%d\n", i, fact);
    
	return 0;
}

一个需求:使用for循环和while循环都可以去实现,那么到底两者之间有什么区别?

从内存角度考虑:

  • 局部变量在栈内存中存在,当for循环语句结束,那么变量会及时被gc(垃圾回收器)及时的释放掉,不浪费空间
  • 如果使用循环之后还想去访问循环语句中控制那个变量,使用while循环

从应用场景角度考虑:

  • 如果一个需求明确循环的次数,那么使用for循环(开发中使用for循环的几率大于while循环)
  • 如果一个需求,不知道循环了多少次,使用while循环

for循环像一个计数循环:设定一个计数器,初始化它,然后在计数器到达某值之前,重复执行循环体,而每执行一轮循环,计数器值以一定步进进行调整,比如加1或者减1。

for (i=0; i<5; i++) {
	printf("%d", i);
}
for (初始动作; 条件; 每轮的动作) {
}
// for中的每一个表达式都是可以省略的
for (; 条件;) == while (条件)

Tips for loops:

  • 如果有固定次数,用for
  • 如果必须执行一次,用do_while
  • 其他情况用while

2. 循环控制

2.1 goto
// 接力break
int x;
int one, two, five;
int exit = 0;

scanf("%d", &x);
for (one=1; one<x*10; one++) {
	for (two=1; two<x*10/2; two++) {
		for (five=1; five<x*10/5; five++) {
			if (one+two*2+five*5==x*10)) {
				printf("可以用%d个1角加%d个2角加%d个5角得到%d元\n", one, two, five, x);
				exit = 1;
				break;
			}
		}
		if (exit==1) break;
	}
	if (exit==1) break;
}
// goto
int x;
int one, two, five;

scanf("%d", &x);
for (one=1; one<x*10; one++) {
	for (two=1; two<x*10/2; two++) {
		for (five=1; five<x*10/5; five++) {
			if (one+two*2+five*5==x*10)) {
				printf("可以用%d个1角加%d个2角加%d个5角得到%d元\n", one, two, five, x);
				goto out;
			}
		}
	}
}
out:
	return 0;
  • out:写到前面或许只是格式习惯,方便阅读,并不影响后面的程序。

3. 循环应用

3.1 正序分解整数
#include <stdio.h>

int main(int argc, char const *argv[]){
	int x;
	scanf("%d", &x);
	
	int mask = 1;
	int t = x;
	while (t > 9) {
		t /=10;
		mask *= 10;
	}   // 以上目的在于得到x的位数mask 
	printf("x=%d, mask=%d\n", x, mask);
	do{
		int d = x/ mask;
		printf("%d", d);
		if (mask > 9) {
			printf(" ");
		}
		x %= mask;
		mask /= 10;
	} while (mask > 0);
	printf("\n");
    
	return 0;
}
3.2 求最大公约数

3.2.1 方法一:枚举

  1. 设t为2;
  2. 如果u和v都能被t整除,则记下这个t;
  3. t加1后重复第二步,直到t等于u或v;
  4. 那么,曾经记下的最大的可以同时整除u和v的t就是gcd
int a, b;
int min;

scanf("%d %d", &a, &b);
if (a<b) {
    min = a;
} else {
    min = b;
}
int ret = 0;
int i;
for (i=1; i<min; i++) {
    if (a%i==0) {
        if (b%i==0) {
            ret = i;
        }
    }
}
printf("%d和%d的最大公约数是%d。\n", a, b, ret);

3.2.2 方法二:辗转相除法

  1. 如果b等于0,计算结果,a就是最大公约数;
  2. 否则,计算a除以b的余数,让a等于b,而b等于那个余数;
  3. 回到第一步。
#include <stdio.h>

int main(int argc, char const *argv[]){
	int a, b;
    int t;
    scanf("%d %d", &a, &b);
	while (b!=0) {
		t = a % b;
		a = b;
		b = t;
        printf("a=%d,b=%d,t=%d\n", a, b, t);
	}
	printf("gcd=%d\n", a);
    
	return 0;
}

NEXT:编程之基 --- C语言基础大全 II
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值