光辉's profile胡言乱语PhotosBlogListsMore Tools Help

Blog


    9/1/2008

    返回函数指针的函数

    第一次看到 void(*signal(int sig, void(*func)(int)))(int)时,半天没有反应过来这是个什么东西。其实这是Linux中系统函数signal的函数声明,也就是说signal是个函数。signal的两个参数分别为int sig和void (*func)(int),第二个参数是一个函数指针。如果将signal(int sig, void(*func)(int))看做一个整体,使用SIGNAL代表它,那么原表达式可以化为void(*SIGNAL)(int),这时可以清晰地看出SIGNAL是一个与参数func类型相同的函数指针,也就是说函数signal(int sig, void(*func)(int))的返回值是一个函数指针void(*)(int)。
     

    7/18/2008

    设置Ubuntu的本地环境

     
     
    Ubuntu的locale命令
     
    在Ubuntu中输入locale命令可以查看系统的本地环境设置。在我的机器上运行locale命令,结果如下:
     
    LANG=zh_CN.GBK
    LANGUAGE=zh_CN:zh
    LC_CTYPE="zh_CN.GBK"
    LC_NUMERIC="zh_CN.GBK"
    LC_TIME="zh_CN.GBK"
    LC_COLLATE="zh_CN.GBK"
    LC_MONETARY="zh_CN.GBK"
    LC_MESSAGES="zh_CN.GBK"
    LC_PAPER="zh_CN.GBK"
    LC_NAME="zh_CN.GBK"
    LC_ADDRESS="zh_CN.GBK"
    LC_TELEPHONE="zh_CN.GBK"
    LC_MEASUREMENT="zh_CN.GBK"
    LC_IDENTIFICATION="zh_CN.GBK"
    LC_ALL=

    可以通过以下方式修改Ubuntu的本地环境:
     
    1) cd  /etc/default/  可以看到这个目录下有一个locale文件。
    2) vim locale  在我的Ubuntu上打开locale文件,可以看到下面的内容:
        LANG="zh_CN.GBK"
         LANGUAGE="zh_CN:zh"
    3) 可以通过修改LANG和LANGUAGE的值来修改本地环境。假设修改为UTF-8编码的英文:
        LANG="en_US.UTF-8"
         LANGUAGE="en_US:en"
    4) 退出系统重新登录并运行locale命令,可以看到本地环境已经改变。

    注意:如果在第三步中将locale文件的内容全部删除,使其成为空文件,那么在第四步中将看到本地环境被设置为POSIX。

    Ubuntu的locale-gen命令

    local-gen用来生成一系列的locale定义文件,Ubuntu正是根据这些文件来确定相应的locale格式。locale-gen会根据目录/var/lib/locales/supported.d/下的local文件生成相应的一系列locale文件。生成的文件默认存放在/usr/lib/locale/目录下。

    在我的机器上/var/lib/locales/supported.d/local这个文件的内容为下:
     
       en_US.UTF-8 UTF-8
        zh_CN.GBK GBK
    当运行命令locale-gen之后,将会在/usr/lib/locale/目录下生成两个文件夹en_US.utf-8和zh_CN.gbk,这两个目录中包含了相应的一系列locale文件。

    如果想让我的Ubuntu支持zh_CN.UTF-8,那么需要按照以下步骤进行操作:
    1) 修改文件
    /var/lib/locales/supported.d/local,在其中添加一行zh_CN.UTF-8 UTF-8
    2) 使用管理员帐户运行locale-gen命令,运行完之后将在
    /usr/lib/locale/目录下发现一个新的文件夹zh_CN.utf-8
    这样我的Ubuntu系统就获得了支持zh_CN.UTF-8的能力,然后可以按照前面所说的方法修改文件/etc/default/locale中的LANG和LANGUAGE变量值为zh_CN.UTF-8和zh_CN:zh并重新登录Ubuntu就可以转换到zh_CN.UTF-8的本地环境中。

    Ubuntu支持的所有locale

    文件/usr/share/i18n/SUPPORTED中列出了Ubuntu支持的所有locale。如果要使用这个文件中列出的某个locale,必须先通过locale-gen生成相应的一系列定义文件。

    应用

    由于windwos的命令行工具cmd只支持GBK的编码方式,而Ubuntu的默认安装一般支持的是UTF-8的编码方式,所以在cmd中使用telnet登录Ubuntu就会产生部分乱码问题。可以通过上面介绍的命令将Ubuntu的locale设置为zh_CN.GBK,从而解决乱码问题。

    小结

    主要的几个文件和目录:
    /etc/default/locale
    /var/lib/locales/supported.d/local

    /usr/lib/local/
    /usr/share/i18n/SUPPORTED

    有关locale和locale-gen的更多信息,请直接man。


     

    7/12/2008

    对PHP的一点想法

     
    目前的web开发基本都是使用基于MVC模式的开发框架。作为web开发中的主力军,PHP也有着各种不同的开发框架。特别是在Rails出现以后,出现了不少的PHP开发框架,有完全模仿rails的CakePHP,有官方开发的ZendFramework,还有国内的FleaPHP等等。
     
    PHP的设计初衷就是为了方便web开发,针对web开发中的各种常见的需求PHP都有内置的函数帮助我们实现。而使用MVC模式进行开发已经成为了一种web开发中的常见需求,所以考虑是否可以直接在PHP语言中支持MVC模式的开发,而不是通过各种框架来支持MVC。
     
    大概说来,需要内至于PHP语言中的有Model和Controller两个模块,至于View就是直接使用PHP实现的页面。这样带来的好处就是程序员无需学习掌握各种复杂繁琐的框架,直接内至于PHP语言中的Model和Controller也会有更好的性能表现。但是,这样也会带来灵活性上的限制。
     
    忽然想到:其实浏览器中可以内在地支持人机交互,不要再编写复杂的javascript代码来搞人机交互了。可以提供一种类似于HTML,CSS这样的语言来设置用户的动作以及响应。
    11/13/2007

    JavaScript关闭Excel

    使用JavaScriptActiveXObject打开Excel以后,即使你调用了Excel.Quit()方法,Excel也会一直运行,直到你关闭了当前IE窗口或者在当前窗口中打开另一个页面。

    JavaScript是带有垃圾收集(Garbage Collection)功能的语言,它会在适当的时候收集那些不再使用的变量,而不是在你把一个变量设置为NULL的时候。所以,当你调用Excel.Quit()方法,并把指向Excel的引用(Reference)赋值为NULL时,JavaScript不见得会真正释放Excel。当你关闭当前窗口或者在当前窗口打开另外的页面时,JavaScript会强制进行垃圾收集,这时,Excel才会真正的关闭。

    为了解决这个问题,你可以调用函数CollectGarbage来强制立即进行垃圾收集,这就会释放指向Excel的引用。下面的代码简单说明了如何使用该函数:

    <HTML> 
    <BODY> 
    <INPUT type="button" value="Automate Excel" name=AutomateExcel onclick="StartExcel()"> 
    <SCRIPT LANGUAGE=Javascript> 
      var idTmr = "";
    
      function StartExcel() { 
        var oExcel; 
    
        oExcel = new ActiveXObject("Excel.Application"); 
        oExcel.Quit(); 
        oExcel = null;
        idTmr = window.setInterval("Cleanup();",1);
      } 
    
      function Cleanup() {
        window.clearInterval(idTmr);
        CollectGarbage();
      }
    </SCRIPT> 
    </BODY> 
    </HTML> 
    注意:1)不能在调用Excel.Quit()之后立即调用CollectGarbage,需要等待一小段时间;
    2)CollectGarbage不是ECMA-262标准,在将来的版本中有可能不能使用;
    3)强制使用CollectGarbage可能会对程序性能带来一定影响。
    3/29/2007

    变态的C语言声明

     
    请问以下每个A声明的是什么东西?
     
    int (*A)[5];
     
    int *(*A)[5];
     
    int (*A[5])();
     
    char (*(*A())[])();
     
    char (*(*A[5])())[12];
     
    答案如下:
     
    int (*A)[5];
    A是一个指向长度为5的 int 型数组的指针。
     
    int *(*A)[5];
    A是一个指向长度为5的 int* 型数组的指针。
     
    int (*A[5])();
    A是一个长度为5的数组 ,该数组中的项为返回 int 型的函数指针。
     
    char (*(*A())[])();
    char(*(*A())[])(); 首先 A与它右边的()结合形成A(),可以确定A是一个函数;
    令F=A(),也就是说F是函数A()的返回值,原式变为char(*(*F)[])();
    可以看出F是一个指针,令L=*F,那么L就是F指向的对象,原式变为char(*L[])();
    可以看出L是一个数组,令P=L[],那么P就是数组L中的元素的类型,原式变为char(*P)();
    可以看出P是一个函数指针。
     
    从上面的分析可以看出:A是一个函数,这个函数没有任何参数,它的返回值是一个指针,这个指针指向一个数组,而这个数组的元素是char(*)()类型的函数指针。
     
    char (*(*A[5])())[12];
    首先A与它右边的[5]结合形成A[5],可以确定A是一个长度为5的数组;
    令E=A[5],也就是说数组A[5]中的元素为E,原式变为char(*(*E)())[12],可以看出E是一个指针;
    令P=*E,也就是说指针E指向P,原式变为char(*P())[12],可以看出P是一个函数;
    令F=P(),也就是说函数P的返回值为F,原式变为char(*F)[12],可以看出F是一个指针;
    令L=*F,也就是说指针F指向L,原式变为char L[12],很显然L是一个长度为12的char型数组。
     
    从上面的分析可以看出:A是一个长度为5的数组,它的元素是函数指针,这个函数指针指向的函数没有参数并返回一个长度为12的char型数组。
     
     

    3/17/2007

    1,3,4,6如何运算得到24?

    如何通过加减乘除运算使得1,3,4,6得到24?
     
    6/(1-(3/4))
     
    还有变态的题目:四个0如何运算得到24?
     
    (0!+0!+0!+0!)! = 24
     
    从一副扑克牌中任意取出四张牌,如何通过加减乘除运算使得其结果为24?
    能否通过编程实现,输出所有的可能情况?
     
     
    11/24/2006

    子序列求和

     
    问题:已知一个整数数组A,求一个最小的正整数M,使得M等于数组A的一个子序列的和。
     
     
    第一步:
    设数组A的长度为n,令数组sum[1..n]为以下值:
    sum[1] = A[1];
    sum[2] = A[1] + A[2] = sum[1] + A[2];
    ......
    sum[i]  = A[1] + A[2] + ... + A[i] = sum[i-1] + A[i];
    ......
    sum[n] = A[1] + A[2] + ... + A[n] = sum[n-1] + A[n];
     
    这一步的时间复杂度为O(n)。
     
     
    第二步:
    对数组sum[1..n]进行快速排序,使该数组按升序排列。
    如果sum[a] == sum[b], 按以下规则排序:
    如果 a < b,则 sum[b] 排在 sum[a] 前面。反之,sum[a] 排在 sum[b] 前面。
     
    这一步的时间复杂度为O(nlogn)。
     
     
    第三步:
    设排序后的sum数组为:
    sum[a1], sum[a2], ...... sum[an]
     
    按如下方式遍历该数组:
        i = [1..n]。
    1) 如果 sum[ai] > 0 并且 sum[ai] < min, 令 min = sum[ai]。
    2) 如果 0 < sum[a(i+1)] - sum[ai] < min 并且 a(i+1) > ai,令 min = sum[a(i+1)] - sum[ai]。
     
    注意边界情况。
    最后求得的min为该问题的解。
     
    这一步的时间复杂度为O(n)。
     
    //
    该算法总的时间复杂度为O(nlogn)。
    //
     
    详细代码:
     
    struct subsum
    {
       int value;
       int length;
    };

    int minsubsum(int A[], int length)
    {
       int i;
       int min = 0;

       struct subsum *sum = new struct subsum[length+1];

       sum[0].value = 0;
       sum[0].length = length+1;

       for(i=1; i<=length; i++)
       {
           sum[i].value = sum[i-1].value + A[i-1];
           sum[i].length = i;
       }

       qsort(sum+1, length, sizeof(struct subsum), compare);

       for(i=1; i<=length; i++)
       {
           if (sum[i].value > 0)
           {
               if (min == 0)
                   min = sum[i].value;
               else if (sum[i].value < min)
                   min = sum[i].value;
           }
      
           if (sum[i].value != sum[i-1].value && sum[i].length > sum[i-1].length)
           {
               if (min == 0)
                   min = sum[i].value - sum[i-1].value;
               else if (sum[i].value-sum[i-1].value < min)
                   min = sum[i].value - sum[i-1].value;
           }
       }

       delete [] sum;
       return min;
    }

    int compare(const void *a, const void *b)
    {
        if (((struct subsum*)a)->value == ((struct subsum*)b)->value)
             return ((struct subsum*)b)->length - ((struct subsum*)a)->length;
        else
             return ((struct subsum*)a)->value - ((struct subsum*)b)->value;
    }

    10/28/2006

    常数附加空间的非递归方法遍历二叉树

    class TreeNode
    {
        TreeNode* leftchild;
        TreeNode* rightchild;
        TreeNode* parent;
        T  data;
    }
     
    //前序遍历
    PreOrder(TreeNode* root)
    {
        TreeNode* t = root;
        while(1)   
        {
            visit(t);
            if (null != t->leftchild) {
                t = t->leftchild;
            } else if (null != t->rightchild) {
                t = t->rightchild; 
            } else {
                while (t->parent && ( null == t->parent->rightchild || t == t->parent->rightchild) )
                       t  = t->parent;
     
                if (null == t->parent) 
                    break;
                else
                    t = t->parent->rightchild;
            }
        }
    }
     
    如果要遍历的树有n个节点,则运行时间的上界为3n。
    这是因为对于树中的每个节点,最多有3个指针指向它(父节点的指针以及两个孩子的父指针),
    所以对于某个给定的节点,最多经过3次。所以对于有n个节点的树来说,遍历时间的上界为3n。
    由于根节点和叶子节点的存在,事实上不可能达到3n。
     
    10/14/2006

    C++中结构的内存结构

    1) 无数据成员的结构
    struct s_0 { // };
    在C++ 中 sizeof(s_0) = 1
    在C语言中 sizeof(s_0) = 0

    在C++中一个struct中可以有成员函数,例如:
    struct s1 { void f(); }  A;
    struct s2 { void f(); }  B;
    如果 sizeof(s1) = 0,  sizeof(s2) =0, 那么A, B将会有相同的内存地址,所以无数据成员的结构应当拥有最小的非零长度。

    2)
    struct s_char_int
    {
           char a;
           int   b;
    }

    sizeof(s_char_int) = 8, 而不是sizeof(char) + sizeof(int) = 1 + 4 =5。

    3)
    struct s_carray_int_char
    {
          char     a[3];
          int        b;
          char     c;
    }

    struct s_carray_char_int
    {
          char     a[3];
          char     c;
          int        b;
    }

    以上两个结构含有相同的数据成员,但是数据成员的先后顺序不同。
    sizeof(s_carray_int_char) = 12;
    sizeof(s_carray_char_int) = 8;

    10/11/2006

    C++中浮点变量与零值的比较

    今天下午参加了一个笔试,考的题目就是《高质量C/C++编程指南》后面附带的试题。当时感觉还不错,回来后看了一下答案,发现还是很有差距。
    其中第一题问道:写出 float x 与 “零值”比较的 if 语句。我当下就写下了 if ( 0.0 == x )可答案是:
     
    const float EPSINON = 0.00001;
    if ( (x >= -EPSINON) && (x <= EPSINON ) )
     
    看完后还不明所以!
     
    再看《高质量C/C++编程指南》,有如下解释:
    【规则4-3-3】不可将浮点变量用“==”或“!=”与任何数字比较。
    千万要留意,无论是float 还是double 类型的变量,都有精度限制。所以一定要避
    免将浮点变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。
    假设浮点变量的名字为x,应当将
    if (x == 0.0) // 隐含错误的比较
    转化为
    if ((x>=-EPSINON) && (x<=EPSINON))
    其中EPSINON 是允许的误差(即精度)。