问一个初级问题:为何 C 指针这么难

2019-04-27 21:59:56 +08:00
 jziwenchen
我是从 C# / JAVA 开始学的编程 . 能从具体实例问题出发解释为何指针这么难吗?
10411 次点击
所在节点    C
89 条回复
lynskylate
2019-04-27 23:41:35 +08:00
单纯的指针概念不难,难在内存管理,这个指针指向对象的生命周期你很难了解,尤其是大项目,一不小心就可能忘记 free 或者 free 了非法内存。当然,这些一般有约定俗称的规矩。
ipwx
2019-04-28 00:02:52 +08:00
楼上的说法都是指针不难。但我觉得并非如此,指针确实不是 elementary 的编程范式。

这个问题的实质在于,有些程序根本不是 elementary 的编程范式能写完的。

追求性能的算法,为了抠复杂度里面的常数,需要手工管理内存分配、需要尽可能避免条件分支带来的 cpu 流水线失效开销,需要注意 cpu 的 L1、L2、L3 缓存。这些都只有 C++ 才能搞定,Java 不行,C# 也不行。

没错,Java 也能写算法。不用指针,用数组,用下标。实在不行就无脑 new,反正 gc 会回收。除了最后一条,C++ 也能用数组,也能用下标。但是就无法优化到流水线和缓存都妥当的程度。
- - - -

这里随便给你看一个 C++ 实现的哈希表,里面不仅有一级指针还有二级指针,顺便还有 memset、定长数组这种写算法题最常用的手段,你可以感受一下:

https://gist.github.com/haowen-xu/c73fa08f6450b6d8dec9605ae0aa320a
b00tyhunt3r
2019-04-28 00:17:01 +08:00
说不难的都是没刷过国内考研题集的 用谁还不会用啊- -;
lance6716
2019-04-28 00:40:44 +08:00
@ipwx 这个哈希表写的挺一般啊…而且你说的那些也不是 C++写代码能搞定的,而是编译器优化的好吧。像是循环展开、条件传送啥的也跟指针没啥关系啊
CodeCommunist
2019-04-28 00:45:25 +08:00
课程编排问题,如果先学计算机组成原理和汇编就不难了
shijingshijing
2019-04-28 00:52:45 +08:00
我觉得一个比较好的例子是指向函数的指针,其他的一般指向变量的指针,数组的指针其实只要理解了底层原理并不难。而函数的指针则不太一样,指向(函数的指针数组)的指针则更进了一层。

以 Java 和 C#中的订阅模式为例,为什么发布者( Publisher )一旦达到某种条件就发布消息,订阅者( Subscriber)就能根据收到的消息执行对应的操作?实际上是因为发布者维护了一个函数的指针数组 or 链表,一旦达到某种条件,发布者就会依次执行这个函数的指针数组 or 链表中所有指针指向的函数。

不管是 C++还是 Java 还是 C#都是基于这个原理来实现的,只是各自有不同的表述或者实现时略有差异。
masker
2019-04-28 01:23:40 +08:00
我是从 PHP 开始学的编程,自考计科专业,课程有学 c,但我觉得指针并不难
Phariel
2019-04-28 01:29:45 +08:00
你要不从 Golang 学起吧 指针这东西是有点绕
chenjau
2019-04-28 02:03:49 +08:00
花一个月学下汇编. 8086 的汇编即可, 对于指针的了解会有很大帮助. c 是为了解决汇编的痛点而产生的. 你了解了汇编, 不但会知道指针是啥, 而且也能了解为啥要指针.
boris1993
2019-04-28 02:30:04 +08:00
我的体会是,老师胡逼讲,打个比喻就能说明白的事,非要东扯葫芦西扯瓢整的巨 TM 复杂

指针就是胡同门牌号(内存地址)
指针指向的就是那个胡同的口(地址指向实际某个内存区域的起始位置)
silvernoo
2019-04-28 02:32:39 +08:00
难得是指针的指针的指针
xiadong1994
2019-04-28 03:08:33 +08:00
指针概念本身不复杂,复杂的是实际使用的时候。从低往上学一般就不会有这种问题。
jim9606
2019-04-28 03:22:52 +08:00
如果你是对编程概念“指针”(保存指向一个内存地址的变量)疑惑,慢慢习惯吧,因为这涉及到内存的概念。
如果你是对 C/C++的几种引用方式疑惑(引用、左值引用、右值引用、裸指针、智能指针),我觉得这是 C/C++为了贴近底层的理念和历史遗留原因。
GeruzoniAnsasu
2019-04-28 03:31:57 +08:00
指针不“指”向任何东西。

一个叫做 pointer 的东西但却一点“指”的语义都没有,还要让人去以箭头的概念想象它的作用和细节,当然会令人感到困惑。

在我初学 C 的时候(初中)指针这一张死磕了 1 个月都没怎么看懂。一个叫做 a 的 int,它里面放了 1,要用它的时候用它的名字 a 代表它,要改它的值的时候 a=2,就行。这个语义很直观。

但要想象一个“指针”:
一个叫 p 的“指向 int ”的指针,它指向 a,到这里都还很好理解,可是!!
1. a 的地址跟 p 是 tm 什么关系?
2. p 的地址跟 p 是 tm 什么关系?
3. p 的地址咋就又成指针了?没有东西在指它啊???
4. 把一个箭头传进函数里是什么鬼,这个意象好难想象
5. 蛤?修改箭头的指向本身?为什么我不能把箭头放进一个函数里让这个函数改这个箭头就完事了??
6. 为什么修改 a 里边的值我不能把 a 传进来,必须传一个指着 a 的箭头??明明 a=2 却可以


上面的意象中是含有矛盾的地方的(比如修改 a 的值不能使用 a 这件事)如果把指针想象成箭头,一定会陷入上面的某些困惑里。但实际上指针并不是这种东西。



C 语言其实跟汇编极其接近,别看 C 有这么多内置类型(初中刚学的时候两周卡在第一章的数据类型上差点没放弃),实际上语义上总共也就分为两种,值和地址。

放值的变量,就是那堆 char int float..一开始学背单词背了好久的类型
放地址的变量,就是所谓的指针。

指针不“指”向任何东西,它不是箭头,它仅仅是放了地址的一个变量。


题主问的是指针为什么会这么难,本篇回复也主要想讨论为什么看起来会这么难。我对过去学习的历程一直有记忆与不断的反思,所以大概还能想起一点点当时的想法,但说实话现在已经完全没法理解到难点在哪了,全凭印象。

实际上我接触汇编比接触 C 早,但刚开始的时候依然很难理解指针是个什么鬼东西。因为刚开始学一门语言的时候,你摸不清那些语句的语义细节和全貌是怎样的。就比如同样是闭包这种概念,有谁能说出来在 js golang python c++里,闭包 capture 的是引用还是值?一个相似的概念在不同语言里语义很可能都不一样,更别说完全陌生的东西。

pointer 在我看来是个十分糟糕的抽象,赋予了某种完全误导的意义。如果 pointer 这种类型叫做 address type,可能理解它就完全不需要这么费劲,只需要解释一下值传递以及地址的作用就好: 参数传递时会把外面的值复制了一份放到函数里面的叫另一个名字的变量的地址上去。就算只是作为规则默认接受也要尝试用箭头的意象去理解指针然后再尝试解释为什么要用箭头才能修改函数外的变量的值要容易得多。更糟糕的是为了拟合“指针”或者“箭头”这种抽象,设计了一个真是箭头->的操作符。这个操作符完全不能解释 strcut *p; p->是在干什么。它还不如设计成 assign/binding 用的 operator,比如 let p -> a ;也要比 int *p = &a 这种看起来多此一举的 declaration 要直观得多。(当然如果能理解 declaraion 到底在“声明什么”的话还算能理解为什么得写成这样)


所以会的人,尤其是理解了指针就是放地址的变量的人,是很难再想象到指针难点在哪的。指针很难学纯粹是因为设计时期采用了一个令人迷惑的抽象才造成了这种现象。观察一下其它的高级语言,大多数用“引用”这个概念去对变量的地址进行抽象。这个概念真的要比“指针”准确得太多,所以很少见到有人会说引用的概念很难理解或很难学。当遇到深浅拷贝的问题的时候也很容易理解复制一个地址跟复制其中的值是两码事。

顺带一提 c++里既有从 c 继承来的指针也有跟其它高级语言无限相近的“引用”概念,题主如果还没学过的话可以猜一下“一个指向引用的指针”应该是个什么东西 のωの
b00tyhunt3r
2019-04-28 05:35:11 +08:00
@GeruzoniAnsasu
老哥写的极好,不过这句貌似有点读不通?
“参数传递时会把外面的值复制了一份放到函数里面的叫另一个名字的变量的地址上去。”
GeruzoniAnsasu
2019-04-28 06:10:03 +08:00
@b00tyhunt3r emmm 因为这句话改过,然后漏把了字删掉了

参数传递时,会把外面的值 复制一份,放到函数里面的 叫另一个名字的 变量的 地址 上 去
gramyang
2019-04-28 06:34:02 +08:00
指针并不难,指针是非常的复杂,容易产生错误比如野指针。所以才用类似智能指针,GC 等一揽子自动解决方案来屏蔽对指针的操作。
dezhou
2019-04-28 06:58:53 +08:00
你学好 java 就行了,最多看看 python
mmdsun
2019-04-28 07:03:53 +08:00
@reus 我记得 c#是可以用指针的。
@MonoLogueChi
Daming
2019-04-28 07:16:39 +08:00
@mmdsun #39
可以用指针,但是一般场景都用不到 unsafe。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/559289

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX