Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts

Wednesday, November 30, 2011

programming languages and their statements

最近看了《黑客与画家》,其中有一点感受就是语言的能力是有差异的,姑且不去研究究竟谁是老大,了解各种语言的优劣总是没有害处的,正巧在hack news上看到了这篇文章programming languages and their statements,关于各个语言以及对这些语言的陈述,同时还可以对各种语言进行对比。按照字母序罗列了50种语言,如下:

programming languages



  1. Ada

  2. Agda

  3. APL

  4. Assembler

  5. AWK

  6. C

  7. C#

  8. C++

  9. Clojure

  10. Cobol

  11. Common Lisp

  12. Coq

  13. D

  14. Delphi

  15. Eiffel

  16. ELisp

  17. Erlang

  18. F#

  19. Factor

  20. Forth

  21. Fortran

  22. Go

  23. Groovy

  24. Haskell

  25. Haxe

  26. Io

  27. J

  28. Java

  29. Javascript

  30. Lua

  31. Mathematica

  32. Matlab

  33. Mozart-Oz

  34. Objective C

  35. O'Caml

  36. Pascal

  37. Perl

  38. PHP

  39. Prolog

  40. Python

  41. R

  42. REBOL

  43. Ruby

  44. Scala

  45. Scheme

  46. Shell

  47. Smalltalk

  48. Standard ML

  49. TCL

  50. Visual Basic 


每种语言对应的页面大概如下:

  1. 说明在这系列活动中有多少人响应了该语言

  2. 罗列该语言在哪些statement的rank较高或者较低,各10条,从这些statements中我们很容易了解该语言适合做什么以及不适合做什么

  3. 然后列出和这种语言最像以及最不像的5种语言,方面比较

  4. 提供和某种语言比较的功能,选择某种语言既可以比较

  5. 关于这个语言的所有陈述(排名越高说明赞成的人越多),从这个可以进一步了解该语言,当然更好的了解语言的方法应该是去使用它,但是可能不是每个人都能够这么做


举个python的例子,如下

Python




Based on 87822 responses from 7295 people, we've built up the following picture of Python











All statements



  1. I would use this language for casual scripting

  2. This language would be good for teaching children to write software

  3. This language is good for beginners

  4. Code written in this language is very readable

  5. I find this language easy to prototype in

  6. I would use this language as a scripting language embedded inside a larger application

  7. It is easy to tell at a glance what code in this language does

  8. This language excels at text processing

  9. I would use this language to write a command-line app

  10. This language is well suited for an agile development approach using short iterations.

  11. I would use this language for a web project

  12. This language has a good community

  13. This language has a good library distribution mechanism.

  14. This language encourages writing code that is easy to maintain.

  15. Libraries in this language tend to be well documented.

  16. This language has a wide variety of agreed-upon conventions, which are generally adhered to reasonably well, and which increase my productivity

  17. This language is best for very small projects

  18. I would use this language for a desktop GUI project

  19. The resources for learning this language are of high quality

  20. This is a high level language

  21. This language is expressive

  22. I find code written in this language very elegant

  23. This language is good for scientific computing

  24. Third-party libraries are readily available, well-documented, and of high quality

  25. I often write things in this language with the intent of rewriting them in something else later

  26. This language encourages writing reusable code.

  27. I use this language out of choice

  28. I usually use this language on solo projects

  29. I enjoy using this language

  30. This language has well-organized libraries with consistent, carefully thought-out interfaces

  31. I can imagine this will be a popular language in twenty years time

  32. This language is very flexible

  33. I can imagine using this language in my day job

  34. I rarely have difficulty abstracting patterns I find in my code

  35. This language is well documented

  36. I regularly use this language

  37. There are many good open-source tools for this language

  38. I would use this language for writing server programs

  39. There is a wide variety of open source code written in this language

  40. I would like to write more of this language than I currently do

  41. It is easy to debug programs written in this language when it goes wrong

  42. This language excels at symbolic manipulation

  43. This language has unusual features that I often miss when using other languages

  44. Programs written in this language tend to play well with others

  45. When I run into problems my colleagues can provide me with immediate help with this language

  46. Code written in this language tends to be terse

  47. I still discover new features of this language on a fairly regular basis

  48. This language has a very coherent design

  49. Code written in this language will usually run in all the major implementations if it runs in one of them.

  50. I would list this language on my resume

  51. I would recommend most programmers learn this language, regardless of whether they have a specific need for it

  52. This language is good for numeric computing

  53. I usually use this language on projects with many other members

  54. This language is best for very large projects

  55. This language has a very rigid idea of how things should be done

  56. This language has a very dogmatic community

  57. This language is likely to have a strong influence on future languages

  58. When I write code in this language I can be very sure it is correct

  59. Learning this language significantly changed how I use other languages.

  60. I know many other people who use this language

  61. I know this language well

  62. Code written in this language tends to be very reliable

  63. There are many good tools for this language

  64. Learning this language improved my ability as a programmer

  65. This language is easier to use for it's problem domain by removing unneeded expressiveness (such as not being Turing complete).

  66. This language is good for distributed computing

  67. I would use this language for mobile applications

  68. This language has a high quality implementation

  69. This language is likely to be a passing fad

  70. I use many applications written in this language

  71. If this language didn't exist, I would have trouble finding a satisfactory replacement

  72. This language is large

  73. This language matches it's problem domain particularly well.

  74. It's unusual for me to discover unfamiliar features

  75. This is a mainstream language

  76. This language has many features which feel "tacked on"

  77. I find it easy to write efficient code in this language

  78. This language is built on a small core of orthogonal features

  79. This language is likely to be around for a very long time

  80. The semantics of this language are much different than other languages I know.

  81. This language excels at concurrency

  82. This language is minimal

  83. There are many good commercial tools for this language

  84. I use a lot of code written in this language which I really don't want to have to make changes to

  85. I would use this language for writing embedded programs

  86. This language is frequently used for applications it isn't suitable for

  87. If my code in this language successfully compiles, there is a good chance my code is correct.

  88. I would use this language for writing programs for an embedded hardware platform

  89. This language allows me to write programs where I know exactly what they are doing under the hood

  90. I enjoy playing with this language but would never use it for "real code"

  91. This language has a niche in which it is great

  92. Programs written in this language will usually work in future versions of the language

  93. It is too easy to write code in this language that looks like it does one thing but actually does something else

  94. I am sometimes embarrassed to admit to my peers that I know this language

  95. Code written in this language tends to be verbose

  96. Programs written in this language tend to be efficient

  97. This language is suitable for real-time applications

  98. I am reluctant to admit to knowing this language

  99. I learned this language early in my career as a programmer

  100. The thought that I may still be using this language in twenty years time fills me with dread

  101. This language has a strong static type system

  102. This is a low level language

  103. Developers who primarily use this language often burn out after a few years

  104. This language has a niche outside of which I would not use it

  105. I often feel like I am not smart enough to write this language

  106. This language makes it easy to shoot yourself in the foot

  107. This language has an annoying syntax

  108. There is a lot of accidental complexity when writing code in this language

  109. I often get angry when writing code in this language

  110. Writing code in this language is a lot of work

  111. This language is unusually bad for beginners





Tuesday, August 23, 2011

如何通过不编程成为一个好的程序员

daily hack news看到推荐的一篇文章,原文是比较老的,2007年,How To Become a Better Programmer by Not Programming

摘录里面的几句话:

  • 对于Does accumulating experience through the years necessarily make programming easier?盖茨的回答如下:


    • I think after the first three or four years, it's pretty cast in concrete whether you're a good programmer or not.

    • 3-4年既可以看出来你是否是个好的程序员,编程经验的积累并不能让编程更加容易




  • The older I get, the more I believe that the only way to become a better programmer is by not programming. You have to come up for air, put down the compiler for a moment, and take stock of what you're really doing. Code is important, but it's a small part of the overall process.


    • 你需要离开电脑一会,仔细思量你做的东西,代码很重要,但只是整个过程的一小部分


  • To truly become a better programmer, you have to to cultivate passion for everything else that goes on around the programming.


    • 将热情倾注到编程相关的所有事情上去


  • You won't-- you cannot-- become a better programmer through sheer force of programming alone. You can only complement and enhance your existing programming skills by branching out. Learn about your users. Learn about the industry. Learn about your business.The more things you are interested in, the better your work will be.


    • 加入纯粹靠编程,你不可能成为一个更好的程序员

    • 了解你的用户,了解业界,了解你的生意,你感兴趣的事情越多,你的工作将会越美好


Wednesday, October 20, 2010

[zz]C++虚函数表解析

转载自http://www.cppblog.com/xczhang/archive/2008/01/20/41508.html
C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的 成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。比如:模板技 术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议。
关于虚函数的使用方法,我在这里不做过多的阐述。大家可以看看相关的C++的书籍。在这篇文章中,我只想从虚函数的实现机制上面为大家 一个清晰的剖析。
当然,相同的文章在网上也出现过一些了,但我总感觉这些文章不是很容易阅读,大段大段的代码,没有图片,没有详细的说明,没有比较,没有举一反三。不利于学习和阅读,所以这是我想写下这篇文章的原因。也希望大家多给我提意见。
言归正传,让我们一起进入虚函数的世界。

虚函数表

对C++ 了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。 在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其容真实反应实际的函数。这样,在有虚函数的类的实例中这个表被分配在了 这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,它就像一个地图一样,指明了实际所应该调用的函数。
这里我们着重看一下这张虚函数表。在C++的标准规格说明书中说到,编译器必需要保证虚函数表的指针存在于对象实例中最前面的位置(这是为 了保证正确取到虚函数的偏移量)。 这意味着我们通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。
听我扯了那么多,我可以感觉出来你现在可能比以前更加晕头转向了。 没关系,下面就是实际的例子,相信聪明的你一看就明白了。
假设我们有这样的一个类:
class Base {
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
virtual void h() { cout << "Base::h" << endl; }
};
按照上面的说法,我们可以通过Base的实例来得到虚函数表。 下面是实际例程:
typedef void(*Fun)(void);
Base b;
Fun pFun = NULL;
cout << "虚函数表地址:" << (int*)(&b) << endl;
cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl;
// Invoke the first virtual function
pFun = (Fun)*((int*)*(int*)(&b));
pFun();
实际运行经果如下:(Windows XP+VS2003, Linux 2.6.22 + GCC 4.1.3)
虚函数表地址:0012FED4
虚函数表 — 第一个函数地址:0044F148
Base::f
通过这个示例,我们可以看到,我们可以通过强行把&b转成int *,取得虚函数表的地址,然后,再次取址就可以得到第一个虚函数的地址了,也就是Base::f(),这在上面的程序中得到了验证(把int* 强制转成了函数指针)。通过这个示例,我们就可以知道如果要调用Base::g()和Base::h(),其代码如下:
(Fun)*((int*)*(int*)(&b)+0); // Base::f()
(Fun)*((int*)*(int*)(&b)+1); // Base::g()
(Fun)*((int*)*(int*)(&b)+2); // Base::h()
这个时候你应该懂了吧。什么?还是有点晕。也是,这样的代码看着太乱了。没问题,让我画个图解释一下。如下所示:

注意:在上面这个图中,我在虚函数表的最后多加了一个结点,这是虚函数表的结束结点,就像字符串的结束符“\0”一样,其标志了虚函数表的 结束。这个结束标志的值在不同的编译器下是不同的。在WinXP+VS2003下,这个值是NULL。而在Ubuntu 7.10 + Linux 2.6.22 + GCC 4.1.3下,这个值是如果1,表示还有下一个虚函数表,如果值是0,表示是最后一个虚函数表。
下面,我将分别说明“无覆盖”和“有覆盖”时的虚函数表的样子。没有覆盖父类的虚函数是毫无意义的。我之所以要讲述没有覆盖的情况,主要目的是为了给一个对比。在比较之下,我们可以更加清楚地知道其内部的具体实现。

一般继承(无虚函数覆盖)

下面,再让我们来看看继承时的虚函数表是什么样的。假设有如下所示的一个继承关系:

请注意,在这个继承关系中,子类没有重载任何父类的函数。那么,在派生类的实例中,其虚函数表如下所示:
对于实例:Derive d; 的虚函数表如下:

我们可以看到下面几点:
1)虚函数按照其声明顺序放于表中。
2)父类的虚函数在子类的虚函数前面。
我相信聪明的你一定可以参考前面的那个程序,来编写一段程序来验证。

一般继承(有虚函数覆盖)

覆盖父类的虚函数是很显然的事情,不然,虚函数就变得毫无意义。下面,我们来看一下,如果子类中有虚函数重载了父类的虚函数,会是一个什么样子?假设,我们有下面这样的一个继承关系。

为了让大家看到被继承过后的效果,在这个类的设计中,我只覆盖了父类的一个函数:f()。那么,对于派生类的实例,其虚函数表会是下面的一个样子:

我们从表中可以看到下面几点,
1)覆盖的f()函数被放到了虚表中原来父类虚函数的位置。
2)没有被覆盖的函数依旧。
这样,我们就可以看到对于下面这样的程序,
Base *b = new Derive();
b->f();
由b所指的内存中的虚函数表的f()的位置已经被Derive::f()函数地址所取代,于是在实际调用发生时,是Derive::f()被调用了。这就实现了多态。

多重继承(无虚函数覆盖)

下面,再让我们来看看多重继承中的情况,假设有下面这样一个类的继承关系。注意:子类并没有覆盖父类的函数。

对于子类实例中的虚函数表,是下面这个样子:

我们可以看到:
1) 每个父类都有自己的虚表。
2) 子类的成员函数被放到了第一个父类的表中。(所谓的第一个父类是按照声明顺序来判断的)
这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。

多重继承(有虚函数覆盖)

下面我们再来看看,如果发生虚函数覆盖的情况。
下图中,我们在子类中覆盖了父类的f()函数。

下面是对于子类实例中的虚函数表的图:

我们可以看见,三个父类虚函数表中的f()的位置被替换成了子类的函数指针。这样,我们就可以任一静态类型的父类来指向子类,并调用子类的f()了。如:
Derive d;
Base1 *b1 = &d;
Base2 *b2 = &d;
Base3 *b3 = &d;
b1->f(); //Derive::f()
b2->f(); //Derive::f()
b3->f(); //Derive::f()
b1->g(); //Base1::g()
b2->g(); //Base2::g()
b3->g(); //Base3::g()

安全性

每次写C++的文章,总免不了要批判一下C++。这篇文章也不例外。通过上面的讲述,相信我们对虚函数表有一个比较细致的了解了。水可载舟,亦可覆舟。下面,让我们来看看我们可以用虚函数表来干点什么坏事吧。
一、通过父类型的指针访问子类自己的虚函数
我们知道,子类没有重载父类的虚函数是一件毫无意义的事情。因为多态也是要基于函数重载的。虽然在上面的图中我们可以看到Base1的虚表中有Derive的虚函数,但我们根本不可能使用下面的语句来调用子类的自有虚函数:
Base1 *b1 = new Derive();
b1->f1(); //编译出错
任何妄图使用父类指针想调用子类中的未覆盖父类的成员函数的行为都会被编译器视为非法,所以,这样的程序根本无法编译通过。但在运行时,我们可以通过指针的方式访问虚函数表来达到违反C++语义的行为。(关于这方面的尝试,通过阅读后面附录的代码,相信你可以做到这一点)
二、访问non-public的虚函数
另外,如果父类的虚函数是private或是protected的,但这些非public的虚函数同样会存在于虚函数表中,所以,我们同样可以使用访问虚函数表的方式来访问这些non-public的虚函数,这是很容易做到的。
如:
class Base {
private:
virtual void f() { cout << "Base::f" << endl; }
};
class Derive : public Base{
};
typedef void(*Fun)(void);
void main() {
Derive d;
Fun pFun = (Fun)*((int*)*(int*)(&d)+0);
pFun();
}

结束语

C++这门语言是一门Magic的语言,对于程序员来说,我们似乎永远摸不清楚这门语言背着我们在干了什么。需要熟悉这门语言,我们就必需要了解C++里面的那些东西,需要去了解C++中那些危险的东西。不然,这是一种搬起石头砸自己脚的编程语言。

类型转换-基类和派生类之间的转换

    对于内置类型,类型之间的转换比较明显,而且接触得比较多,但是对于自定义类型,尤其是基类和派生类之间到底可以有哪些转换我还是比较模糊,翻了翻书,同时自己试了试,总结如下(如有不对地方,欢迎支持):
    1.子类转成父类
using namespace std;
class A {
    public:
        void display()
        {
            cout << "in A" << endl;
        }
};

class B : public A {
    public:
        void display()
        { 
            cout << "in B" << endl;
        }
};

int main(int argc, char *argv[])

    A a;
    a.display();// A
    B b;
    //a = b; 隐式转换
    //两种旧的强制转换
    //a = A(b); function-style cast
    //a = (A)b; c-style cast
    //推荐
    b.display(); //B
    a = static_cast<A>(b);
    a.display();          // A         
}
当然转了的时候,b就转成了a

2. 父类转子类
假如参考上面的做法,将a转换给b的话,4种方法都是不可行的,那么父类在什么情况下可以转成子类呢?
参考了c++ primer的dynamic_cast操作符的解释:
可以使用dynamic_cast操作符将基类类型对象的引用或指针转换为同一层次中其他类型的引用或者指针。与dynamic_cast一起使用的指针必须是邮箱的--为0或者指向一个对象。
注意:dynamic_cast涉及运行时类型检查,如果绑定到引用或者指针的对象不是目标对象,则dynamic_cast失败的(我认为本质上指针指向的实际对象还是和目标同类型,只是指针是基类而已)。如果转换到指针类型的dynamic_cast失败,则dynamic_cast的结果为0;如果转换到引用类型的dynamic_cast失败,则抛出一个bad_cast类型的异常。
同时,需要基类至少带有一个虚函数,(这点我认为是因为运行时类型检查,类似多态) 

例子:
2.1 目标类型和运行时类型不一致,dynamic_cast的结果为0
class A {
    public:
    virtual void test()
    {
        cout << "A" << endl;
    }
};

class B : public A {
    public:
    void test()
    {
        cout << "B" << endl;
    }
};

int main(int argc, char *argv[])
{
    A *a = new A();
    B *b = dynamic_cast<B*>(a); // can't
    a->test();
    if (b != NULL ) {
        b->test();
    }
}
output:
A
2.2 没有虚函数:error
class A {
    public:
    void test()       
    {
        cout << "A" << endl;
    }
};

class B : public A {
    public:
    void test()
    {
        cout << "B" << endl;
    }
};

int main(int argc, char *argv[])
{
    A *a = new B();
    B *b = dynamic_cast<B*>(a); // 没有虚函数can't
    a->test();
    if (b != NULL ) {
        b->test();
    }
}
compiler error:cannot dynamic_cast ‘a’ (of type ‘class A*’) to type ‘class B*’ (source type is not polymorphic)  

假如目标类型和运行时类型一致,且基类含虚函数的话,即可以
如   
class A {
    public:
    virtual void test()
    {
        cout << "A" << endl;
    }
};

class B : public A {
    public:
    void test()
    {
        cout << "B" << endl;
    }
};

int main(int argc, char *argv[])
{
    A *a = new B();
    B *b = dynamic_cast<B*>(a); // can't
    a->test();
    if (b != NULL ) {
        b->test();
    }
}
                                                
output:   
B
B                                  
    

Tuesday, October 19, 2010

类型转换-基类和派生类之间的转换

    对于内置类型,类型之间的转换比较明显,而且接触得比较多,但是对于自定义类型,尤其是基类和派生类之间到底可以有哪些转换我还是比较模糊,翻了翻书,同时自己试了试,总结如下(如有不对地方,欢迎支持):
    1.子类转成父类
using namespace std;
class A {
    public:
        void display()
        {
            cout << "in A" << endl;
        }
};

class B : public A {
    public:
        void display()
        { 
            cout << "in B" << endl;
        }
};

int main(int argc, char *argv[])

    A a;
    a.display();// A
    B b;
    //a = b; 隐式转换
    //两种旧的强制转换
    //a = A(b); function-style cast
    //a = (A)b; c-style cast
    //推荐
    b.display(); //B
    a = static_cast<A>(b);
    a.display();          // A         
}
当然转了的时候,b就转成了a

2. 父类转子类
假如参考上面的做法,将a转换给b的话,4种方法都是不可行的,那么父类在什么情况下可以转成子类呢?
参考了c++ primer的dynamic_cast操作符的解释:
可以使用dynamic_cast操作符将基类类型对象的引用或指针转换为同一层次中其他类型的引用或者指针。与dynamic_cast一起使用的指针必须是邮箱的--为0或者指向一个对象。
注意:dynamic_cast涉及运行时类型检查,如果绑定到引用或者指针的对象不是目标对象,则dynamic_cast失败的(我认为本质上指针指向的实际对象还是和目标同类型,只是指针是基类而已)。如果转换到指针类型的dynamic_cast失败,则dynamic_cast的结果为0;如果转换到引用类型的dynamic_cast失败,则抛出一个bad_cast类型的异常。
同时,需要基类至少带有一个虚函数,(这点我认为是因为运行时类型检查,类似多态) 

例子:
2.1 目标类型和运行时类型不一致,dynamic_cast的结果为0
class A {
    public:
    virtual void test()
    {
        cout << "A" << endl;
    }
};

class B : public A {
    public:
    void test()
    {
        cout << "B" << endl;
    }
};

int main(int argc, char *argv[])
{
    A *a = new A();
    B *b = dynamic_cast<B*>(a); // can't
    a->test();
    if (b != NULL ) {
        b->test();
    }
}
output:
A
2.2 没有虚函数:error
class A {
    public:
    void test()       
    {
        cout << "A" << endl;
    }
};

class B : public A {
    public:
    void test()
    {
        cout << "B" << endl;
    }
};

int main(int argc, char *argv[])
{
    A *a = new B();
    B *b = dynamic_cast<B*>(a); // 没有虚函数can't
    a->test();
    if (b != NULL ) {
        b->test();
    }
}
compiler error:cannot dynamic_cast ‘a’ (of type ‘class A*’) to type ‘class B*’ (source type is not polymorphic)  

假如目标类型和运行时类型一致,且基类含虚函数的话,即可以
如   
class A {
    public:
    virtual void test()
    {
        cout << "A" << endl;
    }
};

class B : public A {
    public:
    void test()
    {
        cout << "B" << endl;
    }
};

int main(int argc, char *argv[])
{
    A *a = new B();
    B *b = dynamic_cast<B*>(a); // can't
    a->test();
    if (b != NULL ) {
        b->test();
    }
}
                                                
output:   
B
B                                  
    

Sunday, October 17, 2010

c专家编程-对链接的思考

    本章主要是对如何link的思考,包括编译的过程,编译时候的选项,动态连接,静态链接等等,另外就是要提防interpositioning(编写与库函数同名函数)。
    给个直观的图来说明编译器的组成:

    静态链接:如果函数库的一份copy是可执行文件的物理组成部分。以.a结尾
    动态链接:如果可执行文件只是包含了文件名,让载入器在运行时能够寻找程序所需要的函数库。即just in time JIT 链接以.so结尾
    直观上的,静态库要比动态库大。
    动态链接的优点在于体积小,以及可以共享函数库,同时,函数库升级更加容易
    关于链接相关知识,还可以参考Makefile文件的编写,chinaunix的这个写得很有味道

Tuesday, October 12, 2010

c风格字符串的疑问

最近再看c++ primer,比本科时候看的时候体会要深得多,以前看来真的是打酱油的。
看到c风格字符串的时候,有了几个疑问,如下:
程序 1 如下:
#include
#include
#include

using namespace std;
int main(int argc, char *argv[])
{
    const char ca[] = {'h', 'e', 'l', 'l', 'o'};
    cout << strlen(ca) << endl;
    int i = 0;
    while (ca[i] != '\0') {
        cout << ca[i++] << endl;
    }
    cout << strlen(ca) << endl;
}
windows xp下的mingw 结果
D:\c_c_plus>a.exe
5
h
e
l
l
o

6
当时我非常纳闷怎么ca的长度会变化了(5,6),试了几次结果都是这样,没有想明白,于是换到了linux下同样的程序,结果如下
sulong@sulong-desktop:~/Documents/c_c_plus$ ./a.out 
8
h
e
l
l
o

6
这里,ca的长度再次变化了(5,8),我的第一反应是在此好像strlen对ca不起作用了,仔细看书,归结出原因在此:
  • strlen等标准库函数的参数是c风格字符串(c++ primer中描述到,传递给这些函数的指针必须具有非零值,而且指向以null结束的字符数组中的第一个元素); 
  • 而c风格字符串有一点值得注意,需要以null结束,如char ca1[] = {'1', '2'}就不是,而char ca2={'1', '2', '\0'}则是,同时字符串字面量也是c风格字符串的实例; 
  • strlen总是假定其参数字符串以null字符结束,当调用该函数时,系统将会从实参指向的内存空间开始一致搜索结束符,知道恰好遇到null位置,strlen返回的这一段内存空间内总共有多少个字符; 
  • 当实参是非c风格字符的时候,这个数值是不可预知的;
了解原因之后,修改程序,程序 2 和结果如下(windows和linux都正确,这里只列出linux的):
#include
#include
#include

using namespace std;
int main(int argc, char *argv[])
{
    const char ca[] = {'h', 'e', 'l', 'l', 'o', '\0'};
    cout << strlen(ca) << endl;
    int i = 0;
    while (ca[i] != '\0') {
        cout << ca[i++] << endl;
    }
    cout << strlen(ca) << endl;
}

output:
5
h
e
l
l
o
5
不过有一点不是很明白,对于第一个程序,为什么对ca进行解引用之后,ca的长度变化了?windows(5,6),linux(8,6),我的猜测是c++允许计算数组的超出末端的地址,但是不允许对此地址进行解引用操作,否则结果是未定义的。
除了strlen函数,我同时测试了其他cstring中的库函数,参数必须严格安装说明和建议,否则结果也是未定义的,如下 程序 3 :
#include
#include

using namespace std;

int main(int argc, char* argv[])
{
    const char *c1 = "hello";
    const char *c2 = "world";
    char pc[5 + 5 + 1];
    strncpy(pc, c1, 5);
    
    cout << pc << endl;
}

output:
hello6
因为strncpy(pc, c1, 5)的5只够存储hello,而null字符也是需要空间的,使用的时候,时刻记住一定要算上结束符null,需要修改为6以上(当然不能超过pc的size),结果才能正确输出为hello。

两点感悟:
1. 少用c风格字符串,用string
2. 多多编程测试细节

Sunday, October 10, 2010

c专家编程-数组和指针的恩怨情仇

这里把4,9,10章的内容结合在一起,主要谈论的是数组,指针的使用,以及何时相同,何时不同。
通常情况下对于数组和指针单独使用的时候,还是比较清晰的,这里就只简单提下容易混的地方。

什么时候数组和指针式相同的
c语言标准作了如下说明:
    规则1. 表达式中的数组名(与声明不同)被编译器当做一个指向数组第一个元素的指针
int a[10], *p, i = 2;
p = a;
p[i];
p = a;
*(p + i);
p = a + i;
*p是等同的
需要声明这里有极其特殊的理我,对数组的引用不能用指向数组第一个元素的指针来代替
sizefo的时候,sizeof(数组)是数组的大学,而sizeof(指针)是指针的长度
    规则2. 下标总是与真正的偏移量相同
c语言把数组下标改成指针偏移量的根本原因是指针和偏移量是底层硬件使用的基本类型
使用&去数组的地址
数组是一个字符串(或宽字符串)常量初始值
    规则3. 在函数参数的声明中,数组名被编译器当作指向该数组第一个元素的指针
作为形参的数组和指针等同主要出于效率的考虑,假如用传值,传递整个数组代价很大,而指针则不同
这里建议参数定义为指针

除上述情况外,定义和指针必须匹配,如果定义数组,在其他文件对他进行声明时候,必须声明为数组,指针也是

同时需要注意的是,数组名被改写成一个指针参数 并不是地规定义的,数组的数组改写为数组的指针,而不是指针的指针,本来是数组的需要改变,而本来是指针的不需要改变

其他:关于多维数组以及字符串数组等情况不再说明,需要详细参考原文啦:)
--
BlogSpot http://xusulong.blogspot.com Twitter  http://twitter.com/econsh

c专家编程-分析c语言的声明

本章主要说明声明如何构成,如何去解读声明,以及哪些声明是非法的,包括对typedef的详解

1. 声明合法与否
a. 函数的返回值不能是一个函数,如f()(),可以是函数指针,如int(* fun())();
b. 函数的返回值不能是一个数组,如f()[],可以是指向数组的指针,如int(*foo())[];
c. 数组里面不能有函数,如a[](),数组里面可以有函数指针,如int(* a[])(),可以有其他数组,如
int a[][]

2. c语言声明的优先级规则
        A 声明从它的名字开始读取,然后按照优先级顺序依次读取;

        B 优先级从高到低依次是:

            B.1 声明中被括号括起来的那部分;

            B.2 后缀操作符:括号()表示这是一个函数,而方括号[]表示这是一个数组;

            B.3 前缀操作符:星号*标识“指向……的指针”;

        C 如果const和(或者)volatile关键字的后面紧跟类型说明符(如int,long等),那么它作用于类型说明符,在其他情况下,const和(或)volatile关键字作用于它左边紧邻的指针星号。
举例说明:char * const * (*next)();
        A      next                ——next为声明的名字

        B.1 (*next)              ——next为一个指向……的指针

        B.2 (*next)()          ——next是一个函数指针

        B.3 *(*next)()         ——next是一个函数指针,这个函数返回一个指向……的指针

        C    char * const     ——指向字符类型的常量指针
故 char * const *(*next)();的含义就是: next是一个函数指针,这个函数返回一个指向字符类型的常量指针

3. 图示解析c语言的声明
 此图也是一种解析c声明的方法,不过2中的ABC的方式更加简单明了

Saturday, October 9, 2010

c专家编程-这货不是bug,而是语言特性

本章从c语言的一些看上去有点缺陷的地方来提醒我们对相应的知识点需要加倍注意

1 switch的fall through,这个很明了,case后记得加break,否则依次执行

2 字符串会自动连接,如
#include

int main(int agrc, char* argv[])
{
    printf("hello"
            "world \n");
}
会打印出结果helloworld,这个时候要注意,在下面例子中
char * a[] = {
    "one",
    "two"
    "three"
};
因为"two" 之后少了逗号","而变成了"one"和"twothree"组成的字符串数组

3 优先级以及操作符的重载(比如*可以是乘法,也用于指针),有时候并不像想象的很自然的意思,需要对优先级更加理解和掌握

4 局部变量在堆栈分配内存,函数退出,内存被回收问题,可以通过用全局变量,静态变量,显示分配内存,让调用者提供内存(传入以分配内存指针)等等方式来解决

5 lint程序不应该分出来,主要意思是,代码需要更多的检验

c专家编程-穿越时空的迷雾

这是本书的第一部分,主要讲述c语言的前世今生,这里点一下几点
1     K&R C,即Brain KernighanDennis Ritchie
2     ANSI C,这里面说的很幽默,其实ANSI C应该叫做ISO C,因为ANSI采纳的是ISO C,因为在标准之前,已经交了ANSI C,已经广泛使用了。
3     可移植的代码( portable code):严格遵循标准的程序应该是这样的
3.1    只使用已经确定的特性(在某些正确情况下的做法,标准并未明确规定应该怎样做,如参数求职顺序)
3.2    不突破任何由编译器实现的限制
3.3    不产生任何依赖与编译器定义的未确定的或未定义的特性的输出
4     多多阅读ANSI C,里面对细节的问题,描述的很清楚,有相应的约束条件
4.1    里面例举了const char **p的形参,char ** a的实参不相容的例子来说名赋值如何合法等等

Thursday, October 7, 2010

初读C专家编程(Expert C Programming)

    国庆前入手c专家编程,甚为喜欢,虽然假期回家做了不少活,但是依然看得津津有味,粗略地完成了10章,习题之类的并没有去做,回顾的时候会去试试,先有个总体的概念也比较不错。
    Expert C Programming其实是tooold的书了,94年,但因为ANSI C并没有很大的改动,以及所述内容的典型和有趣,一直畅销。
    书的style不像其他教条的书籍,很多故事充斥其中,让你豁然开朗,也会令人捧腹,其中多次调侃sun公司,也设计了apple,以及不少知名it公司和名人。
    在讲述每个知识点的时候,通常会涉及以下内容
  • 阐明观点
  • 铺开来陈述原理,包括为何ansi c这么去定规则,规则的细细剖析
  • 类似知识点,或者容易混淆的知识点,之间的比较
  • 例子
  • 图示
  • 编程挑战
  • 轻松一下,回顾过往因为相关知识点引起的bug造成的趣闻等
通过这些方面的陈述,对一个知识点的理解慢慢加深,搞清楚所以然来
    另外,本书并不是一本c语言的语法,使用等的详细的讲解,而是对其中比较关键的点,难点进行的仔细剖析,需要少许的c语言基础。推荐下C程序设计语言,徐大宝文老师翻译的Brian W. Kernighan和Dennis M. Ritchie的经典书籍。
    后续将回顾每章的知识点:)