现在来看, C/C++ 的 int、long 等不定宽类型是失败的设计吗? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
w568w
V2EX    程序员

现在来看, C/C++ 的 int、long 等不定宽类型是失败的设计吗?

  •  
  •   w568w
    w568w 215 天前 4132 次点击
    这是一个创建于 215 天前的主题,其中的信息可能已经有所发展或是发生改变。
    如题,今天给 C 库写其他语言 binding 的时候想到的。

    论据 1:C/C++ 之后,几乎所有语言都 (1) 定死了各个整数类型的宽度(如 Java ),或者 (2) 换用定宽类型(例如 i64 、u32 、int64 、usize )。即使 C 自己也引入了 int64_t 这样的固定宽度类型。

    论据 2:非常不利于编写可移植的库。比如用两个不同编译器编译出的代码,虽然函数声明一样,但因为一边 long 是 64 位整数,另一边是 32 位整数,导致不能互相调用。

    论据 3:不确定宽度导致程序员不得不随时检查数值范围,一旦疏忽(例如,换了新平台或者编译器)很容易造成溢出问题。为了检查,编写大量含宏代码,给程序员带来额外心智负担。

    论据 4:使数值类型系统变得过于复杂,很多场景下并不需要严格区分 long long 、long 、int 、short 、char 。但程序员由于认识不清楚而随意使用,反而可能造成 linter 不能正确给出 warning 。

    ---

    这个 int 、long 不定宽的规定是当时随意决定的吗?还是刻意设计出来的?为什么直到今天还有大量 C 库、接口仍在使用这样的类型?(例如 POSIX 、libc 、OpenMP )
    第 1 条附言    214 天前

    感谢大家的热烈讨论,我总结了一下现有的观点,讲一讲我的想法。

    我觉得有道理的:

    • 时代的局限性,当时处理器种类太杂(有 18-bit、12-bit 等处理机)、没有跨平台的需求

    • 早期问题可以理解,但 C90、C99 还继续创造 long long 等类型,是「屎山拉屎」行为

    我觉得有待商榷的:

    • 出于性能考虑/当时内存太小:我觉得这个和定不定宽关系不大,反倒是定宽类型更方便程序员按照业务需要精准描述需要的空间(如 uint8、uint4,肯定比 short 表意更清楚),便于编译器优化内存占用

    • 不是所有指令集都有操作各种宽度的指令:32 位机操作 64 位整数应该也会有字撕裂(Word Tearing)的问题吧?改用不定宽只是隐藏了问题,而没有实际解决问题


    我的看法是,不定宽确实有历史原因的考量,但一些新近的库还在坚韧不拔地使用不定宽类型,则实实在在恶心到了写移植代码的人(比如我)。

    有一点我不能理解的是,既然认识到不足,为什么今天的 C/C++ 教材、课程、网课还在孜孜不倦地教人优先去用 int、long,而把 int64_t 藏在附录里?对初学者来说,「int64_t 是 64 位整数」难道不比「long 是至少 32 位整数,但也不一定,要视编译器而定,64 位机器上也可能是 64 位」更好理解吗?

    33 条回复    2025-03-11 15:01:09 +08:00
    ebi5oowiiy1llo
        1
    ebi5oowiiy1llo  
       215 天前 via Android   1
    刻意设计出来的,为了匹配当时的处理器的一个 world ,参考: https://www.quora.com/Why-are-type-sizes-inconsistent-among-different-platforms-eg-short-int-long-in-C
    Nasei
        2
    Nasei  
       215 天前
    如果让我从 c99 前的整数类型和 java 的整数类型二选一,我还是选宁愿用 c 的
    buxudashi
        3
    buxudashi  
       215 天前   2
    当然是失败设计。

    rust 的更好点。i8,i16,i32,i64 非常清楚。c 的 int 都在不同平台叫同一个名字。然而内存占用不一样。非常坑。
    coderluan
        4
    coderluan  
       215 天前   3
    编程语言是服务于程序,而不是服务于程序员的,C/C++的很多设计都是出于性能考虑,数据类型也是一样的,C/C++都是 70 年代产生的语言,当时内存可能都不到 1M ,现在内存虽然大了,但是 CPU 缓存可能也就十几 M ,更别说还有嵌入式设备,而有些需求对于性能的追求是没上限的,很多 C 库,底层实际上是 SIMD 和汇编。
    wnpllrzodic
        5
    wnpllrzodiac  
       215 天前   1
    时代的局限性,盖茨都说过 64k 已经够用了。
    vituralfuture
        6
    vituralfuture  
       215 天前 via Android
    这样做是为了实现通过宏检测 feature 后,可以一份源码在不同平台使用不同编译器也能编译出正确的程序
    justou
        7
    justou  
       215 天前
    很大部分是历史原因,各种类型设备的出现,都需要 C/C++ 编写能运行在之上的程序(比如驱动),C++11 标准化的 (Fixed width integer types)[https://en.cppreference.com/w/cpp/types/integer] 就是为了方便编写可移植性代码,以前的都是完全由开发者用宏来处理数据类型的差异。
    Thymolblue
        8
    Thymolblue  
       215 天前
    目前正在开发跨平台应用,不等宽确实是一个不方便的设计。但是这些在设计过程中都可以规避,现在的 C++标准支持 int32_t 类似的 Fixed width integer types 定义。如果在实践过程中实在需要根据平台来使用不同长度的类型,一般是先定义一个类型,然后再使用不同平台的宏来定义不同长度。
    icyalala
        9
    icyalala  
       215 天前
    早期硬件什么设计都有,int long 这些类型不定长反而才能可移植。你知道 char 有可能不是 8bit 吗?
    至于现在跨平台基本都在用 stdint.h 里面那些定义,C99 就支持了。
    moudy
        10
    moudy  
       215 天前 via iPhone
    @vituralfuture 完全可以通过 feature 检测后用 typedef 来换数据类型。int 不等宽除了方便 compiler 实现,真没啥实际好处。
    icyalala
        11
    icyalala  
       215 天前   1
    说白了 int long 不定宽是就刻意设计的,目的就是为了适应多样化的硬件,方便移植,保持最佳性能。
    posix 和 libc 也是从那个时代过来的,一直需要维持 API 兼容,所以这才是历史问题。
    cnbatch
        12
    cnbatch  
       215 天前   6
    要甩锅那得甩给早期计算机的制造公司,尤其是 DEC

    UNIX 是在 PDP-7 开发出来的,而 PDP-7 的一个 Word (当时的最小操作单位)是 18 字节:
    https://gunkies.org/wiki/PDP-7

    在 C 语言诞生的那个年代,既有 PDP-11 ( C 语言初版诞生的平台),使用现在大家熟知的 16bit / 32bit / 64bit 操作方式;也有 PDP-12 ,使用的是 12-bit ,以现在的标准来看够奇怪吧。

    如果当时直接定死了各个基础类型的宽度,那么想要做源码级移植就麻烦多了。

    就像如此简单的代码:

    int number = 12;
    printf("Number: %d\n", number);

    int 是 18-bit 还是 12-bit ,又或者是 16-bit ,都由目标机器的编译器自己决定,在当时来看显然是很省事的。

    发明人哪能预料到后来会统一为 8 / 16 /32 / 64 bit 标准呢

    ---------------

    当然啦,ANSI / ISO 也有部份责任,第一个 ANSI C 标准制定的年代,已经是 8 / 16 /32 / 64 bit 标准的时期了,完全可以区分得更清晰一些,比如这样:

    int 必须比 short 宽,long 必须比 int 宽

    可惜标准只规定了最低限度,搞得后来不同系统的 int 和 long 都一塌糊涂。到了 C99 就只能用 macro 打补丁。
    cnbatch
        13
    cnbatch  
       215 天前
    @cnbatch 勘误一下,DP-7 的一个 Word (当时的最小操作单位)是 18 比特(不是字节)

    补充一个:IBM 的“锅”也不小,DEC 之所以这么设计,可能是受到 IBM 的影响(毕竟要跟 IBM 竞争)。PDP-1 诞生前的 IBM 机器,字长就是 36-bit ,这里有发展史可以看:
    https://en.wikipedia.org/wiki/Word_(computer_architecture)

    1970 年之前的硬件操作数长度标准简直乱作一团
    cnbatch
        14
    cnbatch  
       215 天前
    @moudy 早期 C 语言可没有 typedef 这种做法,有人提到这么一句
    there is no "typedef" in early C
    出处: https://news.ycombinator.com/item?id=13441621
    billccn
        15
    billccn  
       215 天前
    这个问题的本质是 C/C++定位是系统编程语言,数字类型的是为了方便在不同指令集之间移植来设计的,比如说:

    * 所有类型都只有最小宽度而没有绝对宽度,因为不是所有指令集都有操作各种宽度的指令
    * int 就是在那个平台寻址范围内做下标比较合适的长度
    * short 就是可能比 int 节省空间,但是至少有 16 位; long 就是至少有 32 位

    当然我也觉得理想是美好的,现实是骨感的,这些语言出现不久互联网就爆发了,有了跨机型交换数据的需求,导致这些依平台而变的类型不好用。

    理论上说交换格式可以和内存里的数据类型分离,比如内存里的 struct 用 int, long 等类型,交换时翻译到到固定长度的 char[](这样还解决了 endianness ),但显然没有几个人这么勤快。

    当然我觉得 long long 出现时,这个情况已经很明显了,应该直接定义 int64 而不是新增一个关键字。
    paopjian
        16
    paopjian  
       215 天前
    还是因为历史的局限性吧? 当时的计算机处理器规格都各做各的, 兼容机都算是个不小的突破, 但是各家的位宽都不一样, 很多兼容软件的传递方式是以源码形式传递的, 就是为了方便用户编译到自己的机器上,
    billccn
        17
    billccn  
       215 天前
    @cnbatch 我发出来才看见你的,我觉得你说的“int 必须比 short 宽,long 必须比 int 宽”虽然让部分初学者可以更清楚的了解这些类型的区别,但是与系统编程语言的定位不符合。比如说我记得早期的 64 位机就有完全不支持 16 位长度的指令集,这样 short 也必须是至少 32 位,那么 int 就得 64 位,long 不能 128 位吧?
    billccn
        18
    billccn  
       215 天前
    @paopjian 这个问题到现在还有啊,比如嵌入式系统 32 位的已经很高级了,用 8 位机的也大有人在,很多机器连整数除法都做不了,这也是 C/C++最大的客户群。
    adian
        19
    adian  
       215 天前
    这不是设计问题,是当时的硬件问题
    cnbatch
        20
    cnbatch  
       214 天前
    @billccn 或许,特殊机器可以特殊对待,比如以编译器 extension 的形式去处理。
    假设“标准状况”下,char: 8-bit, short: 16-bit, int: 32-bit, long: 64-bit
    那么开启 extension 就无视标准规定,按照机器的硬件手册内容去做。

    如果硬件级别无法支持“假设的标准状况”,那就强制必须使用 extension 模式。

    当然啦,这都是马后炮了,我有这种想法(强制开启 extension )主要是因为知道 Linux 内核源码长期以来大量使用编译器扩展模式。微软编译自家系统时应该也是这样( MSVC 也有自己的 extension )。对于 20 世纪 80 年代的标准制定者而言,可能都预料不到编译器 extension 会用得满天飞。
    mayli
        21
    mayli  
       214 天前
    当初差不多没有标准化的 8/16/32 , 当时的字长一大堆,有 9 12 啥的,你这论调属于事后诸葛亮,新语言基本上都是 64 位处理器起跳了
    iorilu
        22
    iorilu  
       214 天前
    当时电脑, 能扣几个字节都干的
    tdxdxoz
        23
    tdxdxoz  
       214 天前 via Android
    rust 的 usize 也是不定宽的啊,你为什么说“例如 i64 、u32 、int64 、usize ”
    ugpu
        24
    ugpu  
       214 天前
    好比工地钻头,尺寸不一 你说他们不严谨?不照样摩天大楼如雨后春笋冒出来嘛?

    都是符合当时的业务背景需求罢了, 不是那么严谨的产业 只是基础服务设施 不行在升级迭代就行了. 至于内存?不够就加, 硬盘不够就扩容; 实在到了拿节骨眼上, 企业也会舍得花钱重建;
    至于一开始就一眼观望未来产生精妙的设计结构 的计算机语言 符合未来几十年的体系 业务。 只有两种可能:
    1. 运气好 营销也到位 存粹撞上了 该的。
    2. 造物主说的,就要这么干 我说的就是对的。
    普通人?忙着吃饭挣钱过好日子呢。造福全球人类的计划怕是一提出来 结合设计经费; 在资本家看来这是他们的命
    moudy
        25
    moudy  
       214 天前
    @cnbatch #14 早期是早期,后面 C90 C99 还不在这个问题上做出更新就是不负责任。也直接导致了 64 位 c 编程时 int long longlong 实际宽度的混乱。
    moudy
        26
    moudy  
       214 天前
    @billccn #17 指令集和数据宽度没直接关系哦。64 位指令也可以只操作 byte halfword
    moudy
        27
    moudy  
       214 天前
    @ugpu #24 技术标准上定一个指标,不给指标上下界真的是不严谨。老罗在星巴克被中杯 大杯 超大杯都折磨成啥样了。
    ugpu
        28
    ugpu  
       214 天前
    @moudy 定了呀, 常规的就按:int 4 个字节 bool 一个字节 char 一个字节 还有 short. 够用了; 很满足当时的需求了.
    ugpu
        29
    ugpu  
       214 天前
    @moudy 这种帖子 老技术都应该知道没啥意义;不管 op 的出发角度是恶意的还是友善的;
    无非证明一个: 在事情已经发生了 跳出来说 xxx 当初的设计怎么怎么不对;
    这好比什么。举几个恶臭的例子 ?
    《一座城市 一座老桥, 要拆了重建,跳出来个新人. 你们当初干什么去了! 怎么不 xxxx ! 一副说教嘴脸, 》
    《项目上线了 没达到预期。老二站出来反讽当初拍板的人, 我当时就说了 xxxxx 》

    技术角度看没问题; 就是当初没考虑到扩容. 但是没有人能考虑未来的事情.
    为什么不正向看?
    1. 迭代说明升级了 行业欣欣向荣 起码这玩意有价值 需求在增加导致原有支持不满足目前场景; 需要改进
    2. 提供了新的就业机会
    3. 产生了其他技术的融合
    junkun
        30
    junkun  
       214 天前
    肯定是失败的,按照兼容性和可移植性来说,肯定应该使用绝对值。比如二进制文件就得用 int64 ,后来的 java 能够跨平台也是不同平台的大小是一致的。就算架构使用的位数不同,使用绝对值也可以更好地模拟,就算是 10 位机,也可以用 int10 来模拟 int8 ,int20 来模拟 int16 ,但是 C/C++这样就更可能出问题。

    如果按每个平台的特性来说,定义几种特殊类型就够了,比如 ifast 和 usize ,现在这样连 char ,short 和 long 标准定义上都没有固定的长度就太扯淡了。使用特殊类型也能更方便地检索出这里是平台相关的代码,而不是像 C/C++这样默认全都是平台相关的,只有写 intXX_t 才是平台无关的。
    moudy
        31
    moudy  
       213 天前
    @ugpu #28 也只是大家这么觉得而已,LLP64, LP64 现在依然在打架。 具体到项目踩坑:enum 在不同编译器上默认大小不一样,导致存储的数据换了编译器读不出来,也是够了。
    ugpu
        32
    ugpu  
       213 天前
    @moudy
    如果你说 LLP64 和 LP64 那这是操作系统所谓的官方编译器的事情, 是人为原因导致的., 语言本身是没问题的 语言规范了 byte short int bool ; 我的意思是 语言设计之初考虑的是字节流 在当时时代主流设计上的规范, 并不需要为后期的拓展负责; 以及正式有了这些问题才能推进后期高级语言的规范...
    my3157
        33
    my3157  
       212 天前
    glibc 代码里面那一层一层的 define 看着就难受, musl 稍好一些但也好不到哪儿去, 还有就是文档也是一拖而且注释极少
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     900 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 19:30 PVG 03:30 LAX 12:30 JFK 15:30
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86