11.01学习
一、字符串类型
1. 字符串的定义:
在C语言中,字符串被定义为字符数组,以空字符( '\0' )作为结束符。
2. 字符串的表示:
字符串可以用双引号括起来的字符序列表示,例如 "Hello, World!" 。
3. 字符串的存储:
字符串在内存中是连续存储的,每个字符占用一个字节,最后一个字符是空字符 '\0' 。
4. 字符串的输入输出:
使用 printf 和 scanf 函数可以对字符串进行输入输出操作,其中 %s 用于格式化字符串。
5. 字符串处理函数:
C标准库提供了许多处理字符串的函数,如 strcpy (复制字符串)、 strcat (连接字符串)、 strlen (计算字符串长度)、 strcmp (比较字符串)、 strchr (查找字符)、 strstr (查找子字符串)等。
6. 字符串字面量:
字符串字面量是不可修改的,存储在程序的只读数据段中。
7. 字符数组:
可以定义字符数组来存储字符串,例如 char str[10] = "Hello"; 。
8. 字符串的动态分配:
使用 malloc 或 calloc 函数可以在堆上动态分配内存来存储字符串。
9. 字符串的修改:
字符串中的字符可以被修改,因为它们存储在可写的内存区域。
10. 字符串的结束符:
字符串必须以空字符 '\0' 结束,这是C语言识别字符串结束的标志。
11. 字符串的遍历:
可以通过循环遍历字符串中的每个字符,直到遇到空字符 '\0' 。
12. 字符串的内存管理:
动态分配的字符串需要使用 free 函数来释放内存。
13. 字符串的安全性:
在处理字符串时需要注意缓冲区溢出等安全问题。
14. 字符串的比较:
字符串比较是按字符进行的,直到找到不相等的字符或遇到空字符。
15. 字符串的搜索和替换:
可以使用 strchr 、 strstr 等函数搜索字符串,也可以通过循环实现自定义的搜索和替换逻辑。
16. 字符串的分割:
可以使用 strtok 函数来分割字符串,但需要注意线程安全性问题。
17. 宽字符和多字节字符串:
C语言还支持宽字符( wchar_t )和多字节字符( mbstate_t ),用于处理国际化的字符串。
二、取模运算符
1. 操作数要求:
取模运算符 % 的操作数必须是整数类型,包括 int 、 long 、 short 、 char (包括有符号和无符号类型)。
2. 结果类型:
取模运算的结果类型与操作数的类型相同,如果操作数是无符号整数,结果也将是无符号的。
3. 操作数符号:
如果两个操作数都是正数或负数,结果的符号与被除数(左侧操作数)相同。
如果一个操作数是正数,另一个是负数,结果的符号同样与被除数相同。
4. 零作为除数:
在C语言中,取模运算不能以零作为除数,这会导致运行时错误(通常是除以零的异常)。
5. 结果范围:
结果的绝对值总是小于除数的绝对值。
6. 整数除法与取模:
取模运算通常与整数除法一起使用,以获取除法的余数。
7. 表达式求值:
在表达式中,取模运算与其他算术运算一样,遵循运算符优先级规则。
8. 复合赋值:
可以与赋值运算符结合使用,如 a %= b ,这等价于 a = a % b 。
9. 无符号数的取模:
对于无符号数,取模运算的结果总是非负的。
10. 浮点数不支持取模:
取模运算符 % 不能用于浮点数,因为浮点数的除法没有余数的概念。
11. 取模运算的用途:
取模运算常用于检查一个数是否能被另一个数整除(余数是否为0),或者用于循环中实现特定的迭代次数。
12. 位运算与取模:
取模运算与位运算(如按位与 & 、按位或 | 、按位异或 ^ )不同,它不涉及位级别的操作。
13. 编程实践:
在编程时,应确保除数不为零,以避免运行时错误。
14. 编译器优化:
某些编译器可能会对取模运算进行优化,尤其是在循环中使用时。
15. 语言标准:
C语言标准(如C99、C11)定义了取模运算的行为和规则。
三、if多条件语句与多行语句表示
1. 顺序 if 语句: 最简单的方式是将多个 if 语句顺序排列,每个 if 语句检查不同的条件。
if (condition1) {
// 代码块1
} else if (condition2) {
// 代码块2
} else if (condition3) {
// 代码块3
} else {
// 默认代码块
}
2. 嵌套 if 语句: 在一个 if 语句的代码块内部,可以嵌套另一个 if 语句。
if (condition1) {
// 代码块1
if (condition2) {
// 代码块2
}
}
3. if-else if-else 链: 使用 else if 来链式连接多个条件,这样可以在多个条件中寻找第一个为真的条件。
if (condition1) {
// 如果condition1为真,则执行这里的代码
} else if (condition2) {
// 如果condition1为假且condition2为真,则执行这里的代码
} else {
// 如果以上条件都不为真,则执行这里的代码
}
4. 逻辑运算符组合: 使用逻辑运算符( && 和 || )来组合多个条件。
if (condition1 && condition2) {
// 如果condition1和condition2都为真,则执行这里的代码
} else if (condition1 || condition2) {
// 如果condition1或condition2至少一个为真,则执行这里的代码
}
5. switch语句: 如果 if 语句的条件是基于同一个变量的不同值,可以使用 switch 语句来简化代码。
switch (variable) {
case value1:
// 如果variable等于value1,执行这里的代码
break;
case value2:
// 如果variable等于value2,执行这里的代码
break;
// ...
default:
// 如果variable不匹配任何case,执行这里的代码
}
6. 三元运算符: 对于简单的条件,可以使用三元运算符来替代单个 if-else 语句。
result = condition ? value_if_true : value_if_false;
7. 函数封装: 如果多个 if 语句执行相似的操作,可以将这些操作封装到函数中,然后在 if 语句中调用这些函数。
void doSomething(bool condition) {
if (condition) {
// 执行操作
}
}
if (condition1) {
doSomething(true);
} else if (condition2) {
doSomething(false);
}
四、goto相关知识点
1. 基本用法: goto 语句的基本语法是:
goto label;
其中 label 是跳转目标的标签,必须位于同一函数内。
2. 标签定义: 标签定义的语法是:
label:
标签名可以是任意有效的标识符,但通常使用小写字母和下划线组合,以区别于变量名。
3. 跳转限制: goto 语句不能跳转到另一个函数中,也不能跳转到 goto 语句之后的代码块中。
4. 标签作用域: 标签的作用域是整个函数,但 goto 语句不能跳转到包含它的复合语句之外。
5. 常见用途:
跳出多层嵌套的循环:在复杂的循环结构中, goto 可以用来快速跳出所有循环。
错误处理:在检测到错误时, goto 可以用来跳转到错误处理代码块。
菜单和状态机:在处理复杂的菜单或状态机时, goto 可以简化代码结构。
6. 滥用问题: goto 语句的滥用会导致“goto地狱”(spaghetti code),使得代码难以阅读和维护。因此,许多编程风格指南建议尽量避免使用 goto 。
7. 替代方案:
使用函数分解复杂逻辑。
使用循环控制语句(如 break 和 continue )来控制循环的执行。
使用异常处理机制(在支持异常的语言中)。
8. 标签位置: 标签必须位于可执行语句之前,不能放在表达式或声明中。
9. 跨函数跳转: goto 不能用于跨函数跳转,这意味着不能从一个函数跳转到另一个函数。
10. 结构化编程: goto 语句与结构化编程的原则相悖,后者鼓励使用顺序、选择和迭代控制结构,而不是无条件跳转。
11. 编译器优化: 由于 goto 的存在,编译器在优化代码时需要考虑更多的跳转路径,这可能会影响编译器优化的效果。
12. 代码可读性: 使用 goto 可能会降低代码的可读性,因为它打破了代码的自然顺序。
13. 错误处理: 在某些错误处理场景中, goto 可以用于快速跳转到清理资源的代码块,但这种做法仍然存在争议。
14. 调试难度: 使用 goto 的代码在调试时可能更困难,因为程序的执行路径可能不如顺序执行的代码那样直观。