速通C语言理论¶
C语言标识符¶
C 语言标识符 = 字母 / 数字 / 下划线
不能以数字开头
其他符号一律不行
左值右值¶
左值:++i
右值:i++
a=b=c合法可执行:b=c->a=b
(i=j)=1不合法
常量指针和指针常量¶
const int *p //常量(的)指针
int const *p //与上面相同
int a = 10;
int b = 20;
const int *p = &a;
*p = 30; // ❌ 错误:不能通过 p 修改 a
p = &b; // ✅ 正确:p 可以指向 b
int * const p = &a; //指针常量
int a = 10;
int b = 20;
int * const p = &a;
*p = 30; // ✅ 正确:a 被改成 30
p = &b; // ❌ 错误:p 是常量指针,不能改指向
const int *p | ✅ 可以 | ❌ 不可以 |
| 指针常量 | int * const p | ❌ 不可以 | ✅ 可以 |
const 在 * 左边 → 值不能改
const 在 * 右边 → 指针不能改
函数指针¶
sizeof的区别¶
sizeof(数组名) 等于整个数组大小
但前提是:数组名没有发生“退化为指针”
二维数组传参¶
行数:可以省
列数:必须给
类型本质:int (*)[列数]
| 形参声明 | 是否正确 |
|---|---|
int a[][2] |
✅ |
int a[3][2] |
✅ |
int (*a)[2] |
✅ |
int a[][] |
❌ |
int a[][3] |
❌ |
int **a |
❌ |
int a[3][] |
❌ |
指针的数组和数组的指针¶
看 [] 和 * 谁“离变量名更近”
- [ ] 更近 → 指针数组
- *更近(通常要用括号) → 数组指针
| 项目 | 指针数组 | 数组指针 |
|---|---|---|
| 定义 | int *p[3] |
int (*p)[3] |
| 本质 | 数组 | 指针 |
| 元素 | 指针 | 数组 |
sizeof(p) |
3 × sizeof(int*) | sizeof(指针) |
| 常见用途 | 字符串数组 | 二维数组 |
malloc¶
其返回 void*,不需要强制类型转换为合适指针类型。
C语言基本数据类型¶
- 整型
- 实型
- 字符型
合法字符串定义¶
| 定义方式 | 合法 | 常用 | 是否可修改 | 备注 |
|---|---|---|---|---|
char s[] = "abc"; |
✅ | ⭐⭐⭐ | ✅ | 最推荐 |
char s[4] = "abc"; |
✅ | ⭐⭐⭐ | ✅ | 要留 \0 |
char s[] = {'a','b','c','\0'}; |
✅ | ⭐⭐ | ✅ | 本质等价 |
char *s = "abc"; |
✅ | ⭐⭐⭐ | ❌ | 不能改内容 |
char *s; s="abc"; |
❌ | ❌ | — | 非法 |
char s[3] = "abc"; |
❌ | ❌ | — | 少了 \0 |
char s[4]; s="abc"; |
❌ | ❌ | — | 数组不可整体赋值 |
strlen vs sizeof¶
- 关注的是:s 占了多少内存
- 与字符串内容无关
- 对数组:返回 数组总大小
👉 结果是 10
- 关注的是:字符串有多长
- 逐字符扫描,直到
'\0' - 不包含结束符
👉 结果是 5
| 项目 | sizeof | strlen |
|---|---|---|
| 关注点 | 内存大小 | 字符串长度 |
| 是否函数 | ❌(运算符) | ✅(函数) |
| 计算时机 | 编译期(普通数组) | 运行期 |
是否统计 \0 |
✅ | ❌ |
| 是否依赖内容 | ❌ | ✅ |
sizeof(s)返回的是字符数组s占用的存储空间大小(10 字节), 而strlen(s)返回的是字符串中实际字符的个数,不包含结尾的'\0',因此其值为 5。
空字符串¶
代码分析相关¶
这里输出的值为无法确定,因为ch为局部变量,其中的值未初始化
int main(){
char str1[100];
char str2[100];
strcpy(str1,"theseStrings");
strcpy(str2,"thesestrings");
printf("%d\n",strncmp(str1,str2,5));
printf("%d\n",strcmp(str1,str2));
return 0;
}
,第二个printf输出为小于0,注意小写字母的ASCII码更大
#include <stdio.h>
#include <string.h>
int val(char c) {
switch (c) {
case 'I': return 1;
case 'V': return 5;
case 'X': return 10;
case 'L': return 50;
case 'C': return 100;
case 'D': return 500;
case 'M': return 1000;
default: return 0;
}
}
int romanToInt(const char* s) {
int res = 0;
int len = strlen(s);
int i;
for (i = 0; i < len; i++) {
if (i + 1 < len && val(s[i]) < val(s[i+1])) {
res += (val(s[i+1]) - val(s[i]));
//此处需要增加i++才是正确的代码
} else {
res += val(s[i]);
}
}
return res;
}
int main(void) {
char ch[] = "IX";
printf("Input: %s, Output: %d\n", ch, romanToInt(ch));
return 0;
}
在减法规则下,一个字符对(如 IX)会在同一次循环中被一起处理, 若不手动递增 i 跳过下一个字符,则该字符会在下一轮循环中被重复计算, 因此必须在 if 分支中增加 i++。
出现未定义行为
字符串与字符数组¶
char s[] = {'h', 'e', 'l', 'l', 'o'}; 能够正确赋值(作为字符数组),但作为字符串,此定义不合法
char s[]和char*p¶
这里char s[]指向的字符串的内部元素可修改,但后者不可以
结构体相关¶
struct ord {
int x;
int y;
int z;
};
struct ord a;//正确
struct ord {
int x;
int y;
int z;
} a;//正确
struct ord {int x; int y; int z;}struct ord a;//错误
student为结构体名,struct student为结构体类型名,stu为结构体变量
对齐值¶
设 #pragma pack(n) 生效,则:
成员的对齐值
成员对齐值 = min(该成员类型的自然对齐值, n)
结构体整体对齐值
结构体整体对齐值 = min(结构体中最大成员的自然对齐值, n)
⚠️ 注意:
整体对齐值 ≠ sizeof(struct)
整体对齐值只决定 结构体结尾是否要补齐
#pragma pack(2)
struct A {
char c; // 1
int i; // 4
short s; // 2
};
//大小为8
#pragma pack(1)
struct A {
char c;
int i;
short s;
};
//大小为7
位域¶
- 类型可以是int、unsigned int、_Bool,不能是float
- 匿名位域:跳过若干位
- 0宽度位域:占满当前整数并切换到下一个
枚举¶
✅ 能做的
-
枚举常量是 整数
-
可以比较大小
-
可以指定整数值
-
可以多个成员同值
❌ 不能做的
-
不能赋字符串
-
不能赋浮点数
-
不能当真正的强类型用(在 C 中)
C语言编译过程¶
预处理->编译->链接
因为是对二进制文件链接,需要先编译