C语言的发展历史
- 起始于上世纪70年代
- C语言是从B语言发展而来的,B语言是从BCPL发展而来的,BCPL是从FORTRAN发展而来的
- FORTRAN是第一个高级程序设计语言(1950s)
- BCPL和B都支持指针间接方式,所以C也支持
- C语言还受到了PL/1(底层编程语言,接近于汇编)的影响,还和PDP-11的机器语言有很大的关系
- 1973年3月,第三版的Unix上面出现了C语言的编译器
- 1974年11月,第4版的Unix((System Four)发布,这个版本是完全用C语言写的
- 最早的C叫做"K&R the C"-经典C
- 1989年ANSI发布了一个标准-ANCI C
- 1990年ISO接受了ANSI的标准-C89
- C的标准在1995年和1999年两次更新C95和C99(目前最新版本)
- 目前所有的编译器都支持C99
第一个简单C程序
#include<stdio.h> //头文件
int main() {
//主函数
printf("hello\n"); //打印输出
return 0; //结束
}
- ""里面的内容为字符串,原封不动的输出里面的内容
- \n表示换行
printf做简单计算
printf("%d",12+32); //46
可以做加、减、乘、除、取余、括号
命令行编译和运行
编译:gcc hello.c 此时编译生成的文件为a.out
执行:./a.out ./是Unix为了保证安全的手段
若需要对.c文件进行编辑,可以用vi hello.c,i进行编辑,esc退出编辑,wq退出编辑
变量
#include<stdio.h>
int main() {
int num = 0; //定义变量初始值为0的变量
printf("输入数字:\n");
scanf_s("%d", &num);
int end = 100-num;
printf("100减去该数的结果是%d,\n",end);
return 0;
}
变量的定义:
<类型名称><变量名称>
变量的名字是一种标识符,基本的组成原则是:
标识符只能由字母、数字和下划线组成,数字不能出现在第一个位置上面,C语言的关键字不能作为标识符。
关键字:
赋值与初始化
int price=0;
变量名为price,类型为int,赋值为0;
在计算机中,=是赋值,==是等于;
在变量定义的时候就进行赋值就叫做变量的初始化;
在一行里面若有多个变量,那么在进行赋值的时候必须一个一个的赋值,不能一次性把一个值赋给多个变量;
表达式
int b=20;
a=b-10;
表达式是一系列运算符和算子的组合,用来计算一个值;
运算符:进行运算的动作 + - * /
算子:参与运算的值,可能是常量也可能是变量,还能是一个方法的返回值
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jFnAJxHc-1657249548918)(https://s2.loli.net/2022/07/02/xiMTn7lHwpXQgRy.png)]
C语言中所有的变量在使用之前必须定义或声明,所有的变量必须具有确定的数据类型;
C99与ANSI C在变量定义上的区别:
ANSI C 只能在程序的开头定义变量
C99可以在程序的任何位置定义变量
读整数输入 scanf
scanf("%d",&price)
price前面的**&**是必写的
要求sanf这个函数读入下一个整数,读到的结果赋值给变量price;
常量与变量
在程序运行过程中不变的数字是常量,会变的是变量,直接写在程序中的数字是直接量;
定义常量:
const int AMOUNT = 100 ;
const是一个修饰符,加在int前面,给该变量加上一个不变的属性,一但初始化以后就不能再修改了,
将用户输入的两数相加:
#include<stdio.h>
int main() {
int a ;
int b ;
printf("请输入两个整数:\n");
scanf_s("%d %d", &a, &b);
printf("%d+%d=%d\n", a, b, a + b);
}
浮点数
-
即为带小数点的数值
-
两个整数的运算结果只能是整数
-
10.0是浮点数
-
在C中,10与10.0是两个完全不同的数
-
double为双精度浮点数,float 为单精度浮点数
占位符
数据类型 | 占位符 |
---|---|
int | %d |
short | %hd |
unsigned int | %u |
unsigned short | %hu |
long(是int 的修饰,不是单独的数据类型,只是比int多了四个人字节的空间) | %ld |
unsigned long | %lld |
char | %c |
float(%g float默认是6位小数输出,可在%f中控制) | %f和%g |
double | %lf和%lg |
char | %s |
unsigned | %u |
八进制 | %o 以0开头 |
十六进制 | %x 以0x开头 |
二进制 | 无占位符 |
地址值、指针值 | %p |
* | 去指针里地址指向的地方的值 |
& | 取改值存储位置的地址值 |
运算
//计算时间差
#include<stdio.h>
void main() {
int hour1, minute1;
int hour2, minute2;
printf("请输入时间 小时,分钟\n");
scanf_s("%d %d", &hour1, &minute1);
printf("请输入时间 小时,分钟\n");
scanf_s("%d %d", &hour2, &minute2);
int time1 = hour1 * 60 + minute1;
int time2 = hour2 * 60 + minute2;
int time = time2 - time1;
printf("时间差为:%d分钟",time);
}
运算符的优先级
单目运算:只有一个算子,例如 -a
对C来说,赋值也是一个运算,也有结果
结合关系
- 一般自左向右
- 单目±和赋值= 自右向左
例题:
#include<stdio.h>
void main() {
int x;
scanf_s("%d", &x);
double money = x * (1 + 0.033) * (1 + 0.033) * (1 + 0.033);
printf("%f",money);
}
交换两个变量的值
#include<stdio.h>
void main() {
int a = 5;
int b = 6;
int c;
c = a;
a = b;
b = c;
printf("a=%d b=%d", a, b);
}
复合赋值
即5个基本运算符(+ - * / %)与赋值运算符结合起来,形成
复合赋值运算符:
- +=
- -=
- *=
- /=
- %=
例:total+=5 等价于 total=total+5
递增递减运算符
- ++
他们是单目运算符,算子必须是变量,作用就是给变量+1或者-1;
- ++与–可以放在变量的前面,叫做前缀形势,放在变量后面叫做后缀形势
- a++的值是a+1以前的值。而++a的值是a+1以后的值,无论哪个,a自己的值都+1了
#include<stdio.h>
void main() {
int a = 10;
printf("a++=%d\n", a++);
printf("a=%d\n", a);
printf("++a=%d\n", ++a);
printf("a=%d\n", a);
}
做判断
if语句
if(条件成立){
(执行)
}
当然,也可以没有大括号,但是后面只能跟一句语句,而且不能跟分号,那么当if里面的条件满足时就会执行紧跟在if后面的那条语句。
嵌套的判断
当if的条件满足或者不满足的时候要执行语句也可以是一条if或if-else语句,这就是嵌套的if语句
else的匹配
- 在不加大括号**{}**的情况下,else总是和最近的那个if匹配
- 最好是在if或者else后面始终跟上大括号,防止编译器与自己的理解不一样
判断的条件
- 计算两个值之间的关系,为关系运算
- 关系运算符的结果为整数,符合预期时结果为1,否则为0
1.优先级:所有的关系运算符的优先级比算术运算的低,但是比赋值运算的高:
- 7>=3+4 //1
- int r=a>0 //a>0的结果为1,将1赋值给r
2.判断是否相等的==和!=的优先级比其他的低,而连续的关系运算是从左到右进行的
- 5>3==6>4 //1
- 6>5>4 //0
- ab6
- ab0
注释
- // 两个**/**的单行注释
- /* (注释内容)*/ 多行注释,也可用于行内注释
常见错误
分号问题
假如在if语句后面有接分号,那么就意味着在if语句后面有一条什么都不做的空白语句,分号后面的大括号里面的内容只是代码中很普通的语句,代码中可以将代码用大括号进行包裹,编译器也不会报错
int a=0;
if(a>0)
; //空白语句,执行完以后会执行下面的printf语句
{
printf("%d",a)
}
错误使用==和=
例如
int a=5,b=6;
if ( a=b );
printf("%d\n", a)
该if语句中,不会判断a与b是否相等,反而会将b的值赋给a
switch-case
switch(type){
case 1:
printf("你好");
break;
case 2:
printf("早上好");
break;
case 3:
printf("晚上好");
breakk;
}
-
switch(控制表达式){
case 常量:
语句;
break;
case 常量:
语句;
break;
default:
语句;
break;
}
需要注意
- 控制表达式只能是常量(int)
- 常量可以是常数,也可以是常数计算的表达式(C99)
- break的作用与Java中的类似,若没有break则会继续往下执行,有break则会离开当前switch-case
例题:成绩等级转换
#include<stdio.h>
void main() {
int gread = 0;
scanf_s("%d", &gread);
gread /= 10;
switch (gread)
{
case (10):
printf("A");
break;
case (9):
printf("A");
break;
case (8):
printf("B");
break;
case (7):
printf("C");
break;
default:
printf("E");
break;
}
}
循环
1.while循环
案例(计算数字的位数):
#include<stdio.h>
void main() {
int a = 0;
int num = 0;
scanf_s("%d", &a);
num++;
a=a / 10;//这两句的存在主要是为了处理0的位数,否则的话,0就会显示为0位数
while (a>0)
{
num++;
a=a / 10;
}
printf("%d",num);
}
与Java类似,while循环当条件满足时会不断的执行后面大括号里面的代码(循环体),循环体内也要有改变条件的机会,否则可能会变成死循环
2.do-while循环
先无条件做do后面的语句,再进行条件判断
while结尾必须有分号
do
{
<首先执行的循环体语句>
}while(<循环条件>)
案例:猜数游戏
#include<stdio.h>
#include <time.h>
#include<stdlib.h>
void main() {
srand(time(0));
int number = rand() % 100 + 1;
int a;
int count = 0;
do {
printf("输入猜的数:\n");
scanf_s("%d", &a);
count++;
if (a > number) {
printf("输入的数太大\n");
}
else if(a<number) {
printf("输入的数太小\n");
}
} while (a != number);
printf("用了%d次猜对",count);
}
案例:算平均数
#include<stdio.h>
void main() {
int sum = 0;
int num;
int count = 0;
do {
scanf_s("%d", &num);
if (num != -1) {
sum += num;
count++;
}
} while (num != -1);
int average = sum / count;
printf("一共输入了%d个数字平均值为%d", count, average);
}
案例:整数求逆
- 一个整数是由1至多位数字组成的,如何分解出整数的各个位上的数字,然后加以计算:
- 对一个整数做**%10的操作,就得到它的个位数**;
- 对一个整数做**/10的操作,就去掉了它的个位数**;
- 然后再对2的结果做**%10**,就得到原来数的十位数;
- 依此类推。
3.for循环
案例:求阶乘
#include<stdio.h>
void main() {
int a;
int factor = 1;
scanf_s("%d", &a);
for (int begin = 1; begin <= a; begin++) {
factor *= begin;
}
printf("阶乘为:%d", factor);
}
三种循环的选择
- 若有固定次数,用for循环
- 如果必须执行一次,用do…while循环
- 其他情况用while循环
循环控制
素数:只能被1和自己整除的数,不包括1;
//生成指定范围内的所有素数
#include<stdio.h>
void main() {
printf("输入范围(1-__)已生成该范围内的所有素数\n");
int x,a;
scanf_s("%d", &a);
for ( x = 1; x <= a; x++)
{
int i;
int isPrime = 1;
for (i = 2; i < x; i++) {
if (x % i == 0) {
isPrime = 0;
break;
}
}
if (isPrime == 1) {
printf("%d ",x);
}
}
return 0;
}
循环的嵌套
可以不同的循环嵌套,也可以嵌套很多层循环
需要注意的是,计算循环的变量尽量不要一样
案例:凑硬币
#include<stdio.h>
int main() {
int x;
int one, two, five;
x = 2;
int exit = 0;
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;
goto 语句
goto语句后面要接一个标号,这个标号是自己设置的,程序将会跳转到该标号的位置上去,声明标号时需要在标号后面接冒号,然后再接语句;
例如:
......
goto out;
}
out:
return 0;
......
- 当有多层嵌套时,使用goto语句是非常方便的,除此以外,其他场合尽量不要使用,因为goto语句会破坏程序的结构性
案例:前n项求和
//1 1/2 1/3 1/n求和
#include<stdio.h>
void main() {
int i = 1;
int n;
scanf_s("%d", &n);
double sum = 0.0;
for (i = 1; i <= n; i++) {
sum += 1.0 / i;
}
printf("%f", sum);
}
数据类型
C语言是有类型的语言,C语言的变量必须:
- 在使用之前定义
- 确定类型
- C++/Java更加强调类型,甚至对类型的检查比C更为严格
- JavaScript、Python、PHP不看重类型,甚至不需要实现定义
类型安全
- 支持强类型的观点有助于尽早发现程序中的简单错误
- 反之的观点则认为会迫使程序员面对底层、实现而非事务逻辑
- 总的来说,早期语言强调类型,面向底层的语言强调类型
- C语言需要类型,但是对类型的安全检查并不够
C语言的数据类型
- 整数
- char 、 short、 int、 long、 longlong
- 浮点数
- float 、 double、 long double
- 逻辑
- bool(人为定义)
- 指针
- 自定义类型
exp:以上加粗部分的数据类型为C99的类型
不同点:
- 输出输入时的格式化: %d、%ld、%lf
- 表达数的范围:char<short<int<float<double
- 在内存中占据的大小也不同:1字节到16字节
- 内存中的表达形式:二进制数(补码)–整型、编码–浮点数
sizeof运算符
给出某个类型或变量在内存中所占据的字节数
1字节=8比特
注意:
- 是静态运算符,结果在编译时刻就决定
- 不要在sizeof()的括号里面做运算,这些运算是不会做的,例如:
int a = 6;
printf("sizeof(a++)=%d\n"