C语言入门12个核心概念-新手避坑指南
自学C语言别死磕!12个核心概念,搞懂少踩80%新手坑
网上的C语言入门教程有个通病:只教你怎么写,不告诉你为什么会崩。抄完代码运行一看——乱码、崩溃、编译器甩一堆看不懂的英文,你开始怀疑自己是不是不适合学编程。
其实不是。绝大多数新手bug,根源就那十几个基础概念没搞透。0、'0'、\0 分不清,字符串哪段能改哪段不能改不知道,if后面多写个分号找了两小时——这些才是真凶。
这篇把入门阶段最容易踩坑的12个概念掰开揉碎讲,末尾附编译错误对照表和printf格式符速查。建议先收藏,写代码遇到报错回来翻,基本都能对上号。
目录
C语言是什么?
main函数
printf和库函数
关键字与标识符
字符
ASCII编码
字符串(乱码崩溃重灾区)
\0结束符
转义字符
语句
语句分类
代码注释
附录
1. C语言是什么?
1972年,丹尼斯·里奇在贝尔实验室搞出了C语言,初衷是用它重写Unix操作系统。这门语言牛逼的地方在于:写起来像高级语言一样能读,跑起来又贴近硬件够快。所以几十年过去了,底层开发还在用它,编程入门也还在教它。
说人话就是:计算机只认二进制的0和1,你说中文它听不懂,所以需要一门编程语言当中介。C语言就是众多中介里的一个,跟Python、Java、Go干的是同一类活。
唯一要注意的:C是编译型语言,你写的 .c 文件不能直接跑,得先用编译器翻译成机器指令,生成可执行文件才能运行。新手最开始容易在这一步懵——写完代码点运行没反应,大概率是编译没通过,不是代码没保存。
2. main函数
main 就是程序的入口。操作系统启动你的程序之后,从 main 函数的第一行开始往下执行,就这么简单。
有且只有一个规矩:能直接运行的程序,有且只能有一个main函数。 写两个就报 redefinition of 'main',这个后面错误对照表里会提到。
库项目(只给别人提供功能、自己不跑的代码)不需要main,但初学阶段你碰不到这个,记住"一个程序一个main"就够了。
关于 return 0;:C99标准允许main函数末尾不写return,编译器会自动帮你补。但只有main有这个待遇,你自己写的函数该return还是得return。新手建议老老实实写上,养成习惯没坏处。
最小可运行程序长这样,以后写任何代码都可以从这个骨架往上加:
#include
int main(void)
{
printf("hello, world\n");
return 0;
}
3. printf和库函数
你不用自己写怎么在屏幕上打印文字,C标准库已经帮你写好了,printf 就是其中一个工具。这种"标准预先实现好、你直接调用"的函数,叫库函数。
用 printf 之前必须写 #include
别搞混:#include 既不是函数也不是头文件,它是预处理指令——在编译之前先执行的一道命令。
4. 关键字与标识符
关键字
C语言自带的、意思固定死的词,叫关键字。int、if、while、return、char、for、else 这些都是。
三条铁律:
不能拿来当变量名或函数名,硬用就编译报错
你改不了它的意思,编译器底层直接解析
全是小写,没有大写的关键字
注意:printf 不是关键字,它是库函数。关键字是语言本身的词汇,库函数是别人写好的工具,两码事。
标识符(命名规则)
你自己给变量、函数起的名字叫标识符。规则很简单,但新手天天在这翻车:
只能用字母、数字、下划线
不能数字开头——2name 非法,name2 合法
区分大小写——Age 和 age 是两个东西
不能跟关键字重名
合法
非法
原因
age
2num
数字开头
student_name
my-name
含减号
_count
int
关键字
num2
我的变量
含中文
写中文变量名、变量名里带空格、不小心用了关键字——这三种情况占了新手编译错误的半壁江山。
5. 字符
单个符号就是字符。字母、数字、标点、空格,都算。
C语言里用单引号包一个字符:'A'、'5'、' '(空格也是字符)。类型是 char,占1个字节。
单引号里只能放一个字符。放两个?语法错误,编译器不跟你客气。
6. ASCII编码
计算机存的是数字,不是字母。那怎么表示'A'?靠一张约定好的映射表——ASCII编码,给每个常用符号分配一个整数编号。
三个必须背下来的数:
字符
ASCII值
'A'
65
'a'
97
'0'
48
标准ASCII一共128个编号(0~127):
0~31 是控制字符,看不见,用来干换行、制表这种事。10号是换行 \n,9号是制表符 \t
32~126 是能打印出来的,空格、数字、大小写字母、英文标点都在这
两个容易忽略的点:
ASCII里没有中文。中文靠UTF-8、GBK这些编码,一个汉字通常占好几个字节
有些编译器里 char 默认是有符号的,范围-128~127。你往char里塞个大于127的数,结果可能跟你想的不一样
7. 字符串(乱码崩溃重灾区)
这一节是新手翻车最密集的区域,打起精神。
C语言没有专门的字符串类型,字符串本质就是char类型的数组。用双引号包起来:"hello"、"abc"。
编译器会在双引号字符串的末尾自动加一个你看不见的 \0,作为结束标记。\0 占1个字节,但不会打印出来。
所以 "abc" 在内存里实际是:'a' 'b' 'c' '\0',4个字节,不是3个。
'A' 和 "A" 完全不是一回事
写法
类型
内存里存的
占几个字节
'A'
char字符
'A'
1
"A"
字符串
'A' + '\0'
2
单引号一个字符,双引号是字符串末尾还藏个 \0,这个区别搞不清,后面全是坑。
三个最容易犯的错
双引号字符串别手动加 \0——编译器已经帮你加了,你再写一个就是两个结束标记,虽然不一定崩,但纯属多余
自己用char数组拼字符串,必须手动加 \0——不加的话,printf("%s") 会一直往后读内存,直到碰巧读到一个0为止,中间全是垃圾数据,屏幕上就是一堆乱码
字符串里可以写转义字符,"hello\nworld" 里的 \n 算一个字符,不是两个
很多教程不会告诉你的坑
双引号直接写出来的字符串字面量,存在只读内存段里。意思就是:程序运行的时候你不能改它,硬改直接崩溃(段错误)。
别问我怎么知道的,问就是新手期写过 char *s = "test"; s[0] = 'T'; 然后调了半小时bug。
需要能改的字符串?用字符数组:
#include
int main(void)
{
char buf[] = "test"; // 字符数组,内容可以改
buf[0] = 'T';
printf("%s\n", buf); // 输出 Test
return 0;
}
8. \0结束符
\0 是一个转义字符,ASCII编号是0,专门用来标记字符串的结束。
三条规则:
双引号写的字符串,编译器自动补 \0
自己拿char数组拼字符串,得自己写 \0
\0 不显示,但占1个字节
0、'0'、\0 是三个完全不同的东西
写法
是什么
ASCII值
能打印吗
\0
字符串结束标记(转义字符)
0
不能
0
int类型的数字0
—
能(打印数字0)
'0'
字符0
48
能(打印字符0)
做字符判断的时候特别容易搞混——比如你想判断一个字符是不是数字0,得写 if(c == '0'),写 if(c == 0) 判断的是字符串结束符,完全两码事。
9. 转义字符
有些符号没法直接写在引号里,比如你想在字符串里打个双引号,写 "他说"你好"" 编译器直接懵了——它不知道哪个双引号是结束。
C语言的解决方案:反斜杠 \ + 一个字符,两个符号合起来代表一个逻辑字符,占1个字节。
常用的就这几个:
转义字符
干啥的
\n
换行,光标跑到下一行开头
\t
水平制表符,相当于按Tab
\\
输出一个反斜杠本身
\'
在单引号里表示单引号字符
\"
在双引号字符串里输出双引号
三个坑:
反斜杠 \ 和斜杠 / 是两个东西,转义字符只能用反斜杠。写 \n 是换行,写 /n 就是两个普通字符
\n 是一个完整字符,写 '\n' 才对,别拆开
孤零零写一个 \ 后面啥都没有?语法错误,编译器直接报错
对了,\0 也是转义字符,上一节讲过了。
10. 语句
完成一个执行动作的代码就是语句。普通语句以分号 ; 结尾:int a = 10;、printf("hi");。
用大括号 { } 把多条语句包起来,叫复合语句也叫代码块,整体算一条语句。if、for、while后面跟的几乎都是代码块。
记住一件事:大括号后面不要加分号。 加了等于多了一个空语句,虽然不一定报错,但可能带来逻辑问题。
11. 语句分类
① 表达式语句
表达式后面加个分号就是。赋值、计算、函数调用都算:
a = 10;
printf("hi");
② 空语句
就一个分号 ;,啥也不干,语法合法。循环体不需要干活的时候用得上,平时基本见不到。
③ 复合语句
大括号包起来的一坨,整体当一条语句用。if、for、while里天天写。
代码块里定义的变量,出了大括号就没了。这个叫局部变量的作用域,后面学函数的时候会深入讲。
④ 控制语句
改变代码从上到下执行顺序的语句:
分支: if-else
循环: for、while、do-while
跳转: break、continue、return
⑤ 声明语句
定义变量、定义数据类型的语句。int a; 就是最典型的声明语句。
老标准C89要求变量声明必须写在代码块最开头,C99以后随便在哪定义都行。现在的编译器全支持C99,不用管这个老规矩。
两个致命新手坑
坑一:if后面多写分号
if (a > 0); // 这个分号直接把if结束了!
printf("正数"); // 这行不受if控制,永远执行
if条件后面跟了个分号,等于if的体是空语句,后面的printf跟if半毛钱关系没有。这种bug编译不报错,运行结果不对,找起来巨烦。
坑二:不写大括号只管第一行
if (a > 0)
printf("正数"); // 只有这行受if控制
printf("结束"); // 这行永远执行,缩进骗不了编译器
if、while、for 不加大括号,只管辖紧跟的第一条语句。第二行不管你缩进多好看,都不受控制。新手建议永远写大括号,省得后面加代码的时候踩坑。
12. 代码注释
注释是写给人看的,编译器直接忽略,不参与编译,不影响运行。
两种写法:
// 这是单行注释,从//到行尾都是注释
/* 这是多行注释
可以写好几行
直到遇到 */结束
注意:多行注释不能嵌套。你在 /* */ 里面再写一个 /*,编译器会把最近的 */ 当成结束,后面的代码就暴露出来了,直接编译报错。
// 单行注释是C99加的,现在所有编译器都支持。古老的C89不支持,但你基本碰不到C89环境了。
13. 附录
附录A:printf常用格式符速查
printf靠格式符决定数据怎么打印,新手常用的就这几个:
格式符
对应类型
用途
示例
%d
int
打印整数
printf("%d", 10);
%c
char
打印单个字符
printf("%c", 'A');
%s
字符串
打印字符串(遇到\0停)
printf("%s", "hi");
%f
float/double
打印小数
printf("%f", 3.14);
%x
int
十六进制打印
printf("%x", 255);
%%
—
打印百分号本身
printf("100%%");
用 %s 的时候确保字符串有 \0 结尾,不然printf会一直读内存读到碰巧遇到0,中间全是乱码。
附录B:新手常见编译错误对照表
报错关键词
大概率原因
怎么改
implicit declaration of function 'printf'
没写 #include
加上头文件
'printf' was not declared in this scope
没写头文件(C++环境下)
加上 #include
undefined reference to 'printf'
链接器找不到库(编译参数问题,正常gcc不会出现)
检查编译命令,确保链接了标准库
expected ';' before ...
上一行漏了分号
补分号
expected declaration or statement
大括号不配对
检查 { } 是不是成对
redefinition of 'main'
写了两个main函数
删掉多余的
expected expression
转义字符写错、单独写了 \
检查反斜杠
stray '\xxx' in program
代码里混了中文标点/全角符号
改成英文半角
'xxx' undeclared (first use in this function)
变量名拼错、没定义就用、用了关键字
检查变量名拼写和定义
unterminated comment
多行注释 /* 忘了写 */
补上结束标记
最后说两句
这12个概念看着基础,但后面学指针、学数组、学函数的时候,全是在这些地基上盖楼。\0 搞不清,打印永远有乱码;字符串只读特性不知道,段错误调到怀疑人生;if后面乱加分号,逻辑bug找两小时。
入门阶段那些看起来"莫名其妙"的bug,十有八九根在这。把这篇吃透,少走很多弯路。错误对照表和格式符速查建议收藏,写代码的时候随时翻。
有问题评论区聊,看到会回。