Skip to content

速通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
常量指针为指针p可以改,不能通过p修改所指向的值

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 在 * 右边 → 指针不能改

函数指针

int Fun(int x);
int (*p)(int x);//定义一个函数指针
p=Func;

sizeof的区别

sizeof(数组名) 等于整个数组大小

但前提是:数组名没有发生“退化为指针”

int a[2][3];
sizeof(a)=24;


void f(int a[3][2]) {
    sizeof(a)=4;
}

二维数组传参

行数:可以省

列数:必须给

类型本质: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

sizeof(s)
  • 关注的是:s 占了多少内存
  • 与字符串内容无关
  • 对数组:返回 数组总大小

👉 结果是 10

strlen(s)
  • 关注的是:字符串有多长
  • 逐字符扫描,直到 '\0'
  • 不包含结束符

👉 结果是 5

项目 sizeof strlen
关注点 内存大小 字符串长度
是否函数 ❌(运算符) ✅(函数)
计算时机 编译期(普通数组) 运行期
是否统计 \0
是否依赖内容

sizeof(s) 返回的是字符数组 s 占用的存储空间大小(10 字节), 而 strlen(s) 返回的是字符串中实际字符的个数,不包含结尾的 '\0',因此其值为 5。

空字符串

""//空字符串

'\0'//不是空字符串

代码分析相关

int main(void){
    char ch[10];
    int i;
    for(i=0;i<10;i++){
         printf("%d",ch[i]);
    }
    return 0;
}

这里输出的值为无法确定,因为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++。

int m=20; 
while (m<30)
m=m++;

出现未定义行为

字符串与字符数组

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;//错误
struct student 
{
 int num;
 char name[9];
} stu;

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语言编译过程

预处理->编译->链接

因为是对二进制文件链接,需要先编译