C_and_CPP 板


LINE

在 C 语言里面对 NULL 取值是非法的 通常伴随的是发生 segmentation fault 而这通常也跟 CPU 的指令集实作有关 因为编译器会将对 NULL 取值的程式码 假定 NULL 就是常数数值 0 的情况下 编译成类似 movl $10, 0x0 等的指令 因此 CPU 在处理相关指令的时候 竖立 flag 触发 OS 处理是可以预期的 但是既然 C 语言被定调成高阶语言 为什麽不提供一些抽象一些的语意 像是对「虚无指针」取值是代表忽略的意思 比如说 VACANT 代表「虚无指针」 则 void *arr[] = {VACANT, VACANT}; *arr[1] = (uintptr_t)12345; 代表什麽事(包含副作用)都不会发生 感觉对空虚取值不发生作用非常实用 就像指令集几乎都会有 NOP 一样 表面上看起来没什麽用 但是却能在不少 corner cases 发生作用 NOP 可以去解决 hazard 的问题 VACANT 可以解决不少指针初始化的问题 可以减少程式设计所需的 sentinel value 还在特定的时候能让程式减少无谓的判断式 基本上我还没想到什麽负面的影响 我的问题是为什麽不设计类似的指针? 类似 /dev/null 的概念 看起来没什麽用 但是却大大有用 XD -- 作者 sr29 (owo) 看板 Linux 标题 [问题] git add 失败 时间 Wed Jul 12 15:31:13 2017
1F:→ qsort : 我猜,该档案被锁住了,所以git无法access,才会报错07/12 15:52
2F:→ qsort : 建议用.gitignore把这类temp档ignore掉,不要上到git。07/12 15:53
3F:推 gitignore: 有人叫我?07/13 04:41
--



※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 122.116.185.23
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/C_and_CPP/M.1507613289.A.22D.html
4F:推 jerryh001: C语言的特色就是更快,还要更快 额外的动作都浪费时间 10/10 13:38
5F:推 AstralBrain: 那你希望int x = *VACANT; 的x的值是多少 10/10 13:45
6F:→ AstralBrain: 未初始化的垃圾值? 这样跑到後面会出现更多问题 10/10 13:45
7F:→ AstralBrain: 还不如在一开始直接死 10/10 13:45
就跟 NULL 一样 型态都是 void* 只是一种特殊指标 1. void *ptr = VACANT; *ptr = dont_care; // 没事发生 或是说 2. void val = "i don't care"; // 一样没事 也就是说对 void 赋值是无反应的 www 但是如果是 3. void *ptr = NULL; *ptr = "segfault" // 错误发生 这个就是按照 lagacy 的做法
8F:→ AstralBrain: 如果等号左边是复杂的c++ class那更无解了 10/10 13:47
C++ 太复杂惹
9F:推 AstralBrain: 所以其实你要的不是VACANT, 而是对void做各种运算..? 10/10 13:53
10F:→ AstralBrain: 那 int *ptr = (int*)VACANT; 会发生什麽事 10/10 13:55
这个应该就真的比较复杂了 假定编译器头文件是这样实作的 #define VACANT ((void*)(0xFFFFFFFFFFFFFFF)) 也就是要从 CPU 设计变更下手 只要对存取最後一个保留地址当作没反应的指令 就像 NOP 一样 这样也许可行吧(?
11F:推 AstralBrain: 其实我想问的是你允不允许VACANT转型成其他指标 XD 10/10 14:06
12F:→ AstralBrain: 如果允许的话就回到我一开始的问题 10/10 14:06
二楼的那个问题在於「把 void 型态赋值到 int」 如果改成 int x = *(uintptr_t*)VACANT; 的话... 答案大概就是... x = 0xFFFFFFFF 吧(从 64bit 变成 int: 32bit 截断)
13F:→ AstralBrain: *(int*)VACANT 要segfault还是要NOP 10/10 14:07
14F:→ AstralBrain: segfault => 跟null有87%像, nop => 跑到後面更惨 10/10 14:08
*(int*)VACANT = (int)5487; 的话应该比较简单 毕竟 VACANT 代表的就是最後的保留地址的数值,那转型再取值应该就还是没反应吧 应该是这麽说 VACANT 的数值也是机器依赖的 编译器要端看 CPU 是怎麽实作的 以假设对 CPU 来说地址第 8~15 位元组用作忽略地址的话 那麽 #define VACANT ((void*)(0x8)) 才是正确的 不过这在 CPU 解码前期就要多一个小电路 让该送进来指令变成气泡或是直接让下一个指令直接递补上 这样其实也不会太难 只要 63 个 OR gates 和 1~2 个 NOT gate 去当 mux 的 select 看是是不是要触发 instruction queue 的递补(?
15F:推 AstralBrain: 实作好处理 问题是要怎麽限制vacant只能write-only 10/10 14:23
好问题 @@ 如果是 int *ptr = VACANT; printf("%d\n", *ptr); 的话呢... 应该还是 segfault 跟 NULL 一样 我觉得应该是这样 毕竟 VACANT 的地址不在程序的可以读的权限里 但是所有的程序对那个地址都有写入的权限 跟 /dev/null 一样 只要读就是直接拿到一个 EOF
16F:推 AstralBrain: 现在你要的功能应该可以用c++自己做一个出来 10/10 14:25
17F:→ AstralBrain: 在smart pointer外面再包一层之类的 10/10 14:26
18F:→ AstralBrain: 可以先自己试用看看 XD 10/10 14:26
19F:推 CoNsTaR: 就算这样比较好编译器也做不到啊 10/10 14:27
20F:→ CoNsTaR: 因为很多情况下编译器没办法知道一个变数(当然也包含指 10/10 14:27
21F:→ CoNsTaR: 标变数)的值 10/10 14:27
22F:→ CoNsTaR: 例如假设有个不可判定的问题,它有两种可能的答案(例如是 10/10 14:27
23F:→ CoNsTaR: 、否) 10/10 14:27
24F:→ CoNsTaR: 写一个试图解决这个问题的函式,如果答案是是,那就将指 10/10 14:27
25F:→ CoNsTaR: 标 assign 为 NULL,否则 assign 为其他值 10/10 14:27
26F:→ CoNsTaR: 那如果编译器要知道这个指标的值为何,就必须要先知道这 10/10 14:27
27F:→ CoNsTaR: 个问题的答案 10/10 14:27
28F:→ CoNsTaR: 所以很明显这样的构想是不能成立的 10/10 14:27
没错 正因为如此 所以才用 preprocessor macro 指定一个机器相依的数值 编译器不需要知道 void* 型态的变数是不是装 VACANT 因为 VACANT 就是一个定值 当语言被编译成执行档的时候 会变成 movl $10, 0x8 当机器要执行这行指令的时候 就会直接把 10th register 的值直接丢掉 读取的时候也会 因为程序没有这段地址的读取权限产生 segfault
29F:推 CoNsTaR: 或者说,那个问题的答案是一个整数,然後你把答案转型 as 10/10 14:32
30F:→ CoNsTaR: sign 给指标,那编译器就得知道它的答案是否为零 10/10 14:32
31F:推 LPH66: 你在讲的不就是在编译时期挡下来吗? 10/10 15:50
32F:→ LPH66: 那你又提执行时期的数值做什麽... 10/10 15:50
ㄛㄛ我忘了改 XD 那是最早的讨论 所以後来我们改变了一点实作的方向 现在觉得用软解不太好 用硬体去解应该好得多
33F:→ LPH66: 然後就是因为编译时期挡不下来 (理由如上述) 才会变成 UB 10/10 15:51
34F:推 LPH66: 硬解那就跟 C 语言无关啦, 其他人又不用你的特别硬体 10/10 15:56
没错 一开始觉得靠 compiler 就行了 但是好像问题越来越复杂 如果把这个问题交给 CPU 去处理会简单非常多
35F:→ LPH66: 然後 (虽然离原题离版题都很远) 你或许可以看看 MIPS 10/10 16:02
36F:→ LPH66: 它的 $0 这个 register 是常数零, 读它得零, 写它是 NOP 10/10 16:02
37F:→ LPH66: 虽然这是 register 不是位址但大概跟你想要的东西沾了个边 10/10 16:03
恩恩 $0 是 hard-wired 的 如果要像我说的设计的话就是要写一个小组合电路去控制指令伫列 有点不太一样
38F:推 Lipraxde: 不是应该先设计指令集在用硬体实作吗? 10/10 19:15
39F:推 sppmg: C不是一直被称作中阶语言吗 ^_^ 10/10 19:33
C 语言素有最低阶的高阶语言之称 www
40F:推 chchwy: Objective-C提供了喔 10/10 19:49
41F:推 Bencrie: x86 realmode 就不会喷 segfault 啦 10/10 20:01
42F:→ Bencrie: 但是改 IVT 会发生什麽事就不知道惹 10/10 20:02
43F:推 lc85301: 好了大家别吵了都来写Rust吧(误 10/10 21:17
噗要
44F:→ PkmX: 好奇你的VACANT要怎麽减少sentinel value和减少判断 10/10 22:10
我想要设计一个 circular queue 如果先填入 sentinel value (SV) 的话 那接下来的使用就要去判断 如果是 SV 的话就要略过 这样效率不好 问题卡在 queue 一刚开始运作还没填满的时候 就算用额外的变数储存目前伫列储存了几个元素 使用的时候还是要去判断是不是已经满了 而在我目前的这个情况 刚好不适合宣告两个变数分别指向头跟尾 所以只有一个变数的话 SV 就显得重要
45F:推 AstralBrain: 仔细一想反正你只是要一块write-only的垃圾位址 10/10 22:22
46F:→ AstralBrain: 那 void* vacant = new char[4096]; 就好啦 XD 10/10 22:22
47F:推 AstralBrain: 是不是真的nop也不是很重要 10/10 22:25
没错我後来就是用这个方法 workaround
48F:推 CoNsTaR: 其实 Idris、Agda 的编译器可以做到你想的东西,只是它 10/10 23:35
49F:→ CoNsTaR: 们是和 C 非常不同的语言 10/10 23:35
50F:→ CoNsTaR: 我是觉得 C 应该无法(也不需要)做到这样的事情啦 10/10 23:35
硬体支援 C 语言就能跟上惹(误
51F:→ azureblaze: 没有痛觉神经就不怕被刀砍了 10/11 00:19
52F:→ azureblaze: 程式写错会crash是好事 10/11 00:21
53F:→ azureblaze: debug写入资料遗失听起来就超好玩 10/11 00:26
提早 crash 是好了 至少知道程序是错的 没挂掉有时候不代表是正确的 XD
54F:→ james732: 其实有点好奇其他语言(如rust)怎麽解决这问题 10/11 00:59
55F:→ PkmX: 痾你还是没有解释如何用VACANT避免circular buffer的SV和 10/11 01:26
56F:→ PkmX: 判断啊 有了VACANT不用判断就可以知道buffer满了? 10/11 01:26
是的 因为我的 circular queue 装的是 pointers to type T 所以如果能 pre-filled with VACANT which is of type void* 那麽如此一来我每次要使用的时候就不怕 dereference 到 sentinel 了 就算遇到 sentinel 我也能直接 deref. 而不用怕写至奇怪的地址 因为我处理的这个情况比较特别 要 push 一整圈完之後才开始 pop 如果能等价於在 push 的同时也 pop 前一个那麽 code 会比较 concise 这个时候 VACANT 就很有用 可以让我只需要一个 local variable
57F:→ PkmX: 你一开始里面都塞VACANT那你push的东西到底写到哪里去了? 10/11 01:53
前几次要取的时候会有很多个 VACANT 等到操作越来越多次後 VACANT 就会渐渐被我塞入的值取代直到完全消失为止 就不必再之後的每次回圈里产生 conditional branch 来判断 SV 概念上大概是 int index = -1; TYPE *queue[64] = {VACANT, ...}; // 64 times while ( /* CONDITIONS */ ) { index = (index + 1) % 64; // range 0~63 *(queue[index]) = ...; // write once /* 问题在这 如果没有 VACANT 事情就会比较麻烦 */ queue[index] = ...; }
58F:推 CoNsTaR: 可是 C 预设是不做 prefill 的啊,这不符合 C 的精神10/11 02:10
59F:→ CoNsTaR: 我觉得你这样又 prefill 拖效能,遇到错误又让他蒙混过去10/11 02:10
60F:→ CoNsTaR: 当作没发生,而且又不直觉,真心觉得不是什麽好办法10/11 02:10
61F:→ CoNsTaR: 现在其他语言处理这种问题不拖效能而且又能在编译时期处10/11 02:10
62F:→ CoNsTaR: 理完的通常都是用 depnedent types 吧…10/11 02:10
这是个好问题 我要操作这个 queue 超过 10兆 次以上 但是 prefill 这个 queue 也不过 64 个 elements 几乎是微乎其微 两权相害取其轻 XD
63F:推 CoNsTaR: 像 rust 也有用的 linear types 可以知道哪些东西存取过10/11 02:15
64F:→ CoNsTaR: 了,哪些还没,和存取次数,而且也是编译时期就检查完,10/11 02:15
65F:→ CoNsTaR: 也是一种方法10/11 02:15
66F:→ PkmX: 你那样写的意思是前面几个iteration *(queue[index]) 因为 10/11 03:00
67F:→ PkmX: pointer 是 VACANT 所以值写进去被 discard 也是合理的? 10/11 03:00
68F:→ Hazukashiine: 嗯嗯 我希望是这样没错 10/11 03:03
69F:→ PkmX: 这样的话 最直觉的方式还是就分配一个垃圾位置当初始值就好 10/11 03:22
70F:→ PkmX: 或是既然你知道最後SV会不见 就分成两个版本 前面的需要检查 10/11 03:23
71F:→ PkmX: 而後面的phase已知SV不存在 就不需要检查 当然比较进阶的 10/11 03:23
72F:→ PkmX: type system可以帮你纪录你的资料结构里面是否还存在SV 10/11 03:24
73F:→ PkmX: (e.g. phantom type) 不过这个已经扯远了 10/11 03:25
74F:→ PkmX: 当然程式语言或是硬体是否要支援这种blackhole的位置我想 10/11 03:26
75F:→ PkmX: 实作上都是没有问题的 只是有没有必要为了这个例子而去复杂 10/11 03:26
76F:→ PkmX: 化语言的spec或是硬体的ISA罢了 10/11 03:26
77F:→ PkmX: 另外多了一个branch在整个loop中+有分支预测的CPU执行下所 10/11 03:28
78F:→ PkmX: 造成的效能影响多寡也是一个要探讨的问题 10/11 03:28
79F:推 kaneson: 不会叫的bug才是最难找的 10/11 13:00
80F:推 dou0228: 同意楼上,忽略是一个最糟糕的写法,不应该这样做 10/11 17:10
81F:→ samuelcdf: 看起来好像只是把programmer的责任丢给compiler一样 10/12 15:36
82F:→ samuelcdf: 就跟很多有自动记忆体配置回收的语言一样, 写的不好, 10/12 15:38
83F:→ samuelcdf: 只是延後整个软体挂掉的时间一样. 而且更难除错. 10/12 15:38
84F:→ uranusjr: 只要忽略的逻辑清楚行为直观就不糟糕, 断言「不应该」是 10/14 02:12
85F:→ uranusjr: 不太好...其实也有很多语言这麽做, 尤其 Smalltalk 派 10/14 02:12
86F:→ uranusjr: 其实原 po 的发想完全很合理, 也是已经被充分讨论的议题 10/14 02:13
87F:→ uranusjr: 只是和 C 类语言发展的方向不同, 所以感觉在这里没温暖 10/14 02:14
88F:→ uranusjr: 结论是快转换阵营吧 C-like languages 不是一切 (欸 10/14 02:14
※ 编辑: Hazukashiine (122.116.185.23), 10/14/2017 11:40:43
89F:推 Killercat: 这东西我在Obj-C被婊了无数次,这不是一个好方法... 10/14 21:55
90F:→ Killercat: Obj-C你对任何nil(相当於nullptr)的操作都会无声的过去 10/14 21:56
91F:→ Killercat: 应该说,这对大多数C/C++ user来讲 是很不习惯的事情 10/14 21:56
92F:→ Killercat: 另外除错困难等级里面 pre-compiler < compiler < run 10/14 21:58
93F:→ Killercat: time <<<<<<< silently pass 这刚好属於最糟的一种 10/14 21:58
94F:→ LiloHuang: 推 Killercat 的见解,没有 segfault 无声无息很糟糕.. 10/15 00:41
95F:推 steve1012: 不会报错的错是最惨的 几乎没好处 10/15 06:47
96F:→ y3k: 这个设计应该有某部分人会需要 感觉也可行 但是对正常人来说 10/15 11:25
97F:→ y3k: B>Z 原因很简单就是上面说的不会叫的bug最难找 这种设计下去 10/15 11:26
98F:→ y3k: 往往只是逼Programer更频繁的check null或你说的虚无... 10/15 11:27
99F:→ y3k: 而且你要知道正常的C/C++ Code规模XD 10/15 11:28
100F:→ y3k: 所以如果要做 应该是可以做 但是绝对不会预设为启用 这很糟 10/15 11:28







like.gif 您可能会有兴趣的文章
icon.png[问题/行为] 猫晚上进房间会不会有憋尿问题
icon.pngRe: [闲聊] 选了错误的女孩成为魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一张
icon.png[心得] EMS高领长版毛衣.墨小楼MC1002
icon.png[分享] 丹龙隔热纸GE55+33+22
icon.png[问题] 清洗洗衣机
icon.png[寻物] 窗台下的空间
icon.png[闲聊] 双极の女神1 木魔爵
icon.png[售车] 新竹 1997 march 1297cc 白色 四门
icon.png[讨论] 能从照片感受到摄影者心情吗
icon.png[狂贺] 贺贺贺贺 贺!岛村卯月!总选举NO.1
icon.png[难过] 羡慕白皮肤的女生
icon.png阅读文章
icon.png[黑特]
icon.png[问题] SBK S1安装於安全帽位置
icon.png[分享] 旧woo100绝版开箱!!
icon.pngRe: [无言] 关於小包卫生纸
icon.png[开箱] E5-2683V3 RX480Strix 快睿C1 简单测试
icon.png[心得] 苍の海贼龙 地狱 执行者16PT
icon.png[售车] 1999年Virage iO 1.8EXi
icon.png[心得] 挑战33 LV10 狮子座pt solo
icon.png[闲聊] 手把手教你不被桶之新手主购教学
icon.png[分享] Civic Type R 量产版官方照无预警流出
icon.png[售车] Golf 4 2.0 银色 自排
icon.png[出售] Graco提篮汽座(有底座)2000元诚可议
icon.png[问题] 请问补牙材质掉了还能再补吗?(台中半年内
icon.png[问题] 44th 单曲 生写竟然都给重复的啊啊!
icon.png[心得] 华南红卡/icash 核卡
icon.png[问题] 拔牙矫正这样正常吗
icon.png[赠送] 老莫高业 初业 102年版
icon.png[情报] 三大行动支付 本季掀战火
icon.png[宝宝] 博客来Amos水蜡笔5/1特价五折
icon.pngRe: [心得] 新鲜人一些面试分享
icon.png[心得] 苍の海贼龙 地狱 麒麟25PT
icon.pngRe: [闲聊] (君の名は。雷慎入) 君名二创漫画翻译
icon.pngRe: [闲聊] OGN中场影片:失踪人口局 (英文字幕)
icon.png[问题] 台湾大哥大4G讯号差
icon.png[出售] [全国]全新千寻侘草LED灯, 水草

请输入看板名称,例如:Boy-Girl站内搜寻

TOP