3

【浙大翁恺C语言】从0入门笔记【国家精品课程】(上)

 2 years ago
source link: https://blog.csdn.net/qq_59418188/article/details/120642535
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

C语言程序设计

所有代码都展示main函数里面的代码

计算机的思维

辗转相除法

int u = 32;
int v = 32;
//如果v=0,计算结束,u就是最大公约数
//v!=0计算u/v的余数,让u=v v=余数
while(v!+0)
{
	int temp = u%v;
    u = v;
    v = temp;
}
printf("%d",u);

从计算机到程序再到算法

计算机的思维和优势是把所有可能列出来挨个试(枚举)

用二分法可以进一步简化运算提高算法效率

程序的执行

解释:借助一个程序,那个程序能试图理解你的程序,然后按照你的要求执行

编译:借助一个程序,那个程序能将你的程序翻译成机器语言后执行

编译和解释的区别与看法

  • 语言本无编译和解释之分
  • 一个语言使用编译还是解释更多是取决于常用的执行方式是什么
  • 解释型语言有特殊的计算能力
  • 编译型语言有确定的运算性能

案例:算找零

  • 有地方放输入的数字
  • 有办法输入数字
  • 输入的数字参加运算
int price = 0;
printf("请输入金额(元)");
scanf("%d",&price);
int change = 100 - price;
printf("找您%d元。\n",change);
  • 便于修改维护
  • 减少不明意义的数字
int price = 0;
const int AMOUNT = 100;
printf("请输入金额(元)");
scanf("%d",&price);
int change = AMOUNT - price;
printf("找您%d元。\n",change);

const

const是个修饰符,加在int前面,const修饰变量一经初始化则无法修改,还可以进行再次优化,让用户输入那个变量的值,而不是固定的100

变量定义的一般形式:<类型名称><变量名称>;

在数学中a=b和b=a意义完全相同,但是在程序设计中意义完全相反

所有变量在第一次使用时之前应被赋值(初始化)

<类型名称><变量名称>=<初始值>;

关于scanf

scanf("%d",&price);

要求scanf这个函数去读入下一个整数,读到的结果赋值给price

双引号里的时格式字符串,%d表示读取整数

小心price前面的&,在scanf变量前要加&

案例:计算时间差

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 = t1 + t2;
printf("时间差是%d小时%d分",t/60,t%60);
//利用t/60抽象出小时,t%60抽象出分钟

案例:求俩个整数的平均值

int a,b;
scanf("%d %d",&a,&b);
//人们利用浮点数表达所有的带小数点的数,当浮点数和整数放到一起运算的时候,c会将整数转换为浮点数,然后进行浮点运算
double c = (a+b)/2.0;
printf("%d和%d的平均值为%lf",a,b,c);
数据类型中文名称格式字符串double双精度浮点数“%lf”float单精度浮点数“%f”int整数“%d”

案例:交换俩个变量

int a = 5;
int b = 6;
int t = 0;
t = a;
a = b;
b = t;
printf("a=%d b=%d",a,b);

+= -= *= /= %= ++ –

这些符号是因为c语言继承了曾经的机器语言

count ++;
count += 1;
count = count + 1;

上述三种是一个意思都能完成加一

a++表示加一以前的值

++a表示加一以后的值

但这俩种运算过后,下一行再用a都是加一过后的值

/*if(条件成立){
	...;
}*/
// ==相等 !=不等 >大于 >=大于或者等于 <小于 <=小于或等于
//当两个值的关系符合关系运算符的预期时,关系运算的结果为1,否则为0
printf("%d\n",5==3);
printf("%d\n",5>3);
printf("%d\n",5<3);
//所有的关系运算符的优先级比算术运算的低,但是比赋值运算的高
7 >= 3+4;
int r = a>0;
//判断是否相等的优先级比其他的低
//连续的关系运算是从左到右进行的

案例:找零计算器

//初始化
int price = 0;
int bill = 0;
//读入数据
printf("请输入金额");
scanf("%d",&price);
printf("请输入票面");
scanf("%d",&bill);
printf("应该找您:%d\n",bill-price);

但是我们上面那个程序无法判断给够钱了嘛,即判断票面够不够

我们进行一个逻辑判断对程序进行优化

//初始化
int price = 0;
int bill = 0;
//读入数据
printf("请输入金额");
scanf("%d",&price);
printf("请输入票面");
scanf("%d",&bill);
if(bill>=price)
{
	printf("应该找您:%d\n",bill-price);
}
else
{
	printf("钱不够还差:%d\n",price-bill);
}

案例:比较数的大小

案例1:两个数的比大小

int a,b;
printf("请输入两个正整数:");
scanf("%d %d",&a,&b);
int max = 0;
if(a>b)
{
	max = a;
}
else
{
    max = b;
}
printf("%d和%d的最大值是%d",a,b,max);

案例2:三个数的比大小(if嵌套方法)

int  a,b,c;
scanf("%d %d %d",&a,&b,&c);
int max = 0;
if(a>b)
{
	if(a>c)
    {
		max = a;
    }
    else
    {
		max = c;
    }
}
else
{
	if(b>c)
    {
		max = b;
    }
    else
    {
		max = c;
    }
}
printf("最大值是%d",max);

案例二也可以用别的方法解决,如冒泡排序等

编程习惯tips

  • else总是和离它最近的if匹配,所以日常建议写一个if就写一个else
  • 缩进不能暗示else的匹配!!! 缩进不能暗示else的匹配!!! 缩进不能暗示else的匹配!!!
  • if后面只有单行语句的时候可以不用花括号,但是为了整体风格协调好阅读建议都加花括号

案例:分段函数的表达(if-else if-else)

有如下分段函数,要求设计一个程序,输入x输出y

f(x) = -1 x<0

f(x) = 0 x=0

f(x) = 2x x>0

//只展示if部分
if(x<0)
{
    f = -1;
}
else if(x=0)
{
    f = 0;
}
else
{
    f = 2*x;
}

编程习惯tips

  • 缩进时注意else对齐(级联结构)
  • 代码编写尽量单一出口,多封装减少出口(如上文f)

if语句常见错误

  • 忘加大括号
  • if后加入了分号
  • else不规范

switch结构

若级联过多,执行效率会非常低,可用switch结构

switch(控制表达式){
case 常量:

语句;

break;

case 常量:

语句;

break;

default:

语句

break;

}

控制表达式只能是整数类型的结果

常量表达式可以是常数,也可以是表达式

switch语句可以看作一种基于计算的跳转,多个值对应一个语句

案例:成绩转换

成绩大于等于90为A

成绩大于等于80为B

成绩大于等于70为C

成绩大于等于60为D

成绩小于60为E

int grade;
scanf("%d",&grade);
grade /= 10;
//我们只抽象出十位数进行运算,这样就可以使用switch结构
switch(grade)
{
    case 10:
    case 9:
        printf("A\N");
        break;
    case 8:
        printf("B\N");
        break;
    case 7:
        printf("C\N");
        break;
    case 6:
        printf("D\N");
        break;
    default:
        printf("E\N");
        break;
}

级联>的时候从高往下判断

级联<的时候从下往高判断

案例:数数几位数(while循环)

int x;
int n = 0;
scanf("%d",&x);
//这行代码是保证输入0的时候输出位数是1
n++;
x /= 10;
while(x>0)
{
    n++;
    x /= 10;
}
printf("%d\n",n);

tips

  • 循环体一定要有改变循环条件的机会
  • 打断点自己测试程序的时候,测试数据常用的有边界数据,有效范围,特殊的倍数
  • 在程序中适当加入注释以及printf进行测试可以提升程序可行性和可读性

优化

我们现实中很多情况都需要先执行一次再判断循环

do-while会先执行一次再判断循环

int x;
int n = 0;
scanf("%d",&x);
do
{
    n++;
	x /= 10;
}
//这个while后面一定要打分号
while(x>0);
printf("%d\n",n);

拓展案例:计数循环

int count = 100;
while(count>=0)
{
    //这两行决定第一个输出和最后一个输出的数据是什么
	count--;
    printf("%d\n",count);
}
printf("发射\n");

案例:猜数循环

计算机想一个数,然后让用户去猜

猜的时候提醒大了还是小了直到猜中为止

补充知识

  • 每次召唤rand()就能得到一个随机的整数
  • x%n的结果是[0,n-1]的整数
  • x%100是表示x对100取余也就是只剩下十位个位

程序设计思路

  • 储存用户输入的数并且设置不猜中就不能出去的循环
  • 判断大了还是小了
  • 创建变量储存到底输入了多少次
srand(time(0));
int number = rand()%100+1;
int count = 0;
int a = 0;
printf("我已经想好了1-100的整数");
do{
    printf("请输入这个1-100的整数");
    scanf("%d",&a);
    count++;
    if(a>number)
    {
		printf("大了");
    }
    else
    {
		printf("小了");
    }
}
while(a!=number);
prinf("用了%d次猜中了数字%d",count,number);

案例:算平均数

让用户输入一系列正整数,最后输入-1结束,然后程序计算出这些数字的平均数和数字个数

程序设计思路

  • 用循环让用户持续输入,并且等于-1时不进行循环
  • 创建变量存储数字的个数
  • 利用1.0的妙用强制转换数据类型为浮点型
int number;
int sum = 0;
int count = 0;
scanf("%d",&number);
while(number!=-1)
{
	sum +=number;
    count++;
    scanf("%d",&number);
}
printf("%f\n",1.0*sum/count);

案例:整数的分解

输入一个正整数(int范围内的整数),输出逆序的数

程序设计思路

  • 对一个整数%10得到个位,对一个整数/10%10得到十位以此类推得到每一位
  • 对结尾是0的进行处理
int x = 0;
int sum = 0;
int t = 0;
scanf("%d",&x);
int count = 0;
//防止x改变利用一个变量代替x进行操作
t = x;
//我们先得到x是几位数,为了防止输入的是0,我们要先进行一次运算
t /= 10;
count++;
while(t!=0)
{
    t /= 10;
    count++;
}
//逆序处理
//有几位数就进行几次运算
for(int i=0;i<count;i++)
{
    //抽象出最后一位,并且把x从左往右少一位
	int a = x % 10;
    x /= 10;
    //利用j=count-i-1抽象出每次的一位要变到前面去需要×10的几次方,这种方法也能解决末尾是0的问题因为0*任何数都是0
    for(int j=count-i-1;j>0;j--)
    {
		a *= 10;
    }
    //计算和即可
    sum += a;
}
printf("%d",sum);

for循环总结

案例1:算阶乘

int n;
scanf("%d",&n);
int fact = 1;
int i = 1;
for(i=1;i<=n;i++)
{
	fact *= i;
}
printf("%d",fact);

案例2:是否是素数

int x;
scanf("%d",x);
int i;
int isPrime = 1;
for(i=2;i<x;i++)
{
	if(x%i == 0)
    {
		isPrime = 0;
        break;
    }
    else
    {
        continue;
    }
}
if(isPrime = 0)
{
    printf("%d不是素数",x);
}
else
{
    printf("%d是素数",x);
}

关于for循环

  • for循环像一个计数循环:初始化——循环条件——重复执行并调整
  • for中的每一个表达式都是可以省略的
  • for(;条件;)=while(条件)

tips

  • 如果有固定次数用for
  • 必须执行一次用do_while
  • 其余用while

案例:凑硬币(如何从嵌套循环中脱出)

如何用1.2.5角凑10元以下的金额

方案1:接力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("%d1角%d2角%d5角",one,two,five);
                //设置一个变量作为出口条件,创建接力break
                exit = 1;
                break;
            }
            if(exit == 1)
            {
                break;
            }
        }
        if(exit == 1)
        {
                break;
        }
    }
}

方案2:goto

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("%d1角%d2角%d5角",one,two,five);
                goto FLAG;
            }
        }
    }
}
FLAG:
return 0;

案例:正序分解整数

输入一个非负整数,正序输出每一位,每一位中间有空格,最后无空格

程序设计思路

  • 先判断有几位数字
  • 进行抽象每位正序数,并且间隔空格
  • 处理最后一位不空格
int x;
scanf("%d",&x);
int mask = 1;
while(t>9)
{
    t /= 10;
    mask *= 10;
}
do
{
	int d = x / mask;
    printf("%d",d);
    if(mask>9)
    {
        printf(" ");
    }
    x %= mask;
    mask /= 10;
}
while(mask>0);
printf("\n");

案例:最大公约数(枚举与辗转相除)

方法一:设t为2,如果u,v都能被整除则记下t,t++后重复第二步直到等于u和v的最小值,那么曾经记下的最大的t就是goc

int a,b;
int min=0;
scanf("%d %d",&a,&b);
if(a>b)
{
	max = a;
}
else
{
	max = b;
}
int ret = 0;
for(int i=0;i<=min;i++)
{
	if(a%i==0)
    {
		if(b%i==0)
        {
            ret == i;
        }
    }
}
printf("%d",ret);

方法二:辗转相除法

int u = 32;
int v = 32;
//如果v=0,计算结束,u就是最大公约数
//v!=0计算u/v的余数,让u=v v=余数
while(v!+0)
{
	int temp = u%v;
    u = v;
    v = temp;
}
printf("%d",u);

C的数据类型以及语言特性

C是有类型的语言,在使用前必须定义并且确定类型

C以后的语言向俩个方向发展

  • C++/Java更加强调类型,对类型的检查更加严格
  • Javascrip,Python,PHP不看重类型,甚至不需要定义

C语言的类型

  1. 整数
    • short
    • long long
  2. 浮点数
    • float
    • double
    • long double
  3. 自定义类型

sizeof

sizeof是一个运算符,算出表达式在内存中占几个字节

  • sizeof是静态运算符,他的运算符在编译的时候就已经注定
  • 不要在sizeof里的括号作运算

整数的内部表达

计算机内部一切都是二进制

那我们如何表达负数呢?

在十进制的时候,我们负号其实是独立于数字单独运算

那么二进制负数有三种方案

  1. 仿照十进制,用一个特殊标志
  2. 取中间数字为0,比它大是整数比它小是负数

但是前两种对于人类来说好理解,但是在计算机设计上比较复杂

利用位数上限进行设计

0->00000000

1->00000001

-1->11111111

全一被当作纯二进制看待时,是255,被当作补码看待时是-1

补码的意义就是拿补码和原码可以加出来一个溢出来的0

整数的范围

char c = 255;
int i = 255;
printf("c=%d,i=%d",c,i);

输出结果c的值为-1,i则为255

所以不同类型有不同的范围

负的数字范围会比整数多一个

unsigned

如果我们不想用补码的形式表示,我们可以使用unsigned关键字,让变量表达为纯二进制

正数范围扩大,负数不再表达

unsigned的初衷并非扩展数能表达的范围,而是为了去做纯二进制运算,为了进行移位

案例:计算int类型的最大数

利用原理:超过上限会溢出变成负的最大值,减一以后刚好回归正数最大值

int a = 0;
int b = 0;
while(++a>0);
printf("int数据类型最大数是:%d\n",a-1);
b++;
while((a=a/10)!=0)
{
    b++;
}
printf("int数据类型组最大数的位数是:%d",b);

整数的格式化

整数的输入输出只有两种形式:int或者long long

一个数字以0开头就是8进制

一个数字以0x开头就是16进制

%0用于8进制

%x用于16进制

计算机内部永远只有二进制,你写的八进制编译的时候会进行转换

8进制和16进制只是如何把数字表达成字符串,与内部如何表达数字无关

16进制一位刚好是4位2进制的数

选择整数类型

整数为什么要有那么多种,是为了准确表达内存,做底层程序的需要

但是一般来说,没有特殊需要就选择int

  • 现在的cpu的字长普遍是32或者64位,一次内存读写和计算都是一个int,选择更短的类型不见得会更快,反而有可能更慢
  • 现在编译器会设计内存对齐,所以更短的类型实际也会占据一个int大小,虽然sizeof说比int小

unsigned与否只是输出不同,内部计算是一样的

double与float

float有效数字为7位

double有效数字为15位

总有一块靠近0但不等于0的数字我们无法表达,即靠近0的无穷小无法表达

科学计数法用E表示

输出精度

在%和f之间加上.n可以指定输出小数点后几位,这样的输出是可以做四舍五入的

printf("%.3f\n",-0.0049);
printf("%.30f\n",-0.0049);
printf("%.3f\n",-0.00049);

Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK