C语言:学习篇(2)
本文摘要:C语言基础概述与数据类型详解 本文系统介绍了C语言的核心概念,重点包括: 程序结构特点:必须包含main函数,预处理指令置于开头,语句以分号结尾等基本语法规则 数据类型分类:详细说明整型、浮点型、字符型等基本类型及其内存占用情况 常量表示方法:涵盖整型、浮点型、字符型、字符串常量及符号常量的定义与使用规范 编译器选择:对比MSVC、GCC、Clang等主流编译器的特点与适用场景 进制转换
内容提要
-
C语言概述
-
数据类型
-
常量
C语言概述
C 语言程序的设计特点
C 语言程序设计需注意以下细节:
-
程序由一个或多个函数组成,但必须且仅有一个
main函数(程序入口),main函数可置于任意位置,但若调用其他自定义函数,需确保被调用函数在main之前声明或定义。 -
预处理指令(
#include、#define等)通常放在程序最前面; -
每条语句以分号
;结尾,注意:预处理指令、函数头、分支语句(if、switch)、循环语句(while、for)后面不能加;,否则会导致语法错误; -
函数体必须用一对
{}括起来,不可省略; -
注释内容(
/*..*/或//..)不参与编译,预处理阶段会被删除; -
一行可写多个语句(不推荐),一个语句也可拆分为多行(需保证语法正确);
int main() { // 一行多个语句(不推荐) int a, b, c; a = 1; b = 2; c = 3; // 一个语句拆分行(合法) while (1) { ... } } // 格式化代码快捷键:shift + tab(缩进调整)
C语言中的注释
C语言注释分为单行注释和多行注释,作用是解释代码、屏蔽临时无用代码,不参与编译(预处理阶段删除)
单行注释
-
格式:用
//开头,注释从//到行尾。 -
适用场景:变量说明、常量说明、函数调用语句、简单逻辑注释;
-
语法:
// 注释内容
-
范例:
// 预处理指令:引入系统标准输入输出库(stdio.h printf()/scanf()) #include <stdio.h> int main() { // 向控制台输出 hello world! (\n写在""中,表示换行,不会作为字符显示到控制台) printf("hello world!\n"); return 0; }
多行注释
-
格式:用
/*开头、*/,可单行,可跨多行,不能嵌套。 -
适用场景:文件头说明、函数功能说明、复杂代码块注释。
-
语法:
/* 注释内容 */ /* 注释内容 注释内容 注释内容 */
-
范例:
/* 函数名:main 功能:程序入口,输出hello world 返回值:0(表示程序正常结束) */ int main() { ... }
文档注释(扩展)
由多行注释衍生而来,用于生成标准化文档,格式更规范,一般替代传统的多行注释。
/**
* 函数名:main
* @author 开发者姓名
* @date 2026/1/22
* @param argc 解释参数,比如 命令行参数个数
* @param argv 解释参数,比如 命令行参数数组
* @return 0 程序正常结束;非0 程序异常结束
*/
int main(int argc, char *argv[])
{
...
}
数据类型
定义
数据类型是固定大小内存的别名,用于描述变量存储的数据类别(比如整数、小数、字符),核心作用:
-
组织和操作数据(明确数据的运算规则)
-
优化内存使用(根据数据需求分配合适的内存空间)
数据:计算机需处理的信息(数字、文字、符号、图片、音视频等),程序运行时所有数据都需要加载到内存中。
数据类型分类和计算方法
数据类型分类(重点)
-
基本类型(内置类型)
-
整型(数值类型,存储整数)
-
短整型:
short(short int的表示) -
基本型:
int(使用频率最高) -
长整型:
long(long int的表示) -
尝尝整型:
long long(long long int,C99标准新增)
-
-
浮点型(数值类型:存储小数/实数)
-
单精度型:
float -
双精度型:
double(使用频率最高) -
长双精度型:
long double(C99标准新增)
-
-
字符型:
char(存储单个字符,本质是ASCII码值)
-
-
构造类型(用户基于C语言语法自定义)
-
结构体:
struct(组合多个不同类型的数据) -
联合体/共用体:
union(多个数据共享同一内存空间) -
枚举类型:
enum(对于无参宏的扩展,用来定义一组命名常量)
-
-
指针类型(存储内存地址)
-
空类型:
void(表示无类型,用于函数返回值、函数形参、指针类型)
数据类型在内存中的大小(重点)
| 序号 | 数据类型 | 中文说明 | 大小(字节) | 常见取值范围 |
|---|---|---|---|---|
| 1 | short |
短整型 | 2 | -32768 ~ 32767 |
| 2 | int |
基本整型 | 4 | -2147483648 ~ 2147483647 |
| 3 | long |
长整型 | 4(32 位系统)/ 8(64 位系统) | 32 位:-2147483648 ~ 2147483647; 64 位:-9223372036854775808 ~ 9223372036854775807 |
| 4 | long long |
长长整型 | 8 | -9223372036854775808 ~ 9223372036854775807 |
| 5 | float |
单精度浮点型 | 4 | 约 ±3.4×10³⁸(精度 6~7 位有效数字) |
| 6 | double |
双精度浮点型 | 8 | 约 ±1.7×10³⁰⁸(精度 15~16 位有效数字) |
| 7 | char |
字符型 | 1 | -128 ~ 127(有符号)/ 0 ~ 255(无符号) |
| 8 | bool(_Bool,C99 新增) |
布尔型(true - 真,false - 假) | bool:1;true(1):4;false(0):4 |
true(非 0)、false(0) |
小贴士:
计算机存储最小单位是
bit(位),1 个bit仅能存储 0 或 1;程序中最小操作单位是Byte(字节),1 字节 = 8 位;C语言中没有Byte这个单位,我们使用char表示字节获取数据类型 / 变量的字节数,使用
sizeof运算符,语法:sizeof(数据类型)或sizeof(变量名);
bool类型需包含头文件<stdbool.h>,也可直接用int类型的 0(表示 false)和 1(表示 true)替代。
sizeof运算符使用示例:
#include <stdio.h>
#include <stdbool.h> // 启用bool类型
int main()
{
printf("true 占用字节数:%d\n", sizeof(true)); // 输出4(true本质是int类型的1)
printf("bool 占用字节数:%d\n", sizeof(bool)); // 输出1(bool类型本身占1字节)
printf("int 占用字节数:%d\n", sizeof(int)); // 输出4
printf("double 占用字节数:%d\n", sizeof(double)); // 输出8
return(0);
}
总结:
① 数据类型的字节大小与 C 语言编译系统(32 位 / 64 位)、操作系统相关;
② sizeof是运算符(非函数),计算结果为无符号整数;
③ bool类型依赖<stdbool.h>头文件,兼容旧编译器时可使用int替代。
课堂练习
编写程序,校验short、int、float、char等数据类型的字节大小。
常量
常量是程序执行过程中值不可改变的量,与变量一样需要用到内存空间存储。
分类
按数据类型分为:整型常量、浮点型常量、字符型常量、字符串常量、符号常量(宏定义)。
整型常量
整型常量(整数)有十进制、八进制、十六进制三种表示形式:
表示形式
-
十进制常量:
-
组成:数字
0~9,不能以0开头,0本身除外 -
示例:正确写法如:
99、218、0,错误写法如:09、00
-
-
八进制常量:
-
组成:数字
0~7,以0为前缀(不可省略) -
注意:不能表示小数
-
示例:正确写法如:
023、077、00,错误写法如:099
-
-
十六进制常量:
-
组成:数字
0~9+字母a~f/A-F(忽略大小写),以0x或者0X为前缀 -
注意:不能表示小数
-
示例:
0xFFFFFF、0X123、0x0/0x00000000
-
后缀说明
-
长整型常量:在数值后加
L/l,推荐用L -
示例:
long num = 123L;明确指定为长整型,此时的L只起到一个标识作用,数值是123
有符号和无符号
-
有符号(默认):表示范围为
负数 + 0 + 正数,如char的有符号范围-128~127 -
无符号:表示范围为
0 + 正数(内存中负数位用于存储正数,范围更大),如char的无符号范围0~255,需要在类型前添加修饰关键字unsigned -
示例:
unsigned char num = 100;无符号范围:0~255
浮点型常量
浮点型常量(小数/实数)有两种表示形式:
十进制小数形式
-
组成:数字
0~9、小数点.,必须包含小数点(即使整数部分为0或小数部分为0) -
示例:
0.123/0.123F、-12.6、3.0、0.0 -
注意:
-
float类型常量需在末尾添加F/f,默认浮点型常量为double类型。 -
示例:
float f = 0.123F(明确指定为单精度浮点型)
-
指数形式(科学计数法)
-
格式:
数字 e/E 整数指数,e/E表示10的幂次。 -
正数示例:1234.5 → 1.2345*10^3 → 1.2345e3(十进制小数 → 指数形式)
-
负数示例:0.012 → 1.2*10^{-2} → 1.2e-2(十进制小数 → 指数形式)
-
注意:
-
e/E前面必须有数字(1~9) -
e/E后面必须是整数(不能带小数点) -
单精度常量需加
f/F,如1.2e3F
-
字符型常量
普通字符常量
-
格式:用单引号
''括起来的单个字符。英文输入法下的字母、数字、符号,ASCII规范的字符。 -
示例:正确示例,如:
'a','A','4','_','+','\n',错误示例,如:'何',"a" -
本质:硬件存储的是字符对应的ASCII码值,如
a的ASCII码为97
转义字符常量
以反斜杠\开头的特殊的字符序列,用于表示控制字符或不可见字符(如换行符、制表符),常见转义符如下:
| 字符形式 | ASCII码 | 中文解释 | 用途 |
|---|---|---|---|
'\n' |
10 | 换行 | 光标移动至下一行开头 |
'\r' |
13 | 回车 | 光标移动至当前行开头 |
'\0' |
0 | 空字符 | 字符串结束标志(重要) |
'\t' |
9 | 水平制表符 | 相当于按Tab键(8个空格) |
'\\' |
92 | 反斜杠 | 表示字符\ |
'\'' |
39 | 单引号 | 表示字符' |
'\"' |
34 | 双引号 | 表示字符" |
小贴士:在 Linux 终端输入man ascii可查看所有 ASCII 字符及对应码值。
字符串常量
定义
用双引号""括起来的字符序列(可包含多个字符、数字、符号),C语言无内置字符串类型,字符串常量和变量均需要通过字符数字或者字符指针来实现。字符串常量末尾自动添加\0结束标志,但是这个标志是看不见的,也会显示出来。
示例
"How do you do? shenjiamin" // 实际存储:"How do you do? shenjiamin\0" "hello world!" "123" // 实际存储:"123\0" "true" "12.25" "" // 空字符串,仅包含\0
输出方式
通过printf函数的%s格式输出:
printf("%s","hello world!\n"); // 直接输出字符串常量
printf("hello world!\n"); // 等价于上面写法,字符串常量可以不用指定格式直接输出
与字符常量的区别
| 对比项 | 字符常量(如'a') |
字符串常量(如"a") |
|---|---|---|
| 定界符 | 单引号'' |
双引号"" |
| 内存占用 | 1 字节(存储 ASCII 码) | 2 字节('a' + '\0') |
| 结束标志 | 无 | 自动添加\0(字符串结束标志) |
| 表示范围 | 仅单个字符 | 可多个字符或空 |
关键:字符串常量末尾会被系统自动添加
\0,用于标识字符串结束,这是字符串与字符常量的核心区别。
符号常量(宏定义)
定义
通过预处理指令#define定义的标识符,用于代表一个固定常量(预处理阶段会直接替换为宏值)。
优点
-
增强代码可读性(用有意义的标识符代替魔法数字);
-
提升代码可维护性(需修改常量时,仅需修改宏定义,无需逐个修改代码)。
语法格式
#define 宏名称 宏值
-
注意:
① 宏名称与宏值之间用空格分隔;
② 行末不能加分号
;(否则替换时会带入分号,导致语法错误);③ 宏名称命名规范:大写字母,多个单词用下划线
_分隔(如MAX_VALUE)。
示例
#include <stdio.h>
// 定义符号常量:PI(圆周率)、R(半径)
#define PI 3.1415926
#define R 20
int main(int argc, char *argv[])
{
// 计算圆的周长:2 × PI × R(预处理时替换为2 * 3.1415926 * 20)
printf("圆的周长:%f\n", 2 * PI * R); // 2 * 3.1415926 * 20
// 计算圆的面积:PI × R × R
printf("圆的面积:%f\n", PI * R * R);
return 0;
}
附1:编译器
桌面操作系统
对于当前主流桌面操作系统而言,可使用 MSVC、GCC 以及 Clang 这三大编译器。
Visual C++(简称 MSVC)是由微软开发的,只能用于 Windows 操作系统;GCC 和 LLVM Clang 除了可用于 Windows 操作系统之外,主要用于类 Unix(包括 Unix、Linux、macOS 等)操作系统。
像现在很多版本的 Linux 都默认使用 GCC 作为C语言编译器,而像 FreeBSD、macOS 等系统默认使用 LLVM Clang 编译器。由于当前 LLVM 项目主要在 Apple 的主推下发展的,所以在 macOS中,Clang 编译器又被称为 Apple LLVM 编译器。
MSVC 编译器主要用于 Windows 操作系统平台下的应用程序开发,它不开源。用户可以使用 Visual Studio Community 版本来免费使用它,但是如果要把通过 Visual Studio Community 工具生成出来的应用进行商用,那么就得好好阅读一下微软的许可证和说明书了。
而使用 GCC 与 Clang 编译器构建出来的应用一般没有任何限制,程序员可以将应用程序随意发布和进行商用。
MSVC 编译器对 C99 标准的支持十分有限,直到发布 Visual Studio Community 2019,也才对 C11 和 C17 标准做了部分支持。 所幸的是,Visual Studio Community 2017 加入了对 Clang 编译器的支持,官方称之为——Clang with Microsoft CodeGen,当前版本基于的是 Clang 3.8。
C语言从诞生到现在,更新、迭代了多个版本,比如 C99、C11、C17 等。有关这些版本和它们之间的区别,我会在《C语言的四套标准:C89、C99、C11和C17》一文中做详细地介绍。
也就是说,应用于 Visual Studio 集成开发环境中的 Clang 编译器前端可支持 Clang 编译器的所有语法特性,而后端生成的代码则与 MSVC 效果一样,包括像 long 整数类型在 64 位编译模式下长度仍然为 4 个字节,所以各位使用的时候也需要注意。
为了方便描述,本教程后面涉及 Visual Studio 集成开发环境下的 Clang 编译器简称为 VS-Clang 编译器。
嵌入式系统
而在嵌入式系统方面,可用的C语言编译器就非常丰富了,比如:
-
用于 Keil 公司 51 系列单片机的 Keil C51 编译器;
-
当前大红大紫的 Arduino 板搭载的开发套件,可用针对 AVR 微控制器的 AVR GCC 编译器;
-
ARM 自己出的 ADS(ARM Development Suite)、RVDS(RealView Development Suite)和当前最新的 DS-5 Studio;
-
DSP 设计商 TI(Texas Instruments)的 CCS(Code Composer Studio);
-
DSP 设计商 ADI(Analog Devices,Inc.)的 Visual DSP++ 编译器,等等。
通常,用于嵌入式系统开发的编译工具链都没有免费版本,而且一般需要通过国内代理进行购买。所以,这对于个人开发者或者嵌入式系统爱好者而言是一道不低的门槛。
不过 Arduino 的开发套件是可免费下载使用的,并且用它做开发板连接调试也十分简单。Arduino 所采用的C编译器是基于 GCC 的。
还有像树莓派(Raspberry Pi)这种迷你电脑可以直接使用 GCC 和 Clang 编译器。此外,还有像 nVidia 公司推出的 Jetson TK 系列开发板也可直接使用 GCC 和 Clang 编译器。树莓派与 Jetson TK 都默认安装了 Linux 操作系统。
在嵌入式领域,一般比较低端的单片机,比如 8 位的 MCU 所对应的C编译器可能只支持 C90 标准,有些甚至连 C90 标准的很多特性都不支持。因为它们一方面内存小,ROM 的容量也小;另一方面,本身处理器机能就十分有限,有些甚至无法支持函数指针,因为处理器本身不包含通过寄存器做间接过程调用的指令。
而像 32 位处理器或 DSP,一般都至少能支持 C99 标准,它们本身的性能也十分强大。而像 ARM 出的 RVDS 编译器甚至可用 GNU 语法扩展。
下图展示了上述C语言编译器的分类。
附2:进制转换
将二进制、八进制、十六进制转换为十进制
二进制、八进制和十六进制向十进制转换都非常容易,就是“按权相加”。所谓“权”,也即“位权”。
假设当前数字是 N 进制,那么:
-
对于整数部分,从右往左看,第 i 位的位权等于Ni-1
-
对于小数部分,恰好相反,要从左往右看,第 j 位的位权为N-j。
更加通俗的理解是,假设一个多位数(由多个数字组成的数)某位上的数字是 1,那么它所表示的数值大小就是该位的位权。
1) 整数部分
例如,将八进制数字 53627 转换成十进制:
53627 = 5×8^4 + 3×8^3 + 6×8^2 + 2×8^1 + 7×8^0 = 22423(十进制)
从右往左看,第1位的位权为 8^0=1,第2位的位权为 8^1=8,第3位的位权为 8^2=64,第4位的位权为 8^3=512,第5位的位权为 8^4=4096 …… 第n位的位权就为 8^{n-1}。将各个位的数字乘以位权,然后再相加,就得到了十进制形式。
注意,这里我们需要以十进制形式来表示位权。
再如,将十六进制数字 9FA8C 转换成十进制:
9FA8C = 9×16^4 + 15×16^3 + 10×16^2 + 8×16^1 + 12×16^0 = 653964(十进制)
从右往左看,第1位的位权为 16^0=1,第2位的位权为 16^1=16,第3位的位权为 16^2=256,第4位的位权为 16^3=4096,第5位的位权为 16^4=65536 …… 第n位的位权就为 16^{n-1}。将各个位的数字乘以位权,然后再相加,就得到了十进制形式。
将二进制数字转换成十进制也是类似的道理:
11010 = 1×2^4 + 1×2^3 + 0×2^2 + 1×2^1 + 0×2^0 = 26(十进制)
从右往左看,第1位的位权为 2^0=1,第2位的位权为 2^1=2,第3位的位权为 2^2=4,第4位的位权为 2^3=8,第5位的位权为 2^4=16 …… 第n位的位权就为 2^{n-1}。将各个位的数字乘以位权,然后再相加,就得到了十进制形式。
2) 小数部分
例如,将八进制数字 423.5176 转换成十进制:
423.5176 = 4×8^2 + 2×8^1 + 3×8^0 + 5×8^{-1} + 1×8^{-2} + 7×8^{-3} + 6×8^{-4} = 275.65576171875(十进制)
小数部分和整数部分相反,要从左往右看,第1位的位权为 8^{-1}=1/8,第2位的位权为 8^{-2}=1/64,第3位的位权为 8^{-3}=1/512,第4位的位权为 8^{-4}=1/4096 …… 第m位的位权就为 8-m。
再如,将二进制数字 1010.1101 转换成十进制:
1010.1101 = 1×2^3 + 0×2^2 + 1×2^1 + 0×2^0 + 1×2^{-1} + 1×2^{-2} + 0×2^{-3} + 1×2^{-4} = 10.8125(十进制)
小数部分和整数部分相反,要从左往右看,第1位的位权为 2^{-1}=1/2,第2位的位权为 2^{-2}=1/4,第3位的位权为 2^{-3}=1/8,第4位的位权为 2^{-4}=1/16 …… 第m位的位权就为 2-m。
更多转换成十进制的例子:
-
二进制:1001 = 1×2^3 + 0×2^2 + 0×2^1 + 1×2^0 = 8 + 0 + 0 + 1 = 9(十进制)
-
二进制:101.1001 = 1×2^2 + 0×2^1 + 1×2^0 + 1×2^{-1} + 0×2^{-2} + 0×2^{-3} + 1×2^{-4} = 4 + 0 + 1 + 0.5 + 0 + 0 + 0.0625 = 5.5625(十进制)
-
八进制:302 = 3×8^2 + 0×8^1 + 2×8^0 = 192 + 0 + 2 = 194(十进制)
-
八进制:302.46 = 3×8^2 + 0×8^1 + 2×8^0 + 4×8^{-1} + 6×8^{-2} = 192 + 0 + 2 + 0.5 + 0.09375= 194.59375(十进制)
-
十六进制:EA7 = 14×16^2 + 10×16^1 + 7×16^0 = 3751(十进制)
下表列出了前 17 个十进制整数与二进制、八进制、十六进制的对应关系:
| 十进制 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 二进制 | 0 | 1 | 10 | 11 | 100 | 101 | 110 | 111 | 1000 | 1001 | 1010 | 1011 | 1100 | 1101 | 1110 | 1111 | 10000 |
| 八进制 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 |
| 十六进制 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | 10 |
注意,十进制小数转换成其他进制小数时,结果有可能是一个无限位的小数。请看下面的例子:
-
十进制 0.51 对应的二进制为 0.100000101000111101011100001010001111010111...,是一个循环小数;
-
十进制 0.72 对应的二进制为 0.1011100001010001111010111000010100011110...,是一个循环小数;
-
十进制 0.625 对应的二进制为 0.101,是一个有限小数。
二进制和八进制、十六进制的转换
其实,任何进制之间的转换都可以使用上面讲到的方法,只不过有时比较麻烦,所以一般针对不同的进制采取不同的方法。将二进制转换为八进制和十六进制时就有非常简洁的方法,反之亦然。
1) 二进制整数和八进制整数之间的转换
二进制整数转换为八进制整数时,每三位二进制数字转换为一位八进制数字,运算的顺序是从低位向高位依次进行,高位不足三位用零补齐。
八进制整数转换为二进制整数时,思路是相反的,每一位八进制数字转换为三位二进制数字,运算的顺序也是从低位向高位依次进行。
2) 二进制整数和十六进制整数之间的转换
二进制整数转换为十六进制整数时,每四位二进制数字转换为一位十六进制数字,运算的顺序是从低位向高位依次进行,高位不足四位用零补齐。
十六进制整数转换为二进制整数时,思路是相反的,每一位十六进制数字转换为四位二进制数字,运算的顺序也是从低位向高位依次进行。下图演示了如何将十六进制整数 A5D6 转换为二进制:
在C语言编程中,二进制、八进制、十六进制之间几乎不会涉及小数的转换,所以这里我们只讲整数的转换,大家学以致用足以。另外,八进制和十六进制之间也极少直接转换。
更多推荐



所有评论(0)