嵌入式C语言:大小端详解

目录

一、大小端的概念

1.1. 大端序(Big-endian)

1.2. 小端序(Little-endian)

二、大小端与硬件体系的关系

2.1. 大小端与处理器架构

2.2. 大小端与网络协议

2.3. 大小端对硬件设计的影响

三、判断系统的大小端方式

3.1. 方法一:使用联合体(Union)

3.2. 方法二:使用指针强制类型转换

3.3. 方法三:宏定义法

3.4. 方法四:利用标准库函数(特定平台)

四、大小端转换的方法

4.1. 手动位操作

4.2. 使用联合体(Union)

4.3. 使用指针和数组

4.4. 使用标准库函数(如果可用)

4.5. 位操作宏定义法

五、大小端对嵌入式系统的影响

六、注意事项

6.1. 数据存储和读取

6.2. 数据类型转换

6.3. 结构体和联合体的使用

6.4. 跨平台开发

七、总结

八、参考文献


在嵌入式C语言中,大小端(Endianness)是一个重要的概念,它涉及到多字节数据在内存中的存储顺序。

一、大小端的概念

大小端,又称端序(Endianness),是指多字节数据在内存中的存储顺序。对于单字节数据,不存在存储顺序的问题,因为每个字节都是独立的。但是,对于多字节数据(如int、float、double等),就需要考虑字节的存储顺序了。

1.1. 大端序(Big-endian)

  • 高位在前大端序将数据的最高有效字节(MSB,Most Significant Byte)存储在内存的最低地址处,而最低有效字节(LSB,Least Significant Byte)则存储在最高的地址处
  • 特点:符合人类阅读习惯的表示方法,因为人类通常从左到右(即从高位到低位)阅读数字。
  • 示例:对于int num = 0x12345678,在大端模式下,在内存中的存储顺序为:
| 内存地址(低 -> 高)| 存储内容 |
|----|----|
|0x00|0x12|
|0x01|0x34|
|0x02|0x56|
|0x03|0x78|

1.2. 小端序(Little-endian)

  • 低位在前:数也称小端序或小字节序。与大端相反,数据的低位字节存于低地址,高位字节存于高地址。如同从右到左(低位在前)存储字节。
  • 特点:在某些处理器架构中,小端序可以简化指令集和内存访问操作,因为最低有效字节通常更容易被处理器直接访问。
  • 示例:对于int num = 0x12345678,在小端模式下,在内存中的存储顺序为:
| 内存地址(低 -> 高)| 存储内容 |
|----|----|
|0x00|0x78|
|0x01|0x56|
|0x02|0x34|
|0x03|0x12|

二、大小端与硬件体系的关系

大小端与硬件体系的关系十分密切,不同的处理器和平台可能会采用不同的大小端方式。

2.1. 大小端与处理器架构

  • x86系列处理器:多数x86系列的处理器,如Intel和AMD的处理器,通常采用小端序。意味着在这些处理器上运行的系统和数据存储也倾向于使用小端方式。
  • 其他处理器架构:与x86系列不同,一些其他处理器架构可能采用大端序。例如,PowerPC处理器通常使用大端序。因此,在这些平台上运行的系统和数据存储也相应地遵循大端方式。

2.2. 大小端与网络协议

  • TCP/IP协议网络协议中的字节序与处理器的大小端方式并不总是一致。例如,TCP/IP协议规定使用大端序来解析数据,被称为网络字节序。这种规定是为了确保数据在网络传输过程中的一致性和兼容性。
  • 转换需求:由于网络字节序与某些处理器的大小端方式可能不同,因此在数据通过网络传输时,发送方需要将主机字节序转换为网络字节序(大端序),而接收方在收到数据后则需要将网络字节序转换回自己的主机字节序以便正确解析。

2.3. 大小端对硬件设计的影响

  • 寄存器设计:在硬件设计中,寄存器的位序定义可能与大小端方式有关。例如,在大端模式下,寄存器的最高有效位(MSB)可能定义在较低的地址上,而在小端模式下则相反。

  • 总线设计:数据总线和小端外设的连接也需要考虑大小端方式。例如,当大端处理器访问小端外设时,可能需要进行一定的处理以确保数据的一致性。

  • 软件兼容性:在处理器系统中,如果存在大端和小端模式同时存在的情况,可能会对软件和硬件的设计带来额外的挑战。软件开发者需要编写能够处理不同端模式的代码,而硬件设计师则需要在寄存器、指令集、数据总线等方面进行相应的调整。

三、判断系统的大小端方式

在嵌入式C语言中,可以通过编写程序来判断当前系统的大小端方式。以下是一个简单的判断方法。

3.1. 方法一:使用联合体(Union)

联合体是一种特殊的数据结构,它允许在相同的内存位置存储不同类型的数据。利用联合体的这一特性,可以方便地判断系统的大小端方式。

#include <stdio.h>

int main() {
    union {
        unsigned int i;
        unsigned char c[sizeof(unsigned int)];
    } test_union;

    test_union.i = 0x12345678; // 设定一个已知的整数值

    if (test_union.c[0] == 0x78) {
        printf("The system is Little Endian!\n");
    } else {
        printf("The system is Big Endian!\n");
    }

    return 0;
}

定义了一个联合体test_union,它包含一个无符号整型i和一个字符数组c。我们将一个已知的整数值0x12345678赋给i,然后检查c[0]的值。如果c[0]等于0x78,说明系统的最低有效字节存储在最低的内存地址处,即系统是小端序;否则,系统是大端序。 

3.2. 方法二:使用指针强制类型转换

另一种常用的方法是使用指针强制类型转换。我们可以将一个整数的地址转换为字符指针,然后检查该指针所指向的第一个字节的值。

#include <stdio.h>
#include <stdint.h>

int main() {
    uint32_t x = 0x12345678;
    char *c = (char*)&x;

    if (*c == 0x78) {
        printf("This system is little-endian.\n");
    } else if (*c == 0x12) {
        printf("This system is big-endian.\n");
    } else {
        printf("Unable to determine endianness.\n");
    }

    return 0;
}

 

我们创建了一个32位的无符号整数x,并将其地址转换为一个字符指针c。然后,我们检查*c的值来判断系统的大小端方式。

3.3. 方法三:宏定义法

可以通过预处理器指令来定义一个宏,在编译时根据不同的大小端模式进行不同的操作。示例代码如下:

#include <stdio.h>

#if defined(__BIG_ENDIAN__)
#define ENDIAN "大端"
#elif defined(__LITTLE_ENDIAN__)
#define ENDIAN "小端"
#else
#define ENDIAN "未知"
#endif

int main() {
    printf("当前系统是 %s 模式\n", ENDIAN);
    return 0;
}

3.4. 方法四:利用标准库函数(特定平台)

在某些平台上,可以利用标准库函数来判断系统的大小端方式。例如,在POSIX兼容的系统上,可以使用htons(Host TO Network Short)和ntohl(Network TO Host Long)等函数来检查系统的字节序。然而,这种方法依赖于特定的平台和库,因此并不是所有系统都适用。

四、大小端转换的方法

在嵌入式系统开发、网络编程等场景中,当涉及不同大小端模式设备之间的数据交互时,就需要进行大小端转换。

4.1. 手动位操作

通过手动位操作来交换字节顺序,这是最直接的方法。对于16位、32位和64位的数据类型,可以分别编写转换函数。

#include <stdio.h>
#include <stdint.h>

// 16位数据大小端转换
uint16_t swap_endian_16(uint16_t x) {
    return (x >> 8) | (x << 8);
}

// 32位数据大小端转换
uint32_t swap_endian_32(uint32_t x) {
    return ((x >> 24) & 0x000000FF) |
           ((x >>  8) & 0x0000FF00) |
           ((x <<  8) & 0x00FF0000) |
           ((x << 24) & 0xFF000000);
}

// 64位数据大小端转换
uint64_t swap_endian_64(uint64_t x) {
    return ((x >> 56) & 0x00000000000000FFULL) |
           ((x >> 40) & 0x000000000000FF00ULL) |
           ((x >> 24) & 0x0000000000FF0000ULL) |
           ((x >>  8) & 0x00000000FF000000ULL) |
           ((x <<  8) & 0x000000FF00000000ULL) |
           ((x << 24) & 0x0000FF0000000000ULL) |
           ((x << 40) & 0x00FF000000000000ULL) |
           ((x << 56) & 0xFF00000000000000ULL);
}

int main() {
    // 测试16位数据转换
    uint16_t num16 = 0x1234;
    uint16_t swapped16 = swap_endian_16(num16);
    printf("16位数据: 原始值 0x%04x, 转换后 0x%04x\n", num16, swapped16);

    // 测试32位数据转换
    uint32_t num32 = 0x12345678;
    uint32_t swapped32 = swap_endian_32(num32);
    printf("32位数据: 原始值 0x%08x, 转换后 0x%08x\n", num32, swapped32);

    // 测试64位数据转换
    uint64_t num64 = 0x123456789ABCDEF0ULL;
    uint64_t swapped64 = swap_endian_64(num64);
    printf("64位数据: 原始值 0x%016llx, 转换后 0x%016llx\n", num64, swapped64);

    return 0;
}

 

swap_endian_16swap_endian_32 和 swap_endian_64 三个函数分别用于对 16 位、32 位和 64 位无符号整数进行大小端转换。

使用 %04x%08x 和 %016llx 格式化输出 16 位、32 位和 64 位的十六进制数,确保输出的结果长度符合预期。 

4.2. 使用联合体(Union)

联合体允许在同一内存位置存储不同类型的数据。通过联合体,可以方便地访问和修改数据的不同字节。

#include <stdint.h>
#include <stdio.h>

// 使用联合体实现32位数据的大小端转换
uint32_t swap_endian_32_union(uint32_t x) {
    union {
        uint32_t i;
        uint8_t c[4];
    } u;
    u.i = x;
    uint32_t swapped = (u.c[0] << 24) | (u.c[1] << 16) | (u.c[2] << 8) | u.c[3];
    return swapped;
}

int main() {
    // 初始化一个32位无符号整数用于测试
    uint32_t num = 0x12345678;

    // 调用大小端转换函数
    uint32_t swapped_num = swap_endian_32_union(num);

    // 输出原始值和转换后的值
    printf("原始的32位数据: 0x%08x\n", num);
    printf("转换后的32位数据: 0x%08x\n", swapped_num);

    return 0;
}

 

swap_endian_32_union 函数:定义了一个匿名联合体,该联合体包含两个成员:一个 32 位无符号整数 i 和一个包含 4 个 8 位无符号整数的数组 c。由于联合体的所有成员共享同一块内存空间,所以可以通过不同的方式来访问这块内存。

  • 将输入的 32 位无符号整数 x 赋值给联合体的 i 成员。
  • 通过位运算和移位操作,将数组 c 中的每个字节重新组合成一个新的 32 位无符号整数 swapped,实现了大小端的转换。

4.3. 使用指针和数组

通过指针和数组也可以实现大小端转换,这种方法与联合体类似,但更加直接地操作内存。

#include <stdint.h>
#include <stdio.h>

// 使用指针实现32位数据的大小端转换
uint32_t swap_endian_32_pointer(uint32_t x) {
    uint8_t *bytes = (uint8_t *)&x;
    uint32_t swapped = (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3];
    return swapped;
}

int main() {
    // 定义一个32位无符号整数用于测试
    uint32_t original_num = 0x12345678;

    // 调用大小端转换函数
    uint32_t swapped_num = swap_endian_32_pointer(original_num);

    // 输出原始值和转换后的值
    printf("原始32位数据: 0x%08x\n", original_num);
    printf("转换后32位数据: 0x%08x\n", swapped_num);

    return 0;
}

 

swap_endian_32_pointer 函数:接收一个 uint32_t 类型的参数 x,表示需要进行大小端转换的 32 位无符号整数。

  • 通过将 x 的地址强制转换为 uint8_t * 类型的指针 bytes,这样就可以将 32 位整数按字节访问。
  • 利用位运算和移位操作,将 bytes 数组中的每个字节重新组合成一个新的 32 位无符号整数 swapped,实现了大小端的转换。 

4.4. 使用标准库函数(如果可用)

在某些编译器和系统库中,提供了专门用于大小端转换的函数。例如,在 GCC 编译器中,有htonshtonlntohsntohl等函数,主要用于网络编程中主机字节序和网络字节序(大端序)之间的转换:

  • htons:将 16 位无符号整数从主机字节序转换为网络字节序。
  • htonl:将 32 位无符号整数从主机字节序转换为网络字节序。
  • ntohs:将 16 位无符号整数从网络字节序转换为主机字节序。
  • ntohl:将 32 位无符号整数从网络字节序转换为主机字节序。
#include <stdio.h>
#include <arpa/inet.h>

int main() {
    uint16_t num16 = 0x1234;
    uint32_t num32 = 0x12345678;

    uint16_t swapped16 = htons(num16);
    uint32_t swapped32 = htonl(num32);

    printf("16位原始数据: 0x%x, 转换后: 0x%x\n", num16, swapped16);
    printf("32位原始数据: 0x%x, 转换后: 0x%x\n", num32, swapped32);

    return 0;
}

4.5. 位操作宏定义法

使用宏定义可以更方便地实现大小端转换,尤其是在需要频繁进行转换的代码中。以下是 32 位数据转换的宏定义示例:

#include <stdio.h>
#include <stdint.h>

#define SWAP32(x) \
    ((((x) & 0xFF000000) >> 24) | \
     (((x) & 0x00FF0000) >> 8) | \
     (((x) & 0x0000FF00) << 8) | \
     (((x) & 0x000000FF) << 24))

int main() {
    uint32_t num = 0x12345678;
    uint32_t swapped = SWAP32(num);
    printf("原始数据: 0x%x, 转换后: 0x%x\n", num, swapped);
    return 0;
}

 

五、大小端对嵌入式系统的影响

在嵌入式系统中,大小端的影响主要体现在以下几个方面:

  • 数据通信:当嵌入式系统与外部设备或网络进行通信时,需要确保数据的大小端方式与通信协议一致。如果不一致,可能需要进行大小端转换。
  • 文件存储:当嵌入式系统将数据存储在文件中时,需要考虑文件的大小端方式。如果文件将在不同大小端方式的系统上读取,可能需要进行转换。
  • 跨平台开发:在跨平台开发中,不同平台可能采用不同的大小端方式。因此,开发者需要注意数据的大小端问题,并确保在不同平台上都能正确处理和存储数据。

六、注意事项

6.1. 数据存储和读取

  • 明确数据存储方式:要清楚所使用的硬件平台是大端还是小端模式。比如,PowerPC 架构通常是大端模式,而 x86 架构一般是小端模式。不同的模式下,数据在内存中的存储顺序不同。
  • 避免数据错误:在对多字节数据(如 intfloat 等)进行存储和读取时,要考虑大小端的影响。例如,在小端模式下,int num = 0x12345678,实际在内存中存储的顺序是 0x780x560x340x12。如果按照大端模式的思维去读取,就会得到错误的数据。

6.2. 数据类型转换

  • 注意字节顺序:当进行不同数据类型之间的转换时,特别是涉及到跨平台或与外部设备交互时,要确保大小端的一致性。例如,将一个 int 类型转换为 char 数组时,在小端模式下,低字节先存储,转换后数组中的元素顺序与大端模式下是相反的。
  • 使用合适的转换函数:对于一些需要进行大小端转换的情况,要使用正确的转换函数。比如,网络编程中,常需要在主机字节序和网络字节序(大端)之间进行转换,可以使用 htonlhtonsntohlntohs 等函数,以确保数据在不同环境下的正确传输和处理。

6.3. 结构体和联合体的使用

  • 结构体成员对齐:在结构体中,成员的存储顺序和对齐方式可能会受到大小端的影响。编译器会根据目标平台的大小端模式和结构体成员的类型来进行内存对齐,以提高访问效率。但这可能导致在不同大小端平台上结构体的内存布局不同。例如:
struct {
    char a;
    int b;
    short c;
} myStruct;

在小端模式下,假设 char 占 1 字节,short 占 2 字节,int 占 4 字节,且内存对齐为 4 字节,那么 myStruct 的成员在内存中的布局可能是 a 占 1 字节,然后填充 3 字节达到 4 字节对齐,接着是 b 的 4 字节,最后是 c 的 2 字节。而在大端模式下,布局可能不同。

  • 联合体的特性:联合体可以利用其所有成员共享同一块内存的特性来判断当前平台的大小端模式。例如: 
union {
    uint32_t i;
    uint8_t c[4];
} u;

u.i = 0x12345678;
if (u.c[0] == 0x78) {
    printf("小端模式\n");
} else {
    printf("大端模式\n");
}

6.4. 跨平台开发

  • 编写可移植代码:在进行跨平台开发时,要编写能够适应不同大小端模式的代码。可以通过条件编译等方式,根据不同的平台定义来选择合适的大小端处理方式。例如:
#if defined(__BIG_ENDIAN__)
// 大端模式下的代码
#elif defined(__LITTLE_ENDIAN__)
// 小端模式下的代码
#else
#error "未知的字节序"
#endif
  • 数据一致性:确保在不同大小端平台之间传输和处理的数据具有一致性。在进行数据交换时,要进行必要的大小端转换,以保证数据的正确解读。

七、总结

综上所述,大小端是嵌入式C语言中一个重要的概念,它涉及到多字节数据在内存中的存储顺序。了解并掌握大小端的概念和判断方法对于嵌入式系统的开发和调试具有重要意义。

八、参考文献

  • 《嵌入式系统原理及应用教程》详细讲解了嵌入式系统的基础原理和应用,其中包含对大小端模式的阐述,结合实际的嵌入式硬件架构,介绍大小端在不同平台下的表现和影响。
  • 《C Primer Plus》经典的 C 语言入门书籍,虽然不是专门针对嵌入式领域,但对 C 语言的基础概念和数据存储方式有深入讲解。
  • 《深入理解计算机系统》这本书从计算机系统的底层原理出发,深入探讨了数据的表示和存储,包括大小端模式的原理、产生原因以及在不同体系结构中的应用。
  • ARM Architecture Reference Manual:如果使用的是 ARM 架构的嵌入式处理器,这份文档是必不可少的参考资料。它详细介绍了 ARM 处理器的体系结构和指令集,其中包含了关于 ARM 处理器大小端模式的设置和操作方法,以及在不同模式下数据的存储和处理规则。
  • Intel® 64 and IA - 32 Architectures Software Developer’s Manual:对于基于 Intel 架构的嵌入式系统,该手册提供了详细的技术信息。它涵盖了 Intel 处理器的各种特性,包括大小端模式的相关内容,能帮助你了解在 Intel 平台上如何处理大小端问题,以及如何编写与大小端相关的 C 语言代码。
  • Stack Overflow:一个知名的技术问答社区,有大量关于嵌入式 C 语言大小端的讨论和解决方案。
  • GitHub:全球最大的开源代码托管平台,上面有许多嵌入式 C 语言项目。
  • 电子发烧友网、开源中国等技术论坛:这些论坛上有很多嵌入式开发相关的板块,其中包含了关于大小端的讨论和经验分享。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/963118.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ZZNUOJ(C/C++)基础练习1031——1040(详解版)

1031 : 判断点在第几象限 题目描述 从键盘输入2个整数x、y值&#xff0c;表示平面上一个坐标点&#xff0c;判断该坐标点处于第几象限&#xff0c;并输出相应的结果。 输入 输入x&#xff0c;y值表示一个坐标点。坐标点不会处于x轴和y轴上&#xff0c;也不会在原点。 输出 输出…

8.[前端开发-CSS]Day08-图形-字体-字体图标-元素定位

一、额外知识补充 1 border图形 边框的形状 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport"…

Qt事件处理:理解处理器、过滤器与事件系统

1. 事件 事件 是一个描述应用程序中、发生的某些事情的对象。 在 Qt 中&#xff0c;所有事件都继承自 QEvent &#xff0c;并且每个事件都有特定的标识符&#xff0c;如&#xff1a;Qt::MouseButtonPress 代表鼠标按下事件。 每个事件对象包含该事件的所有相关信息&#xff…

大白话讲清楚embedding原理

Embedding&#xff08;嵌入&#xff09;是一种将高维数据&#xff08;如单词、句子、图像等&#xff09;映射到低维连续向量的技术&#xff0c;其核心目的是通过向量表示捕捉数据之间的语义或特征关系。以下从原理、方法和应用三个方面详细解释Embedding的工作原理。 一、Embe…

git笔记-简单入门

git笔记 git是一个分布式版本控制系统&#xff0c;它的优点有哪些呢&#xff1f;分为以下几个部分 与集中式的版本控制系统比起来&#xff0c;不用担心单点故障问题&#xff0c;只需要互相同步一下进度即可。支持离线编辑&#xff0c;每一个人都有一个完整的版本库。跨平台支持…

利用metaGPT多智能体框架实现智能体-1

1.metaGPT简介 MetaGPT 是一个基于大语言模型&#xff08;如 GPT-4&#xff09;的多智能体协作框架&#xff0c;旨在通过模拟人类团队的工作模式&#xff0c;让多个 AI 智能体分工合作&#xff0c;共同完成复杂的任务。它通过赋予不同智能体特定的角色&#xff08;如产品经理、…

当WebGIS遇到智慧文旅-以长沙市不绕路旅游攻略为例

目录 前言 一、旅游数据组织 1、旅游景点信息 2、路线时间推荐 二、WebGIS可视化实现 1、态势标绘实现 2、相关位置展示 三、成果展示 1、第一天旅游路线 2、第二天旅游路线 3、第三天旅游路线 4、交通、订票、住宿指南 四、总结 前言 随着信息技术的飞速发展&…

windows10 配置使用json server作为图片服务器

步骤1&#xff1a;在vs code中安装json server, npm i -g json-server 注意&#xff1a;需要安装对应版本的json server&#xff0c;不然可能会报错&#xff0c;比如&#xff1a; npm i -g json-server 0.16.3 步骤2&#xff1a;出现如下报错&#xff1a; json-server 不是…

洛谷 P1164 小A点菜 C语言

P1164 小A点菜 - 洛谷 | 计算机科学教育新生态 题目背景 uim 神犇拿到了 uoi 的 ra&#xff08;镭牌&#xff09;后&#xff0c;立刻拉着基友小 A 到了一家……餐馆&#xff0c;很低端的那种。 uim 指着墙上的价目表&#xff08;太低级了没有菜单&#xff09;&#xff0c;说&…

向上调整算法(详解)c++

算法流程&#xff1a; 与⽗结点的权值作⽐较&#xff0c;如果⽐它⼤&#xff0c;就与⽗亲交换&#xff1b; 交换完之后&#xff0c;重复 1 操作&#xff0c;直到⽐⽗亲⼩&#xff0c;或者换到根节点的位置 这里为什么插入85完后合法&#xff1f; 我们插入一个85&#xff0c;…

50. 正点原子官方系统镜像烧写实验

一、Windows下使用OTG烧写系统 1、在Windos使用NXP提供的mfgtool来向开发烧写系统。需要用先将开发板的USB_OTG接口连接到电脑上。 Mfgtool工具是向板子先下载一个Linux系统&#xff0c;然后通过这个系统来完成烧写工作。 切记&#xff01;使用OTG烧写的时候要先把SD卡拔出来&…

AI智能化模型助力太阳能光伏板自动巡检运维,基于YOLOv5全系列【n/s/m/l/x】参数模型开发构建无人机航拍场景下太阳能光伏板污损缺陷智能检测识别系统

随着全球科技和能源领域的飞速发展&#xff0c;清洁新能源&#xff0c;尤其是太阳能&#xff0c;正以前所未有的速度融入我们的日常生活。太阳能光伏板作为转换太阳能为电能的关键设备&#xff0c;其普及程度日益提高&#xff0c;从偏远乡村到繁华都市&#xff0c;无处不在地展…

深度学习 DAY3:NLP发展史

NLP发展史 NLP发展脉络简要梳理如下&#xff1a; (远古模型&#xff0c;上图没有但也可以算NLP&#xff09; 1940 - BOW&#xff08;无序统计模型&#xff09; 1950 - n-gram&#xff08;基于词序的模型&#xff09; (近代模型&#xff09; 2001 - Neural language models&am…

FireFox | Google Chrome | Microsoft Edge 禁用更新 final版

之前的方式要么失效&#xff0c;要么对设备有要求&#xff0c;这次梳理一下对设备、环境几乎没有要求的通用方式&#xff0c;universal & final 版。 1.Firefox 方式 FireFox火狐浏览器企业策略禁止更新_火狐浏览器禁止更新-CSDN博客 这应该是目前最好用的方式。火狐也…

【问题记录】DeepSeek本地部署遇到问题

详细的部署过程以及原理&#xff0c;各位大佬已经解释的很详细了&#xff0c;这里不重复只是记录过程中遇到的一个问题。 本地部署 DeepSeek-R1 模型全攻略 - 王浩宇的博客) 问题详情 Error: Post "http://127.0.0.1:11434/api/show": read tcp 127.0.0.1:57395-&g…

【react-redux】react-redux中的 useDispatch和useSelector的使用与原理解析

一、useSelector 首先&#xff0c;useSelector的作用是获取redux store中的数据。 下面就是源码&#xff0c;感觉它的定义就是首先是createSelectorHook这个方法先获得到redux的上下文对象。 然后从上下文对象中获取store数据。然后从store中得到选择的数据。 2、useDispatc…

可视化相机pose colmap形式的相机内参外参

目录 内参外参转换 可视化相机pose colmap形式的相机内参外参 内参外参转换 def visualize_cameras(cameras, images):fig plt.figure()ax fig.add_subplot(111, projection3d)for image_id, image_data in images.items():qvec image_data[qvec]tvec image_data[tvec]#…

Python sider-ai-api库 — 访问Claude、llama、ChatGPT、gemini、o1等大模型API

目前国内少有调用ChatGPT、Claude、Gemini等国外大模型API的库。 Python库sider_ai_api 提供了一个完整的解决方案。通过调用 sider.ai 的API&#xff0c;开发者可以实现对这些大模型的访问。 众所周知&#xff0c;sider是一个Chrome&#xff0c;以及Edge的浏览器插件&#xf…

FreeRTOS学习笔记2:FreeRTOS的基础知识

1.FreeRTOS介绍 FreeRTOS是一个免费的嵌入式实时操作系统&#xff0c;同时它在市面上也是一款主流的操作系统&#xff0c;是工作上必不可少的技能。它具有以下六种特点&#xff1a; 1.免费开源&#xff1a;在商业产品中使用&#xff0c;无潜在商业风险&#xff0c;无需担心。 2…

TensorFlow 简单的二分类神经网络的训练和应用流程

展示了一个简单的二分类神经网络的训练和应用流程。主要步骤包括&#xff1a; 1. 数据准备与预处理 2. 构建模型 3. 编译模型 4. 训练模型 5. 评估模型 6. 模型应用与部署 加载和应用已训练的模型 1. 数据准备与预处理 在本例中&#xff0c;数据准备是通过两个 Numpy 数…