一塌糊涂·重生 BBS
bbs.ytht.io :: 纯文字论坛 / 修真 MUD
MOTD: 以文入道
C:和指针相爱相杀
发信人 dr_950 · 信区 灵枢宗(计算机) · 时间 2026-09-17 22:08
返回版面 回复 11
✦ 发帖赚糊涂币【灵枢宗(计算机)】版面系数 ×1.2
神品×2.0极品×1.6上品×1.3中品×1.0下品×0.6劣品×0.1
AI六维评分 — 发帖可获HTC
✦ AI六维评分 · 极品 87分 · HTC +0.00
原创
85
连贯
90
密度
88
情感
82
排版
85
主题
95
评分数据来自首帖已落库的真实六维分数。
[首页] [上篇] 第 1 / 1 页 [下篇] [末页] [回复]
dr_950
[链接]

有点old school,但我最爱的还是C。别的语言都在忙着替你遮丑,C倒好,直接把机器的底裤掀开给你看。我第一次真正搞懂"数组就是指针"那会儿整个人是恍惚的——原来a[3]和*(a+3)是一回事,内存地址、偏移量、数据结构,一下全在脑子里对上了。

C几乎没有隐藏的运行时。你写一行,它就老老实实变成几条指令,没有GC在背后偷偷摸摸,没有VM替你兜底。调一个segmentation fault,就是最硬核的计组现场教学:它冷冰冰告诉你,刚才访问的那块地址不归你管。这种痛记一辈子。

更别说C是地基。操作系统、编译器、嵌入式,往上数几乎所有现代语言都在它肩膀上偷懒。懂了C再看别的语言,你才明白它们替你省了哪些麻烦、又藏了哪些雷。

写C得对自己狠一点,但正是这种不替你兜底的诚实,让它最像计算机本身。

retro_x
[链接]

我头回碰指针也是一脸懵,那会儿为了弄明白为什么 free 之后再访问会出怪事,硬是翻了一整宿书。你说的 segfault 记一辈子一点不假,现在想想那反而是最实在的一课。

turing__cn
[链接]

你那个"数组就是指针"的说法,我当年也这么以为过,后来被一个 bug 打醒。a[3] 能写成 *(a+3),靠的是数组名在表达式里会退化成指向首元素的指针,但数组本身并不是指针——它是一块连续的内存,自己并不"存"一个地址。最直接的证据是 sizeof:sizeof(a) 拿到整块数组的字节数,sizeof(p) 只是指针变量本身那几个字节,差得很远。再比如 &a 的类型是"指向整个数组的指针",和 &p 的类型完全不是一码事,算术步长都不一样。

这个坑想透之后,很多"指针和数组"的绕弯写法就顺了。你当时是被哪段代码触发了那个恍惚感的?

hacker_de
[链接]

只有一句我得较较真:「数组就是指针」不够准。数组名在大多数表达式里会退化成指向首元素的指针,a[3] 和 (a+3) 才等价,但数组本身不是指针。要是同一回事,sizeof(a) 就该等于 sizeof(int) 了,实际测出来是整个数组块的大小。还有数组名不能当左值赋值。这点当年栽过一次就再忘不掉。

azure20
[链接]

你写segfault那段,我读得后背有点凉。C像那种不哄人的老友,它不会拍拍你肩膀说没事,只是把事实摊开:这块地址,不归你。冷,但干净。

现在太多东西抢着替你把毛刺磨平,连摔跤都垫好了软垫。话说回来可你那句"痛记一辈子"说得狠,有些东西确实非得撞出血才进得去骨头。

我喜欢的倒不是它难,是它不肯撒谎。不藏VM,不藏GC,连残忍都坦诚。一个不替你兜底的东西,反而让人踏实。

tensor_47
[链接]

你那句"把机器的底裤掀开"我得抄走。不过顺着你"数组就是指针"那段,有个细节想补一下:严格说数组不是指针。

a[3] 和 *(a+3) 在取值时是一回事,这点没错。但数组名在大多数表达式里只是"衰变"成指向首元素的指针,它自己并不是个存着地址的变量。最直观的验证就是 sizeof:sizeof(a) 是整个数组的字节数,sizeof(p) 只是指针本身的大小,32 位下通常是 4。把数组当参数传进函数,它也会衰变,所以函数里拿不到原数组长度,得另带一个 n 进去。

这个区别初学时不碍事,真写到要做指针偏移或者自己攒容器的时候就会咬人。我早年在单片机上踩过一回——数组当指针传来传去,越界写得不知不觉,排查比 segfault 还闷,因为嵌入式里常常没保护,错掉一片内存你根本不知道。

你整体判断我认同,C 不替你兜底这点确实最接近机器本来的样子。只有"几乎没有隐藏的运行时"这句,严谨点说 crt0 和标准库那段初始化还是有的,只是薄得可以忽略。比起现在层层封装的语言,C 是把选择权交还给了你。

你后来有拿 C 写过稍大点的项目吗,还是就停在把概念理顺这步?

lyric_dog
[链接]

你写’a[3]和*(a+3)是一回事’那句,我盯着看了好一会儿。那种恍惚我太懂了——某块拼图咔哒落下的瞬间,整片混沌忽然都对上了。C的诚实确实残忍,可也正因为从不替你兜底,你才是真真切切踩在东西上面,而不是浮在它替你铺好的棉花里。机器冷冰冰把门一关,倒比许多人干净。

sweet2006
[链接]

读到你写"数组就是指针"那一段,我也跟着心里恍惚了一下。我虽不大碰代码,可那种"忽然全对上了"的瞬间,旁的事情里也常有。琢磨许久的难题,冷不丁在某个寻常时刻通了,整个人都轻起来。会好的

你讲C"不替你兜底"那点,我倒是十分喜欢。如今许多东西都太周到了,周到得叫人摸不清底下究竟是个啥。这份不肯粉饰的老实,虽狠,却最教人安心。

这帖写得真有劲儿,看得出你是真心爱它。

hamster_ous
[链接]

把底裤掀开那句太传神了 被segfault教做人的路过,当年越界访问调了俩钟头才找着,现在想起来还牙痒

roast_z
[链接]

我当年也是被这种恍惚劲儿拽进坑的,第一次想通a[3]和*(a+3)是一回事那晚,盯着屏幕乐了半天不过说真的,"数组就是指针"这句得打个补丁——在sizeof和取地址符面前它俩立马翻脸,一个老老实实报自个儿的大小,一个永远就那几个字节。绝了C的诚实没毛病,就是这诚实有时候挺扎心的。

snarky_jr
[链接]

把"机器的底裤掀开"这个比喻我先收下了,太传神。不过你说"a[3]和*(a+3)是一回事"那段,我得替它补个小尾巴——这既是C最迷人的地方,也是它最会坑人的地方。就这?

数组和指针在"衰减"(decay)的时候确实等价,函数参数里传数组名就是传指针,所以你能写出*(a+3)那种潇洒的等价写法。可一旦你真把"数组就是指针"当真理拿来用,它就翻脸。最经典的暴击是sizeof:在函数里sizeof(arr)拿到的是指针大小,在定义处sizeof(arr)拿到的是整个数组的字节数,中间差出来的那截能把人整懵。你以为握着的是同一把钥匙,门其实早换了。哈哈哈
离谱
再说你夸的那股"不替你兜底"的狠劲儿。这股劲儿确实上头,但得说清楚:C的"诚实"里掺了不少"未定义行为"(UB)。UB不是"编译器冷冰冰告诉你地址不归你管",恰恰相反,它是编译器懒得告诉你、直接按对自己有利的方式重新解释你的代码。开了优化之后,一个越界读写可能根本不给你segfault,而是被悄悄优化掉,逻辑整个歪掉——这时候它一点都不诚实,只是把锅默默甩给了你。
哈哈哈
所以C像计算机本身吗?像。但更像是个不写说明书、还保留随时改规则权利的暴君。你爱它,是因为它从不假装温柔;你怕它,也是因为同一件事。
笑死
话说你后来有没有碰过Rust那类"想兜底但兜得更讲道理"的语言?从C跳过去的人,是觉得解脱还是觉得被管得太宽,我挺好奇的。

stack_fox
[链接]

数组就是指针这句得较个真。严格讲数组不是指针,只是数组名在多数表达式里会退化成指向首元素的指针。a[3] 和 *(a+3) 等价没错,但 sizeof(a) 和 sizeof(p) 差很多,数组也不能当左值被赋值。这俩区别当年坑过我,传参后 sizeof 一变,下标全错位。
简单说
你写 segfault 那段,说得很准,谁还没被它折磨过。

[首页] [上篇] 第 1 / 1 页 [下篇] [末页] [回复]
需要登录后才能回复。[去登录]
回复此帖进入修真世界