载入中
自定义HTML载入中... loading
日历
<<  <  2008 - 8  >  >>
Su Mo Tu We Th Fr Sa
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            
载入中
边栏内容载入中... loading

搞懂之后,发现只需要在A上输入下面几个命令:

ssh-keygen -t rsa #输入一个不同于登陆密码的密码
scp .ssh/id_rsa.pub  hostname_B:/home/youruser/.ssh/authorized_keys
exec /usr/bin/ssh-agent $SHELL
ssh-add #输入刚才那个密码


 

 

(字节数 : 221)
注定 [原创 2007-3-13 14:18:40]  

人生匆匆,
亘古一刻。
一百年前无你我,
相爱如风云际会,
百年只为初吻那刻。
灵魂有无不消说,
概率遥遥,
缘分似乎天算过。
蝴蝶颤翅,
也关乎你我。
相遇的那刻,
我们紧拥,
一秒当一年过。

 

(字节数 : 230)
原来C++支持 类成员函数指针 [原创 2007-1-23 14:02:44]  
CODE:
#include <iostream>
using namespace std;

class A;
typedef void (A::*pAFun)(void);  //定义类成员函数指针

class A{

public:
    void fun(){
        cout<<"A::fun run"<<endl;
    };
};

class B
{
protected:
    A *_a;
    pAFun _callback;

public:
    B():_callback(NULL), _a(NULL)
    {};
  
    void setOnDo(pAFun _pf_callback, A * pA)
    {
        _callback = _pf_callback;
        _a = pA;
    }
   
    void Do()
    {
        cout << "class B::Do ..." <<endl;
        cout << "class B::Do callback: ..." <<endl;
        if(_callback && _a)
        {
            (_a->*_callback)();  //以一种奇怪的方式进行“类成员函数指针”的调用
        }
        cout << "class B::Do return" <<endl;
    }
   
};

int main(int argc, char *argv[])
{
    A a;
    B b;

    b.setOnDo(&A::fun, &a);

    b.Do();

    return 0;
}
      刚刚写代码验证的,做个记号。

 

(字节数 : 2192)
《Programming in C》学习笔记 [原创 2007-1-6 20:39:05]  

Programming in C》学习笔记

 

我花了几个月时间精读《Programming in C》一书, 为的是查缺补漏, 打好基础, 进而深刻理解C语言. 现在把书上曾经作了标记的地方(或者写过代码验证过的细节)整理成笔记.

 

一.       基本数据类型

a)    基本数据类型和常量

基本数据类型

常量举例

printf 如何格式输出

_Bool

0,1

%u  %i

char

‘c’ ‘a’

%c

unsigned char

‘c’ ‘a’

%c

short int

--

%hi  %ho  %hx

unsigned short int

--

%hi  %ho  %hx

int

10, -20, 0xff, 0777

%i  %o  %x

unsigned int

10u, 0xffu, 0777U

%u  %o  %x

long

10l(这个字母l还是写成大写L更好看),  10L

0xffL ,  077777L

%li  %lo  %lx

unsigned long

10UL,  0xffffffUL

%lu  %lo  %lx

long long

10LL,  0xffffffLL

%lli  %llo  %llx

unsigned long long

10ULL, 0xffffffULL

%llu  %llo  %llx

float

10.00f  3.14e-7f , 0x10.0p20

%f  %e  %g  %a

double

10.00,  3.14e-7 , 0x10.0p20

%f  %e  %g  %a

long double

10.00L, 3.14e-7L

%Lf  %Le  %Lg

float _Complex

编译器自己实现

double _Complex

long double _Complex

_Imaginary

 

仔细观察, 找到规律就可以记住了:

u表示unsigned

i表示int

d表示10进制

o表示8进制

l表示long

f表示float

e表示科学计数法

g表示啥我不知道(general?),智能输出浮点数格式

b)    字符常量

  i.      转义字符:

\a \b \f \n \r \t \v \\ \” \’ \?

注意:

\nnn  

nnn是八进制数, 如果不符合下面的条件,则属于未定义行为, vc6会忽略\字符

正则表达式: \\[0-7]{1,3}

一个转义字符只能表示一个8bit字节所容纳的8进制数, 即 \000 -- \377

\unnnn \Unnnn

nnnn是十六进制数

正则表达式: \\[Uu][0-9a-fA-F]{1,n}

具体可以用多大的十六进制数,要看编译器为这个字符准备了多大空间(vc6不支持)

\xnn

nn是十六进制数, 如果不符合下面的条件,则属于未定义行为, vc6会忽略\字符

正则表达式: \\[0-9a-fA-F]{1,2}

一个转义字符只能表示一个8bit字节所容纳的16进制数, 即 \x00 -- \xFF

 

ii.      多个字符常量

不同的编译器自己决定如何实现,不推荐使用,比如vc见过这样的危险代码:

  long LL = 'abcd';

  printf("%c %c %c %c \n",

((char*)&LL)[0], ((char*)&LL)[1],

((char*)&LL)[2], ((char*)&LL)[3]);

iii.      宽字符常量

宽字符类型名: wchar_t

vc6这样定义它: typedef unsigned short   wchar_t;

我的GCC定义: typedef long wchar_t;

宽字符常量在窄字符常量前加L, 如 L’a’  L’9’

 

二. 符号数和无符号数类型转换陷阱
   一般的数据类型转换原则大家都知道, 但是一些特殊的情况是C语言没有定义的.例如把一个无符号数赋值给有符号数,并且超过了有符号数的范围:
                char c = 200;                        //结果 c == -56
                int i = 0xFFFFFFFF;        //结果 i == -1
   因为这是未定义行为, 原则上不同的编译器会作出不同的处理, 事实上vc6是使用”二进制复制”来赋值的,即把200 (0xC8) 这个字节复制到字符c中; 把0xFFFFFFFF四个字节复制到整型i中.

三. 数组初始化
   int iarr[10] = { 0 };  //这样显示初始化第一个元素为0, 然后默认把其他元素初始化为0
                       //总体效果: 将所有元素初始化为0
   int iarr[10] = { [5] = 5, [7] = 1}; //C语言可以指定数组的索引下标进行初始化
        //注意C++中不能这么用
        
四. 变量长度数组
    “变量长度数组”是C99新引入的数组. 我测试发现VC6是不支持这个的,但是GCC支持!我写了这样的测试代码,发现程序居然也支持作为i是负数,而且在负数的情况下,GCC的内存分配虽然怪异(0索引元素作为数组物理内存中的最后一个元素,依次向前排列),但也是保证正确的(数组/下标/元素地址/指针计算不是产生错误)。

CODE:
#include <stdlib.h>
#include <stdio.h>

void fun(int i)
{
    char kk = 'B';
    char buf[ i ];
    char mm = 'E';

    printf("size :: %d %x -- %x\n", sizeof(buf), (size_t)buf, (size_t)&buf[i-1]);
    buf[i-1] = 'a';
    printf("\t\t\t buf[i-1]:%c\t %x:%c \t %x:%c \n", buf[i-1], (size_t)&kk, kk, (size_t)&mm, mm);
}

int main(int argc, char * argv[], char * envp[])
{
    fun(2);
    fun(3);
    fun(4);
    fun(1);
    fun(0);
    fun(-1);
    fun(-10);
}

GCC安全的为负数长度的数组分配了空间,保证了这种数组的安全使用, 不会影响栈上的其他变量空间。
        下面是输出:

CODE:
size :: 2 bfbfec50 -- bfbfec51
                         buf[i-1]:a      bfbfec7f:B      bfbfec7e:E
size :: 3 bfbfec50 -- bfbfec52
                         buf[i-1]:a      bfbfec7f:B      bfbfec7e:E
size :: 4 bfbfec50 -- bfbfec53
                         buf[i-1]:a      bfbfec7f:B      bfbfec7e:E
size :: 1 bfbfec60 -- bfbfec60
                         buf[i-1]:a      bfbfec7f:B      bfbfec7e:E
size :: 0 bfbfec60 -- bfbfec5f
                         buf[i-1]:a      bfbfec7f:B      bfbfec7e:E
size :: -1 bfbfec60 -- bfbfec5e
                         buf[i-1]:a      bfbfec7f:B      bfbfec7e:E
size :: -10 bfbfec60 -- bfbfec55
                         buf[i-1]:       bfbfec7f:B      bfbfec7e:E

我估计这种不通用的东西产品里应该很少用。尽量避免使用,以增强移植性。

        至于数组长度是负数的情况,我是这么想的:
        GCC就像数学家发现自然数后又发现了负数那样,GCC为人们实现了负数数组长度,并告诉我们数组长度也可以是负数。至于负数有什么物理意义,数学家先不管了;数组长度负数有什么实际意义,Gcc就不管了,它只是保证了正确的实现。

五. 结构的初始化
   结构的初始化尽管可以这样:

CODE:
        typedef struct
        {
                int a;
                char buf[10];
        } Recode;

        Recode rr = {
                10,
                {'0','1','2','3','4'}
                };

但是这样的隐患是初始化时必须牢记结构成员的顺序, 而且不利于结构声明以后的修改. 如果编译器支持,最好使用下面的形式:

CODE:
        Recode rr = {
                .a = 10,
                .buf = {'0','1','2','3','4'}
                };
       

六. 0长度数组

   0长度数组是个奇怪的东西, 下面的代码(两种形式之一)是可以通过编译的.
                char buf[];
   或者
                char buf[0];
   有什么用处呢? 大家知道数组名其实是数组所在内存的首地址, 那么0长度数组的名字,其实是在内存某个地方中作了一个标记, 在适合的时候将这个标记后面的一段内存作为这个数组的内容. 貌似数组下标溢出了,但是善于利用这点可以实现一个”变长”结构体.

例如下面的代码:

CODE:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static const size_t def_name_len = 32 ;
typedef struct __Name
{
        size_t index;
        size_t len;
        char buf[0];
} Name, *PName ;

Name * createName(size_t index, const char * strname)
{
        size_t len;       
        PName pname = NULL;
       
        if (strname == NULL)
        {
                len = def_name_len;
        }       
        else
        {
                len = strlen(strname);
        }
       
        pname = (PName) malloc( sizeof(Name) + len + 1);  
       
        if(pname == NULL) return NULL;

        pname->index = index;
        pname->len = len;
        pname->buf[0] = '\0';
        if (strname)  strncpy(pname->buf, strname, len+1);
        return pname;
}

void freeName(PName pname)
{
        if(pname == NULL) return;
        free(pname);
        pname = NULL;
}

int main()
{
        int i;

        PName namelist[4] = {
                createName(1, "name1"),
                createName(2, "name2"),
                createName(3, "name3"),
                createName(4, "name4"),
        };
       
        for(i=0; i<4; ++i)
        {
                if(namelist[i])
                        printf("index %u \t name: %s \n", namelist[i]->index, namelist[i]->buf);
        }
        for(i=0; i<4; ++i)
        {
                freeName(namelist[i]);
        }
        return 0;
}

struct __Name有三个成员size_t index; size_t len; char buf[0]; 但是sizeof(Name)的结果是8, 为什么呢?因为上面说了,” 0长度数组的名字,其实是在内存某个地方中作了一个标记”, 所以不占空间, 上面代码中的pname = (PName) malloc( sizeof(Name) + len + 1);  一行,申请了一个Name结构体变量,然后这块内存后面紧跟了一块长len+1的内存,所以我们就可以用buf[0..len]来访问这段内存了. 图示如下:

CODE:
-------------------------------------------------------------------------
|  index (4byte) | len (4byte)  |<------- len+1 byte -------->|
-------------------------------------------------------------------------
| <----------Name-------------->|                     
                                |<-----buf[len+1] ------------|

    可见, 原理用一句话来总结,就是利用数组下标”故意”溢出来访问数组首地址后的内存.

再找一个实际应用的例子:
   在MS GDIPlus 提供的类库中,有这样一个结构体来表示调色板数据

CODE:
typedef struct {
    UINT Flags;
    UINT Count; //下面数组Entries的实际元素数
    ARGB Entries[1]; //只包含一个元素的数组,用法类似0长度数组
} ColorPalette;

下面的代码使用GetPalette函数得到一个ColorPalette结构体

CODE:
   UINT size = image->GetPaletteSize();//ColorPalette结构体的实际长度.
   printf("The size of the palette is %d bytes.\n", size);
   ColorPalette* palette = (ColorPalette*)malloc(size); //一块内存
   image->GetPalette(palette, size);
   if(size > 0)
   {
      printf("There are %u colors in the palette.\n", palette->Count);
      printf("The first five colors in the palette are as follows:\n");
      for(INT j = 0; j < palette->Count; ++j)
         printf("%x\n", palette->Entries[j]);
   }

未完待续, 书上还画了很多地方,看来明天还要继续总结了...

 

 --eof--

 

(字节数 : 58355)
告别2006 [原创 2006-12-31 19:43:01]  

    记得2006年元旦的时候,我没想那么多,糊里糊涂的就过了,这次决不能这样了----元旦啊,那是应该反省和总结的日子.

    今年我做了什么?嘘~~, 这是隐私,不说了:D.  说说我今年学习到了什么吧.

    感谢她.在恋爱中, 我学到了宽容. 当我和她产生矛盾时,我学会了宽容她,发现这样做我也得到了安宁. 我认识到自己,以前的"宽阔心胸"都是嘴上说的,我还远不够. 君子坦荡荡, 小人常戚戚, 我现在终于对此有了真正的理解. 恋爱,也是历练心灵的过程啊.

    感谢命运, 我亲爱的弟弟躲过一劫. 我从此更加明白亲情的宝贵. 各位京飘的同志们,每周给亲人打电话问平安吧.

    感谢小甜甜和牛夫人, 跟他同事实在是幸运, 我从他身上学到很多东西,比如做事和做人,深厚和成熟.

    感谢CW, 也很幸运和他同事, 我从他身上学到了规划自己的职业生涯. 这位GG学生时代就是武汉大学双学士,而且爱情和学业双丰收,现在买房结婚生子读研,该做的都做了,仅仅比我大一岁,实在佩服他.

    信仰? 我今年工作第五年, 又开始寻找信仰了. 人不能没有信仰. 感谢我的老乡----孔丘, 他的《论语》教我很多做人的道理. 虽然现在我还没有找到信仰,但是我的内心不再空虚. 三十而立,有望.

    俯下身学习,这是我最近两个月的收获. 收获的并不是学习的结果,而是这种坚持学习的生活. 我已经读完《Programming in C》, 查缺补漏,感觉很好.

 

    曾子曰: 吾日三省吾身:为人谋而不忠乎? 与朋友交而不信乎? 传不习乎?

    曾子一天三省, 我也一年三省一次吧.

    --eof--

 

(字节数 : 1503)
CODE:
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

#ifdef WIN32
    #include <windows.h>
#else
    #include<sys/time.h>
    #include<unistd.h>
#endif

double get_time_ms()
{
#ifdef WIN32
    return (double)GetTickCount();
#else
    struct timeval tv;
    struct timezone tz;
    gettimeofday (&tv , &tz);
    return tv.tv_sec * 1000 + tv.tv_usec / 1000 ;
#endif
}

//这是微软公开的crt src文件夹内的源代码:
void * memcpy_1 (void * dst, void * src , size_t n)
{
    void * ret = dst;
    while (n--) {
            *(char *)dst = *(char *)src;
            dst = (char *)dst + 1;
            src = (char *)src + 1;
    }
    return ret;
}


//这是我写的,按照最近流行的优化方式做的
void * memcpy_2 (void * dst, void * src , size_t n)
{
    void * ret = dst;
    size_t l = n>>2;
    while (l--) {
        *(size_t *)dst = *(size_t *)src;
        dst = (size_t *)dst + 1;
        src = (size_t *)src + 1;
    }
    l = n & 3;
    while (l--) {
        *(char *)dst = *(char *)src;
        dst = (char *)dst + 1;
        src = (char *)src + 1;
    }
    return ret;
}

void main(int argc, char ** argv)
{
    const int cstMemSize = 0x2FFFFFF;

    if(argc<2)
    {
        printf("请输入参数 [ 0 | 1 | 2 | 3] ");
        return ;
    }

    char * str  = (char*)malloc(cstMemSize);
    char * str2 = (char*)malloc(cstMemSize+16);

    size_t t1 = get_time_ms();

    switch(argv[1][0])
    {
        case '0': memcpy(str, str2, cstMemSize );
            break;
        case '1': memcpy_1(str, str2, cstMemSize );
            break;
        case '2': memcpy_2(str, str2, cstMemSize );
            break;
        case '3': memcpy_2(str, str2+1, cstMemSize );  //考虑字节不对齐的情况
            break;
    }
   
   
    double t2 = get_time_ms();
    free(str);
    free(str2);
    printf("memset_%c %f\n", argv[1][0] ,t2-t1);

}
在windows xp/vc6环境下:

结果发现memcpy_1是最慢的,当然,这是串操作,当然很慢;
但是直接调用memcpy函数是最快的,这大概是微软在vc链接程序时做的优化.
而上面memcpy_2的效果一般般,比memcpy_1快,但是不如直接调用memcpy快.


在gcc/freebsd环境下,如果未加优化参数-O2,case '3'的情况速度更慢.我怀疑可能是字节不对齐带来的恶果.
不过,当开启了-O2后,4个case的测试时间差不多了.但是还是gcc自己的memset函数最快.

我现在怀疑,象(int *)这样的奇技淫巧如果使用不当,带来的效果不明显,还不如不用.现实情况是复杂的,编译器比我考虑的多的多.
 

 

(字节数 : 4289)
C中把int类型>>32位会发生什么? [原创 2006-12-27 17:21:26]  


    unsigned int nn = ~0;
    unsigned int ii = 0;
    while( nn>>ii )
    {
        printf("%8x \n", nn>>ii);
        ii++;
        if(ii == 100)break;
    }

本来我想让nn移位32位然后跳出循环的(安全的做法当然是nn =>> 1;),后来发现这个关系 :

   (nn>>32 == nn)

粗浅的查<Programming in C> , 书里面没有把>>的操作数超过32当作"未定义行为" .

从vc6里面看汇编,发现对应的指令是:
0040B89F   mov         edx,dword ptr [ebp-4]
0040B8A2   mov         ecx,dword ptr [ebp-8]
0040B8A5   shr         edx,cl

看起来C编译器没有对ii进行取模操作,而是直接把ii交给shr这个cpu指令

这是intel的文档原文:
[pdf 下载:http://www.intel.com/cd/ids/developer/apac/zho/dc/pentium4/reference/79962.htm  ]

The SAL and SHL instructions perform the same operation (see Figure 7-6). They shift the source operand left by from 1 to 31 bit positions. Empty bit positions are cleared. The CF flag is loaded with the last bit shifted out of the operand.

我的理解: 虽然没说明shr 操作数是0和32的情况会发上什么,但是已经说明了1 to 31 , 所以默认把大于31的数对32取模,也算是理所当然的了 
另, 如果>>操作符后面是一个常数,例如 int n = nn >> 32 ;这样的语句, GCC编译器会报告一个警告: right shift count >= width of type

还是GCC周全一些啊.

 

 

(字节数 : 2006)
firefox浏览器和Opera浏览器都可以在地址栏输入下面的字符串,回车,可以看到页面显示hi!!!
data:text/html;charset=utf-8;base64,PGh0bWw+PGJvZHk+aGkhISEhPC9ib2R5PjwvaHRtbD4=

发现这个的原由是lzlhero 告诉我这个网页可以做到把数据放到firefox的剪切板内。
通过分析,发现原来firefox支持这样格式的url:data:text/html;charset=utf-8;base64,base64编码内容
测试发现opera也是这样。

这是什么?类似javascript:的伪协议?先不深究,记录下来。

[更新]:
具体一查才知道,原来这是一种叫做“The "data" URL scheme”的东西。
格式如下:
data:[<mediatype>][;base64],<data>

References:
RFC 2397 - The "data" URL scheme
Technical review of RFC 2397
About data: URLs and the mozilla implementation

Based on - Reading RFC's for testing
Mozilla对这种url的测试页面:http://www.mozilla.org/quality/networking/testing/datatests.html

 

(字节数 : 1334)
对ajax cache的一些思考 [原创 2006-12-3 14:31:37]  

ajaxcn.orgbbs中看到这篇《对ajax cache的一些思考》,做了回复,如下:

-------------------------------- 

       去年我做ajax时,使用了cache,  cc.eyou.com,ajax部分是我设计的,可惜我后来离开了,没有把它做到尽善尽美。

        我使用了观察者模式组织界面:

  • 把不同界面划分成不同的单元,作为view;
  • 其中每个view对应一个view类和一个javascript template文件 ;
  • 当view进行更新时,载入javascript template用到了cache ;
  • 数据集在doc类里面,里面的数据用到了cache ;

        我用cache实现了这样的效果:

  • 当页面载入时,初始化用到的view,每个view载入各自的javascript template文件后,这个文件就被缓存了,以后的view更新时不必再次请求服务器。
  • 页面载入后,载入“联系人列表”第一页,同时缓存第一页数据,向后翻页继续缓存,并合并缓存。这样当重新访问第一页时,不必请求互联网。 


        不过,我与到了很多问题:

1。一个函数用来载入数据,那么它要判断是从缓存读取数据,还是从服务器获取数据,由于xmlHttpRequest是异步调用,所以这个函数必须为异步调用定义回调函数。
如果这个函数被别的函数调用,那么调用者必须知道,被调用者是个异步函数,需要为他做回调函数。
这样这个接口就很复杂了。
如果用同步请求,可以避免这个问题,不过这就不叫ajax了哈哈。
我是没办法,纯手工做的。特别是这些函数都是在不同的类里面,模型很复杂,我的uml顺序图最终理顺了这些问题。不过我只是消极抵御,没有更好的办法。

2。如同楼主所言,缓冲数据的更新问题比较复杂。

3。我做的是分页缓冲,这些每个页面缓冲数据集要合并在一起,当数据总共有10页,而我只缓冲了5页数据时,要保证分页顺序不会出错,和服务器分页顺序要一致。

        总之从目前的浏览器功能来说,没有浏览器的支持, js级别的缓存还是不宜使用太多。使用了缓存,编码复杂度会很复杂,可能会陷入进去难以自拔。 

        另外,js做缓存只能通过变量存储字符串的形式来做。这样做的缺点很明显,不能跨页面,关了窗口就没了。 

        我考虑过的其他途径,都被否定了。

1。如果依赖浏览器对静态页面的缓存,可能浏览器仍然会发出一个If-Modified-Since之类的http头请求。
2。使用本地文本文件?不行,有安全限制。不过,用js开发HTA之类的应用程序,可以这么做。
3。使用cookie? 嗯,貌似不错,但是cookie会随着每次http会话一起提交,带来不必要的流量。 

        忽然想到,最近看到qyb的文章,介绍Set-Cookie 的 secure 属性, 它表示创建的 cookie 只能在 HTTPS 连接中被浏览器传递到服务器端进行会话验证,如果是 HTTP 连接则不会传递该信息。
      不知道可不可以通过这个途径使用cookie实现缓存,哈哈。不过,这是奇技淫巧,不能保证始终有效吧。

 

 

 

(字节数 : 2584)
C++ switch的秘密 [原创 2006-12-2 20:07:20]  

呵呵,有点标题党的感觉。其实不算什么秘密吧。

C++的switch只支持整型表达式、以及可以转化成整型的表达式。这是什么意思呢?

这是说,switch不支持字符串、数组、浮点数。

支持整型,包括short / unsigned short / int / unsigned int/ long / unsigned long 
此外,还有long long和unsigned long (如果编译器支持long long的话)  。

除了整型,如果一个变量或表达式可以转化成整型,那么switch也支持,例如:



class A{
public:
 A() : _i(0), _c('a'), _l(2L) {}
 operator int () { return _i; } //从class A到int的类型转换操作符
private:
 int  _i;
 char _c;
 long _l;
};

int main()
{
 A a;

 switch(a) //a转换成int类型,返回i
 {
 case 0:
  printf("0\n");
  break;
 case 'a':
  printf("a\n");
  break;
 case 5L:
  printf("5L\n");
  break;
 }

 return 0;
}

不过有趣的是,如果象下面这样,定义了多个类型转换操作符,则引起二义性错误:


class A{
public:
 A() : _i(0), _c('a'), _l(2L) {}
 operator int () { return _i; }
 operator char() { return _c; }
 operator long() { return _l; }
private:
 int  _i;
 char _c;
 long _l;
};

int main()
{
 A a;

 switch(a) // error C2450: switch expression of type 'class A' is illegal
{                       //Ambiguous user-defined-conversion //二义性错误
 case 0:
  printf("0\n");
  break;
 case 'a':
  printf("a\n");
  break;
 case 5L:
  printf("5L\n");
  break;
 }

 return 0;
}

 

(字节数 : 2054)
关于C++类内const函数 [原创 2006-11-30 13:37:58]  
据说有这种面试题:
class A
{
        public :
        char get_m() const
        {
                 return m;
        }
        private:
        char m;
}
请在函数get_m中改变m的值。

我想,对于这种流氓问题,只好使用流氓的做法:
        char get_m() const
        {
                char * p = (char *) &m;
                *p = 'C';
                 return m;
        }
呵呵,可以做到,因为类内const函数是通过“把类内成员都作为const”来实现的。
不过这是我这种“流氓程序员”的做法。

实际上,经过同事的教导,应该使用mutable修饰符:
mutable char m;
这样即使在类的const函数内,m的类型也是可以改变的了。

另外,如果想不要这份工作,也可以这么写:
#define const
哈哈,这样面试官就不敢要你啦。:D


 

(字节数 : 1318)
C语言的"变量长度数组" [原创 2006-11-28 17:03:40]  
       最近细读《Programming in C》,里面讲数组时讲到了“变量长度数组”,说有的编译器支持这样的用法:

      int i=10;
      char buf[ i ];

      我测试发现VC6是不支持这个的,但是GCC支持!我写了这样的测试代码,发现程序居然也支持i是负数,而且在负数的情况下,GCC的内存分配虽然怪异,但也是保证正确的。

CODE:
#include <stdlib.h>
#include <stdio.h>

void fun(int i)
{
    char kk = 'B';
    char buf[ i ];
    char mm = 'E';

    printf("size :: %d %x -- %x\n", sizeof(buf), (size_t)buf, (size_t)&buf[i-1]);
    buf[i-1] = 'a';
    printf("\t\t\t buf[i-1]:%c\t %x:%c \t %x:%c \n", buf[i-1], (size_t)&kk, kk, (size_t)&mm, mm);
}

int main(int argc, char * argv[], char * envp[])
{
    fun(2);
    fun(3);
    fun(4);
    fun(1);
    fun(0);
    fun(-1);
    fun(-10);
}
GCC安全的为负数长度的数组分配了空间,保证了这种数组的安全使用, 不会影响栈上的其他变量空间。
下面是输出:

CODE:
size :: 2 bfbfec50 -- bfbfec51
                         buf[i-1]:a      bfbfec7f:B      bfbfec7e:E
size :: 3 bfbfec50 -- bfbfec52
                         buf[i-1]:a      bfbfec7f:B      bfbfec7e:E
size :: 4 bfbfec50 -- bfbfec53
                         buf[i-1]:a      bfbfec7f:B      bfbfec7e:E
size :: 1 bfbfec60 -- bfbfec60
                         buf[i-1]:a      bfbfec7f:B      bfbfec7e:E
size :: 0 bfbfec60 -- bfbfec5f
                         buf[i-1]:a      bfbfec7f:B      bfbfec7e:E
size :: -1 bfbfec60 -- bfbfec5e
                         buf[i-1]:a      bfbfec7f:B      bfbfec7e:E
size :: -10 bfbfec60 -- bfbfec55
                         buf[i-1]:       bfbfec7f:B      bfbfec7e:E
我的疑问:


  1. 0长度的数组有什么意义? [I] 这个经过搜索资料,我已经知道了,参考:http://bbs.chinaunix.net/viewthread.php?tid=854949 [/I]
  2. 在产品级的软件开发中,这种“变量长度数组”有人应用吗?

嗯,我估计这种不通用的东西产品里应该很少用。尽量避免使用,增强移植性。

至于数组长度是负数的情况,我是这么想的:
       GCC就像数学家发现自然数后又发现了负数那样,GCC为人们实现了负数数组长度,并告诉我们数组长度也可以是负数。
       至于负数有什么物理意义,数学家先不管了;数组长度负数有什么实际意义,Gcc就不管了。

 

(字节数 : 4015)
上一页 | 1 | 2 | 3 | 4 | 5 | ... 11 | 下一页
和讯个人门户 v1.0 | 和讯部落 | 客服中心