变量命名
命名规范
- 只能由字母(含
"_"
)和数字组成; - 首字母不能是数字;
- 不能与关键字重名,例如
float
、double
和if
等。
注意事项
- 不使用
"_"
开头,因为很多库函数这样命名,容易冲突; - 区分大小写;
- 变量名使用小写字母,符号常量使用大写字母;
- 局部变量使用短变量名,全局变量使用长变量名。
数据类型
基本数据类型:
- char 单字节
- short 一般为双字节
- int 一般为四个字节
- long 一般为八个字节
- float 单精度
- double 双精度
常量
整型常量:
- 1234 int类型
- 1234L或1234l long类型
浮点型常量:
- 123.4或1e-2(0.01)double类型
- 123.4f或123.4F float类型
- 123.4L long double类型
八进制和十六进制常量:
- 037 八进制
- 0x1f或0X1F 十六进制
字符常量:
- ‘0’
常用符号常量表:
举例:编写一个统计字符串的字符数的函数。
int strlen(char str[]) {
int i;
i = 0;
while (str[i] != '\0')
++i;
return i;
}
枚举常量:
int main() {
enum boolean {NO = 0, YES = 1};
enum boolean flag = NO;
printf("%d\n", flag);
}
变量声明
第一种方式:
int lower, upper, step;
char c, line[1000];
第二种方式:
int lower;
int upper;
int step;
char c;
char line[1000];
第三种方式,声明式赋值:
char esc = '\\';
int i = 0;
int limit = MAXLINE+1;
float eps = 1.0e-5;
使用const
关键字声明不变量:
const double e = 2.71828182845905;
const char msg[] = "warning: ";
// 还用用在函数的参数上
int strlen(const char[]);
算术运算符
二元运算符:
- + 加
- - 减
- * 乘
- / 除
- % 取模
举例:判断年份是否为闰年。
int main() {
int year = 2011;
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
printf("%d is a leap year\n", year);
else
printf("%d is not a leap year\n", year);
}
关系和逻辑运算符
关系运算符:
> >= < <= == !=
注意:因为算术运算符优先级高于关系运算符,所以i < lim - 1
会被视作i < (lim - 1)
。
逻辑运算符:
- && 且
- || 或
- ! 取反
类型转换
隐式类型转换(自动类型转换):
- 当不同数据类型的操作数参与运算时,编译器会自动将它们转换为同一类型,以便于执行运算。
- 转换规则通常是基于操作数的数据范围和精度,较小的数据类型会被转换为较大的数据类型。
- 例如,char 类型和 int 类型相加时,char 类型通常会被转换为 int 类型。
显式类型转换(强制类型转换):
- 使用强制类型转换运算符 (type) 来明确指定将一个类型的值转换为另一种类型。
- 程序员需要在代码中明确写出转换的类型,以确保转换的意图是清晰的。
- 例如,(int)3.14 将 double 类型的 3.14 转换为 int 类型,结果是 3。
举例
将字符串转换为整数。
#include <stdio.h> // 引入标准输入输出库,用于printf函数
// 函数声明:将接收一个字符串s,并返回其数值形式
int atoi(char s[]);
int main(void) {
char s[] = "1234"; // 定义一个字符串数组s,并初始化为"1234"
printf("%d\n", atoi(s)); // 调用atoi函数,并将结果打印出来
}
// 函数定义:实现字符串转换为整数
int atoi(char s[]) {
int i, n; // 定义循环计数器i和数值n
n = 0; // 初始化数值n为0
// 循环遍历字符串s,直到遇到非数字字符或字符串结束
for (i = 0; s[i] >= '0' && s[i] <= '9'; i++) {
// 将字符转换为数值并累加到n上
n = 10 * n + (s[i] - '0');
}
return n; // 返回计算得到的数值
}
在循环中,通过 s[i] - ‘0’ 将字符转换为相应的整数值。例如,字符 ‘5’ 转换为整数 5。
练习
- 编写一个函数 htoi(s),将一串十六进制数字(包括可选的 0x 或 0X)转换为其等效的整数值。
#include <stdio.h> // 引入标准输入输出库,用于printf函数
// 函数声明:将接收一个表示十六进制数的字符串s,并返回其数值形式
int htoi(char s[]);
int main(void) {
char s[] = "0x1af"; // 定义一个字符串数组s,并初始化为"0x1af"
printf("%d\n", htoi(s)); // 调用htoi函数,并将结果打印出来
}
// 函数定义:实现十六进制字符串转换为十进制整数
int htoi(char s[]) {
int i, n; // 定义循环计数器i和数值n
n = 0; // 初始化数值n为0
// 从字符串的第三个字符开始循环遍历(跳过前缀"0x")
for (i = 2;
(s[i] >= '0' && s[i] <= '9') ||
(s[i] >= 'a' && s[i] <= 'z') ||
(s[i] >= 'A' && s[i] <= 'Z');
i++) {
// 根据字符在ASCII表中的位置计算数值
switch(s[i]) {
case 'a': case 'A': // 对小写和大写的'a'或'A'进行处理
n = 16 * n + 10; // 十六进制的'a'或'A'对应十进制的10
break;
case 'b': case 'B': // 对小写和大写的'b'或'B'进行处理
n = 16 * n + 11; // 以此类推
break;
// ... 其他字符的处理
case 'f': case 'F':
n = 16 * n + 15; // 十六进制的'f'或'F'对应十进制的15
break;
default: // 对数字0-9的处理
n = 16 * n + (s[i] - '0'); // 直接转换
break;
}
}
return n; // 返回计算得到的数值
}
增量和减量运算符
前缀递增/递减
- 先将变量的值加一/减一,然后返回新值。
后缀递增/递减
- 先返回变量的值,然后变量值加一/减一。
举例
- 从字符串中移除所有的指定字符。
#include <stdio.h> // 引入标准输入输出库,用于printf函数
// 函数声明:从字符串s中删除所有出现的字符c
void squeeze(char s[], int c);
int main() {
char s[] = "Hello world!"; // 定义一个字符串数组s,并初始化为"Hello world!"
squeeze(s, 'l'); // 调用squeeze函数,删除s中的所有'l'字符
printf("%s\n", s); // 打印修改后的字符串s
}
// 函数定义:从字符串s中删除所有出现的字符c
void squeeze(char s[], int c) {
int i, j; // 定义循环计数器i和用于跟踪新字符串结束位置的j
// 使用for循环和i作为计数器,j用于重写修改后的字符串
for (i = j = 0; s[i] != '\0'; i++) {
// 如果当前字符不是要删除的字符c
if (s[i] != c) {
// 将当前字符复制到s[j]的位置,然后递增j
s[j++] = s[i];
}
}
// 在新字符串的末尾添加空字符,标记字符串结束
s[j] = '\0';
}
- 把一个字符串拷贝另一个字符串末尾。
#include <stdio.h> // 引入标准输入输出库,用于printf函数
// 函数声明:将字符串t连接到字符串s的末尾
void StrCat(char s[], char t[]);
int main() {
char s[100] = "Hello world!"; // 定义一个足够大的字符数组s,并初始化为"Hello world!"
char t[] = " nihao shijie"; // 定义一个字符串数组t,并初始化为" nihao shijie"
StrCat(s, t); // 调用StrCat函数,将t连接到s的末尾
printf("%s\n", s); // 打印连接后的字符串s
}
// 函数定义:将字符串t连接到字符串s的末尾
void StrCat(char s[], char t[]) {
int i, j; // 定义循环计数器i和j
i = j = 0; // 初始化计数器i和j为0
// 使用while循环找到s字符串的结束位置
while (s[i] != '\0')
i++;
// 从s的末尾开始,将t字符串的每个字符复制到s中
while ((s[i++] = t[j++]) != '\0')
; // 空循环体,直到t字符串结束
}
练习
- 编写 squeeze(s1,s2) 的另一个版本,删除 s1 中与字符串 2 中的字符匹配的每个字符。
#include <stdio.h> // 引入标准输入输出库,用于printf函数
// 函数声明:从字符串s中删除所有在字符串t中出现的字符
void squeeze(char s[], char t[]);
int main() {
char s[] = "Hello world!"; // 定义一个字符串数组s,并初始化为"Hello world!"
char t[] = " lo"; // 定义一个字符串数组t,并初始化为" lo"
squeeze(s, t); // 调用squeeze函数,删除s中所有在t中出现的字符
printf("%s\n", s); // 打印修改后的字符串s
}
// 函数定义:从字符串s中删除所有在字符串t中出现的字符
void squeeze(char s[], char t[]) {
int i, j, z; // 定义循环计数器i, j和用于跟踪新字符串结束位置的z
// 外层循环:遍历字符串t中的每个字符
for (i = 0; t[i] != '\0'; i++) {
z = 0; // 重置z为0,准备构建新的字符串
// 内层循环:遍历字符串s中的每个字符
for (j = 0; s[j] != '\0'; j++) {
// 如果s[j]不等于t[i],则将其复制到s的前面位置
if (s[j] != t[i]) {
s[z++] = s[j]; // 复制字符,并递增z
}
}
// 内层循环结束后,在新字符串的末尾添加空字符
s[z] = '\0';
}
}
- 编写函数 any(s1,s2),返回字符串 s1 中第一个出现字符串 s2 中任意字符的位置,如果 s1 不包含 s2 中的字符,则返回 -1。
#include <stdio.h> // 引入标准输入输出库,用于printf函数
// 函数声明:检查字符串t中的任意字符是否出现在字符串s中
int any(char s[], char t[]);
int main() {
char s[] = "Hello world!"; // 定义一个字符串s,并初始化为"Hello world!"
char t[] = "l"; // 定义一个字符串t,并初始化为"l"
// 调用any函数,检查t中的字符是否出现在s中,并打印返回的索引或-1
printf("%d\n", any(s, t));
}
// 函数定义:检查字符串t中的任意字符是否出现在字符串s中
int any(char s[], char t[]) {
int i, j; // 定义循环计数器i和j
// 外层循环:遍历字符串t中的每个字符
for (i = 0; t[i] != '\0'; i++) {
// 内层循环:遍历字符串s中的每个字符
for (j = 0; s[j] != '\0'; j++) {
// 如果在s中找到与t[i]匹配的字符,则返回当前索引j
if (s[j] == t[i])
return j; // 返回匹配字符在s中的索引
}
}
// 如果遍历完t中的所有字符都没有找到匹配项,则返回-1
return -1;
}
三元表达式
z = (a > b) ? a : b; /* z = max(a, b) */