[ 基本难度系数 ]:★★☆☆☆
一、C语言的基本结构
(1)、第一个程序:hello world
配置部署好 vsCode 之后,就可以直接在上面写代码了,新建一个新的C程序文件,向屏幕输出一串字符 “Hello World!”
下面,从整体上来分析一下这个最简单的C语言程序,将这个最简程序的各个部分剖析清楚,明白我们写下的每一个字符的具体含义。
(2)、第一个程序结构解析
1、头文件
#include <stdio.h> // 告诉编译器在.h文件中(声明)有这么一个符号,你需要去其相应的.c文件里去找其源文件(实现(定义))
- C语言程序中,任何符号(包括变量和函数)的使用,都是需要提前声明的。
- 如果程序用到了系统库函数,那么就需要包含相应的系统头文件来进行声明。
上述代码中用到库函数printf,而这个函数的声明在头文件 stdio.h 中,因此在该程序中需要包含此头文件。
声明
- 概念:对源文件外定义的某个符号进行描述的过程,称为声明
- 作用:经过声明的符号,编译器才能正确识别和编译,否则编译器不认识,无法判定语法错误
- 符号:变量、函数等
- 示例:
// 声明一个变量符号number
extern int number = 100;
// 声明一个函数符号function
extern void function(int size, char *buf);
// 注意:前面的extern的关键字是可以不加的
2、主函数
- 主函数是必须有的,每一个C语言程序都有main函数,哪怕是空的。
- 主函数的名字是固定的,叫 main ,这个函数名不可被挪作他用。
- 主函数是程序的入口,也就是程序开始运行的地方。
- 主函数分解如下:
// int: 返回值类型,代表主函数最终的执行结果是一个整型数据
// main: main是函数名,这个名字不能变
// void: 是函数参数列表,可以省略,表示空的意思,就是没有参数的意思
int main(void)
{
........
}
在 Linux 环境下,主函数 main 根据程序最终是否需要接收外部命令行参数,实际上有两个版本:
// 不接收外部命令行参数的版本
int main(void)
{
...
}
// 接收外部命令行参数的版本
int main(int argc, char const *argv[])
{
...
}
3、printf函数(标准输出 --- 屏幕设备)
C语言被称为是一种面向过程的模块化语言,所谓的模块,指得是一个个功能明确、各司其职的封装函数,它们就像一块块积木,被合理有机地拼搭起来,形成复杂、健壮的程序。
在这之中,系统标准C库提供了非常多“模块”,printf 就是其中之一,它的功能就是:向终端输出一段指定格式的数据。
面向对象:我吃饭,我唱歌,我洗澡,我走路,我打架
面向过程:吃饭,唱歌,洗澡,走路,打架
// 向终端输出一段字符串
printf("Hello World!\n");
解析:
a、常见的格式控制符:
一、基础格式控制:
%d // 十进制整数,decimalism
%o // 八进制整数,octonary
%x // 十六进制整数,hexadecimal
%c // 字符,character
%s // 字符串,character string
%f // 单精度浮点数,float
%p // 输出地址、输出指针的值,pointer
二、格式控制拓展(后面讲):
.........
示例代码:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int num = 100;
char buf[128] = "shijienameda,woxiangdainiqukankan";
float f1 = 3.14;
double f2 = 3.14;
// 1、没有格式控制符的printf函数
printf("hello world!\n");
// 2、打印整型数
printf("整型数:%d\n", num); // 要求打印这个num的值的时候,用十进制数表示:decimalism
// 3、打印八进制数
printf("八进制数:%o\n", num); // 要求打印这个num的值的时候,用八进制数表示:octonary
// 4、打印十六进制数
printf("十六进制数:%x\n", num); // 要求打印这个num的值的时候,用十六进制数表示:hexadecimal
// 5、打印字符
printf("字符: %c\n", num); // 要求打印这个num的值的时候,用字符表示:character
/*
一开始计算机中是没有固定的数据(1010...)代表字符的
所以后面定了一个标准:ASCLL表(这个表描述了相关字符对应的数据(0-128))
*/
// 6、打印字符串(因为num不是字符串格式的,所以不能打印或者说打印会有误)
printf("字符串: %s\n", buf); // 要求打印这个buf的值的时候,用字符串表示:character string
/*
printf("字符串: %s\n", num);
类型不匹配
format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat=]
格式百分号S要求参数类型为char *, 但是printf函数的参数2的类型却是int数据
*/
// 7、打印小数(单精度浮点型)
printf("小数(单精度): %f\n", f1); // 要求打印这个f1的值的时候,用单精度浮点型来表示:float
// 8、打印小数(双精度浮点型)
printf("小数(双精度): %f\n", f2); // 要求打印这个f2的值的时候,用双精度浮点型来表示:double float
// 面试题3:
/*
判断两个浮点型数据是否一致
if( f1 == f2)
{
printf("两个浮点型数相等\n");
}
else
{
printf("两个浮点型数不相等\n");
}
*/
// 9、打印地址数据(打印指针里面的数据(指针里面的数据就是地址数据))
// printf("num的地址数据为: %p\n", num); // 以地址的方式打印num的值(地址是用十六进制的方式打印的)(注意:地址是用十六进制打印的,但不意味着十六进制的数据是地址)
printf("num的地址数据为: %p\n", &num); // 打印num的地址
return 0;
}
b、常见转义字符、显示特效转义字符:
转义字符通常前面有\符号
一、基本转义字符
\n - 换行符。将光标移动到下一行的开始位置。******
\t - 水平制表符。通常会将光标移动到下一个制表位。
\\ - 反斜杠。用来表示实际的反斜杠字符。
\' - 单引号。用来表示实际的单引号字符。
\" - 双引号。用来表示实际的双引号字符。
\? - 问号。虽然问号本身不需要转义,但在某些情况下为了防止与三字母序列混淆,可能会用到。
\a - 警报声。发出警报声。
\b - 退格。将光标向后移动一个字符的位置。
\f - 换页。跳转到下一页的开始位置。
\r - 回车。将光标移动到当前行的开始位置,但不换行。
\v - 垂直制表符。通常会将光标移动到下方的垂直制表位。
\ooo - 其中ooo是1到3位的八进制数
\xhh - 其中hh...是一个或多个十六进制数字。(\101 和 \x41 都代表大写字母 'A' 的ASCII码值65)
二、背景色、字体色显示转义字符
a、格式:\033[40-47;30-37m
b、说明:
40/30: 黑色(背景色/字体色)
41/31: 红色
42/32: 绿色
43/33: 黄色
44/34: 蓝色
45/35: 紫色
46/36: 深绿色
47/37: 白色
c、使用:
printf("\033[40;32m《学生信息管理系统》\n");
三、显示效果转义字符
\033[0m // 关闭特效
\033[1m // 高亮效果
\033[4m // 下划线效果
\033[5m // 闪烁效果(只有ubuntu18.04系统具备这个效果)
\033[7m // 反显(交换前景色和背景色)
\033[8m // 消隐(文本不可见)
四、光标设置
\033[2J // 清除屏幕
\033[x;yH // 定位光标(x:横轴坐标, y:纵轴坐标)
\033[yA // 上移光标(y是纵轴移动的位置)
\033[yB // 下移光标
\033[xC // 右移光标(x是横轴移动的位置)
\033[xD // 左移光标
\033[H // 复位光标
\033[?25l // 隐藏光标
\033[?25h // 显示光标
\033[u // 恢复光标位置
\033[s // 保存光标位置
\033[K // 清除从光标到行尾的内容
示例代码1
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
// 1、\n - 换行符:newline*********
printf("hello word\n");
// 2、\t - 水平制表符:Horizontal tab
printf("111\t222\t333\t\n");
// \\ - 反斜杠。
printf("反斜杠:\\\n");
// \' - 单引号。
printf("单引号:\'\n");
// \" - 双引号
printf("双引号:\"\n");
// \? - 问号
printf("问号:\"\n");
// %% - 想打印%,需要两个
printf("百分号:%%\n");
// \a - 警报声。发出警报声。:alert
printf("警报声\a\n"); // 没有什么用
// \b - 退格: backspace
printf("nihaoi\b,shijie\n");
// \f - 换页。: form feed
printf("nihao,weweoiweogweiogweiogweiohgweiohwioehgwe\f\n"); // 没有什么用
// \r - 回车。:carriage return
printf("qingge\r,nizhenshuai\n"); // 将光标移动到开头,重新覆盖
for (int i = 0; i < 100; i++)
{
printf("\r\033[44;37m现在的进度为:%d%%\033[0m", i);
usleep(50000); // 延时函数,延时微妙(1s == 100 0000us)
fflush(stdout); // 冲刷输出缓冲区(printf函数是将数据输出到输出缓冲区的,没有遇到截止或者是冲刷函数,是不会将字符串输出到屏幕中的)
}
// \v - 垂直制表符。:vertical tad
printf("\n111\v222\v333\v\n");
// \ooo:八进制数
printf("\144\n"); // 打印出d字符:d字符的ascll码值是100
// \xhh:十六进制数
printf("\x64\n"); // 打印出d字符:d字符的ascll码值是100
return 0;
}
示例代码2
#include <stdio.h>
int main(int argc, char const *argv[])
{
printf("\033[44;37m ========================== \033[0m\n");
printf("\033[44;37m * * \033[0m\n");
printf("\033[44;37m * 《 学生信息管理系统 》 * \033[0m\n");
printf("\033[44;37m * * \033[0m\n");
printf("\033[44;37m * (1)、添加学生信息 * \033[0m\n");
printf("\033[44;37m\033[1m\033[7m * (2)、删除学生信息 * \033[0m\n");
printf("\033[44;37m * (3)、查看学生信息 * \033[0m\n");
printf("\033[44;37m * (4)、修改学生信息 * \033[0m\n");
printf("\033[44;37m * (5)、后台管理 * \033[0m\n");
printf("\033[44;37m * * \033[0m\n");
printf("\033[44;37m * 退出(q)* \033[0m\n");
printf("\033[44;37m ========================== \033[0m\n");
return 0;
}
4、return语句
C语言中的关键字 return 的含义是结束一个函数,并返回某个数据(若有)。而具体到 main 函数中时,由于主函数 main 的结束同时也意味着整个程序的结束,因此在上述 Hello World 程序中 return 后整个程序就退出了。
int main(void)
{
... ...
// 一般而言,函数正常退出返回0,非正常退出返回非0
return 0;
}
一般而言,程序正常退出时,main函数返回值是0;当程序异常退出时,main函数返回值是非0。
注意:main函数的返回值是给上一级进程的,因此理论上讲在当前的单进程程序中,main函数的返回值究竟是多少都无所谓,因为根本没有去使用这个返回值,除非在多进程程序中,进程间才需要使用main函数的返回值来传递信息。
二、man帮助手册、在线文档的使用(第一手资料)
1、man手册(manual:手动的)
编程开发的过程中,需要使用众多的系统指令、各类函数库、系统调用、各类系统脚本文件等,这些种类繁多、功能各异的信息,可以通过统一的man手册来获取第一手资料(英文),man文档是帮助信息的权威通道,是大多数教程、书籍的知识来源。
由于 man 手册所需要罗列的帮助条目众多,因此 man 手册被妥善地分为9册,不同的帮助信息被放入对应的man分册中:
- 在ubuntu命令行中敲
man man
- man文档的九个分册:
-
- 可执行程序或Shell命令(默认已安装)
- 系统调用 (Linux内核自带的函数)
- 库函数 (外部的第三方函数)
- 特殊文件(通常出现在/dev目录下)
- 文件的特殊格式或协定(例如/etc/passwd的格式)
- 游戏
- 杂项(例如一些宏定义)
- 系统管理员命令(通常只能由管理员执行)
- 非标准内核例程
a、man手册的安装
在默认情况下,系统只安装第一册,其他的帮助信息一般需要用户自己手动安装,安装指令如下:
gec@ubuntu:~$ sudo apt install manpages
gec@ubuntu:~$ sudo apt install manpages-dev
b、man手册的查询
使用man手册查询命令、函数时,一般直接在man后面接上待查条目即可,但有时候会遇到同一个待查询的条目存在于多个man分册的情况,这就需要先罗列出该条目所在的分册信息,然后再根据需要去选择帮助文档的序号。
1、查询单一分册的条目:
gec@ubuntu:~$ man strcpy
2、查询存在于多个分册的条目:
# 先使用 -f 来查看有哪些分册包含了该条目
gec@ubuntu:~$ man -f printf
printf (1) - format and print data
printf (3) - print formatted output
# 根据需要,指定查询某一册帮助分册
gec@ubuntu:~$ man 3 printf
c、man手册的使用:
1、在Ubuntu系统终端上敲命令man -f strlen // 查看该函数的man手册条目有几条,选择相应的页数
2、man 3 strcmp // 阅读打开man手册文档
3、如何使用一个函数?
a、函数的作用(NAME)
strlen - calculate the length of a string // 计算字符串的长度
b、函数的头文件(SYNOPSIS)
#include <string.h>
c、函数原型(参数是什么、返回是什么)((DESCRIPTION))
size_t strlen(const char *s); // size_t现在不用去理会,因为其本质就是int型数据(为了方便阅读)(被typedef改名的)(只不过可能会有一些限制条件,比如:unsigned)
// 参数一:要计算长度的字符串
注意:
excluding the terminating null byte ('\0').不包括终止的空字节(“\0”)
因为字符串其末尾默认自带一个'\0' (空字节)符号,比如:"nihao",其实有6个字符,其中一个是其末尾的'\0'符号
d、函数的返回值(RETURN VALUE)
返回字符串s的字节数(这个字符串s有多少个字符)
示例代码:
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
char buf[128] = "nihao"; // 创建128个字符空间大小的内存,将这个字符串内容给赋值进去
size_t len = strlen(buf); // 使用字符串函数strlen,去计算buf内存里面有几个字符(不包括'\0')
printf("led = %lu\n", len); // 由于size_t是long unsigned int数据类型,所以此处的格式控制符应为lud(d省略)
return 0;
}
2、在线文档(C/C++)
https://zh.cppreference.com/w/%E9%A6%96%E9%A1%B5
三、代码风格规范
1、标识符命名(变量名、函数名):
-
- 只能包含数字、字母以及下划线,不能包含其他任何特殊字符。
- 只能以字母或者下划线开头。
- 不能跟系统已有的关键字重名,也不能跟本命名空间(namespace)中具有相同作用域的其他标识符重名。
- 尽量能一望便知,做到顾名思义,避免使用无意义字符
通常的命名方式(推荐):
a、函数名:
驼峰命名法: FangHuiQing
下划线命名法: fang_hui_qing
二合一法: Fang_Hui_Qing或Fang_HuiQing
b、变量名:
int led_num = 0; // 下划线命名法,小写
int led_flag = 0;
c、宏定义:
#define LED_ON // 下划线命名法,大写
2、缩进:凡是有代码块的地方都必须缩进,一对大括号 { } 代表一个代码块
3、空格:标点符号后面,一般加空格
4、空行:不同逻辑段落中间,一般加空行
5、排版的快捷方式
让代码块整体左移: 选中代码块+shift键+tab键
让代码块整体右移: 选中代码块+tab键
注释/解除注释: 选中代码块+ctrl+/
练习
【1】、实验1:
使用 VSCode 编写一段代码,显示自己的学校和姓名
【2】、实验2:
麻花疼同学写了下面这个程序,请指出你认为不妥的地方。
#include "stdio.h"
main{}
(
float g; h;
float tax, rate;
g = e21;
tax = rate * g;
printf("%f\n", tax);
)
【3】、实验3:
使用man手册查询strlen函数的用法,并编写一个程序试试它的效果。