Monthly Archives: November 2014

C指针学习吐槽

上周我一直在通过看书、实践来试图弄懂指针。

一直到学习指针之前,我几乎都是不看书的。也没有看老师上课的PPT,因为感觉老师的PPT经常有错误,上课用还凑合,但自己课下看实在是很不舒服。我手头上有两本教材,一本是科大自己的《计算机程序设计(C语言版)》(机械工业出版社的),另一本是C语言经典教材《C程序设计语言(第二版)》是 The C Programming Language 翻译过来的。

按说第二本显然更好。因为LUG里面众人的吐槽和推荐,我不是很相信学校的教材,你看全书代码风格都不统一。而第二本虽说好,之前在阮一峰的博文 C语言学习教材 上面看到,并不适合新手。= =

上周C指针的学习,一开始是看经典教材的,结果看到字符指针就看不懂了,主要是作者为了引入字符指针举了个非常麻烦的例子。几乎看不懂!!

我发现C语言教材有这样的问题,它假设你之前的部分都看过了,所以不时的引入之前的内容而且不附加说明。造成的结果,你即使按照教材上面的代码输了一遍也往往跑不起来。

反正,最后放弃 The C Programming Language 我也认为它太难,太生涩,对新手不友好!相反,上周最后发现,学校的这本教材对指针的解释还可以接受=。就决定看这个了,感觉C指针还是需要资料的指导的。

所以,不仅人和人之间的信任是很重要的,对书本的信任也是很重要的…

看地址学指针2:字符指针

字符指针,也就是指向字符数组(字符串常量)的指针,比起指向数组的指针有略微不同。似乎是因为字符串常量特有的性质。还是通过代码来说明好了。

字符数组

11261

11262

以上操作,并没有体现出字符数组和之前我们学过的数组有什么不同。我只是想看看字符数组的地址啦~(看地址强迫症)

指向字符数组的指针

先回顾一下指向数组的指针

112611

pa 是指向数组 a 的指针变量

112612

pgreeting 是指向字符数组 greeting 的指针变量

112613

所以,从输出来看,指向数组的指针变量和指向字符数组的指针变量没什么区别嘛!输出的地址都差不多长= = 其他的一些操作也应该一样。

字符指针的特殊之处

以上操作实在没搞懂字符指针和普通的指向数组的指针有什么区别…

但字符指针可以这么操作:

1126111

1126112

如果字符指针有特殊之处,就在于它特殊的赋值方式…… 也就是第9-11行,右边写的是字符串常量却代表的是字符数组的指针。

另外还有的就是,由于这样:这些字符指针变量是指向字符串常量的指针,由于常量和变量的储存位置不同,地址要短很多。而还有的就是,两个指向”hello world!”的地址是一样的。

这些应该都是C语言实现的问题。

printf() 函数和字符指针

课本上说,下图第5行的语句中,printf 接受的是一个指向字符数组第一个字符的指针。也就是字符串常量可通过一个指向其第一个元素的指针访问。

好,我们看下以上的代码,pchar 是指向字符串常量第一个元素的指针,它的地址是 0x400671,然后我们可以通过 %s,pchar 来输出这之后的字符,直到遇到 ‘\0’。

所以,以上第9行和第10行应该是等价的,而第5行应该是它们的缩写。感觉上9、10行的写法更规范一些。

看地址学指针1:指针和数组

数组:

学习数组的时候老师强调,数组是在内存空间上连续分布的一群相同数据类型的元素的集合。图中,数组并没有初始化,但运行时仍然分配了内存

11251

11252

的确是这样,并且数组名 a (它的值是地址)和数组第0个元素 a[0] 地址是一样的。

 

指向数组的指针:

11253

11254

以上代码和运行结果就说明了,申明一个指针 pa 指向数组 a 后:

  1. pa + i 和 &a[i] 是等价的
  2. *(pa + i) 和 a[i] 是等价的

事实上,即使你让数组越界也不会有什么事情发现,地址输出仍然是正确的,只是值是不可预知的。如下代码所示:

11255

11256

指向数组的指针没什么特别的。如下,我们可以用指针变量 pa 作为条件判断依据:

11257

11258

 

数组名不是指针变量

数组名是数组首个元素的地址,但不是指针变量。它和指针变量是有区别的!如下:

11267

11268

11265

11266

1.pa 是个变量,可以 pa++ 而 a++ 是非法的。

2.sizeof(数组名)得到的是数组的大小,而sizeof(指针)得到的就是指针的大小啦!

总的来说,数组名更像是指针,而不是指针变量

 

混淆:指针和指针变量

老师说过,指针是指针变量的值。似乎还说过指针和地址是等价的。我们说指针变量是有类型的(那么指针也有类型?),虽然我不是很清楚指针和地址的更多区别,但为了避免混淆,我们还是尽量明确“指针变量”,感觉平时说的“指针”,很多时候是指“指针变量”…这里只是提一下。

 

PS:接下来这部分内容主要参考<The C Programming Language>(C程序设计语言-第2版)来写

这篇文章对应于 第83页 5.3 指针与数组

第5.4 地址算术运算,这部分我没看懂….先跳过了

浪潮之巅读书笔记

书名 作者 出版社 版次 开始阅读 截止阅读
浪潮之巅 吴军 电子工业出版社 第一版 2011年11月第5次 2014-9-23 2014-10-29

浪潮之巅这本书是在李博杰的推荐下开始看的。

关于作者:吴军

书籍目录:百度词条-浪潮之巅(和我看的版本是一样的)

内容回顾

从目录可以看出,这本书花了一半以上的篇幅介绍各个互联网公司,准确的说是曾处在技术浪潮之巅的公司。介绍了它们的兴衰历程,以至于好长一段时间(主要在书籍的前半部分)我一直觉得看的都是各种令人伤心的故事,而且公司看多了那些故事就记不清了,大概记住了这个公司主要是做什么的,曾经多么辉煌…所以,介绍公司这部分我不是很感兴趣(虽然第一次知道一些公司的过去也是蛮好玩的)。

除此之外,本书的另一半篇幅则用于介绍:计算机工业生态链、硅谷、风投、信息产业规律、斯坦福、投行、商业模式、互联网2.0、金融风暴冲击、云计算、下一个Google等内容。这部分偏向于总结出规律性、提到与商业的联系,我觉得正是本书出彩的地方。

正如作者在序言中所说:

我会谈一谈我对每次浪潮的看法,对上述每个公司的看法,以及对其中关键人物的认识。在极度商业化的今天,科技的进步和商机是分不开的。因此,我也要提到间接影响到科技浪潮的风险投资公司,诸如 KPCB 和红杉风投(Sequoia) 以及百年来为科技捧场的投资银行,例如高盛 (Goldman Sachs) 等等。

而作者在后记中再次提到:

我用心去了解商业的规律,本意为了投资;不仅仅是为了让我管理的基金投资有好的会报,更重要的是让我一生的时间投资有效。对我来讲,时间才是我最大的财富,我要把它投到最有意义、最有影响的地方去。经过我的学习、思考和实践,我认定这样一个规律:科技的发展不是均匀的,而是以浪潮的形式出现。对个人来讲,看清楚浪潮,赶浪潮,便不枉此生。

这便解释了本书为什么如此关注商业,并且书名为什么叫做“浪潮之巅”。

收获、联想

看完这本书自然是知道了很多公司的黑历史,算是被科普了一些互联网领域领域的常识,用李博杰的话来说作为谈资是没问题的…(聊天嘛)。

1.公司

其实我以前从来没考虑过将来进入公司工作…(额,之前总想着搞科研…),也是暑假进入果壳实习之后才开始慢慢了解公司的运行。这本书更是加深了我对互联网公司的了解,比如:

  • 我们以后还会多次看到,当一家公司没有人对它有控制权时,它的长期发展就会有问题
  • 一个公司创始者的灵魂常常会永久地留在这家公司,即使他们已经离去。
  • 一家公司的市场份额超过50%以后,就不用再想去将市场份额翻番了。
  • 一个风投公司要想成功,光有钱,有眼光还很不够,还要储备许多能代表自己出去管理公司的人才。这也是著名风险投资公司比小投资公司容易成功的原因之一,前者手中攥着更多更好的管理人才。

2.创业和风投

在【第11章:硅谷的另一面】中花了较大篇幅提到创业的问题,总的来说就是创业成功是很不容易的。一家小公司想要成功,必须具备很多条件,从创始人到团队、从技术到商业模式、执行力判断力、外部环境还有好运气。(具体看185页)而想要在创业时有高回报:

要做到高回报必须首先选对题目。 其次,创业的题目不能和主流公司的主要业务撞车。 除此之外,一个好的题目还必须具备以下几个条件。 1.这个项目一旦做成,要有现成的市场,而且容易横向扩展。(Leverage) 2.今后的商业发展在较长时间内会以几何级数增长。 3.必须具有革命性。

一个月前看到创业和团队管理的内容,我都会想到字幕组管理是否合理和MOOC进中学项目是否具备这些条件。甚至联想到果壳万有青年养成计划主办方和计划发起人有着类似风投公司和创始人的关系。

风投是新兴公司的朋友和帮手,因为它们和创始人的基本利益是一致的。但是通常也有利益冲突的时候。

看到红杉资本对创业者、创业公司的要求就更有感触了,顿时就联想到MOOC进中学项目文案的事情,原来项目文案应该这样写…

找红杉风投前,创业者要准备好一份材料,包括
1.公司目的(一句话讲清楚)。
2.要解决的问题和解决办法,尤其要说清楚该方法对用户有什么好处。
3.要分析为什么现在创业,即证明市场已经成熟。
4.市场规模,再强调一遍,没有十亿美元的市场不要找红杉。
5.对手分析,必须知己知彼。
6.产品及开发计划。
7.商业模式,其重要性就不多讲了。
8.创始人及团队介绍,如果创始人背景不够强,可以拉上一些名人做董事。
9.最后,也是最重要的—想要多少钱,为什么,怎么花。

公司目的、产品介绍,都要做到一句话说清楚。这一点我们应该记住。

3.计算机行业规律

本书不仅讲述科技工业的历史,更重在揭示它的规律性

计算机行业的发展规律:

  • 摩尔定律
  • 安迪-比尔定律
  • 反摩尔定律

信息行业的规律性:

  • 70-20-10律
  • 诺威格定律
  • 基因决定定律

4.硅谷的摇篮——斯坦福:

这一章对我也有较大的启发,按书中所说,斯坦福和工业界的联系会比其他大学紧密一些:

其实和工业界保持联系,并且为工业界做研究,对于创业者来讲,最大的好处在于能够看清产业发展的方向并找到新的机会。这个潜在的好处对于年轻的学生,甚至比对于资深的教授们更明显,因为年轻人更愿意尝试。

开放校园的真正含义在于像斯坦福那样,让大学融入社会。“开放”是斯坦福的“本”,而厂校结合是它的“用”。后者保证了大学开放校园的具体实施。

年轻的学生最有益的校园环境就是那种最贴近今后真实生活的社会环境。

 对于大部分学生来讲,在斯坦福的岁月里学习到的社会知识比课堂知识对自己的一生更有帮助

斯坦福为硅谷输送人才,这样的互利模式的确不错。

5.金融、商业和科技

第13章 讲述风险投资是创业公司幕后英雄,而第16章则讲述了华尔街的贪婪,介绍了美国的金融体系,科技公司的上市过程等等。正如文章第一部分所说,商业模式、金融体系、资金运作等等对科技公司(进而对科技发展)都有很大的影响。由于这部分我自己没弄清楚…就不多说了

 

推荐阅读章节:

第11章 硅谷的另一面

第13章 幕后的英雄—风险投资

第15章 硅谷的摇篮—斯坦福大学

第16章 科技公司的吹鼓手—投资银行

第17章 挑战者—Google 公司

吐槽笔记

边看书的时候有记一些笔记,吐槽在这些地方…

豆瓣读书:我对《浪潮之巅的笔记》(我想知道豆瓣有没有客户端可以记笔记)

涂书笔记:《浪潮之巅》(涂书笔记居然是百度家的…)

现在看来,边看书边吐槽的笔记还是挺有帮助的,可以让我快速回想起本书的内容。

外界评价

这本书的评价比较多,我看的是第一版(豆瓣链接),被吐槽错误非常多…不顾这本书对我相当于接触一个新领域,我并没有察觉出有哪些不严谨的地方。

而知乎这个问题:如何评价《浪潮之巅》这本书(周筠的回答)更是内容丰富,引出了一堆外链。

我还看了一些其他人的读书笔记:

由于我之前完全不了解互联网行业,因此读完这本书我的收获还是挺大的,总的来说这是一本有趣的书,也推荐感兴趣的同学阅读。

 

最后,这本书是从LUG书库借的,欢迎大家来LUG借书。

看地址学指针0:指针的引入

指针的定义

22222

指针的输出(查看地址)

如何printf一个指针呢?指针的格式控制字符是 %p

嗯,从输出结果知道:我的64位(bit)机器上地址大小是8字节(byte)(8 byte = 64 bit)

O(∩_∩)O哈!新技能get√!!以后一定要多多输出指针,当搞不清楚指针到底指向哪里的时候,不妨输出地址看看嘛!

读地址

我们看上图中 x 的地址是 0x 开头的一串字符,也就是十六进制数。地址在计算机中是独一无二的,没有重复的地址,地址的大小在 [0, 2^64 – 1]范围内

2进制64位数字用16进制表示应该有16位,因为每相邻4位数可以等价于1位16进制位。而x的地址之所以不足16位是因为:它把前面的0省略了。我们还可以看到,地址0是(nil),地址1是0x1。

运算符的优先级:* 和 i++ 是一样的

首先,常用运算符的优先级是这样的:

我们知道 i++ 和 ++i 是不一样的:

i++

which means that the current value is used in the expression, and then it is incremented

++i

the current value is incremented first, and then it is used in the expression.

举例:

现在我们看下 * 的优先级:

以上的代码就能说明 *y++ 的执行顺序是从右到左的

这部分代码说明的问题还是很好玩的,我们来解读一下:

  1. 你可以发现两次输出的地址(y的值)差4, 因为int大小位4个字节,也就是 y++ 表示 y = y + 4,加的其实是地址的值
    y = 0x7ffff3405b2c
    y = 0x7ffff3405b30
    0x7ffff3405b2c + 4 = 0x7ffff3405b30
  2. 由于 x 被初始化为1,故 *y = 1;但是 y++之后,y指向x地址的后一个地址,它并没有初始化,故 *y 将不知道得到什么(也就是出现无可预知的值)
  3. (*z)等价于 x,然后我们发现第一次 y 的值和 z 的值是相等的,
    z = 0x7ffff3405b2c
    也就是 x = x + 1,只是把 x 的值加1,并没有改变 x 的地址。相当于往 x 的内存地址上,把原来的值擦掉,写上新的值
  4. *运算,i++,++i 优先级是一样的

 

好啦,最后还有一个新的发现:可执行文件每执行一次,内存地址就重新分配一次!

另外刚刚尝试了这个

* 和 & 互为逆运算

C语言标准库

对于程序员来说,库是最重要的工具之一,可以避免重新造轮子。

课堂上助教给我们展示了:标准库中关于printf( )函数的部分,于是代码刷屏了好几秒 ( ⊙o⊙ )

我们来看下这个wiki词条

  1. 定义:所有目前符合标准的头文件(head file)的集合,以及常用的函数库实现程序→C标准函数库
  2. 标准函数库通常会随附在编译器上。因为 C 编译器常会提供一些额外的非 ANSI C函数功能,所以某个随附在特定编译器上的标准函数库,对其他不同的编译器来说,是不兼容的。
  3. 字符串输入函数gets()(以及 scanf() 读取字符串输入的使用上)是很多缓存溢出的原因,而且大多的程序设计指南会建议避免使用它。
  4. 至此,C标准函数库共有29个头文件。目前我用到的有:<stdio.h>,<math.h>,<complex.h>,<stdlib.h>,<time.h>

  • 最常用的 stdio.h 头文件
  1. C语言为文件输入输出提供了许多标准库函数。这些库函数构成了C标准库头文件<stdio.h>的主体。
  2. 就像 printf() 函数的申明在头文件中,而函数的定义(实现)并不在里面
  3. 原来常量 EOF 和 NULL 都定义在这里,我最近才知道 NULL 相当于0,不是字符0,而是它的ASCII码值为0,并且与字符 ‘\0’ 是等价的
  4. 然后刚刚在ASCII码表上查不到 EOF 的值,它等于 -1。搜了一下又进入阮一峰的博客惹→EOF是什么

最后让我们看下<stdio.h>头文件吧~

  • 头文件与源文件

既然函数的定义不在头文件中,那又在哪呢?又为什么要把函数的申明和定义分开?

除了以 *.h 命名的头文件(header)以外,还有实现函数功能的源文件implementation(*.c),函数的定义就在里面。这么做是基于 information hiding(encapsulation/封装)的原则。

过程是这样,计算机先把 .c 和 .h 文件编译成 .o 文件,再把他们链接在一起,成为一个二进制文件。

而库的编写者只需要把库文件(binary)和头文件(header)给想要用这个库的人就行了

C语言头文件和源文件分开有很多优点,也是有不断发展最后成这样的。有兴趣可以看下这些文章(其实我是自己还没看先记下来…fix me!)

头文件与之实现文件的的关系~

C 语言项目中.h文件和.c文件的关系[转载]

C++:源文件与头文件有什么区别

  • 如何使用C语言库

Step1:include the header files

如果库在 usr/include/ 目录下,那么就用 #include < *.h >

如果库在当前目录下,就用 #include “mylib.h”

Step2:link in the libraries

也就是把你的代码和编译后的二进制库文件链接起来,否则会出现 ”Undefined symbols”

一般 gcc 会自动帮你链接,而简化你的编译操作。但有些情况你还是要自己链接,比如<math.h> 库 (貌似最新的 gcc 已经可以自动链接了?)

输入这一行命令链接即可$: gcc hello.c -l[lib]

(可以尝试写一个…fix me!)

编译时发生了什么?

一、你知道编译的时候发生了什么吗?

你知道C语言编译时发生了什么吗?

是不是像我一样嫌弃老师上课讲的冗长又抽象,搞什么嘛!

说那么多还不如把细节展示给我看!

这周的Shorts视频中有一集叫做Compilers,7分钟不仅详细讲了编译时的四个步骤,还打开查看了 -E -S -c之后的文件。看完感到极大的满足,于是动手试了一遍,下面把视频转换成图文,跟我一起学C语言啦!

首先,写一个大家非常熟悉的”hello world!”代码

哦,我用的编辑器是emacs,操作系统是ubuntu(linux),然后直接在终端(Terminal)下用gcc编译,然后 ./a.out 执行。运行正常

这就完成了一次编译到运行的操作。平时我做作业也都是这样,并没有理会从C语言到机器语言的编译过程具体是什么样。

二、编译的四个步骤

gcc的编译流程分为四个步骤,分别为:

・ 预处理(Pre-Processing)
・ 编译(Compiling)
・ 汇编(Assembling)
・ 链接(Linking)

(1)编译预处理(Pre-Processing)

先输入这一行命令:gcc -E 目标文件.c

回车后就被刷屏了….

把这些代码导入到一个hello2.c的文件中

打开后

这是什么鬼!长这么奇怪= =

哈,居然有800多行,拉到最后终于看到熟悉的 “hello world!”代码片段惹!

这就是编译预处理啦~ 搜了一下什么是编译预处理

【摘】预编译的主要作用如下:

●将源文件中以”include”格式包含的文件复制到编译的源文件中。
●用实际值替换用“#define”定义的字符串。
●根据“#if”后面的条件决定需要编译的代码。

是嘛~刚刚刷屏那个就是把源代码中 #include <stdio.h> 这一行替换成了834行代码..

再拿宏定义试试,注意第3行

相同操作之后,对比预处理之后的.c文件

喏,836行之前没有区别!只有原来 #define name “Jenny” 那一行消失了,取而代之的是源代码中 printf(“Hello %s\n”,name); 中的name 被换成了 ”Jenny”

这就是传说中的,define只做“文本替换”啦

(2)编译(Compiling)

下面用 gcc -S 目标文件.c 来编译,用C语言代码 生成 汇编语言

打开编译后的 hello.s 文件

又是什么鬼,完全看不懂….

(3)汇编(Assembling)

用 gcc -c 目标文件.s 把

打开 hello.o二进制文件

这就是传说中的机器码了,你看到中间的 “hello world!” 了吗?

*Bonus 直接修改二进制文件

让我们改改机器码试试!→_→

改成 no zuo no die ….

运行试试

玩坏了 ..>_<.. 一定是字节不一样!!

改成hello jenny

这样就行啦!

*Bonus 用十六进制看机器码!

哈哈哈,有点像传说中的10101010串了吧!我不知道怎么通过二进制查看机器码….

再改成nozuonodie!!

这次成功了

(4) 链接(Linking)

刚刚一直到这个步骤,其实代码都不能执行。可执行文件 a.out 是我之前一步gcc hello.c生成的。

最后一步链接,需要在二进制文件 hello.o 的基础上,生成可执行文件 .out

命令为 gcc hello.o

列出当前文件夹下面的文件,可以发现只有 hello.out 是绿色的,并且最左边一堆rwx里面只有hello.out带有x,那些rwx表示的是”read”,”write”,”execute”的权限啦!