0和NULL的区别

2012/01/17

在C/C++语言中,NULL是个宏。

在我的Windows机器上的stddef.h中,有如下的宏定义:

1
2
3
4
5
6
7
8
/* Define NULL pointer value */
#ifndef NULL
#ifdef __cplusplus
#define NULL    0
#else
#define NULL    ((void *)0)
#endif
#endif

在我的Linux虚拟机中的stddef.h中,有差不多的定义。

以上定义说明:在C++语言中,NULL是int型的0;在C++语言中,NULL是void *类型的0。表面上是这样,事实却不同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdarg.h>
#include <stdio.h>
 
void read_string_from_va(int i, ...)
{
        const char *p = NULL;
 
	va_list arg_ptr;
	va_start(arg_ptr, i);
 
	while ((p = va_arg(arg_ptr, const char *)) != NULL)
	{
		printf("%s\n", p);
	}
 
	va_end(arg_ptr);
}
 
int main()
{
	read_string_from_va(10, "4", "5", "6", "7", "8", 0);
}

上面的代码在VS2010中编译运行(不论是64bit还是32bit,不论是Debug,还是release),都没问题。

但用64位的gcc/g++在64位的Linux上编译运行,会有segmentation fault. 有图为证:


我们看到,不管你把它当C代码编译,还是当然C++代码编译,运行时都会segmentation fault.
原因很简单: 0是int,而read_string_from_va()中取值的时候,把它当char *。在64bit的linux上,char *是8字节长,int是4字节长,存进去4字节却取出8字节,所以最后挂了。
对于Linux C,换成NULL就不挂了,道理很简单,在C中,NULL是指针,你存进去8字节,拿出来8字节,一切OK。
按理来说,对于Linux C++,换成NULL一样挂,因为在C++中,NULL是int型的0,但事实并非如此:

很诡异,在C++中,0就是NULL,这次为什么没挂呢?g++做了什么事?
如果运行

1
g++ -E test0NULLL.c

我们可以看到,g++把NULL换成了__null,也就是g++自己的一个东西。然后再看看sizeof(__null),是8,所以没挂。
看来stddef.h中的宏没起作用。

还有问题:
read_string_from_va(10, “4″, “5″, “6″, “7″, 0);
上面的代码会挂吗?为什么?这和64bit linux calling convention有关。
为什么windows上vs2010没挂?vs2010聪明还是gcc/g++更相信程序员还是什么其它的?以后再说。

Posted in C/C++,未分类我抢沙发

欲望的层次

2012/01/07

今天看到有人在水木上发帖:发帖人刚怀孕,但这是二胎,自己和工作在体制内,没办法再要了,要打掉,很伤感。
鱼和熊掌不可得兼,舍鱼而取熊掌者也。
孩子和体制内的工作,二者不可得兼,舍孩子而取工作者也。

有些人,为了鱼和熊掌不能兼得而伤感;
有些人,嫌鱼和熊掌太小而伤感;
有些人,因为没鱼也没熊掌而伤感。

Posted in 活着2 条评论

一个练手的程序

2011/12/12
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
typedef enum color_e
{
    RED,
    WHITE,
    BLUE,
} color_t;
 
int __partition(color_t arr[], int _start, int _end, color_t c)
{
    int start = _start;
    int end = _end;
 
    while(start < end)
    {
        while(start <= end && arr[start] == c) start++;
        while(start <= end && arr[end] != c) end--;
        if(start<end)
        {
            color_t tmp = arr[start];
            arr[start] = arr[end];
            arr[end] = tmp;
        }
    }
    return start;
}
 
void partition(color_t arr[], int n)
{
    __partition(arr, __partition(arr, 0, n - 1, WHITE), n - 1, BLUE);
}
 
bool check(color_t arr[], int n)
{
    int i = 0;
    while(i < n && arr[i] == WHITE) i++;
    while(i < n && arr[i] == BLUE) i++;
    while(i < n && arr[i] == RED) i++;
 
    assert(i == n);
    return i == n;
}
 
void init(color_t arr[], int n)
{
    int i = 0;
    color_t tmp_arr[3] = {WHITE, BLUE, RED};
    while(i < n)
    {
        arr[i] = tmp_arr[rand()%3];
        i++;
    }
}
 
int main()
{
    srand(time(0));
    int i = 1;
 
    for( ; i<100000; i++)
    {
        color_t * arr = new color_t[i];
        init(arr, i);
        partition(arr, i);
        if(check(arr, i))
        {
            printf("===================%d\n", i);
        }
        delete []arr;
    }
}

一个练手的程序,关键是快排中的partition。
一个数组中有红白蓝三种颜色,用线性的方法把这三种颜色分开。
我觉得直接调用qsort也肯定是线性的。
还可以统计一下三种颜色的数量,这样更简单,没啥意思。

Posted in C/C++2 条评论

This is a test

2011/11/29

Teach Bing Liu.

先在1号圈圈里写标题。之后会自动生成地址。
可以点击2号圈圈改地址。
点击3号圈圈。
4号圈圈中的东西可以上传图片、音乐、视频。
5号圈圈中的东西就像word一样。
6号圈圈可以让你把word文档直接贴进来。
7号圈圈可以用来加链接,一会儿你做好了第个网页再用。
8号圈圈让你发布,点了它之后,你就可以在www.wildpointer.net看到你的帖子了。

Posted in 未分类2 条评论

螺旋数组

2011/11/07
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#define INDEX(total, x, y) ((x)*(total*2)+(y))
 
void fill(int *matrix, int total, int value)
{
    if(value < 0)
    {
        matrix[INDEX(total, total, total)] = 0;
        return;
    }
 
    int x_top = total - value;
    int y_left = x_top;
    int x_bottom = 2*total - x_top;
    int y_right = x_bottom;
 
    int x = 0;
    int y = 0;
 
    //(x_top, y_left) ---------> (x_top, y_right)           y changes
    for( x=x_top, y=y_left; y<y_right; y++)
        matrix[INDEX(total, x, y)] = value;
 
    //(x_top,y_right) ---------> (x_bottom, y_right)        x changes
    for( x=x_top, y=y_right; x<x_bottom; x++)
        matrix[INDEX(total, x, y)] = value;
 
    //(x_bottom, y_right) -----> (x_bottom, y_left)         y changes
    for( x=x_bottom, y=y_right; y>y_left; y--)
        matrix[INDEX(total, x, y)] = value;
 
    //(x_bottom, y_left) ------> (x_top, y_left)            x changes
    for( x=x_bottom, y=y_left; x>x_top; x--)
        matrix[INDEX(total, x, y)] = value;
 
    fill(matrix, total, value - 1);
}
 
void fill1(int *matrix, int total, int value)
{
BEGIN:
    if(value < 0)
    {
        matrix[INDEX(total, total, total)] = 0;
        return;
    }
 
    int x_top = total - value;
    int y_left = x_top;
    int x_bottom = 2*total - x_top;
    int y_right = x_bottom;
 
    int x = 0;
    int y = 0;
 
    //(x_top, y_left) ---------> (x_top, y_right)           y changes
    for( x=x_top, y=y_left; y<y_right; y++)
        matrix[INDEX(total, x, y)] = value;
 
    //(x_top,y_right) ---------> (x_bottom, y_right)        x changes
    for( x=x_top, y=y_right; x<x_bottom; x++)
        matrix[INDEX(total, x, y)] = value;
 
    //(x_bottom, y_right) -----> (x_bottom, y_left)         y changes
    for( x=x_bottom, y=y_right; y>y_left; y--)
        matrix[INDEX(total, x, y)] = value;
 
    //(x_bottom, y_left) ------> (x_top, y_left)            x changes
    for( x=x_bottom, y=y_left; x>x_top; x--)
        matrix[INDEX(total, x, y)] = value;
 
    value --;
    goto BEGIN;
}
 
void print(int *matrix, int total)
{
    int x = 0;
    int y = 0;
    int width = 3;
    for(x=0; x<=total*2; x++)
    {
        for(y=0; y<=total*2; y++)
            printf("%d\t", matrix[INDEX(total, x, y)]);
 
        printf("\n");
    }
}
 
int main(int argc, char* argv[])
{
    int total = atoi(argv[1]);
    int *matrix = (int *) malloc ((total*2+1)*(total*2+1)*sizeof(int));
    if(matrix == NULL) 
    {
        printf("error: malloc return NULL\n");
        return 1;
    }
 
    fill1(matrix, total, total);
    print(matrix, total);
 
    free(matrix);
    return 0;
}

从http://www.latelee.org/my-library/156-concentric-matrix.html看到的,写写练练手。
fill是递归的,尾递归,改成fill1,由于不想再格式化代码,用了个goto。如果很反对goto的话,可能看着很难受。
代码中可以用goto,只要不是乱goto就可以。

Posted in C/C++,未分类我抢沙发

operator new的故事

2011/11/06

operator new是一组函数,当使用new operator申请内存并调用构造函数时会用到它,operator new函数就是用来申请内存的,差不多相当于malloc.
operator delete也是一组函数,如果new operator调用operator new分配空间后,调用构造函数失败,则operator delete被C++运行环境调用来释放operator new申请到的空间,程序员不会手动直接调用这类函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
namespace std {
class bad_alloc;
class bad_array_new_length;
struct nothrow_t {};
extern const nothrow_t nothrow;
typedef void (*new_handler)();
new_handler get_new_handler() noexcept;
new_handler set_new_handler(new_handler new_p) noexcept;
}
void* operator new(std::size_t size);
void* operator new(std::size_t size, const std::nothrow_t&) noexcept;
void operator delete(void* ptr) noexcept;
void operator delete(void* ptr, const std::nothrow_t&) noexcept;
void* operator new[](std::size_t size);
void* operator new[](std::size_t size, const std::nothrow_t&) noexcept;
void operator delete[](void* ptr) noexcept;
void operator delete[](void* ptr, const std::nothrow_t&) noexcept;
void* operator new (std::size_t size, void* ptr) noexcept;
void* operator new[](std::size_t size, void* ptr) noexcept;
void operator delete (void* ptr, void*) noexcept;
void operator delete[](void* ptr, void*) noexcept;

上面一组东西是C++标准规定的。
在namespace std中,定义了bad_alloc,nothrow_t,以及new_handler类型和set_new_handler函数。
第10、11行:

1
2
void* operator new(std::size_t size);
void* operator new(std::size_t size, const std::nothrow_t&) noexcept;

它用来分配size字节的空间。第一种形式如果分配空间失败,则抛出bad_alloc;第二种形式,如果分配失败了,则返回NULL,不抛出bad_alloc.

1
2
void operator delete(void* ptr) noexcept;
void operator delete(void* ptr, const std::nothrow_t&) noexcept;

上面两种operator delete,分别对应第11、12行两种operator new。

1
2
void* operator new[](std::size_t size);
void* operator new[](std::size_t size, const std::nothrow_t&) noexcept;

上面两种operator new[],用来为数组分配空间,它们的区别与第11、12行的operator new的区别类似。

1
2
void* operator new (std::size_t size, void* ptr) noexcept;
void* operator new[](std::size_t size, void* ptr) noexcept;

上面两种,是最普通的placement new,它们仅仅返回ptr,不做其它事情。

1
2
void operator delete (void* ptr, void*) noexcept;
void operator delete[](void* ptr, void*) noexcept;

上面两种placement delete,什么都不做,只是与placement new对应,很诡异。

分配程序,使用VS2010,

1
2
3
4
5
6
7
int _tmain(int argc, _TCHAR* argv[])
{
    int *p = new int;
    *p = 0;
    delete p;
    return 0;
}

单步跟踪会发现new调用了下面的operator new

1
2
3
4
5
6
7
8
9
10
11
12
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
        {       // try to allocate size bytes
        void *p;
        while ((p = malloc(size)) == 0)
                if (_callnewh(size) == 0)
                {       // report no memory
                static const std::bad_alloc nomem;
                _RAISE(nomem);
                }
 
        return (p);
        }

可以看出来,这个版本的operator new调用了malloc分配空间,如果分配成功,则返回指向分配的空间的指针,如果分配失败,且_callnewh(size)返回0,则抛出异常。对于上面的例子,不会失败。

再构造一个分配内存失败的例子,我们可以看到_callnewh()被调用:

1
2
3
4
5
6
7
int _tmain(int argc, _TCHAR* argv[])
{
    for(;;)
        int *p = new int[1024*1024*255];
 
    return 0;
}

还是单步运行,在第二次new的时候,malloc失败,调用了_callnewh()。

1
2
3
4
5
6
7
8
9
10
11
12
/* pointer to old-style C++ new handler */
_PNH _pnhHeap;
extern "C" int __cdecl _callnewh(size_t size)
{
        {
            _PNH pnh = (_PNH) DecodePointer(_pnhHeap);
 
            if ( (pnh == NULL) || ((*pnh)(size) == 0) )
                return 0;
        }
        return 1;
}

_callnewh的函数体如上:如果new_handler是NULL或者(*pnh)(size) == 0则返回0。我不懂第二个表达式的意思。这次运行我观察到new_handler为NULL,直接就返回0了,然后抛出了异常。

Posted in 未分类我抢沙发

new的故事

2011/10/30
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>
#include <new>
#include <cstdlib>
#define __OUTPUT_FUNC_NAME__ std::cout << __FUNCTION__ \
        <<"\tthis =\t" << this << std::endl;
 
class Foo
{
public:
    Foo()
    {
        __OUTPUT_FUNC_NAME__
    }
    virtual ~Foo()
    {
        __OUTPUT_FUNC_NAME__
    }
};
 
int main()
{
    Foo *psingle = new Foo;
    delete psingle;
 
    std::cout << __LINE__ <<std::endl;
    Foo *psingle_nothrow = new(std::nothrow) Foo();
    delete psingle_nothrow;
 
    std::cout << __LINE__ <<std::endl;
    Foo *parray = new Foo[5];
    delete[] parray;
 
    std::cout << __LINE__ <<std::endl;
    Foo *parray_nothrow = new(std::nothrow) Foo[5];
    delete[] parray_nothrow;
 
    std::cout << __LINE__ <<std::endl;
    Foo *praw = (Foo *)malloc(sizeof Foo);
    new(praw) Foo;
    praw->~Foo();
    free(praw);
 
    std::cout << __LINE__ <<std::endl;
    Foo *praw_array = (Foo *)malloc(sizeof(Foo) * 5);
    for(int i=0; i<5; ++i)
    {
        new(praw_array+i)Foo();
    }
 
    for(int i=0; i<5; ++i)
    {
        (praw_array+i)->~Foo();
    }
    free(praw_array);
 
    return 0;
}

上面这段代码可以用VS2010编译通过并运行。
第7~18行,是一个类Foo,用来示例的。
第22行,是普通的new, 创建单个的对象。如果new分配空间失败,则抛出异常。
第23行,是普通的delete,销毁单个的对象。
第26行,nothrow new,也就是说,当new分配空间失败时,只返回NULL,不会抛出异常。
第30行,new了一个数组。
第31行,将数组delete。
第39行,placement new, 已经有空间了,不用new去分配空间,直接在已经存在的空间上调用构造函数。
第40行,将其销毁。
第44行,申请了一个数组。第45~49行,一个一个地placement new,第50~53行,一个一个地placement delete. 有人会问:为什么不用placement new[], 答:不会,请多指教。

通过以上代码,对new有了直观的认识。new首先会分配空间,然后在得到的空间上调用构造函数。
怎么分配空间?用operator new函数。
分配空间失败了怎么办?如果是普通形式的new,抛出异常;如果是nothrow形式的new,返回NULL。
operator new函数有哪些?且听下回分解。

Posted in C/C++我抢沙发

入职两周

2011/07/30

十八号入职到现在,已经两周了。

这两周来,一直在接受培训。讲讲公司的制度等等。上周五,逛了逛上海,去了好几个地方,东方明珠、七宝古镇、人民公园、田字坊等等。当天挺热的。

有好几次去复旦吃中午饭,光华楼真高。

要开始学习了,老大昨天告诉我,下周要向两个老员工学习存储方面的知识了。这两个老员工,其中有一个是东南大学计算机系汪芸老师的学生,比我高四级。

这一次入职的新员工中,大多来自上交、复旦、北大、南大、浙大、中科院、北邮、北航、人大、同济、东南、中山、华科、武大、中科大、西交、川大、电子科大、重邮、上大,基本上都来自国内最NB的几十所学校,我感觉压力很大。

公司新开了个成都研发中心,这样,公司在中国大陆一共有三个研发中心了,另两个在上海和北京。另外,公司还有印度、爱尔兰、以色列特拉维夫等等数个研发中心。

了解了一些存储方面的基础知识以及公司的产品。公司的产品有Symmetrics、Centera、Celerra、CLARiiON,Celerra和CLARiiON已经成为历史了,现在将这两者合称为VNX。存储方面的基础知识包括SAN、NAS、RAID。还了解到VMware、RSA等等也算我们公司的,我们公司持有VMware 80%的股份。

Posted in 活着2 条评论

公司五周年庆

2011/07/29

今天公司进行了成立五周年庆典,好几个BOSS上台发表讲话,还给员工颁奖,最后吃蛋糕。

乐队唱歌挺好听的,唱了好几首,加州旅馆、春天里等等。

至今天,为期两周的入职培训也结束了,大BOSS还给每位新员工颁发了毕业证,呵呵。证上有我们的相片,另外还送了每位新员工两本书,其中一本是《高效能人士的七个习惯》。

Posted in 活着2 条评论

大BOSS训话

2011/07/26

呵呵,其实也不是训话,就是提醒一下刚入职的同学们:入职后的六个月至关重要!要踏踏实实!

Posted in 未分类4 条评论