Browsed by
分类: 编程语言

C/C++语言中闭包的探究及比较

C/C++语言中闭包的探究及比较

感谢投稿人 @思禽饮霜 

这里主要讨论的是C语言的扩展特性block。该特性是Apple为C、C++、Objective-C增加的扩展,让这些语言可以用类Lambda表达式的语法来创建闭包。前段时间,在对CoreData存取进行封装时(让开发人员可以更简洁快速地写相关代码),我对block机制有了进一步了解,觉得可以和C++ 11中的Lambda表达式相互印证,所以最近重新做了下整理,分享给大家。

0. 简单创建匿名函数

下面两段代码的作用都是创建匿名函数并调用,输出Hello, World语句。分别使用Objective-C和C++ 11:

^{ printf("Hello, World!\n"); } ();
[] { cout << "Hello, World" << endl; } ();

Lambda表达式的一个好处就是让开发人员可以在需要的时候临时创建函数,便捷。

在创建闭包(或者说Lambda函数)的语法上,Objective-C采用的是上尖号^,而C++ 11采用的是配对的方括号[]

不过“匿名函数”一词是针对程序员而言的,编译器还是采取了一定的命名规则。

比如下面Objective-C代码中的3个block,

#import <Foundation/Foundation.h>

int (^maxBlk)(int , int) = ^(int m, int n){ return m > n ? m : n; };

int main(int argc, const char * argv[])
{
    ^{ printf("Hello, World!\n"); } ();

    int i = 1024;
    void (^blk)(void) = ^{ printf("%d\n", i); };
    blk();

    return 0;
}

会产生对应的3个函数:

阅读全文 Read More

好烂啊有点差凑合看看还不错很精彩 (19 人打了分,平均分: 3.89 )
Loading...
无锁队列的实现

无锁队列的实现

————注:本文于2019年11月4日更新————

关于无锁队列的实现,网上有很多文章,虽然本文可能和那些文章有所重复,但是我还是想以我自己的方式把这些文章中的重要的知识点串起来和大家讲一讲这个技术。下面开始正文。

关于CAS等原子操作

在开始说无锁队列之前,我们需要知道一个很重要的技术就是CAS操作——Compare & Set,或是 Compare & Swap,现在几乎所有的CPU指令都支持CAS的原子操作,X86下对应的是 CMPXCHG 汇编指令。有了这个原子操作,我们就可以用其来实现各种无锁(lock free)的数据结构。

这个操作用C语言来描述就是下面这个样子:(代码来自Wikipedia的Compare And Swap词条)意思就是说,看一看内存*reg里的值是不是oldval,如果是的话,则对其赋值newval

int compare_and_swap (int* reg, int oldval, int newval)
{
  int old_reg_val = *reg;
  if (old_reg_val == oldval) {
     *reg = newval;
  }
  return old_reg_val;
}

我们可以看到,old_reg_val 总是返回,于是,我们可以在 compare_and_swap 操作之后对其进行测试,以查看它是否与 oldval相匹配,因为它可能有所不同,这意味着另一个并发线程已成功地竞争到 compare_and_swap 并成功将 reg 值从 oldval 更改为别的值了。

这个操作可以变种为返回bool值的形式(返回 bool值的好处在于,可以调用者知道有没有更新成功):

bool compare_and_swap (int *addr, int oldval, int newval)
{
  if ( *addr != oldval ) {
      return false;
  }
  *addr = newval;
  return true;
}

与CAS相似的还有下面的原子操作:(这些东西大家自己看Wikipedia,也没什么复杂的)

注:在实际的C/C++程序中,CAS的各种实现版本如下:

阅读全文 Read More

好烂啊有点差凑合看看还不错很精彩 (56 人打了分,平均分: 4.25 )
Loading...
一次Ajax查错的经历

一次Ajax查错的经历

先说故事,再说想法吧。

我有一朋友做网站,用jQuery的Ajax方法从后端载入一段HTML代码然后动态插入到网页的Div元件中。这个东西太普遍了。jQuery强大的load方法可以完成这个事情。朋友的代码是这么写的:

[javascript]var tab = jQuery("#dynamic_tab");
var url = "/list_ajax/";
tab.load(url);[/javascript]

简单到不能再简单了。在Chrome,Firefox,Safari下运行一点问题也没有,只有IE不行,不管是IE7,IE8,还是IE9。问题的症壮是,使用IE访问那个Ajax的链接,没有问题,但是在jQuery的Ajax方法返回了“undefined”的respons对象。没有任何报错!

怎么搞也搞不定,只好Google了一下——“jQuery load IE”,一看,很多人都在问这个问题。于是开始了散弹枪编程方式

排在第一的就是StackOverflow被浏览了33K次的这个问题:jQuery’s .load() not working in IE – but fine in Firefox, Chrome and Safari,答案没有被打勾(不靠谱),StackOverflow还有很多人问相似的问题,不过都没有答案。不管三七二十一,先试了一下,散弹枪嘛。试了半天都没有用。

然后上Google查,又看到有人说的IE缓存的问题,什么,要把cache设置成false,或是用下面的方法来解决:

[javascript]var tab = jQuery("#dynamic_tab");
var fuckie = Math.random();
var url = "/list_ajax/"+"?fuckie="+fuckie;
tab.load(url);[/javascript]

反正还是一样,统统不Work,几乎所有的都试了,都不Work。搞了一天的朋友恼怒道:“Microsoft应该快点倒闭吧,产品太烂了”。IE的确是太烂了。

阅读全文 Read More

好烂啊有点差凑合看看还不错很精彩 (35 人打了分,平均分: 3.86 )
Loading...
对技术的态度

对技术的态度

最近人品爆发,图灵社区,InfoQ,51CTO相继对我做了采访,前两天我把InfoQ对我的采访张贴了出来,今天,图灵社区和51CTO对我的采访发布了(图灵的访谈 ,51CTO的访谈),我是一个有技术焦虑症的人,我的经历比较特殊,对大家来说可能也没有什么意思,这两个采都有一些重叠的部分,不过有些观点我想再加强一些,并放在这里和大家一起分享一下。

对于日新月异的新技术,你是什么态度?

遇到新技术我会去了解,但不会把很大的精力放在这些技术(如:NoSQL,Node.js,等)。这些技术尚不成熟,只需要跟得住就可以了。技术十年以上可能是一个门槛。有人说技术更新换代很快,我一点儿都不觉得是这样想。虽然有不成熟的技术不断地涌出,但是成熟的技术,比如Unix,40多年,C,40多年,C++,30多年,TCP/IP,20多年,Java也有将近20年了……,所以,如果你着眼成熟的技术,其实并不多。

我的观点是——要了解技术就一定需要了解整个计算机的技术历史发展和进化路线。(这个观点,我在《程序员练级攻略》和《C++的坑多吗?》中提到过多次了。)因为,你要朝着球运动的轨迹去,而不是朝着球的位置去,要知道球的运动轨迹,你就需要知道它历史上是怎么跑的

如果要捋一个技术的脉络,70年代Unix的出现,是软件发展方面的一个里程碑,那个时期的C语言,也是语言方面的里程碑。(当时)所有的项目都在Unix/C上,全世界人都在用这两样东西写软件。Linux跟随的是Unix, Windows下的开发也是 C/C++。这时候出现的C++很自然就被大家接受了,企业级的系统很自然就会迁移到这上面,C++虽然接过了C的接力棒,但是它的问题是它没有一个企业方面的架构,而且太随意了,否则也不会有今天的Java。C++和C非常接近,它只不过是C的一个扩展,长年没有一个企业架构的框架。而Java在被发明后,被IBM把企业架构这部分的需求接了过来,J2EE的出现让C/C++捉襟见肘了,在语言进化上,还有Python/Ruby,后面还有了.NET,但可惜的是这只局限在Windows平台上。这些就是企业级软件方面语言层面就是C -> C++ -> Java这条主干,操作系统是Unix -> Linux/Windows这条主干,软件开发中需要了解的网络知识就是Ethernet -> IP -> TCP/UDP 这条主干。另外一条脉络就是互联网方面的(HTML/CSS/JS/LAMP…)。我是一个有技术忧虑症的人,这几条软件开发的主线一定不能放弃。

另外,从架构上来说,我们可以看到,

阅读全文 Read More

好烂啊有点差凑合看看还不错很精彩 (43 人打了分,平均分: 4.56 )
Loading...
C++的坑真的多吗?

C++的坑真的多吗?

先说明一下,我不希望本文变成语言争论贴。希望下面的文章能让我们客观理性地了解C++这个语言。(另,我觉得技术争论不要停留在非黑即白的二元价值观上,这样争论无非就是比谁的嗓门大,比哪一方的观点强,毫无价值。我们应该多看看技术是怎么演进的,怎么取舍的。)

事由

周五的时候,我在我的微博上发了一个贴说了一下一个网友给我发来的C++程序的规范和内存管理写的不是很好(后来我删除了,因为当事人要求),我并非批判,只是想说明其实程序员是需要一些“疫苗”的,并以此想开一个“程序员疫苗的网站”,结果,@简悦云风同学直接回复到:“不要用 C++ 直接用 C , 就没那么多坑了。”就把这个事带入了语言之争。

我又发了一条微博

@左耳朵耗子 新浪个人认证 : 说C++比C的坑更多的人我可以理解,但理性地思考一下。C语言的坑也不少啊,如果说C语言有90个坑,那么C++就是100个坑(另,我看很多人都把C语言上的坑也归到了C++上来),但是C++你得到的东西更多,封装,多态,继承扩展,泛型编程,智能指针,……,你得到了500%东西,但却只多了10%的坑,多值啊

结果引来了更多的回复(只节选了一些言论):

  • @淘宝褚霸也在微博里说:“自从5年前果断扔掉C++,改用了ansi c后,我的生活质量大大提升,没有各种坑坑我。
  • @Laruence在其微博里说: “我确实用不到, C语言灵活运用struct, 可以很好的满足这些需求.//@左耳朵耗子: 封装,继承,多态,模板,智能指针,这也用不到?这也学院派?//@Laruence: 问题是, 这些东西我都用不到… C语言是工程师搞的, C++是学院派搞的

那么,C++的坑真的多么?我还请大家理性地思考一下

阅读全文 Read More

好烂啊有点差凑合看看还不错很精彩 (52 人打了分,平均分: 4.42 )
Loading...
一个fork的面试题

一个fork的面试题

前两天有人问了个关于Unix的fork()系统调用的面试题,这个题正好是我大约十年前找工作时某公司问我的一个题,我觉得比较有趣,写篇文章与大家分享一下。这个题是这样的:

题目:请问下面的程序一共输出多少个“-”?

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(void)
{
   int i;
   for(i=0; i<2; i++){
      fork();
      printf("-");
   }

   wait(NULL);
   wait(NULL);

   return 0;
}

如果你对fork()的机制比较熟悉的话,这个题并不难,输出应该是6个“-”,但是,实际上这个程序会很tricky地输出8个“-”。

要讲清这个题,我们首先需要知道fork()系统调用的特性,

阅读全文 Read More

好烂啊有点差凑合看看还不错很精彩 (72 人打了分,平均分: 4.67 )
Loading...
各式各样的验证码

各式各样的验证码

还记得以前那篇《超强验证码》?其实这个世界变态的验证码还有很多,下面是一个列表向像展示了各种稀奇古怪的验证码。不过本文并不单单只是收集这验证码,前面的比较恶搞,后面的会向你展示什么是有accessibility验证码。

完全看不清楚的

这是人类的字符吗?

图案中的字母是什么?

阅读全文 Read More

好烂啊有点差凑合看看还不错很精彩 (24 人打了分,平均分: 4.08 )
Loading...
代码执行的效率

代码执行的效率

在《性能调优攻略》里,我说过,要调优性需要找到程序中的Hotspot,也就是被调用最多的地方,这种地方,只要你能优化一点点,你的性能就会有质的提高。在这里我给大家举三个关于代码执行效率的例子(它们都来自于网上)

第一个例子

PHP中Getter和Setter的效率来源reddit

这个例子比较简单,你可以跳过。

考虑下面的PHP代码:我们可看到,使用Getter/Setter的方式,性能要比直接读写成员变量要差一倍以上。

<?php
	//dog_naive.php

	class dog {
		public $name = "";
		public function setName($name) {
			$this-&gt;name = $name;
		}
		public function getName() {
			return $this-&gt;name;
		}
	}

	$rover = new dog();
        //通过Getter/Setter方式
	for ($x=0; $x<10; $x++) {
		$t = microtime(true);
		for ($i=0; $i<1000000; $i++) {
			$rover->setName("rover");
			$n = $rover->getName();
		}
		echo microtime(true) - $t;
		echo "\n";
	}
        //直接存取变量方式
        for ($x=0; $x<10; $x++) {
		$t = microtime(true);
		for($i=0; $i<1000000; $i++) {
			$rover->name = "rover";
			$n = $rover->name;
		}
		echo microtime(true) - $t;
		echo "\n";
	}
?>

这个并没有什么稀,因为有函数调用的开销,函数调用需要压栈出栈,需要传值,有时还要需要中断,要干的事太多了。所以,代码多了,效率自然就慢了。所有的语言都这个德行,这就是为什么C++要引入inline的原因。而且Java在打开优化的时候也可以优化之。但是对于动态语言来说,这个事就变得有点困难了。

阅读全文 Read More

好烂啊有点差凑合看看还不错很精彩 (18 人打了分,平均分: 3.72 )
Loading...
少即是极多

少即是极多

感谢网友 @innocentim (Twitter) 投稿

这是一篇翻译练习。力图保留原意。若有不准确处,求速速指出。猛击此处(墙)看原文。作者为Rob Pike,贝尔实验室来的大牛,现在就职于Google。他主导了Go语言的创建工作。下面是正文——

——————————————正文分隔线——————————————

这是我在2012年6月的Go SF上演讲的文本。

这是一个个人演讲。 我承认,虽然面前的团队让Go诞生并延续,但是我的观点并不代表任何其他Go语言小组成员的意见。 我也想感谢Go SF的组织者提供这个和你们交流的机会。

几星期前我被问起:“你在推出Go的过程中遇到的最大的惊奇是什么?”我立即意识到了答案: 虽然我们希望C++程序员意识到Go是个较好的选择,但是令人意外的是,大多数Go程序员来自Python和Ruby这样的动态语言,而很少有来自C++的。

我们——Ken,Robert和我——是C++程序员(译者: Ken也用C++?),当时在为解决我们所写的这类软件产生的问题设计一个新的语言。 这似乎有点自相矛盾,因为别的C++程序员根本不关心这些问题,更不会去设计一个语言。

我今天想说的是关于那些激发我们创造Go的事情,和为什么它本不应令我们如此惊讶。 我保证这些内容更多与Go相关而不是C++,所以即使你不很了解C++你也能跟得上。

回答可以这样归结: 你认为”少即是多”呢,还是”少就是少”?

这里有个比喻,将以真实故事的形式给出。 贝尔实验室中心原来发放3位数号码: 物理研究是111,计算科学研究是127,如此这般。 1980年代早期,一个便笺飞过来说”鉴于你们对研究的理解有所加深,将为你们的号码多加上一位,以便更好地体现你们的工作”。 所以我们中心的号码变成了1127。 Ron Hardin半当真地开玩笑说如果我们真的理解我们的世界更好一点的话,我们将丢掉一位数字,将127变成27。 当然主管没听到这个笑话(这也不是我们希望的),但是我想这里面有点值得思考的东西。 少即是多。 你理解得越好,你将变得越简洁。

阅读全文 Read More

好烂啊有点差凑合看看还不错很精彩 (31 人打了分,平均分: 4.29 )
Loading...
Javascript 中的 var

Javascript 中的 var

MelonCard发布了一篇文章——”how one missing var ruined our launch“(”少写了一个var毁了我的网站”),这篇文章是说MelonCard用Node.js做后台,因为出了一个小高峰——有50-100人注册,结果整个网站都不响应了,而且还出现了很多奇怪的问题。当他们调查到问题的要源的时候,他们发现下面的代码少写了一个var。

[javascript]app.all(‘/apps/:user_id/status’, function(req, res, next) {
// …
initial = extractVariables(req.body);
});[/javascript]

为什么inital少写一个var会引发这个问题呢?因为如果你不写var,这个局部的变量会被javascript当成全局变量,而这个变量又是一个函数,所以,当多用户并发的时候,这个本应该在不同用户下互不干扰的变量,成了各个用户共享的东西。试想,用户A的数据被用户B覆盖了,用户A和B的数据还没处理完,结果被新的C给搞乱了,程序的逻辑自然出现了问题。

在stackoverflow.com上有这么一个贴子说明了“有var”和“无var”的差别:

// These are both globals
var foo = 1;
bar = 2;

function test()
{
    var foo = 1; // Local
    bar = 2;     // Global

    // Execute an anonymous function
    (function()
    {
        var wibble = 1; // Local
        foo = 2; // Inherits from scope above (creating a closure)
        moo = 3; // Global
    }())
}

上面这个示例告诉我们,如果你不用var,那么这个js引擎会一层一层地向上找父作用域中的变量,如果找到了,就用,如果找不到了,就会帮你定义一个全局的变量。上面这个例子充分说明了这一点。所以,如果你想在当前的作用域用声明变量,你一定要用var。这对于一些乱写javascript代码的程序员要注意了。这里再给大家介绍一个工具——

阅读全文 Read More

好烂啊有点差凑合看看还不错很精彩 (16 人打了分,平均分: 3.94 )
Loading...