存档

文章标签 ‘C++’

C语言结构体的数组和指针

2014年4月1日 没有评论 5 人阅读    

单看这文章的标题,你可能会觉得好像没什么意思。你先别下这个结论,相信这篇文章会对你理解C语言有帮助。这篇文章产生的背景是在微博上,看到@Laruence同学出了一个关于C语言的题,微博链接。微博截图如下。我觉得好多人对这段代码的理解还不够深入,所以写下了这篇文章。

zero_array

为了方便你把代码copy过去编译和调试,我把代码列在下面:

#include <stdio.h>
struct str{
    int len;
    char s[0];
};

struct foo {
    struct str *a;
};

int main(int argc, char** argv) {
    struct foo f={0};
    if (f.a->s) {
        printf( f.a->s);
    }
    return 0;
}

你编译一下上面的代码,在VC++和GCC下都会在14行的printf处crash掉你的程序。@Laruence 说这个是个经典的坑,我觉得这怎么会是经典的坑呢?我个人觉得这主要还是对C语言理解不深,如果这算坑的话,那么C++到处都是坑了。

阅读全文…

分类: C/C++语言, 编程语言 标签:
好烂啊有点差凑合看看还不错很精彩 (没人打分)
Loading ... Loading ...

一个浮点数跨平台产生的问题

2014年3月15日 17 条评论 7,914 人阅读    

感谢网友唐磊(微博@唐磊_name)投稿,本文原文在唐磊的博客上(原文地址),原文分析还不够好,而且可能对人有误导,所以,我对原文做了很多修改,并加了Linux下的内容。浮点数是一个很复杂的事情,希望这篇文章有助于大家了解浮点数与其相关的C/C++的编译选项。(注:我没有Windows 32位以及C#的环境,所以,对于Windows 32位的程序和C#的程序没有验证过)

背景就简单点儿说,最近一个项目C#编写,涉及浮点运算,来龙去脉省去,直接看如下代码。

float p3x = 80838.0f;
float p2y = -2499.0f;
double v321 = p3x * p2y;
Console.WriteLine(v321);

很简单吧,马上笔算下结果为-202014162,没问题,难道C#没有产生这样的结果?不可能吧,开启Visual Studio,copy代码试试,果然结果是-202014162。就这样完了么?显然没有!你把编译时的选项从AnyCPU改成x64试试~(服务器环境正是64位滴哦!!)结果居然边成了-202014160,对没错,就是-202014160。有点不相信,再跑两遍,仍然是-202014160。呃,想通了,因为浮点运算的误差,-202014160这个结果是合理的。

为什么合理呢?很正常,因为上面的p3x和p2y是两个float类型,虽然v321是double,但也是两个float类型计算完后再转成double的,float的精度本来也只有7位,所以,对于这个上亿的数,自然没有办法保证精度

但是为什么修改CPU的type会有不同的效果?嗯,我们再试试C/C++。

阅读全文…

分类: .NET编程, C/C++语言 标签: , , , ,
好烂啊有点差凑合看看还不错很精彩 (8 人打了分,平均分: 4.88 )
Loading ... Loading ...

由苹果的低级Bug想到的

2014年2月24日 92 条评论 46,985 人阅读    

2014年2月22日,在这个“这么二”的日子里,苹果公司推送了 iOS 7.0.6(版本号11B651)修复了 SSL 连接验证的一个 bug。官方网页在这里:http://support.apple.com/kb/HT6147,网页中如下描述:

Impact: An attacker with a privileged network position may capture or modify data in sessions protected by SSL/TLS

Description: Secure Transport failed to validate the authenticity of the connection. This issue was addressed by restoring missing validation steps.

也就是说,这个bug会引起中间人攻击,bug的描述中说,这个问题是因为miss了对连接认证的合法性检查的步骤。

这里多说一句,一旦网上发生任何的和SSL/TL相关的bug或安全问题,不管是做为用户,还是做为程序员的你,你一定要高度重视起来。因为这个网络通信的加密协议被广泛的应用在很多很多最最需要安全的地方,如果SSL/TLS有问题的话,意味着这个世界的计算机安全体系的崩溃。

Bug的代码原因

Adam Langley的《Apple’s SSL/TLS bug 》的博文暴出了这个bug的细节。(在苹果的开源网站上,通过查看苹果的和SSL/TLS有关的代码变更,我们可以在文件sslKeyExchange.c中找到下面的代码)

阅读全文…

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

一个“蝇量级” C 语言协程库

2014年1月28日 30 条评论 20,078 人阅读    

(感谢网友 @我的上铺叫路遥 投稿)

协程(coroutine)顾名思义就是“协作的例程”(co-operative routines)。跟具有操作系统概念的线程不一样,协程是在用户空间利用程序语言的语法语义就能实现逻辑上类似多任务的编程技巧。实际上协程的概念比线程还要早,按照 Knuth 的说法“子例程是协程的特例”,一个子例程就是一次子函数调用,那么实际上协程就是类函数一样的程序组件,你可以在一个线程里面轻松创建数十万个协程,就像数十万次函数调用一样。只不过子例程只有一个调用入口起始点,返回之后就结束了,而协程入口既可以是起始点,又可以从上一个返回点继续执行,也就是说协程之间可以通过 yield 方式转移执行权,对称(symmetric)、平级地调用对方,而不是像例程那样上下级调用关系。当然 Knuth 的“特例”指的是协程也可以模拟例程那样实现上下级调用关系,这就叫非对称协程(asymmetric coroutines)。

基于事件驱动模型

我们举一个例子来看看一种对称协程调用场景,大家最熟悉的“生产者-消费者”事件驱动模型,一个协程负责生产产品并将它们加入队列,另一个负责从队列中取出产品并使用它。为了提高效率,你想一次增加或删除多个产品。伪代码可以是这样的:

# producer coroutine
loop
while queue is not full
  create some new items
  add the items to queue
yield to consumer

# consumer coroutine
loop
while queue is not empty
  remove some items from queue
  use the items
yield to producer

阅读全文…

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

Lua简明教程

2013年12月3日 72 条评论 41,350 人阅读    

The Programming Language Lua这几天系统地学习了一下Lua这个脚本语言,Lua脚本是一个很轻量级的脚本,也是号称性能最高的脚本,用在很多需要性能的地方,比如:游戏脚本,nginx,wireshark的脚本,当你把他的源码下下来编译后,你会发现解释器居然不到200k,这是多么地变态啊(/bin/sh都要1M,MacOS平台),而且能和C语言非常好的互动。我很好奇得浏览了一下Lua解释器的源码,这可能是我看过最干净的C的源码了。

我不想写一篇大而全的语言手册,一方面是因为已经有了(见本文后面的链接),重要的原因是,因为大篇幅的文章会挫败人的学习热情,我始终觉得好的文章读起来就像拉大便一样,能一口气很流畅地搞完,才会让人爽(这也是我为什么不想写书的原因)。所以,这必然又是一篇“入厕文章”,还是那句话,我希望本文能够让大家利用上下班,上厕所大便的时间学习一个技术。呵呵。

相信你现在已经在厕所里脱掉裤子露出屁股已经准备好大便了,那就让我们畅快地排泄吧……

运行

首先,我们需要知道,Lua是类C的,所以,他是大小写字符敏感的。

下面是Lua的Hello World。注意:Lua脚本的语句的分号是可选的,这个和GO语言很类似

print("Hello World")

你可以像python一样,在命令行上运行lua命令后进入lua的shell中执行语句。

chenhao-air:lua chenhao$ lua
Lua 5.2.2  Copyright (C) 1994-2013 Lua.org, PUC-Rio
> print("Hello, World")
Hello, World
> 

阅读全文…

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

C++11的Lambda使用一例:华容道求解

2013年10月9日 11 条评论 11,540 人阅读    

(感谢网友 @bnu_chenshuo 投稿)

华容道是一个有益的智力游戏,游戏规则不再赘述。用计算机求解华容道也是一道不错的编程练习题,为了寻求最少步数,求解程序一般用广度优先搜索算法。华容道的一种常见开局如图 1 所示。

广度优先搜索算法求解华容道的基本步骤:

  1. 准备两个“全局变量”,队列 Q 和和集合 S,S 代表“已知局面”。初时 Q 和 S 皆为空。
  2. 将初始局面加入队列 Q 的末尾,并将初始局面设为已知。
  3. 当队列不为空时,从 Q 的队首取出当前局面 curr。如果队列为空则结束搜索,表明无解。
  4. 如果 curr 是最终局面(曹操位于门口,图 2),则结束搜索,否则继续到第 5 步。
  5. 考虑 curr 中每个可以移动的棋子,试着上下左右移动一步,得到新局面 next,如果新局面未知(next ∉ S),则把它加入队列 Q,并设为已知。这一步可能产生多个新局面。
  6. 回到第2步。

其中“局面已知”并不要求每个棋子的位置相同,而是指棋子的投影的形状相同(代码中用 mask 表示),例如交换图 1 中的张飞和赵云并不产生新局面,这一规定可以大大缩小搜索空间。

以上步骤很容易转换为 C++ 代码,这篇文章重点关注的是第 5 步的实现。

阅读全文…

分类: C/C++语言 标签: ,
好烂啊有点差凑合看看还不错很精彩 (11 人打了分,平均分: 4.18 )
Loading ... Loading ...

C++面试中string类的一种正确写法

2013年10月9日 24 条评论 19,044 人阅读    

(感谢网友 @bnu_chenshuo 投稿)

C++ 的一个常见面试题是让你实现一个 String 类,限于时间,不可能要求具备 std::string 的功能,但至少要求能正确管理资源。具体来说:

  1. 能像 int 类型那样定义变量,并且支持赋值、复制。
  2. 能用作函数的参数类型及返回类型。
  3. 能用作标准库容器的元素类型,即 vector/list/deque 的 value_type。(用作 std::map 的 key_type 是更进一步的要求,本文从略)。

换言之,你的 String 能让以下代码编译运行通过,并且没有内存方面的错误。

void foo(String x)
{
}

void bar(const String& x)
{
}

String baz()
{
  String ret("world");
  return ret;
}

int main()
{
  String s0;
  String s1("hello");
  String s2(s0);
  String s3 = s1;
  s2 = s1;

  foo(s1);
  bar(s1);
  foo("temporary");
  bar("temporary");
  String s4 = baz();

  std::vector<String> svec;
  svec.push_back(s0);
  svec.push_back(s1);
  svec.push_back(baz());
  svec.push_back("good job");
}

阅读全文…

分类: C/C++语言 标签: ,
好烂啊有点差凑合看看还不错很精彩 (24 人打了分,平均分: 4.17 )
Loading ... Loading ...

C++模板”>>”编译问题与词法消歧设计

2013年10月2日 14 条评论 4,934 人阅读    

(感谢 @文艺复兴记(todd) 投递此文)

在编译理论中,通常将编译过程抽象为5个主要阶段:词法分析(Lexical Analysis),语法分析(Parsing),语义分析(Semantic Analysis),优化(Optimization),代码生成(Code Generation)。这5个阶段类似Unix管道模型,上一个阶段的输出作为下一个阶段的输入。其中,词法分析是根据输入源代码文本流,分割出词,识别类别,产生词法元素(Token)流,如:

int a = 10;

​经过词法分析会得到[(Type, "int"), (Identifier, "a"), (AssignOperator, "="), (IntLiteral, 10)],在后续的语法分析阶段,就会根据这些词法元素匹配相应的语法规则。在我学习编译原理时,教科书中对于词法分析的介绍主要是基于正则表达式的,言下之意就是普通语言的词法规则是可以通过正则表达式描述的。比如,C语言的变量名规则是“包含字母、数字或下划线,并且以字母或下划线开头”,这就可以用正则表达式[a-zA-Z_][a-zA-Z0-9_]*表达。但是,在实践中我发现不管是主流语言,还是自己设计的DSL都大量存在不能简单通过正则表达式进行词法分析的例子。来看C++98的模版例子:

map<int, vector<int>>

上面这段代码会被C++98编译器中报语法错误,原因在于它把“>>”识别成了位右移运算符而不是两个模版右括号,在C++98中必须在两个括号中间加空格,写成

阅读全文…

分类: C/C++语言 标签:
好烂啊有点差凑合看看还不错很精彩 (20 人打了分,平均分: 4.55 )
Loading ... Loading ...

数据即代码:元驱动编程

2013年8月9日 66 条评论 23,664 人阅读    

(感谢 @文艺复兴记(todd) 投递此文)

几个小伙伴在考虑下面这个各个语言都会遇到的问题:

问题:设计一个命令行参数解析API

一个好的命令行参数解析库一般涉及到这几个常见的方面:

1) 支持方便地生成帮助信息

2) 支持子命令,比如:git包含了push, pull, commit等多种子命令

3) 支持单字符选项、多字符选项、标志选项、参数选项等多种选项和位置参数

4) 支持选项默认值,比如:–port选项若未指定认为5037

5) 支持使用模式,比如:tar命令的-c和-x是互斥选项,属于不同的使用模式

经过一番考察,小伙伴们发现了这个几个有代表性的API设计:

1. getopt():

getopt()是libc的标准函数,很多语言中都能找到它的移植版本。

阅读全文…

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

类型的本质和函数式实现

2013年7月22日 28 条评论 13,644 人阅读    

(感谢 @文艺复兴记(todd) 投递此文)

在上一篇文章《二叉树迭代器算法》中,我介绍了一种基于栈的二叉树迭代器实现。程序设计语言和Haskell大牛@九瓜 在看过之后评论到:

这里用了 stack 来做,有点偷懒,所以错失了一个抽象思考机会。如果我们能够理解二叉树到线性表的转换过程,完全可以把 Iterator 当作抽象的线性表来看,只要定义了关于 Iterator 的 empty, singleton, 还有 append 操作,实现二叉树的 Iterator 就变得非常直观。

“错失了一个抽象思考机会”是什么意思呢?我理解九瓜的意思是基于栈的实现虽然是正确的,但它缺乏对于迭代器类型本质的理解,不具有通用性。如果能对迭代器进行合适地抽象就可以像二叉树递归遍历一样自然地得出二叉树迭代器,甚至其他更复杂的数据结构,只要我们能写出它的遍历算法,迭代器算法都可以自然推出。

类型的本质

九瓜提到了通过empty, singleton和append操作对Iterator进行抽象,我本来打算直接根据这个思路介绍函数式的二叉树迭代器实现,但是考虑到其实首要的问题在于理解类型的本质,而并不是所有人都具备这个基础,不如先普及一下类型基础再进入具体实现。那么下面我们就先来认识一下类型到底是什么?我们先以来看看表示元素对的Pair类型,可能有人一提到Pair类型马上就会在脑海中浮现出下面的结构:

阅读全文…

分类: 编程语言 标签: , , ,
好烂啊有点差凑合看看还不错很精彩 (17 人打了分,平均分: 4.53 )
Loading ... Loading ...