内存函数与数据存储

C语言—内存的管理和释放 C语言—内存的管理和释放一、Linux下内存分配管理1.编译好的C程序文件分区2.C程序运行时内存分区3.为什么要进行内存分配4.内存分配方式二、详解堆和栈1.堆和栈的简介2.堆和栈的区别三、内存管理函数四、其它知识1.(嵌入式开发中)程序编译完成后的大小统计2.数据存储区域实例3.字符串与字符数组 一、Linux下内存分配管理 1.编译好的C程序文件分区 分析C语言下程序的内存分配,我们从一段简单C程序源码test.c开始。如下所示: #include <stdio.h> #include 阅读详情

内存函数与数据存储 —— 从 memcpy 翻车到整型提升,一次讲透

1. 一句话总结

内存操作函数(memcpy / memmove / memset)的本质是按字节搬运,理解这一点就能解释为什么 memset 给 int 数组赋值会翻车,以及为什么 memmove 要用"前前后后"的策略处理重叠内存;而数据存储部分的核心是:一切数据在内存中都是二进制,解读方式(有无符号、大小端、整型提升)决定了最终的输出结果。


2. 知识地图

内存函数与数据存储
├── 内存操作函数(<string.h>)
│   ├── memcpy  —— 内存拷贝(不处理重叠)
│   │     └── 实现:逐字节拷贝,src → dest 从前向后
│   ├── memmove —— 内存拷贝(处理重叠)
│   │     └── 实现:"前前后后"策略,根据 dest 和 src 的相对位置
│   │           选择拷贝方向
│   ├── memset  —— 内存初始化(按字节设置)
│   │     └── 陷阱:给 int 数组赋非 0 值会出错(原因见下文)
│   └── memcmp  —— 内存比较(逐字节比较)
│
└── 数据存储规则
    ├── 大小端字节序
    │     ├── 小端:低位存低地址(x86 架构,常见)
    │     └── 大端:高位存低地址(网络字节序)
    ├── signed / unsigned char 的区别
    │     └── char 是否有符号取决于编译器(VS 上是有符号的)
    └── 整型提升(Integer Promotion)
          ├── 触发条件:表达式计算中小于 int 的类型
          ├── 有符号:高位补符号位
          └── 无符号:高位补 0

关系梳理:

  • memcpy 和 memmove 是"兄弟函数",功能一样但 memmove 更安全(能处理重叠)。
  • memset 之所以给 int 赋非 0 值出错,根本原因在于它按字节操作,这和"数据存储"中整型多字节表示直接相关。
  • 理解整型提升必须先理解 signed/unsigned char 的存储差异,而要理解存储差异又必须先知道补码规则。
  • 大小端字节序是理解"数据在内存中长什么样"的基础,和 memcpy 的逐字节拷贝有直接联系——拷贝时你能看到字节的排列顺序。

3. 核心概念深度解析

3.1 memmove 的"前前后后"策略 —— 内存重叠的优雅解法

问题场景: 在同一块内存中做拷贝,比如把 arr[0..4] 拷贝到 arr[2..6],源和目标有重叠区域。

内存布局(假设 arr 从地址 0x100 开始,每个 int 占 4 字节):

arr[0]  arr[1]  arr[2]  arr[3]  arr[4]  arr[5]  arr[6]
 1       2       3       4       5       6       7
 |_______________|
       src (20字节)

                 |_______________|
                       dest (从 arr[2] 开始)

          重叠区域 ↓
         arr[2] arr[3] arr[4]

错误做法(memcpy 式从前向后拷贝):

Step 1: dest[0] = src[0]  →  arr[2] = 1   结果: [1, 2, 1, 4, 5, 6, 7]
Step 2: dest[1] = src[1]  →  arr[3] = 2   结果: [1, 2, 1, 2, 5, 6, 7]
Step 3: dest[2] = src[2]  →  arr[4] = ?   此时 src[2] 已经被 Step 1 覆盖成 1 了!
                                           本该拷贝 3,结果拷贝了 1

memmove 的策略 —— 判断方向:

情况1:dest < src(目标在源前面)
  ┌──── src ────┐
  ┌──── dest ────┐
  从前往后拷贝,安全 ✓

情况2:dest > src(目标在源后面,本次案例)
  ┌──── src ────┐
        ┌──── dest ────┐
  从后往前拷贝,安全 ✓

ASCII 图解"从后往前"为什么安全:

原始数组: [1, 2, 3, 4, 5, 6, 7, 8]
                ↑src[0..4]  要把 1,2,3,4,5 拷到 arr[2..6]

从后往前拷贝(拷贝 5 个 int = 20 字节):

Step 1: dest[4] = src[4]  →  arr[6] = 5   [1, 2, 3, 4, 5, 6, 5, 8]
Step 2: dest[3] = src[3]  →  arr[5] = 4   [1, 2, 3, 4, 5, 4, 5, 8]
Step 3: dest[2] = src[2]  →  arr[4] = 3   [1, 2, 3, 4, 3, 4, 5, 8]
Step 4: dest[1] = src[1]  →  arr[3] = 2   [1, 2, 3, 2, 3, 4, 5, 8]
Step 5: dest[0] = src[0]  →  arr[2] = 1   [1, 2, 1, 2, 3, 4, 5, 8]

最终结果: [1, 2, 1, 2, 3, 4, 5, 8]  ✓ 正确!

关键:先拷贝后面的元素,这些元素还没被覆盖,所以源数据始终完好。

核心代码逻辑:

if ((char *)dest < (char *)src) {
    // dest 在 src 前面 → 从前往后
    while (num--) {
        *(char *)dest = *(char *)src;
        dest = (char *)dest + 1;
        src  = (char *)src  + 1;
    }
} else {
    // dest 在 src 后面 → 从后往前
    while (num--) {
        *((char *)dest + num) = *((char *)src + num);
    }
}

我的理解:*((char *)dest + num) 这种写法从后往前拷贝,利用了 num-- 后置递减的特性,每次循环 num 已经减 1,所以下标从 num-1 慢慢降到 0,天然实现倒序遍历。


3.2 大小端字节序 —— 同一个数字在内存中的两种"长相"

一句话定义: 大小端描述的是多字节数据在内存中的字节排列顺序。

假设 int n = 0x12345678(十六进制),共 4 个字节:

内存地址:  低地址 ←──────────────→ 高地址
          0x100    0x101    0x102    0x103

大端模式(Big Endian)—— 高位存低地址:
          0x12     0x34     0x56     0x78
          高位字节 ──────────────→ 低位字节
          "人的阅读顺序" = 内存存储顺序

小端模式(Little Endian)—— 低位存低地址:
          0x78     0x56     0x34     0x12
          低位字节 ──────────────→ 高位字节
          "人的阅读顺序" 与 内存存储顺序 相反

为什么关注这个?

  • 网络传输统一用大端(网络字节序),x86 机器用小端,所以 htonl / ntohl 这类转换函数很重要。
  • 用 memcpy 逐字节拷贝时,你能"看到"字节序。
  • 理解指针强转:*(char*)&n 取的是低地址那个字节,在小端机器上就是最低位字节。

判断大小端的两种方法:

// 方法1:指针强转法
int check_sys() {
    int n = 1;                // 0x00000001
    return *(char*)&n;        // 取低地址那个字节
    // 小端:低地址存 0x01 → 返回 1
    // 大端:低地址存 0x00 → 返回 0
}

// 方法2:联合体法(更优雅)
int check_sys() {
    union {
        int i;
        char x;
    } s;
    s.i = 1;       // 0x00000001
    return s.x;    // i 和 x 共享低地址起始位置
    // 小端:x 读到 0x01 → 返回 1
    // 大端:x 读到 0x00 → 返回 0
}

我的理解:联合体法和指针强转法本质一样,都是"从同一个起始地址只读第一个字节"。联合体的写法更安全,因为它不涉及指针类型转换,语义更清晰——“我用两种视角看同一块内存”。


3.3 整型提升 —— 小于 int 的类型参与运算时的"隐形转换"

触发条件: 当 char / short 等小于 int 的类型参与表达式计算(如 printf 的参数传递、算术运算)时,编译器会先自动把它们提升为 int。

提升规则:

有符号类型(signed char / signed short):
  高位补符号位(符号扩展)
  例:char a = -1;
      内存中(截断后):11111111
      符号位 = 1
      提升后:11111111 11111111 11111111 11111111  ← 高位全补 1

无符号类型(unsigned char / unsigned short):
  高位补 0(零扩展)
  例:unsigned char c = -1;
      内存中(截断后):11111111
      提升后:00000000 00000000 00000000 11111111  ← 高位全补 0

完整追踪 char a = -1 的存储和打印过程:

第1步:写出原码
  -1 的原码:10000000 00000000 00000000 00000001

第2步:转补码(计算机用补码存储)
  反码:     11111111 11111111 11111111 11111110
  补码:     11111111 11111111 11111111 11111111

第3步:截断(char 只有 8 位)
  内存中:   11111111

第4步:整型提升(打印时,%d 要求 int,触发提升)
  有符号 char,符号位 = 1
  提升后:   11111111 11111111 11111111 11111111  ← 符号扩展

第5步:转回原码打印(%d 打印有符号十进制)
  补码 → 原码:10000000 00000000 00000000 00000001
  结果:-1

对比 unsigned char c = -1:

第1~3步同上,截断后:11111111

第4步:整型提升
  无符号 char,高位补 0
  提升后:   00000000 00000000 00000000 11111111  ← 零扩展

第5步:打印(unsigned 类型原反补相同)
  原码就是:00000000 00000000 00000000 11111111
  结果:255

ASCII 图解整个过程:

  char a = -1
  ┌─────────────────────────────────────────────────────┐
  │ 原码  10000000 00000000 00000000 00000001           │
  │ 补码  11111111 11111111 11111111 11111111           │
  │                         │                           │
  │                   截断(取低8位)                      │
  │                         ↓                           │
  │ 内存   [11111111] ← char 型变量 a 占 1 字节           │
  │                         │                           │
  │              整型提升(%d 打印,需要 int)              │
  │              有符号 → 高位补符号位 1                    │
  │                         ↓                           │
  │ 提升   11111111 11111111 11111111 11111111(补码)    │
  │                         │                           │
  │                  转原码供 printf 输出                  │
  │                         ↓                           │
  │ 输出  -1                                            │
  └─────────────────────────────────────────────────────┘

  unsigned char c = -1
  ┌─────────────────────────────────────────────────────┐
  │ 内存   [11111111] ← 同样存 11111111                  │
  │                         │                           │
  │              整型提升(无符号 → 高位补 0)              │
  │                         ↓                           │
  │ 提升   00000000 00000000 00000000 11111111(原码=补码)│
  │                         │                           │
  │ 输出   255                                          │
  └─────────────────────────────────────────────────────┘

关键认知:同样的二进制 11111111,解释成 signed char 是 -1,解释成 unsigned char 是 255。数据本身没有意义,解读方式赋予它意义。


4. 代码实战

4.1 my_memmove —— 完整实现

#include <stdio.h>
#include <assert.h>

/**
 * 模拟实现 memmove —— 安全的内存拷贝(处理重叠)
 * @param dest  目标内存起始地址
 * @param src   源内存起始地址
 * @param num   要拷贝的字节数
 * @return      目标内存起始地址
 */
void *my_memmove(void *dest, const void *src, size_t num)
{
    void *ret = dest;
    assert(dest && src);

    if ((char *)dest < (char *)src)
    {
        // 情况1:dest 在 src 前面 → 从前往后拷贝
        while (num--)
        {
            *(char *)dest = *(char *)src;
            src  = (char *)src  + 1;
            dest = (char *)dest + 1;
        }
    }
    else
    {
        // 情况2:dest 在 src 后面(或相等)→ 从后往前拷贝
        while (num--)
        {
            *((char *)dest + num) = *((char *)src + num);
        }
    }
    return ret;
}

int main()
{
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8};
    int sz = sizeof(arr) / sizeof(arr[0]);

    my_memmove(arr + 2, arr, 20);  // 把前5个元素拷贝到从第3个开始的位置

    for (int i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
    // 输出:1 2 1 2 3 4 5 8
    return 0;
}

4.2 my_memcpy —— 基础版本(不处理重叠)

/**
 * 模拟实现 memcpy —— 基础内存拷贝(不处理重叠)
 * 注意:当 src 和 dest 指向同一块内存且有重叠时,结果不可预期
 */
void *my_memcpy(void *dest, const void *src, size_t num)
{
    void *ret = dest;
    assert(dest && src);

    while (num--)
    {
        *(char *)dest = *(char *)src;   // 逐字节拷贝
        dest = (char *)dest + 1;        // 泛型指针不能直接 ++,
        src  = (char *)src  + 1;        // 必须强转为 char* 再前进
    }
    return ret;
}

4.3 memset 的正确打开方式 vs 错误用法

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

int main()
{
    // ✅ 正确用法:操作 char 数组(或任何单字节类型)
    char arr[] = "hello world";
    memset(arr + 2, 'y', 7);
    printf("%s\n", arr);  // 输出:hey yyyyyy
    // 从 arr[2] 开始,连续 7 个字节都设为 'y'

    // ❌ 错误用法:给 int 数组赋非 0 值
    int arr2[5] = {1, 2, 3, 4, 5};
    memset(arr2, 2, 20);  // 20 字节 = 5 个 int
    for (int i = 0; i < 5; i++)
    {
        printf("%d ", arr2[i]);
    }
    // 输出:33686018 33686018 33686018 33686018 33686018
    // 而不是期望的 2 2 2 2 2!

    return 0;
}

为什么 int 数组赋值会出错? 看字节级分析:

memset(arr2, 2, 20) 做的事情:
  把 arr2 的前 20 个字节,每个字节都设为 0x02

内存布局(小端,每个 int 4 字节):
  arr2[0]: 0x02 0x02 0x02 0x02  → 解释为 int = 0x02020202 = 33686018
  arr2[1]: 0x02 0x02 0x02 0x02  → 同上
  ...

想要的是每个 int = 2,即内存中应该是:
  arr2[0]: 0x02 0x00 0x00 0x00  ← 这才表示 int 值 2

例外:memset(arr, 0, sizeof(arr)) 将 int 数组清零是安全的,因为 0x00000000 每个字节都是 0。

4.4 判断大小端

#include <stdio.h>

// 方法1:指针强转法
int check_sys_v1()
{
    int n = 1;
    return *(char *)&n;  // 取低地址字节
}

// 方法2:联合体法
int check_sys_v2()
{
    union {
        int i;
        char x;
    } s;
    s.i = 1;
    return s.x;
}

int main()
{
    int ret = check_sys_v1();
    if (ret == 1)
        printf("小端\n");
    else
        printf("大端\n");

    return 0;
}

4.5 signed / unsigned char 整型提升对比

#include <stdio.h>

int main()
{
    char a = -1;           // 取决于编译器,VS 上等价于 signed char
    signed char b = -1;    // 明确有符号
    unsigned char c = -1;  // 明确无符号

    printf("%d %d %d\n", a, b, c);
    // 输出:-1 -1 255

    return 0;
}

4.6 char 类型范围与循环陷阱

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

int main()
{
    char a[1000];
    int i;

    for (i = 0; i < 1000; i++)
    {
        a[i] = -1 - i;
    }
    // i=0:  -1
    // i=1:  -2
    // ...
    // i=127: -128
    // i=128: 127   ← char 范围是 -128~127,-129 溢出变成 127!
    // i=129: 126
    // ...
    // i=254: 1
    // i=255: 0     ← 这里 a[255] = 0,即 '\0'
    // i=256: -1
    // ... 循环往复

    printf("%zd\n", strlen(a));  // 输出:255
    // strlen 遇到 '\0'(ASCII 码 0)就停止计数

    return 0;
}

char 值的"轮回"图:

-1 → -2 → -3 → ... → -128 → 127 → 126 → ... → 1 → 0 → -1 → ...
                         ↑ 溢出翻转点

踩坑:signed char 的范围是 -128 到 127,总共 256 个值。当算术结果超出这个范围时,会发生回绕(wrap-around),而不是报错。这一点在写循环边界条件时尤其要小心。


5. 注释提炼

5.1 知识注解类 —— 扩展科普

memcpy 为什么不处理内存重叠?

C 标准明确规定:如果 src 和 dest 指向的内存区域有重叠,memcpy 的行为是未定义的(undefined behavior)。这不是 bug,而是设计取舍——memcpy 假定两块内存是独立的,从而可以用更激进的方式优化(比如 SIMD 指令批量拷贝)。如果你不确定是否重叠,请用 memmove。

为什么 memcpy / memmove 的参数是 void *?

泛型指针 void * 是 C 语言实现"泛型"的经典手段。它表示"指向任意类型的指针",可以接收任何指针类型而不需要强转。但代价是 void * 不能解引用,也不能做指针算术运算,所以函数内部必须强转为 char * 来逐字节操作。

为什么 memset 按字节设置?

这是 C 标准库的设计哲学:内存操作函数统一以字节为最小单位,保证通用性。memset 不在乎你传的是 int 数组还是 char 数组还是结构体,它只认字节。这种设计简单通用,但需要使用者自己理解多字节类型的存储方式。

5.2 个人理解类

我的理解:memcpy 和 memmove 的关系,可以类比 strcpy 和 strncpy。前者是"快但不安全"的版本,后者是"安全但稍慢"的版本。工程中,拿不准就用 memmove,性能差异微乎其微。

我的理解:void * 指针就像快递员——他只负责把包裹从 A 点送到 B 点,不关心包裹里装的是什么。char * 的强转则是把包裹拆成"每 1 字节一个小件",这样快递员才知道每次搬多少。

我的理解:整型提升被很多人忽视,但它其实是 C 语言隐式转换体系的基石。理解它之后,char c = 128; printf("%d", c); 输出 -128 这种"反直觉"结果就变得可解释了。

5.3 踩坑感悟类

⚠️ 踩坑 1:memset 给 int 数组赋非 0 值

int arr[5];
memset(arr, 1, sizeof(arr));  // 以为每个元素是 1,实际是 0x01010101 = 16843009

教训: memset 只能安全地用于:

  • 清零:memset(arr, 0, sizeof(arr))(无论什么类型)
  • 赋值为 0xFF(即 -1):memset(arr, 0xFF, sizeof(arr))(每个字节都是 0xFF,int 解释为 -1)
  • 操作 char 类型数组

⚠️ 踩坑 2:memcpy 在重叠内存上翻车

int arr[] = {1, 2, 3, 4, 5};
memcpy(arr + 2, arr, 12);  // 未定义行为!实际结果不可预测

教训: 只要源和目标在同一块内存中且 dst > src,就用 memmove。宁可多打几个字,也不要赌编译器行为。

⚠️ 踩坑 3:char 是有符号还是无符号取决于编译器

char a = 0xFF;  // 在 VS 上 a = -1,在 ARM 编译器上可能是 255

教训: 需要明确符号时,永远用 signed char 或 unsigned char,不要裸写 char。跨平台代码中这一点尤其重要。

5.4 对比总结类

函数用途处理重叠操作单位返回值
memcpy内存拷贝不支持字节dest 起始地址
memmove内存拷贝支持字节dest 起始地址
memset内存初始化不适用字节dest 起始地址
memcmp内存比较不适用字节差值(<0, 0, >0)
类型存储 -1 后的值整型提升规则提升后(32位)printf(“%d”)
signed char11111111高位补符号位0xFFFFFFFF-1
unsigned char11111111高位补 00x000000FF255
字节序低地址存什么0x12345678 在内存中常见场景
小端低位字节78 56 34 12x86/x64 PC
大端高位字节12 34 56 78网络传输、部分嵌入式

6. 代码修正

修正 1:check_sys 判断逻辑错误

修改前:

int ret = check_sys(n);
if (ret == 1)
    printf("小端\n");
else
    printf("小端\n");  // ← BUG:两个分支都打印"小端"

修改后:

int ret = check_sys_v1();
if (ret == 1)
    printf("小端\n");
else
    printf("大端\n");  // ← 修正:第二个分支打印"大端"

原因: 显然是复制粘贴导致的笔误,但这类错误在测试时很容易被忽略——因为 x86 机器上永远走 if 分支,else 分支根本不会被执行到。写这种判断逻辑时,建议两个分支的输出字符串差异要足够明显,便于一眼发现问题。

修正 2:被注释的 check_sys() 与调用处参数不匹配

原代码:

// 定义了一个无参的 check_sys()(联合体版本,被注释掉了)
// int check_sys()
// {
//     union S { ... } s;
//     ...
// }

// 但又定义了一个有参的 check_sys(int n)(指针版本)
// int check_sys(int n)
// {
//     return *(char*)&n;
// }

// main 中调用:
int n = 1;
int ret = check_sys(n);  // 调用的是有参版本

说明: 联合体版本不需要参数(内部直接 s.i = 1),指针版本需要参数。原代码两个版本混在一起,应该统一使用一个版本。推荐使用联合体版本,因为它不依赖外部传参,更加内聚。


7. 小结

内存函数的本质是"按字节搬运",数据存储的本质是"同一段二进制可以有不同的解读方式"——把这两条线串起来,memcpy、memmove、memset、大小端、整型提升就都不再是孤立的零散知识点,而是一套关于"数据如何在计算机中表示和流动"的完整认知体系。

32、Erlang 模块功能与使用指南 本文详细介绍了多个Erlang模块的功能和使用方法,涵盖时间计算、数据存储、图形处理、日志记录、代码处理等方面。每个模块都提供了常用函数和操作步骤,并附有示例代码,帮助开发者快速掌握Erlang编程技巧。同时,文章还讨论了资源管理、错误处理和性能优化等注意事项,并展示了模块组合使用的实际场景。 阅读详情

相关推荐

WKWebView 白屏问题,WKWebView request body丢失问题

1,WKWebView 白屏问题 WKWebView 自诩拥有更快的加载速度,更低的内存占用,但实际上 WKWebView 是一个多进程组件,Network Loading 以及 UI Rendering 在其它进程中执行。初次适配 WKWebView 的时候,我们也惊讶于打开 WKWebView 后,App 进程内存消耗反而大幅下降,但是仔细观察会发现,Other Process 的内存占用会...

weixin_37543822的博客 736

C语言:内存函数与数据存储详解

内存函数memcpy用于非重叠拷贝,memmove可处理重叠,memset初始化内存,memcmp比较内存。数据存储:整数用补码,浮点数按IEEE 754标准,大小端影响多字节数据的存储顺序。理解这些知识,能帮助开发者避免内存错误,优化程序性能,并深入理解计算机底层工作原理。

2503_90358233的博客 1474

前端开发学习--登录和注册页面设计(html和css,可完成两个页面点击切换)

今天学习了登录和注册页面的设计,主要是HTML和CSS部分,注册和切换采用了Js代码进行,后面将通过js进行填写内容的验证,进而可通过JSP跳转到Servlet进行DAO数据的校验,这属于后端部分了。 总体设计思路:整个页面设计可以分为主体(content)、头部(content-header)、内容(content-body)三个部分组成,其中是主体是根部,包含了头部和内容,头部和内容各自下设两...

weixin_43691879的博客 1万+

C语言内存函数与数据存储

是C语言标准库中的一个函数,<string.h>,用于内存块之间的复制注意:函数memcpy从src的位置开始向后复制n个字节的数据到dest指向的内存位置。这个函数在遇到'\0'的时候并不会停下来。如果src和dest有任何的重叠,复制的结果都是未定义的。模拟实现(字节)

LBX_0309的博客 954

SAP SD核心流程解析:从销售订单到财务结算的实战指南

本文深入解析SAP SD模块从销售订单到财务结算的核心流程,重点剖析了销售订单中的行项目类别如何作为流程的“总开关”,决定了后续交货、开票及财务集成的走向。文章结合实战案例,详细拆解了正常销售、退货、第三方销售、寄售等典型流程的配置要点与国内化适配方案,并阐述了SD与财务模块集成的关键连接点,为顾问和用户提供了一份全面的实战指南。

weixin_29250403的博客 426

ESP32-S3 SPI接口实战:从引脚分配到高速通信配置

本文详细解析了ESP32-S3的SPI接口配置与高速通信优化技巧,从引脚分配到软件配置,再到DMA传输和时钟优化,帮助开发者快速掌握SPI通信技术。特别针对ESP32-S3的SPI2接口进行了实战演示,包括硬件连接和常见问题排查,适用于各类嵌入式开发项目。

weixin_29189373的博客 679

字符函数、字符串函数、内存函数与数据存储相关知识

字符/字符串函数:注意边界处理和溢出问题内存函数:优先使用memmove处理重叠内存数据存储:理解补码和大小端对编程调试至关重要浮点数:IEEE 754标准解析需结合二进制结构掌握这些知识点,能有效提升C语言编程的底层理解能力!

zhou061025的博客 996

电路板保险丝如何选型

在电路板保险丝选型过程中,需要考虑多个因素以确保其适合特定的电路需求。最后,经过以上程序选定的样品需要在实际的电路中进行测试,以验证所选择的保险丝是否合适。

weixin_57010826的博客 1310

C语言数据存储与内存函数篇

本篇博客到这里就结束了,希望能够帮到各位刚接触C语言的小萌新们,下篇博客将继续讲解C语言的相关知识。

2401_87845450的博客 836

TI硬件笔试面试题型解析上

摘要: 本专栏聚焦大厂硬件笔试面试题解析,当前第14期以德州仪器(TI)为例,涵盖模拟电路与数字电路核心考点。模拟电路部分涉及最大功率传递定理、运放设计(虚短/虚断)、共模抑制比(CMRR)及高频PCB信号完整性;数字电路包括CMOS与TTL对比、建立/保持时间、竞争冒险消除、D触发器分频及“线与”逻辑实现。文章强调通过例题掌握解题方法与知识点,助力读者应对硬件工程岗位的笔试与面试挑战,附常见考点总结与实用建议。全文注重理论与实践结合,提供典型题型解析与设计思路。(150字)

启芯硬件的博客 951

【光子 AI 】LangGraph:Graph = 有向有环图 + 状态机实现原理详解:数据结构模型与核心算法代码实现逻辑解析

LangGraph核心原理解析:基于Pregel模型的消息传递图,结合有向有环图和状态机特性。关键实现包括:1) 共享状态机制(TypedDict/Pydantic模型)配合Reducer逻辑实现状态更新;2) 图拓扑结构通过邻接表维护节点和条件边;3) 检查点机制支持状态持久化和恢复。运行时采用Pregel循环算法,通过超级步迭代执行节点函数、应用状态归约、条件路由下一批节点,同时支持人工中断和异步并行处理。该架构使LangGraph具备循环执行、条件跳转等DAG无法实现的动态流程控制能力。

AI天才研究院 1035

【C语言】C 语言内存函数与数据存储深度解析

C语言内存操作函数与数据存储机制 本文系统介绍了C语言四大内存操作函数:memcpy(非重叠内存复制)、memmove(支持重叠复制)、memset(内存初始化)和memcmp(内存比较),并给出了模拟实现。深入解析了数据存储机制:整数以补码形式存储,浮点数遵循IEEE754标准,以及大小端字节序问题。通过典型例题演示了字符类型存储差异和无符号循环陷阱,最后总结了面试常见考点,包括函数区别、存储规则和边界问题处理。掌握这些底层知识对理解C语言内存管理和系统编程至关重要。

2501_92613722的博客 791

CAN总线介绍及硬件设计

1. CAN是什么 CAN 是 Controller Area Network 的缩写(以下称为 CAN),是 ISO*1 国际标准化的串行通信协议。 在当前的汽车产业中,出于对安全性、舒适性、方便性、低公害、低成本的要求,各种各样的电子控制系统 被开发了出来。由于这些系统之间通信所用的数据类型及对可靠性的要求不尽相同,由多条总线构成的情况很 多,线束的数量也随之增加。为适应“减少线束的数量”、“......

sinat_15677011的博客 9992

数据内存储存函数与【数据存储(一部分)】

是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,存储模式将地址的高低和数据位权有效地结合起来。,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,存储模式类似把数据当作字符串顺序处理。因不知道数据类型所以用void*,可以接收任何类型,num限制拷贝长度,返回类型为void*因不知道数据类型所以用void*,可以接收任何类型,num限制拷贝长度,返回类型为void*返回值为大于0的数,等于0,小于0的数,若为字符串比较,则比较每个字符。

lhxcc_fly的博客 1423

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

【C语言内存操作函数与数据存储详解】

本文详解memcpy/memmove/memset/memcmp模拟实现,剖析大小端、整型提升、浮点存储,并配有经典例题,助你彻底掌握C内存核心知识。

2501_93219710的博客 734

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

C语言知识点 -- 数据存储、字符串与内存操作函数

数据存储、字符串函数和内存操作函数

kissland96166的博客 354

C语言内存函数和数据在内存的存储

掌握补码与字节序机制,避免跨平台数据传输错误。:始终确保操作的内存区域在合法范围内。(GCC/Clang):编译时加入。(网络传输、Java虚拟机)处理不确定内存关系的场景。:检测内存泄漏、越界访问。:数据高位字节存于低地址。:数据低位字节存于低地址。与循环初始化的适用场景。(x86/ARM架构)

2302_79462679的博客 523

09、Python函数执行过程及销毁内存分析

Python函数调用及销毁在内存中的分析

totorobig的博客 778

关于将数据写入文件的两个函数fwrite()与fprintf()的大不同 —————— 开开开山怪

fwrite()与fprintf()的大不同 1.用法 fwrite(void *buffer, sizeof(数据类型), 数据类型的数量, FILE *file); fprintf(FILE *file, 格式符, data); 2.fwrite()函数的详解 重中之重:fwrite()写的是内存印象图 fwrite()函数在进行数据写入文件的时候,其本质是在写内存印象图,将数据写入内存...

String_guai 2255

数据存储,分配

数据存储,分配 大小端模式 大端:低地址存高字节,高地址存低字节 小端:低地址存低字节,高地址存高字节 我们常用的x86 DSP ARM 为小端模式,而KEIL c51为大端模式 在程序中,对变量的数据通过地址访问时,则应该考虑大小端,而直接通过变量名字访问(读取,写入)时,则不必考虑,是对数据本身的值的操作 整型数据: 在内存中以补码的形式存储,处理器·只会加法操作 补码: 正数的补码,反码,...

weixin_43381265的博客 301

【C++语言】动态内存管理

动态内存管理的方法:malloc/calloc/realloc/free 以及new/delete的用法

认真分享博客,认真分享学习 976

动态内存分配函数

本文介绍了C语言中内存动态分配的概念和实现方法。动态分配允许程序在运行时按需申请内存,数据临时存放在堆区,只能通过指针引用。系统提供四个主要函数:malloc分配指定大小空间,calloc为数组分配连续空间,realloc调整已分配空间大小,free释放内存空间。这些函数都声明在stdlib.h头文件中,使用时需包含该文件。动态分配机制提供了灵活的内存管理方式,可根据程序需求随时申请和释放内存空间。

m0_57853854的博客 880

回写与回调函数

在代码中,**回写(Write Back)**是一种数据存储策略,特别是在磁盘I/O操作中。回写是指数据首先被写入到内存(如Cache)中,然后异步地从内存写入到磁盘。这种策略通过暂时将数据存储在内存中,解决了程序计算速度和数据存储速度不匹配的问题,从而提高了系统的性能和吞吐量。通过这种方式,系统可以在数据尚未完全写入磁盘的情况下继续处理其他任务,从而提高了整体的响应速度和效率。是一种数据存储策略,用于优化磁盘I/O操作的性能。1、数据被写入到内存(如Cache)。2、内存中的数据被标记为“已写入”。

encoding-console的博客 501

MATLAB基础--内存的使用和程序调试和优化

内存管理函数 emory:显示可用内存 hos:显示给工作区分配了多少内存 pack:把已经存在的变量保存到磁盘 clear:从内存中删除变量 save:有选择的把变量保存到磁盘 load:把已经保存的数据文件用load函数重新载入。 quit:退出MATLAB,并返回所有分配的内存到系统中。 怎么高效使用内存 压缩内存的使用 1.仅导入需要的文件大小 2.以块为单位处理数据 3.避免建立较大的临时数组 4.使用嵌套函数来传递数据 使用适当的数据存储方式 适当的数据类型 读入文件时选择适当的数据类型(

weixin_44441263的博客 1334

Simulink自动代码生成5——控制函数原型(control function prototype)

simulink可对生成的代码做深度定制化,主要从以下几个方面: 控制函数原型(function prototypes) 函数接口复用(reuse function interface) 数据存储管理(storage class & memory section) 函数模板(function templates) 下面一一说明: 控制函数原型(function prototypes) 默认设置下,3个入口函数和为以下形式 且输入输出和非内联变量会以全局结构体的形式被函数访问。 函数名管理 但

IHTY_NUI的博客 4581

【C语言】c语言内存函数及大小端字节序

介绍c语言内存函数中的memcpy函数,memmove函数,memset函数,memcmp函数,和数据储存大小端字节序

yj20040627的博客 1650
上一篇: 操作符的深入理解学习并实操
下一篇: 万字解析动态内存从编译到运行
DDXYcoder
博客等级 码龄1年 140粉丝 29原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值