不同类型的整形在内存中存储时带来的问题

博客介绍了整形在内存中的截断与提升现象。当长字节整形赋值给短字节整形变量会截断,短字节整形用长字节整形输出会提升。表达式计算时,整形先提升为int类型,不足则提升为unsigned int类型。还通过几个简短代码说明了不同情况下的输出结果及可能出现的死循环问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

由于整形在内存中拥有不同的类型,当把一个字节较长的整形数值赋给一个字节较短的整形变量会出现整形截断,而当将一个字节较短的整形变量用一个字节较长的整形变量输出时则会出现整形提升。在表达式计算时,各种整形首先要提升为int类型,如果int类型不足以表示则要提升为unsigned int类型,然后执行表达式的运算。
这里写图片描述

下面以几个简短的代码来说明问题:

一、
#include <stdio.h>
int main()
{
    char a = -1;
    signed char b = -1;
    unsigned char c = -1;
    printf("a=%d, b=%d, c=%d", a, b, c);
    return 0;
}

在vs编译环境下认为char为有符号的,所以a、b的输出结果相同。
-1在内存中的存储是按其二进制补码存放,格式如下:

11111111 11111111 11111111 11111111

又因为在vs编译环境下数据是按照小端存放的,所以char中只存放了-1的低八位二进制数字

11111111

而后面的打印格式却要求以整形输出,则进行整形提升有符号位补符号位,无符号位补0,所以a、b补全后还原为

11111111 11111111 11111111 11111111

即输出-1
而c在补全的时候无符号位,补0

00000000 00000000 00000000 11111111

则c输出255
这里写图片描述

二、
#include <stdio.h>
int main()
{
    char a = -128;
    printf("%u\n",a);
    return 0;
}

-128在内存中的存放格式

11111111 11111111 11111111 10000000

放入char中的格式

10000000

以%u格式(无符号数格式)打印,但因char为有符号数,补全后还原为为

11111111 11111111 11111111 10000000

此时将此数当成无符号数,输出为
这里写图片描述
当然由于128在内存中的存放格式为

00000000 00000000 00000000 10000000

以上面的代码输出会出现同样的结果。

三、

当在循环的判断部分出现无符号数,则循环极有可能陷入死循环。

#include <stdio.h>
int main()
{
    unsigned int i;
    for (i=9; i>=0; i--)
    {
        printf("%u\n",i);
    }
    return 0;
}

当i从0降到-1时,存放格式为

11111111 11111111 11111111 11111111

但是编译器将此数当成无符号数(即2^33-1)为了便于观察,将输出格式定位%u,可以直观看到一个非常大的数字迅速递减的变化。

#include <stdio.h>
int main()
{
    unsigned char i = 0;

    for (i=0; i<=255; i++)
    {
        printf("hehe\n");
    }

    return 0;
}

当i加到256时存放格式为

00000000 00000000 00000001 00000000

截断放到无符号位的char中仍为0,累加继续,故陷入死循环。

四、
#include <stdio.h>
#include <string.h>
int main()
{
    char a[1000];
    int i;
    for (i=0; i<1000; i++)
    {
        a[i] = -1-i;
    }
    printf("%d\n", strlen(a));
    return 0;
}

因为char的表数范围是-128~127
i从0加到127,a[i]从-1减到-128
-128再减1为-129,其存放格式为

11111111 11111111 11111111 01111111

截断后放入char中为

01111111

因其有符号位,则此数为127
i再从128加到255,a[i]从127减到0
而0前面的数字个数为255
这里写图片描述

### C++ 中 `constexpr` 和 `const` 的内存分配差异与共同点 #### 定义和基本特性 `const` 和 `constexpr` 都可以用来表示不可变的值,但在实现细节上有所不同。`const` 表示运行常量,其值可以在编译确定或仅在运行确定[^3]。相比之下,`constexpr` 更进一步,它强制要求所定义的对象或函数的结果必须能够在编译期计算得出,并且只适用于满足特定条件的表达式或函数[^1]。 #### 内存分配上的相似之处 无论使用 `const` 还是 `constexpr` 声明变量,编译器都有可能对其进行优化处理。如果编译器能证明某个 `const` 或 `constexpr` 变量在其整个生命周期内都不会发生变化,则可以选择不在实际内存中为其保留空间,而是将其替换为直接嵌入到指令流中的立即数值[^1]。这种情况下两者的行为是非常类似的。 #### 差异分析 尽管如此,二者之间仍然存在一些重要的区别: - **编译期约束** `constexpr` 要求更严格,只有那些能在编译期间完全解析出来的值才能被赋予 `constexpr` 标记。这使得所有合法的 `constexpr` 实体必然也是编译期可用的常量,从而减少了潜在的动态资源需求可能性[^3]。 - **适用场景扩展** 不仅仅限于简单的整数或其他内置类型,通过适当设计还可以让复杂的结构甚至自定义类型的对象成为 `constexpr` 支持的一部分。这意味着某些原本需要堆栈支持的大规模数据初始化操作现在也可能转变为纯粹静态的过程完成,进而影响整体应用程序性能表现[^2]。 - **存储类别推导机制的不同效果** 当我们定义一个具有外部链接性的全局 `const` 整形变量,默认情况下会触发强型符号生成并占据相应大小的真实RAM单元格;但是如果是同样的声明改用了 `constexpr` ,则除非特别指定extern linkage 否则一般不会产生任何真实的物理地址映射关系[^4]。 综上所述,虽然 `const` 和 `constexpr` 在减少不必要的运行开销方面有着一致的目标导向,但由于后者具备更强有力的支持能力以及更加灵活的应用方式,在很多场合下都能够带来更为显著的效果提升。 --- ```cpp // 示例代码对比 const 和 constexpr 的内存分配行为 #include <iostream> using namespace std; struct Point { int x, y; // 构造函数标记为constexpr以允许参与编译期运算 constexpr Point(int _x =0 ,int _y=0 ):x(_x),y(_y){} }; // 使用constexpr定义数组长度 constexpr int arraySize() {return 5;} void testArray(){ double data[arraySize()]; // 动态尺寸数组模拟 } int main(){ const int cval = 10; // 普通const变量 constexpr int cxval = 20; // 编译期确定的constexpr变量 cout<< "cval address:"<<&cval<<"\ncxval address:"<<&cxval<<endl; static_assert(cxval == 20,"Error!"); // 只能应用于constexpr情境下的断言检验 // 测试Point类作为constexpr参数传递的可能性 constexpr Point origin{0,0}; auto distFromOrigin=[](Point pt)->double{return sqrt(pt.x*pt.x + pt.y*pt.y); }; static_assert(distFromOrigin(origin)==0.0 ,"Distance calculation failed!"); return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值