目录

有符号无符号所占的字节是一样的 

常量,define

 typedef

static extern

​编辑​编辑

 #define定义常量和宏

指针 

​编辑 操作系统,网络

system执行系统命令

memset 

ifndef

递归

冒泡排序

单目操作符

逻辑与&& 

隐式类型转换

整型提升 

算术转换

栈区

高地址

数组越界访问


有符号无符号所占的字节是一样的 

常量,define

 

 typedef

static extern

static 局部变量:

  • 只在第一次进入函数时执行初始化

  • 之后不再初始化,而是保留之前的值

  • 生命周期贯穿整个程序运行期(和全局变量一样)

声明一下就可以用了 ,函数的外部链接属性,外部链接属性的意思是,外部的其它源文件,只要想使用这个函数extern声明一下就可以了

 该函数只能在自己的源文件使用。

 #define定义常量和宏

指针 

 操作系统,网络

system执行系统命令

memset 

ifndef

避免头文件被引好多次 ,比如都引了#include<stdio.h>,这个头文件有1000行,都引,那不就是好几千行,重复了

当别人在包含add.h时都会先判断if没有定义它,第一次引add.h头文件时已经定义了,所以别人第二次在引用add.h,#endif前面的代码都不会执行。

防止同一个头文件被重复引用多次。_ADD_H_一般都是根据头文件的名字写的

递归

冒泡排序

单目操作符

逻辑与&& 

左边是假,右边不管是什么都不算了

隐式类型转换

整型提升 

无符号数整型提升补零,有符号数整型提升补符号位。

 

char a = 0xb6;
    short b = 0xb600;
    int c = 0xb6000000;
    if (a == 0xb6) {
        printf("a");
    }
    if(b == 0xb600) {
        printf("b");
    }
    if (c == 0xb6000000) {
        printf("c");
    }

算术转换

 

栈区

高地址

数组越界访问

内存中没有“结构体”的概念

  • 在计算机内存里,只有 一串连续的字节(0 和 1)。

  • 内存里不会自动标记“这里是一个结构体,这里是字段”。

  • 结构体是 C 编译器的一个概念,用来告诉编译器“按某种布局去访问这段连续内存”

例子1:结构体

#include <stdio.h>
#include <string.h>
// 取消结构体对齐
// 否则的话item会对齐到自己最大对齐数的整数倍的地方
#pragma pack(1)
typedef struct {
    int opIdx;
    int mlbType;
} Item;

typedef struct {
    unsigned short cnt;
    Item item[5];
} CellUeInstMlb;
# pragma pack()
int main(void) {
    CellUeInstMlb msg;

    // 模拟从外部收到消息,但cnt=0,表示数组里没有有效元素
    msg.cnt = 0;

    // 内存里虽然有 item 数组空间,但逻辑上没有“有效数据”
    // 打印看看地址关系
	printf("unsigned short所占字节:%d\n", sizeof(unsigned short));
    printf("msg addr = %p\n", &msg);
	// 因为第一个元素相对于结构体的偏移量是0
	printf("msg->cnt addr = %p\n", &((&msg)->cnt));
	// opIdx相对于结构体偏移2个字节,从这个地方开始取出4个字节的数据
    printf("msg.item[0].opIdx addr = %p\n", (void*)&msg.item[0].opIdx);

    // ⚠️ 错误做法:直接访问 item[0]
    printf("错误访问:opIdx = %d\n", msg.item[0].opIdx);  // 未定义行为!

    // ✅ 正确做法:先判断 cnt
    if (msg.cnt > 0) {
        printf("正确访问:opIdx = %d\n", msg.item[0].opIdx);
    } else {
        printf("cnt == 0,跳过访问 item[0]\n");
    }

    return 0;
}
/*
内存确实是连续的(你可以看到 msg 和 msg.item[0] 的地址相邻)。

msg.item[0].opIdx 实际只是“偏移到那个位置去取 4 个字节的数据”。

因为我们没初始化它,所以打印出的是 垃圾值。

这就是“未定义行为”:可能打印垃圾、可能崩溃、也可能看似正常。
*/

C 编译器在编译阶段就知道每个字段的偏移量和大小。
这些信息不是运行时计算的,而是编译时确定的。

运行时并不知道“结构体”这个概念,它只看地址和字节数。

.、->、数组下标 [] 其实都是编译器帮你计算偏移量,然后取对应内存的语法糖

  • .字段 或 ->字段 []都只是编译器帮你做的偏移计算 + 类型转换。

最终都是内存地址 + 偏移量 → 取对应字节

例子2

#include <stdio.h>
#include <string.h>

int main(void) {
    int a[5] = {1, 2, 3, 4, 5};
	for (int i = 0; i <= 5; i++) {
// 然后从该地址读取 4 字节并解释为 int
		printf("%d\n", a[i]);
	}

    return 0;
}

更多推荐