【零基础学C语言】老九零基础学编程系列之C语言

p2 常用算法:穷举法、迭代法、递归法。
p9
变量命名规则:

☞相关资料:
保留字_百度百科
https://baike.baidu.com/item/%E4%BF%9D%E7%95%99%E5%AD%97/7674788?fr=aladdin
p10
基本的数据类型分为数值和非数值。
数值分为整型(用来表示整数)和非整型(或者说浮点型,用来表示小数)。
整型包括short、int、long三种。三种由于所占内存不同,所以能表示的数值范围不同(但是C语言中long和int范围一样)。
☞相关资料:
C/C++:long int与long long的区别
https://blog.csdn.net/tjcwt2011/article/details/109687327
非整型(浮点型)包括float、double两种。
非数值的数据类型为char。
☞相关资料:
C语言中的字符(char)详细讲解
https://www.jb51.net/article/88695.htm

p11

一些格式占位符:
%d:代表整数,%f:单精度浮点数float,%fl:双精度浮点数double,%s:字符串,%c:char,%p:指针。
%3.2f:在程序中显示出的单精度浮点数会有小数点前三位、小数点后两位。
把3换成别的数字就相应有小数点前几位。
把2换成别的数字就相应有小数点后几位。
把f换成lf就是改变双精度浮点数的显示精度。
注意:显示精度与实际精度无关。
☞相关资料:
格式占位符_百度百科
https://baike.baidu.com/item/%E6%A0%BC%E5%BC%8F%E5%8D%A0%E4%BD%8D%E7%AC%A6/8574147?fr=aladdin
C语言-格式输入输出中“%d,%o,%x,%e,%f”等的含义
https://blog.csdn.net/u013706540/article/details/82184145
p12
char类型的变量数值为‘A’(注意是单引号),和数值为65的char类型变量本质上一样,输出后都会显示出A,因为A对应的十进制ASCII码就是65。

转义序列:

(先不用记,要用再查。慢慢会记住的。)
我们学习的第一个输入函数:scanf。代码格式和printf相似:scanf("%d",&num)。变量num前面要有&符号。
p15
%:取模(取余数)符号。
如:5%3的结果是2
p16
getchar():获取字符。(不论输入多长只能获取一个)
putchar():输出字符。(只能输出一个)
fflush(stdin):清空缓冲区。
看视频以帮助理解:
开始演示getchar():
使用清空缓冲区功能以实现目标:
(P25的笔记中有相关应用)
开始演示putchar():
经典题目:交换a、b的值
解法:
int a,b,c;
c=a;
a=b;
b=c;
整型数的打印长度控制:
演示%4d
演示%04d
p17
int num=10;
num++; //这句可以换成num = num + 1; 或num += 1; 这几个表达式是等价的。
另外,num = num + 某个数字 和 num += 某个数字 是等价的,都表示给num赋值为num+该数字。
以此类推,num- -; ++是加1那--就是减1咯。
简单了解:类型转换(就是对某个数据类型输入了不符合该类型精度的数值,比如double n=6,double是浮点型而6是整数。)
p18
算术运算符有一元运算符、二元运算符、复合运算符。
二元运算符是+-*/%之类。
%:取模,意思就是取余数,比如5%3的值就是2,因为5除以3等于1余2。
复合运算符 :p17笔记中的+=就是复合运算符,
(num = num + 某个数字 和 num += 某个数字 是等价的,都表示给num赋值为num+该数字。)
别的复合运算符如-=、*=、/=用法也都一样,把+=的等价表达式里的+替换成相应符号,
(如num = num * 某个数字 和 num *= 某个数字 是等价的,都表示给num赋值为num*该数字。)
以此类推。
一元运算符(++、- -)的前置后置区别:
当后置,是先用num代替num++或num--运算完整句,然后再算num++或num--。
除算术运算符外——
关系运算符:就是些比大小之类的运算符。
可能用到的符号:>、<、>=、<=,==是等于,!=是不等于。
计算结果为1或0,其中1是真,0是假。
例:int num=5>8;
输出结果中num的值会是0,因为5并不大于8。
逻辑运算符:就是与或非。
&&与,||或、!非。

sinzeof运算符:

赋值运算符为=,单等号。
选学内容:

和二进制有关了这玩意。
P19
&&存在短路运算,如果&&左边已经为0了,&&右边的表达式就不会被运算了。
运算符优先级示意图:
(越上面的优先级越高。)


P20
if结构:
if(条件)
{
代码块1
}
else
{
代码块2
}
如果条件为真就执行代码块1,不然就执行代码块2。
P21
多重if结构:
if(条件1)
{
代码块1
}
else if(条件2)
{
代码块2
}
else
{
代码块3
}
如果满足条件1就执行代码块1,如果不满足条件1但满足条件2就执行代码块2,都不满足就代码块3。
P22
switch结构,用个例子理解吧。
int star; //玩家所获得物品的星级
scanf("&d",&star); //且不论为什么这个程序还要玩家自己输入星级
switch(star)
{
case 5:
printf("哇,抽到大奖啦!");
break; //如果输入的星级为5,则执行:输出“哇,抽到大奖啦!”,之后运算过程直接跳出这个大括号
case 4:
printf("运气还不错嘛。") ;
break;
default:
printf("呃……这个也能用吧。") ;;
}
注意:
1.switch()——括号中的变量只能是整型或字符型。
2.case后面的常量不能相同,就像上面的范例程序,不能先写一个“case 5; ……break;” 再写一个“case 5;……break;” 。
3.如果其中一个break没了,就会一直往后运行。假设例中去掉第一个break,那么输入5的时候就会输出:“哇,抽到大奖啦!运气还不错嘛。”
4.default子句可以往前放,但是此时如果没有break句就会出现3的情况,详见:
5.可以不使用default子句。
P24
while循环(当型循环)
int i=1;
while(i<=10000)
{
printf("%d只羊\n",i);
i++;
}
这就是一个会从1只羊一直数到10000只羊的while循环程序。
P25
本P中第二道练习题,我的解法(假设密码为四位字母):
#include<stdio.h>
int main()
{
char a='L',b='o',c='n',d='e',e,f,g,h;//四位密码Lone
int i=0; //用来计入错误次数的数值
while (i<3)
{
e=getchar();f=getchar();g=getchar();h=getchar(); //输入四位字母
if (e==a&&f==b&&g==c&&h==d==1) //如果四个字母一一符合Lone
{
printf("密码正确");
break; //跳出循环
}
else
{
printf("密码错误\n");
i++; //错误次数+1
fflush(stdin);//清空缓存区
}
}
return 0;
}
其中涉及到清空缓存区的问题。其实我还是不太明白缓存区具体是什么,但是在这个程序中,如果不在再次输入之前清空缓存区,那么只要第一遍输入错误,后面就怎么着都是错误的。
本P中提到了exit(0),意思是运行到这里就退出程序。
用的时候需要头文件:stdlib.h。
相关资料:
C语言中break和exit()的区别
https://blog.csdn.net/tomatolee221/article/details/89526575
P26
涉及时间函数,需要用头文件:time.h
涉及随机函数,需要用头文件:stdlib.h
随机函数:srand;一般要用的时候可以就写成srand(time(NULL));意思是使用时间作为种子,产生不同的随机数字。
写成srand((unsigned)time(NULL));
rand()是随机数,范围:0~32767。如果单独输出rand(),那么会输出一个随机但是不变的数,编译运行多少次都是这个数。
而如果按图中步骤:

先srand再输出rand(),每次运行就会输出不同的随机数。
Sleep函数:Sleep()的括号中输入一个数字x,就会产生x毫秒的间隔。使用时需要用头文件:windows.h。具体效果参考:
关于单步调试和监视:
P27
do-while循环:
do{
循环操作
}while(循环条件);
使用时会先执行一次循环操作,再判断是否循环条件,是则再执行循环操作再判断,否则退出循环。
如何取四位数中的每一位:

P29
for循环:

const,用于定义常量,常量就是只读的变量。
使用方法:const 数据类型 常量名称以及赋值。
例:const int num=256;
注意:
1.必须在这一个语句中就赋值,在这个语句之后,常量就不能再被赋初值或更新了。
2.循环变量必须更新,否则死循环。
3.打好分号。
一般能确定循环次数时,使用for循环,否则用while或do-while循环。
P30
break语句:用于跳出循环或switch结构。
P31
continue语句的作用:跳过本次循环,继续下次循环。
在while循环中:跳过本次循环,到判断循环条件处。
在do-while循环中:跳过本次循环,到判断循环条件处。
在for循环中:跳过本次循环,到更新循环变量处。

P32
讲解打印各种星号图形
P34


P35&P36
定义数组的语法:数据类型 数组名[容量];
例:char englishgrade[42];//表示42个同学的英语成绩等级(A\B\C\D\E)
初始化数组的语法:数据类型 数组名[容量]={元素0,元素1,元素2,……};
例:char englishgrade[10]={A,B,E,C,B,A,A,E,C,D};
注意:
1.如果填进{ }里的元素个数没有达到[ ]里面所规定的值,那么剩下的元素默认为0。
2.如果[ ]中不填数字,那么{ }中的元素数量就默认为数组的容量。(不可以两个括号内都不填东西!)
3.数组的容量是固定的。
englishgrade[6]表示englishgrade数组中的第7个元素(因为从0开始)。
如果要录入第7个元素对应的数据:scanf("%c",&englishgrade[6]);
另一种定义常量的方法:#define 常量名 数据
例:#define N 42
要写在int main()前面的位置。
写了这条定义之后,上面的char englishgrade[42];也可以写成char englishgrade[N];
与P29笔记中讲的定义常量方法的区别在于,这种定义方法是宏定义。这里的N可以看成int N=42或char N=42等等,宏定义的N是没有确定的数据类型的,它被用在需要什么数据类型的地方就会变成什么数据类型。
P37
数组排序之冒泡排序
基础原理:反复判断,两两交换。
核心算法:
P39
定义二维数组的语法:数据类型 数组名[行数][列数];
初始化二维数组的语法:数据类型 数组名[行数][列数]={ { },{ },{ },...,{ } };//{ }的个数和行数一致,{ }内的元素个数和列数一致。
例:

关于%-8.2lf的解释:
P41
播放声音的关键代码:
#include<windows.h>
#include<mmsystem.h>
#pragma comment(lib, "Winmm.lib")
PlaySound(TEXT("路径\\音乐文件名.wav"), NULL, SND_FILENAME | SND_ASYNC | SND_LOOP);
老师是这么写的,但我并未试验成功,目前还没有弄懂怎么播放音频。
C语言中字符串本质上是字符数组。要将多个字符串存储为一组时(比如某班所有学生的名字),相当于将多个字符数组存成一组,就要用到二维数组。
char names[6][9]表示names数组最多容纳6个字符串,每个字符串最大长度为8个英文字符或4个中文字符。(关于字符串容量的内容,具体见P62笔记。也可以不用管,最大长度直接定个很大的数也行。)
存储语句可以这样写:
char name[6][8]={"小明","小红","小刚","小王","小张","小李"};
其中6可以不写。二维数组里规定高维容量的数字可以不写,但是低维得写。
也可以在引号两边加{ },可以但没必要。
要打印时,以打印出“小红”为例,就是
printf("%s",names[1]};
P42
当输出字符串时,可以规定输出长度。比如:
printf("%12s",names[1]};
输出的字符串长度就有12格,右对齐,缺少字符的部分为空格。
若要调整为左对齐,在12前加上负号-即可实现,即:
printf("%-12s",names[1]};
简单了解查找字符串函数strcmp:
使用strcmp时需要存在预处理命令:#include<string.h>
strcmp的用法:
strcmp(字符串1,字符串2)的运算结果为x;
若两个字符串相等,则x=0;若字符串1>字符串2,则x=1;若字符串1<字符串2,则x=-1。字符串的大小是通过ASCII码来判断的。
注意:
这里的字符串1和字符串2指的是字符串的名字,如果要在strcmp里直接输入一个字符串的内容,应该在内容两边加引号。很多字符串相关操作可以以此类推。
简单了解数组赋值的函数strcpy:strcpy(字符串1,字符串2);
作用:将字符串2的内容赋给字符串1。
P43
指针是一个值为内存地址的变量。是一个变量。是变量。变量。
基本用法:数据类型 * 指针变量名;
暂不确定指针变量值时,将指针变量的初值设为空:NULL,表示指针不指向任何地址。
应用范例:int * ptr_a=NULL;
或:int a; int * ptr_a=&a;
指针变量对应的占位符:%p。
*:取指针所记录的地址上的变量值。
应用范例:
int a=1;//初始化a=1。
int * ptr_a=&a;//初始化指针变量ptr_a为a的地址。
*ptr_a=2;//更改ptr_a所记录的地址上变量的值,使该地址上的变量a的值变成2。
如果要用另一个指针来表示指针ptr_a的地址,请注意,定义指针的指针时需要两个*。应用范例:
int a=1;
int * ptr_a=&a;
*ptr_a=2;
int ** ptr_ptr_a=&ptr_a;若要通过指针ptr_a的指针ptr_ptr_a来访问ptr_a指向的空间(所记录地址上的变量值),则:
printf("%d",**ptr_ptr_a);
P46
数组就是一块连续的内存空间。数组名就是这块连续内存空间的首地址/首元素地址。
也就是说,假设有一数组,名为score,另有一指针变量ptr_score,则:
“ptr_score=&score[0];”与“ptr_score=score;”这两种写法是相等的。
关于指针运算:

使用指针访问数组元素:
打印时,“score[5]”和“*(score+5)”这两种写法是相等的。
P48
二维数组的首地址也可以用数组名表示。也就是说,假设有一二维数组,名为score,另有一指针变量ptr_score,则:
打印时,“score[ i ][ j ]”、“*(score[ i ]+j)”和“*(*(score+i)+j)"这三种写法是相等的。
这一节很多东西难以总结,看视频+上手练习才能有效理解。

P50
函数分为内置函数和自定义函数。
内置函数由C语言系统提供,使用时需要在程序前用预处理指令处理导入头文件。比如我们写代码时都要用#include<stdio.h>,而使用一些用来处理字符串的函数时一定要在程序前写上#include<string.h>。
自定义函数可以带参数也可以不带,可以给返回值也可以不给。
P51
一些常用的内置函数:

注意:
1.使用以上部分函数进行判断时,可能输出结果并不只有0和1,还有其他数字。只需要记住,C语言中0位假,非0为真。
2.使用以上函数时,如果括号中传入的是数字,则表示ASCII码。要输入'数字'表示的才是数字。就像给char型变量赋值一样。
P52
另一些常用的内置函数:

对ceil函数,如果小数点后不为零,则往更大的数上进1,如ceil(9.1)运算结果为10.000000(double型小数点后是这么多位吗?记不得了……)。如果需要运算的数是负数,如ceil(-9.1),那么运算结果是-9,因为-9更大嘛。floor函数对负数的运算规则也同理。
还有一些内置函数:

srand一般用时间作为种子,因为时间会变化,这样出来的rand随机数才会变化。用时间的话要用time.h头文件,语句:srand(time(NULL));
用system函数调颜色玩的话,需要用到的小资料:

用system("cls")清屏可以做动画玩。
P53

我以后再回头来学。
P54
自定义函数格式:
return_type function_name([datatype1 arg1],[datatype2 arg2],[...])
{
//函数体
}
其中的三要素:
1.返回值类型return_type
2.函数名function_name
3.各个参数类型和参数名称(以上标黄部分),参数可有可无、可多可少
自定义函数的完整写法:

如果函数有返回值,调用时要使用对应类型的变量来接收。

要一下返回多个值的话,可以返回数组。
返回空值时,函数返回值类型得是void,语句为:return;
暂时跳过P56-P57
P58
递归就是函数自己调用自己的过程。换个说法,编写函数体的时候就用函数自己。不过要注意!设置递归退出条件,不然就无限调用啦。
来个例子:

暂时跳过P59-P63
P64
一个或多个字符形成的序列叫字符串。像“Hello, I'm Fanta Lone!”是一个字符串,当然这一句的双引号不算字符串的一部分。
C语言中的字符串使用字符数组来存储,相当于每个字符都是字符数组中的一个元素。但是用存储字符数组的方式存储字符串时,结尾一定是\0(这个是空字符,是字符串的终止符)。
比如说存储一个名为“zifuchuan”内容为“Cat”的字符串,语句为:
char zifuchuan[4]={'C','a','t','\0'};
另外也可以用这个语句存储:
char zifuchuan[4]="Cat";
[ ]内的4也可以省略。
P65
新函数:gets,用于录入字符串;puts,用于输出字符串。
用法:
先定义一个字符数组,假设它名为zifuchuan:
char zifuchuan[50];//随便定个数组空间,免得以后不够用。
然后进行录入:
gets(zifuchuan);//效果相当于scanf("%s",zifuchuan);
再进行输出:
puts(zifuchuan);//效果相当于printf("%s\n",zifuchuan);puts函数自带末尾换行。
但是gets函数是存在问题的,已经规定了数组空间是50,除开\0只能读入49个字符,但是你就算输入超过49个字符gets函数也照样接受,这样容易产生bug。
解决方法:不要用gets函数(…),用scanf或fgets函数。
fgets函数用法(同样以zifuchuan为例):
fgets(zifuchuan,50,stdin);//即从标准输入流中读取50字节(49个字符+\n,没错是\n)到数组zifuchuan中。
暂时跳过P65-P70
P71
定义、基本用法、注意事项:

用法1:

用法2(将用法1中大段语句做成类似数组的形式):

P72
写结构语句其实也有两种方法:

结构里面装结构(像递归一样)叫嵌套结构。
例(第38行):

当使用和打印嵌套结构时用的语句(第45、46行):

P73

用法:
struct 结构名 *指针名=&结构名;
访问时:
printf("占位符",(*指针名) . 结构成员);
或:
printf("占位符", 指针名->结构成员);
如图(47、51、52行):

typedef使用方法:
typedef struct 结构名
{
//结构体
}新名字;
以后就可以用新名字代替“struct 结构名”了。
新函数:gets,用于录入字符串;puts,用于输出字符串。
用法
好,今天就学到这里。