C 简介
一提到语言这个词语,自然会想到的是像英语,汉语等这样的自然语言,因为它是人和人交换信息不可缺少的工具。而今天计算机普遍了我们生活的每一个角落,除了人和人的相互交流之外,我们必须和计算机交流。就像人类交流依赖自然语言一样,人类与计算机的沟通也需要语言,而编程语言正是这一沟通的桥梁。
C语言作为编程语言中的一种,具有独特的语法规则,既赋予了开发者与计算机"对话"的能力,又确保了计算机能够"无条件地"执行指令。这种交流方式包括消息传递和命令执行,和人类沟通的基本要素类似。
不过,计算机的"听从"与人的交流有所不同,它必须遵循C语言严格的语法和定义,只有这样,计算机才能准确理解并执行人类的意图。这种规则的严谨性也凸显了编程语言的特殊性------它不仅仅是语言,更是逻辑的表达形式,通过简洁、精确的指令,让计算机完成特定的任务。
C语言一经出现就以其功能丰富、表达能力强、灵活方便、应用面广等特点迅速在全世界普及和推广。C语言不但执行效率高而且可移植性好,可以用来开发应用软件、驱动、操作系统等。C语言也是其它众多高级语言的鼻祖语言,所以说学习C语言是进入编程世界的必修课。
经典"Hello World"
C语言编码规范
- 命名规范
- 变量命名:使用有意义的名称,避免使用无意义的单字母(如x、y),除非是循环变量。变量名要表达其功能,如counter、total_sum。
- 大小写约定:通常小写字母用于变量名,宏定义用全大写字母,使用下划线分隔(如MAX_SIZE)。
- 函数命名:函数名应以动词开头,描述其功能,如calculate_sum()、print_data()。
- 缩进和格式
- 一致的缩进:通常采用4个空格或1个Tab作为缩进,保持代码层级清晰。不要混用空格和Tab,以免不同编辑器显示不一致。
- 代码块:即使在if、for、while等语句中只有一行代码,也建议使用花括号{}包裹,避免省略错误。
- 行宽:每行代码不宜过长,通常建议控制在80-100字符以内,以便阅读。
- 注释规范
必要的注释:给函数、复杂逻辑块和变量添加注释,但避免过度注释。注释要言简意赅,描述代码的意图,而不是逐行解释代码。
函数注释:在函数定义前添加注释,说明函数的功能、参数和返回值,如:
cpp
// 计算数组的平均值
// 参数:arr - 数组指针,size - 数组大小
// 返回值:数组的平均值
double calculate_average(int *arr, int size) {
// 函数实现
}
- 函数的良好设计
- 函数单一职责:一个函数只做一件事,保持函数简洁,避免将多种功能混合在一个函数中。
- 适当的函数长度:尽量控制函数长度,建议20-30行左右,过长的函数不利于阅读和维护。
- 参数数量:函数参数数量应尽量控制在3-5个以内。若参数过多,考虑将相关数据打包到结构体中传递。
- 变量声明和初始化
- 局部变量声明靠近使用处:在C语言中,局部变量声明通常靠近其使用位置,保持代码简洁,减少作用域,避免混淆。
- 及时初始化变量:在使用变量前要确保其已初始化,避免因未初始化而引发的未知行为。
变量及赋值
在C语言中,变量是用于存储数据的命名空间。每个变量都有一个数据类型,定义了它能存储的数据种类和占用的内存空间。
变量的定义语法
cpp
类型 变量名 = 初始值;
变量命名规则
变量名由字母、数字和下划线组成,且不能以数字开头。
变量名区分大小写,如count和Count是不同的变量。
变量名不能是C语言的关键字(如int、return等)。
基本数据类型
unsigned修饰符
基本输入输出语句
预处理命令
类型转换
在C语言中,类型转换(Type Casting)是指将一种数据类型的变量转换为另一种数据类型。类型转换分为自动类型转换 和强制类型转换两种方式。
自动类型转换
自动类型转换(隐式类型转换)是由编译器自动完成的,不需要程序员显式指定。当表达式中的不同类型数据参与运算时,编译器会根据一定的规则自动转换数据类型。
自动类型转换的规则
- 从低精度到高精度:小范围数据类型自动转换为大范围数据类型。例如,int类型可以自动转换为float、double等。
- 类型提升顺序:在表达式中,char和short会自动提升为int,而float会提升为double。
- 双目运算符转换:在算术表达式中,如果操作数类型不同,编译器会将较低类型的操作数转换为较高类型后再进行运算。
cpp
#include <stdio.h>
void main() {
int a = 5;
float b = 3.2;
float result = a + b; // a自动转换为float后与b相加,result为float类型
printf("result = %.1f\n", result);
char c = 'A';
int ascii_value = c; // c自动转换为int类型,ascii_value的值为65
printf("ascii_value = %d\n", ascii_value);
}
自动转换带来的精度问题
有时候自动转换可能会导致精度损失。例如,将一个double转换为float时,可能会丢失小数位数。
cpp
#include <stdio.h>
void main() {
double large_value = 12345.6789;
float smaller_value = large_value; // large_value会自动转换为float,导致精度丢失
printf("%.4f\n", smaller_value);// 输出:12345.6787
}
强制类型转换
强制类型转换(显式类型转换)是指程序员通过在值前加上目标类型,将数据类型从一种显式地转换为另一种。强制类型转换在某些特定场合非常有用,例如当需要将一个float转换为int以去除小数部分时。
强制类型转换语法
cpp
(目标类型) 表达式
强制类型转换示例
cpp
float f = 3.14;
int i = (int)f; // 将f强制转换为int类型,结果为3,舍弃小数部分
在上述示例中,变量f被强制转换为int类型,结果为3,丢失了小数部分。
强制类型转换的应用场景
- 舍弃小数部分:将浮点数转换为整数,去除小数部分。
- 避免精度丢失:当低精度类型参与高精度运算时,强制将低精度类型转换为高精度类型,确保计算精度。
- 指针类型转换:在某些情况下,需要将不同类型的指针进行转换。例如,void*指针可以转换为其他类型的指针。
自动类型转换与强制类型转换的区别
特点 | 自动类型转换 | 强制类型转换 |
---|---|---|
执行者 | 编译器自动进行 | 程序员手动进行 |
转换规则 | 由低精度转向高精度 | 无需遵循高低精度规则 |
代码表示 | 不需要额外的语法 | 需要显式地指定目标类型 |
精度控制 | 可能导致精度损失 | 程序员可以控制转换精度 |
适用场景 | 一般用于表达式运算中的隐式转换 | 精度控制、特殊情况 |
运算符号
在C语言中,运算符(Operator)用于执行各种运算,主要分为以下几类:
1. 算术运算符
算术运算符用于基本的数学运算,支持整数和浮点数。
运算符 | 含义 | 示例 | 结果 |
---|---|---|---|
+ |
加 | a + b |
两数相加 |
- |
减 | a - b |
两数相减 |
* |
乘 | a * b |
两数相乘 |
/ |
除 | a / b |
两数相除 |
% |
取模(余数) | a % b |
整数除的余数 |
注意 :
/
运算符在整数除法时只保留整数部分,%
运算符仅适用于整数。
2. 赋值运算符
赋值运算符用于将右边的值赋给左边的变量。
运算符 | 含义 | 示例 | 等价形式 |
---|---|---|---|
= |
赋值 | a = b |
- |
+= |
加后赋值 | a += b |
a = a + b |
-= |
减后赋值 | a -= b |
a = a - b |
*= |
乘后赋值 | a *= b |
a = a * b |
/= |
除后赋值 | a /= b |
a = a / b |
%= |
取模后赋值 | a %= b |
a = a % b |
3. 关系运算符
关系运算符用于比较两个值,结果是布尔值(0
表示假,1
表示真)。
运算符 | 含义 | 示例 |
---|---|---|
== |
等于 | a == b |
!= |
不等于 | a != b |
> |
大于 | a > b |
< |
小于 | a < b |
>= |
大于等于 | a >= b |
<= |
小于等于 | a <= b |
4. 逻辑运算符
逻辑运算符用于对布尔表达式进行逻辑运算,返回布尔值。
运算符 | 含义 | 示例 | 结果 |
---|---|---|---|
&& |
逻辑与(AND) | a && b |
两者都为真时为真 |
` | ` | 逻辑或(OR) | |
! |
逻辑非(NOT) | !a |
取反,真变假,假变真 |
5. 位运算符
位运算符直接操作二进制位,通常用于底层编程或性能优化。
运算符 | 含义 | 示例 | 结果 |
---|---|---|---|
& |
位与(AND) | a & b |
两个位都为1时为1 |
` | ` | 位或(OR) | `a |
^ |
位异或(XOR) | a ^ b |
位不同为1,相同为0 |
~ |
位取反(NOT) | ~a |
0变1,1变0 |
<< |
左移 | a << 1 |
左移1位,相当于乘以2 |
>> |
右移 | a >> 1 |
右移1位,相当于除以2 |
6. 条件运算符(三元运算符)
条件运算符是简化的if-else结构,根据条件判断返回两个值中的一个。
语法:
cpp
条件 ? 表达式1 : 表达式2;
示例:
cpp
int a = 10, b = 20;
int max = (a > b) ? a : b; // 如果a > b,max为a,否则为b
7. 自增和自减运算符
自增和自减运算符用于对变量进行增减操作,有前置和后置两种形式。
运算符 | 含义 | 示例 | 结果 |
---|---|---|---|
++ |
自增 | a++ 或 ++a |
增加1 |
-- |
自减 | a-- 或 --a |
减少1 |
前置和后置的区别:
- 前置 (如
++a
):先增减,再参与表达式运算。 - 后置 (如
a++
):先参与表达式运算,再增减。
8. sizeof 运算符
sizeof运算符用于返回变量或数据类型的字节大小。
cpp
int a;
printf("%zu", sizeof(a)); // 输出a的字节大小
printf("%zu", sizeof(int)); // 输出int类型的字节大小
运算符优先级比较
优先级别为1的优先级最高,优先级别为10的优先级别最低。
判断与循环语句
goto语句
goto语句在C语言中是一种控制流语句,允许程序跳转到指定位置继续执行。尽管它在特定场景中能简化代码,但一般不推荐使用,因为滥用goto会导致代码难以维护,降低可读性。
语法
cpp
goto label;
// 代码块
label:
// 跳转目标位置的代码
- label 是一个标识符,用来标记跳转位置,通常以冒号 : 结尾。
- goto label; 语句会直接跳转到 label: 所在的位置并继续执行代码。
使用示例
cpp
#include <stdio.h>
int main() {
int i = 0;
start: // 定义标签
if (i < 5) {
printf("i = %d\n", i);
i++;
goto start; // 跳转到标签 start
}
printf("循环结束\n");
return 0;
}
在这个示例中,程序从 goto start; 跳回到 start: 标签位置,形成一个简单的循环,直到 i 达到5。
使用场景
虽然goto语句一般不推荐使用,但在某些情况下它能简化处理,比如:
- 错误处理:特别是在需要释放资源的代码中,goto可以帮助减少重复代码。
- 多层嵌套跳出:在多重循环或条件语句中快速跳出。
函数详解
值调用(call by value)方式和引用调用(call by reference)
c语言中值调用(call by value)方式和引用调用(call by reference)_值调用和引用调用