C语言学习笔记——第一个程序

C语言学习笔记——第一个程序
Kanwuqing准备操作那篇里敲过一段代码, 当时说”目前不用知道原理”. 现在该把那段代码拆开看一看了
这篇不打算先讲什么数据类型、什么运算符, 因为脱离代码去记这些名词没什么意义. 我们直接盯着一个能跑的程序, 一行一行搞清楚它在干什么. 等有了”一个C程序大概长什么样”的印象, 后面的东西才好往这个框架里塞
0 综述
1 | 第一个程序 |
1 最小程序逐行拆解
把准备操作里的那段拿来:
1 |
|
四行代码, 去掉空行其实就四件事:
| 行 | 代码 | 在干什么 |
|---|---|---|
| 1 | #include <stdio.h> |
把”输入输出工具箱”拿进来 |
| 2 | int main() { |
定义主函数, 程序从这里开始 |
| 3 | printf("..."); |
往屏幕上打一行字 |
| 4 | return 0; |
告诉系统”我正常结束了” |
不理解
int是什么意思、return 0为什么要写 0都没关系
现在只要知道一个 C 程序 = 一堆函数, 其中必有且仅有一个叫
main不是mian
2 头文件与 #include
#include <stdio.h> 里的 stdio 是
standard input
output 的缩写, 也就是”标准输入输出”
C 语言本身其实很小,
它连”怎么往屏幕上打印东西”都不管. 打印是别人写好放在库里的,
stdio.h 相当于一张清单,
告诉编译器”我接下来要用这些东西, 你都可以找到”
所以如果把 printf 写在
#include <stdio.h> 之前, 编译器会不认识
printf 从而报错
#include有个细节: 它是预处理指令, 在编译之前就会被处理掉, 属于”编译前的一道工序”. 展开讲要等到编译预处理那篇
<>和""的区别(系统头文件 vs 自己写的头文件)也留到那时, 在之前只要知道类似#include "a.h"就代表a.h是用户自己写的
3 main 函数
一个 C 程序里可以有很多函数, 但有且仅有一个 main
函数. 程序运行时, 一定是从 main 的第一行开始, 到
main 结束为止.
1 | 程序开始 |
需要注意的是, main
写在文件最前面还是最后面都不影响, 它永远是第一个执行的.
换句话说, 一个程序里有几十个函数, 系统偏偏只认 main
这一个入口.
另外注意
main后面那对代表函数的括号不能省. 写成int main而不带()是错的
main 前面的 int
表示这个函数返回一个整数. 所以最后要写
return 0;, 把 0 交回给系统
现在很多高级IDLE会自动加上return 0, 但是考试的时候没有高级货, 这也是刚入门用DevC++的好处
为什么是 0? 因为操作系统约定: 返回 0 表示程序正常结束, 返回非
0 表示出了错. 在准备操作里看到的那个”状态码”,
就是 return 出去的值
4 函数的形状: 首部 + 函数体
main 就是一个函数, 而任何一个 C
函数都由两部分组成:
1 | int main() { printf("hello"); return 0; } |
| 部分 | 内容 | 说明 |
|---|---|---|
| 函数首部 | 返回值类型 + 函数名 + 参数表 | 描述”这个函数叫什么、要什么、还什么” |
| 函数体 | 一对 {} 包起来的部分 |
描述”具体怎么干” |
函数体内部还可以再细分:
- 声明部分: 定义变量(比如
int a;) - 执行部分: 真正干活的语句
这个概念属于”书上写得郑重、考试只考选择题”的类型. 需要记住的就是函数 = 函数首部 + 函数体, 以及函数体 = 声明部分 + 执行部分
{和}是函数体的标志, 缺一个就是语法错误
顺带一提, 函数体可以为空int main() { }
是合法的, 编译器最多给个”没有 return”的警告, 照样能跑,
只不过什么都不干. 但在 main 里通常至少写个
return 0;.
5 语句、分号与注释(★★★)
5.1 分号
C 语言里每条语句都以分号结束. 少了分号是最常见的编译错误之一
报错形式如
[Error] expected ';' before 'xxx' token
但是要注意 #include 后面不写分号.
它是预处理指令而不是语句, 严格来说不该有分号
不过就算手滑写了
#include <stdio.h>;, 编译器只会给个警告, 不会报错, 程序照样能跑. 原因是预处理指令以换行为结束标志, 那个多余的分号被当成了”多余的符号”忽略掉了
但不要因此而养成坏习惯
5.2 注释
1 | // 这是单行注释, 从 // 到本行结束, 中途遇到其他的//不会结束注释状态 |
注释是给人看的, 编译器会直接忽略掉.
但要注意多行注释不能嵌套——
/* /* */ */这样写, 第一个*/就把注释提前结束了, 后面的内容会变成代码导致报错
也要注意注释确实不会让计算机看到, 但不代表没人看
毕竟就是写给人看的. 因此不要在注释中写暴戾语句, 每次相关的程序设计竞赛结束后会将所有选手代码公示, 而每年都有许多人因写暴戾语句被禁赛甚至通报批评, 常见的包括但不限于F**k CCF等
6 输出: printf 初步
printf 的作用是把内容打到屏幕上.
最简单的用法是直接扔一个字符串进去:
1 | printf("love u"); |
但更多时候我们需要输出一些无法实现预测的内容, 如变量, 这就需要用到格式控制符:
1 | int age = 18; // 代表age的值是18 |
输出结果是 我今年 18 岁. 这里的 %d
是个占位符, 它在实际运行过程中会被后面 age
的值顶掉
值得注意的是, printf里的空格是不会消失的
目前只需要记住两个最常用的:
| 格式符 | 用来输出 |
|---|---|
%d |
整数 |
%s |
字符串 |
其余的(%f、%c、%lf
等)等认识了对应的数据类型再说, 现在提前背只会记混
注意引号外面那个逗号:
printf("...", 变量). 要输出的变量跟在字符串后面, 用逗号隔开. 有多个变量就依次往后排, 和%d一一对应, 如printf(“%d%d%d%d%d”, a, b, c, d, e);
7 输入: scanf 初步
scanf 用来从键盘读数据. 用法和 printf 很像,
但有个必须记住的差异
1 | printf("%d", age); // 输出: 不用 & |
为什么输入要加 &?
printf 只需要”知道 age 是多少”, 把值拿出来用就行,
不用知道 age 在内存中的位置. 而 scanf
需要”把读到的数字放进 age 这个盒子里”, 所以它必须知道
age 在内存里的位置. &
就是”取地址”的意思——拿到 age 的门牌号, 才能往里面塞东西
现在只要记住输入加 &, 输出不加,
原理等讲指针时自然就通了
scanf("%d", age);少写&时, 实测下来它能通过编译(编译器只给一句警告, 不是错误), 但运行时程序会直接崩掉——因为程序会把age里那个乱七八糟的值当成地址, 往一个不确定的地方写数据. 这种”编译能过、运行才炸”的 bug 最难查(我们称其为RunTimeError, 简称RTE), 尤其考试时只给结果不给报错, 更要注意数组名做输入时不用加
&(如scanf("%s", name);), 原因也留到数组篇
学会了scanf, 可以将上文中的程序进行扩充了,
实现真正的IO, 让程序让你输入年龄, 并输出你的年龄
代码
1 |
|
如果输入语句中age前没有&,
运行时会在输入后卡一下然后变成下图这样, 注意return的值
而如果你学过Python, 并妄想像python里input语句那样合并第4,5行成为
scanf("Input your age:%d", &age);的话那恐怕要让你失望了, 因为这样像要让程序读入%d的值的话必须这样输入:Input your age:12, 否则格式不对age就不会有值
