文章目录
前言
这个博客是我大一的寒假自学C语言的时候写的,一方面是作为笔记本,记录整理一些学习的进步;另一方面以博客这种形式进行分享,或许可以给学习节奏和我相同的小伙伴们一些帮助。
这篇博客算是我探索编程世界的一个日记本吧,由于代码的可复制性和屏幕截取的方便性,学习过程的记录也是比较方便的。主要是记录了一些可以注意的细节,一些操作的记录,以及一些概念的解释。
题目是learn coding with me, 是抱着和同辈们一起学习的态度,所以非教学指向,非技术指向,甚至有很多闲话细说的成分哈哈。
Anyway, let’s enjoy the learning journey!
Hello World!
打代码的细节
#include <stdio.h>
//回车留一行空白
int main()
{
//放主程序的地方
return 0;
}
这个是程序框架,目前学习的程序就在这个框架里写就够了。我之前在新建文件后习惯把这些删掉,还被室友制止了哈哈,不过自己也得会默写就是了。
当然很容易记忆,但是有些细节我想写得标准好看些,就像练字前要先学笔顺,学笔顺前还要学握笔。起步要正。
这个框架我是抄写的翁恺老师的课件,教授的代码该是标准且优美的。格式上的注意我觉得有两个方面:
- 一方面是空行和空格,如在预处理指令(#符号)里包含头文件时,include和头文件名之间可以打个空格;在预处理指令和主函数之间回车留一行;当然return和返回值之间时一定要用空格隔开的,否则会识别不了,编译错误。
- 另一方面是对齐,首先是大括号怎么打。开括号和使用它的语句放在同一行还是另起一行,这是个问题,尤其对于有些强迫症的我来说。我选择用翁恺老师这样,和我的那位代码很好看室友一样,采用Allman格式,即
“每个大括号都单独成行,并且开括号和闭括号都与使用它们的语句对齐。”(三种大括号使用格式链接:hzy_best的博客,)
return有一个缩进,可以打一个Tab正好,是和main的函数名对齐,不清楚原因,先这样做了。据说python里面缩进是非常要注意的。
程序理解
其实逻辑很好理解,主要是在这里记录一些术语,以后不至于把终端叫做“黑黑的那个窗口”(翁恺教授)。点击编译并运行,那个黑黑的窗口会弹出,显示Hello World!, 这个叫做终端或控制台或命令行。
从头开始,
- #是预处理指令,这里是引用包含了头文件。
- #include是一个文件包含命令,相当于把头文件拉到这边来用。
- <stdio.h>就是我们引用的头文件, “standard input & output",是一个C标准库。这里的尖括号和双引号是通用的(亲测有效哈),区别是头文件的搜索路径不同,据说双引号号更强大一些(这一点我目前理解还不到位),更多了解看这里: C语言中文网#include的用法。
- int main()就是我们的主函数了。在一个文件里,就在程序的最开始直接打上就好了。但是多个文件建在了一个项目下有不是这回事了。最开始用的DEV C++还没感觉,后来用了用clion,直接在同一个项目下新建了练习的代码文件,就不妙了。所以现在用clion都是新建一个项目,新项目里再只有一个代码文件。我还能理解这些情况的本质,等以后要用到多文件项目的时候应该自然就知道了。我这里再main后面打上了括号,是因为之前的一段debug经历。当时用的arduionoI给小车编程(语法大同小异),在执行一个函数的时候,我就直接打上了那个函数的名称,反正那个括号里也没有值,结果就一直在这个函数这里报错,直到我加上那一对空空的括号。从此函数名和它的圆括号在我心里就有了那种形影不离的印象。这里的int是主函数返回值的类型(目前也没太理解)。
打出开括号,我们终于要开始写我们的程序啦,缓缓打出这个世界著名的Hello World!
#include <stdio.h>
int main()
{
printf("Hello World!\n");
return 0;
}
相信每一位学习编程的朋友再看到这一行输出的时候都会有些小激动。
简单解释一下出现的一些新符号
- printf是一个格式化输出函数,可以把我们要输出的东西打印在终端上
- \n 可以理解为word里回车的作用,可以看到hello下面与虚线之间有一个空行,如果不大、打\n是没有的。注意是一个表示转义的反斜杠。
- 还有就是注意一下括号、引号、分号之间的位置关系
到这里呢我对学习之路发生了一点小变化。因为这个时候呢我安装了虚拟机,装的是linux操作系统。关于我决定在虚拟机上学编程这件事,原因一是新鲜感多一些,二是有一位计算机大佬朋友就是这么做的。据说有一些指令会很方便。在虚拟机上安装IDE的时候,我在clion和VScode两者之间还纠结了一阵,最终选择了后者。一方面是之前使用了一段时间clion体验不佳,让我满意的只是图表和页面比较好看;另一方面是听说VS的功能比较强大,甚至可以和人工智能ChatGPT交流,爱了爱了。安装的过程和结果也很干净,期待接下来的学习。
先安装两个插件,目前还没能体会到它们的作用
创建新文件的时候是有一小串连续的步骤的。以后或许有方法简化。
首先要一步步打开想创建新文件的目录,再点击创建文件,记得把扩展名改成.c
OK,终于要接触到编程本身了,接下来我就只贴简单的运行成功的源码和几句话解释作为学习笔记了。
速通《啊哈C》
第一、二章
(编译器可以讲.C的文件变成一个exe,是一个可执行文件,也就是一个计算机可以直接运行的程序了)(啊哈c注释,帮助对文件类型的理解)
下面是书里的第一段示例程序
哈哈,第一个小程序就bug啦
看一下chatGPT的解释
哈哈,删除,debug
这里问chatGPT学了一下在终端编译和运行程序
当然也可以直接点击右上角的一个BUG小虫加运行图表的按钮
#include <stdio.h>
/*#include <stdlib.h>//这里似乎没有用*/
int main()
{
printf("ni hao\n");
return 0;
}
如果不加换行回车的\n就会这样,也行吧,但没有原来好
简单完成了一下小作业
打了下一段代码,果然linux里面有些命令是和win不一样
一段算加法的代码,没啥问题:
#include <stdio.h>
int main()
{
int a,b,c;//变量类型 变量名称
a=1;
b=2;
c=a+b;
printf("%d",c);
return 0;
}
处理一下小数 ((float 和 %f
#include <stdio.h>
int main()
{
float a,b,c;
a=1.5;
b=2.3;
c=a+b;
printf("%f",c);
return 0;
}
在终端显示整个式子:
#include <stdio.h>
int main()
{
float a,b,c;
a=1.5;
b=2.3;
c=a+b;
printf("%f+%f=%f\n",a,b,c);
//每个%f分别从后面的一个变量里拎一个值出来显示
return 0;
}
加入数据输入
- scanf(“%d”, &a);
- 和printf看着差不太多,主要是多了一个&。这是取地址符,简称取址符,作用是得到a这个变量的地址。
“打个此方:假如你要去一个教室上课,那么在上课之前你需要知道这个教室的地址,这样你才能去;但是如果下课了,你需要走出这个教室,因为此时你已经在教室中,所以就不再需要这个教室的地址。”
——啊哈C
看懂了书上的介绍,尝试自己编一个加法计算器。
#include <stdio.h>
int main()
{
float a,b,c;
printf("please type in the two numbers which you want to add up\n");
scanf("%f %f",&a,&b);
c = a + b;
printf("%f+%f=%f",a,b,c);
return 0;
}
在终端输入的时候在两个自己输入的值之间打上空格就行了
看似问题不大,但是输出不是18.210000,有点奇怪,介绍如下:
程序修改如下
#include <stdio.h>
int main()
{
double a,b,c;//用精度更高的double变量类型
printf("please type in the two numbers which you want to add up\n");
scanf("%lf %lf",&a,&b);//注意这里要用%lf
c = a + b;
printf("%f+%f=%f",a,b,c);
return 0;
}
有一个小操作,把输出结果改成15位
printf("%.15f+%.15f=%.15f",a,b,c);//%.15f,就会输出15位
今天睡觉了,最后尝试直接在linux终端运行程序,芜湖,睡觉
我很喜欢虚拟机的环境,所以改了配置的参数。讲内存和处理器内核数量都加到了物理机的一半。确实IDE打开的速度和编译的速度都变快了。
交换变量。逻辑很简单,语法上有一点提醒。在scanf的函数里面,两个%d之间是否有空格是一样的,在终端输入的时候,都是在两个值之间打上空格。我这一段代码在两个%d之间加了逗号,在终端输入的时候就在两个值之间打个逗号。
#include <stdio.h>
//To swap the values of two variables
int main()
{
int a,b,temp;
printf("Please type in the two values you want to swap\n");
scanf("%d,%d",&a,&b);
temp = a;//Use a temporary variable
a = b;
b = temp;
printf("%d,%d\n",a,b);
return 0;
}
尝试不用新变量交换两个数值
#include <stdio.h>
//To swap the values of two variables
int main()
{
int a,b;
printf("Please type in the two values you want to swap\n");
scanf("%d,%d",&a,&b);
a = b - a;//record the differerce between the two variables
b = b - a;//now the value of b is the original value of a
a = b + a;//the original value of a plus this difference is the original value of b
printf("%d,%d\n",a,b);
return 0;
}
啊哈C老师在这里讲了一下代码的可读性、简洁性、高效、优美
就是大家说的“优雅”。在机械设计的时候其实也有这样的追求。
希望我以后能开发出一些优雅的东西出来。
下面是老师的几点建议:
- 代码不但要让机器看得懂,还要让人读得懂。
- 使用Tab这个制表符,代替4个空格
- 用注释代替暂时的删除
第三章
- 六个判断两个数关系的关系运算符的使用
- 相等,大于,小于,大于等于,小于等于,不相等
- == > < >= <= !=
下面写一写判断是否为正数的代码
#include <stdio.h>
//Determine whether a number is positive
int main()
{
printf("Please type in the number you want to determine whether it is positive.");
int a;
scanf("%d", &a);
if(a>0)
{
printf("yes");
//The Tab here indicates that printf("yes") is part of the if statement
}
if(a<=0)
{
printf("no");
}
}
下面写一段判断是否为5的倍数的代码:
- 新的运算符 % 取余,用法如下
#include <stdio.h>
//Determine whether a number is positive
int main()
{
printf("Please type in the number you want to determine whether it is a multiple of 5.\n");
int a;
scanf("%d", &a);
if(a%5 == 0)
{
printf("yes\n");//The Tab here indicates that printf("yes") is part of the if statement
}
if(a%5 != 0)
{
printf("no\n");
}
}
把 if(a%5 != 0)换成else 结果是一样的
#include <stdio.h>
//Determine whether a number is positive
int main()
{
printf("Please type in the number you want to determine whether it is a multiple of 5.\n");
int a;
scanf("%d", &a);
if(a%5 == 0)
{
printf("yes");//The Tab here indicates that printf("yes") is part of the if statement
}
else
{
printf("no\n");
}
}
写一段找出最大数的代码
模拟人的思维过程就好
#include <stdio.h>
//Find out the largest number
int main()
{
int a,b,c,d;
printf("Please enter three numbers\n");
scanf("%d,%d,%d",&a,&b,&c);
d = a;//Just keep the value of d in mind
if(b>=d)
{
d=b;
}
else
{
d=d;
}
if(c>=d)
{
d=c;//Always replace the number d with the largest number I have seen
}
else
{
d=d;
}
printf("the largest number is %d\n",d);
}
这里就跳过无脑分类讨论了,直接加点算法,用个冒泡排序:
#include <stdio.h>
//Sorting
int main()
{
int a,b,c;
printf("Please enter the three numbers you want to sort\n");
scanf("%d,%d,%d",&a,&b,&c);
int temp;//define a temperorary box, used for swap values
if(a>b)
{
temp=a;
a=b;
b=temp;
}
if(a>c)
{
temp=a;
a=c;
c=temp;
}
if(b>c)
{
temp=b;
b=c;
c=temp;
}
//the first round is to put the smallest one in the first place
//the second round is to put the second smallest one in the second place
//we don't eliminate values, we just swap values
printf("the result is %d,%d,%d\n",a,b,c);
}
如果if的是一个数,算正确还是错误呢
#include <stdio.h>
int main()
{
int a;
scanf("%d",&a);
if(a)
printf("yes");
else
printf("no");
}
事实是,除了0,其它都算true
下面开始讨论一下嵌套,并且不用大括号
#include <stdio.h>
int main()
{
int a,b,c;
scanf("%d,%d,%d",&a,&b,&c);
if(a>c)
if(a>b)
printf("%d",a);
else
printf("%d",b);
else
if(c>b)
printf("%d",c);
else
printf("%d",b);
}
注意好if和else的对应,注意if-else语句的完整性就好了
一些简单的命令行操作
再次运行编译后的代码 ./
一些好用的快捷键操作
**ctrl+/**直接把多行代码变为注释
第四章
while
#include <stdio.h>
int main()
{
while (1)
{
printf("wa");
}
}
it is getting crazy
打印从1到100的数
#include <stdio.h>
int main()
{
int a=1;
while (a<=100)
{
printf("%d\n",a);
a=a+1;
}
}
打印一百以内三的倍数
//printf这里加Tab表示if条件满足执行的语句
#include <stdio.h>
int main()
{
int a=1;
while (a<=100)
{
if(a%3==0)
printf("%d\n",a);
a=a+1;
}
}
1到100求和
#include <stdio.h>
int main()
{
int sum=0,i=1;
while(i<=100)
{
sum=sum+i;
i=i+1;
}
printf("%d",sum);
}
求阶乘
#include <stdio.h>
int main()
{
int fac=1,i=1,a=0;
printf("please enter an integer\n");
scanf("%d",&a);
while (i<=a)
{
fac=fac*i;
i=i+1;
}
printf("%d\n",fac);
}
倒计时表
#include <stdio.h>
#include <unistd.h>//different from windows when using 'sleep'
int main()
{
printf("enter the minute and second repectively\n");
int min,sec;
scanf("%d,%d",&min,&sec);
while(min>=0 && sec>=0)//we dont want negtive time
{
printf("%02d:%.2d\n",min,sec);
//use formatting to print leading zeros
//two ways to do it
sleep(1);
//divide into two categories
if(sec<=0)
{
min--;
sec=59;
}
else
{
sec--;
}
}
}
打印a行b列的星号
运用循环嵌套
这里下意识使用的 i 和 j 竟然还挺符合traditioin
这些小东西叫循环变量
默认从外层到内层使用 i j k i m n
#include <stdio.h>
int main()
{
int a=3,b=4;//determine the numbers of rows and lines
int i=1;
while(i<=a)
{
int j=1;
while(j<=b)
{
printf("*");
j++;
}
printf("\n");
j=1;
i++;
}
}
再一个循环嵌套练习
#include <stdio.h>
int main()
{
int a=6;
int i=1,j=1;
while(j<=a)
{
while(i<=j)
{
printf("%d",j);
i=i+1;
}
printf("\n");
i=1;
j=j+1;
}
}
for登场
最简单示例:
#include <stdio.h>
int main()
{
int a;
for(a=1;a<=10;a++)
{
printf("%d\n",a);
}
}
用for求和
#include <stdio.h>
//caldulate the sum from 1 to a
int main()
{
int a,sum=0;
printf("please enter the value of a");
scanf("%d",&a);
int i;//use this little thing again
for(i=1;i<=a;i++)
{
sum=sum+i;
}
printf("%d",sum);
}
列出一百以内所有质数:
#include <stdio.h>
int main()
{
int a=100;
int prime;
for(prime=2;prime<=a;prime++)
{
int i;
int count=0;
for(i=2;i<=prime-1;i++)
{
if(prime%i==0)
count++;
}
if(count==0)
printf("%d\n",prime);
}
}
还不错
《C语言程序设计》——翁恺教授
判断(if - else)
- 写的代码要足够多的人读懂你的代码。
- 计算两个值之间的关系,所以叫做关系运算。运算的结果符合预期,计算结果为1,反之为0。
- 关系运算符的优先级很低,但是比赋值运算高。
- 关系运算符里面==和!=的优先级更低。
- 永远再if 和 else 后面加大括号吧
- error是指程序有错,甚至不编译;warning不是百分百有问题,但是“要尊重warning”。
- “风格是三观”
分支
- 连续的if else会要连续判断,效率不高
- switch可以直接跳到相应的case,一直遇到break为止
#include <iostream>
using namespace std;
int main()
{
cout << "please enter a number" << endl;
int n;
cin >> n;
switch(n)
{
case 1:
cout << "morning" << endl;
break;
case 2:
cout << "afternoon" << endl;
break;
case 3:
cout << "evening" << endl;
break;
default:
cout << "what?" << endl;
break;
}
}
- 级联的if-else if
可以理解为第一个if不满足,就进第一个else;而else里面,又是一对if-else - 单一出口更好,即下图左好于右
嵌套的if-else
循环
tips for loops:
如果有固定次数,用for
至少执行一次,用do-while
其他用while
do-while练习:
//Calculate the number of digits of a number.
#include <iostream>
using namespace std;
int main()
{
cout << "please enter a number" << endl;
int theNum;
cin >> theNum;
int n = 0 ;
do{
theNum /= 10;
n++;
}
while(theNum != 0);
cout << n << endl;
}
数据类型
- inf 表示无穷, nan 表示不存在
//touch the boundry of numbers
#include <stdio.h>
int main()
{
printf("hello\n");
printf("%f\n",12.0/0.0);
printf("%f\n",-12.0/0.0);
printf("%f\n",0.0/0.0);
return 0;
}
hello
inf
-inf
-nan
- float1 = = float2 可能失败
应该float1-float2<1e-12 - 整数一定是准确的,浮点数只能在一定范围内相信它
- 当运算符的两边出现不一致的类型时,会自动转换成较大的类型
- 强制类型转换,如(int)i, 运算优先级比加减乘除高
函数
- 代码复制是程序质量不良的表现
- return有两种作用,一个是终止函数运行,一个是返回后面那个值(可选)
指针
- 指针就是数据的地址。这个地址“指向”那个数据所在的位置
- 这段程序是要用一个函数得到两个返回值
但是如果用return的话只能返回一个值,就像学校里学的函数一样
这里使用指针,我们把两个地址传给这个函数(通过这两个指针带回运算结果)
函数的作用是修改这两个地址上的值
主函数里再访问这两个地址上的值再输出,即输出了函数的处理结果
//use one function to return more than one values
//some of the values could just be returned with the help of pointers
#include <iostream>
using namespace std;
void maxmin(int a[], int* p_max, int* p_min);//define two pointers as parameters
int main()
{
int a[] = {
1,2,3,4,5,87};
int max, min;
maxmin(a, &max, &min);
cout << max << endl << min << endl;
}
void maxmin(int a[], int* p_max, int* p_min)
{
int i;
*p_max = *p_min = a[0];//initialize the values on the two addresses
for(i=0; i<=5; i++){
if(*p_min>a[i]){
*p_min=a[i];
}
if(*p_max<a[i]){
*p_max=a[i];
}
}
}
- 用函数结果返回运算状态
用指针返回运算结果
来防止异常情况(如除数为0
#include <iostream>
using namespace std;
int get_quotient(int a, int b, double* p_c);
int main()
{
cout << "please enter the dividend and divisor repectively:\n" ;
int a, b;
cin >> a;
cin >> b;
double c;
if(get_quotient(a,b,&c)){
cout << c;
}
}
int get_quotient(int a, int b, double* p_c)
{
int ret = 1;
if(b==0){
ret=0;
}
else{
*p_c = (double)a/b; //this double is used to get a double result
// or the quotient of two integers would lead to an integer too
}
return ret;
}