1
2
3
4
5
6
7
$ gcc test.cpp -otest
$ ./test&
$ 0x804a024
$ ./test&
$ 0x804a024
$ ./test&
$ 0x804a024

  对可执行文件test,不同的执行实例产生的输出为什么是一样的呢?为什么呢?难道一直以来我对虚拟地址空间的理解都是错误的?另外,如果int a是局部变量,输出就不同。

Tags: .
1
2
3
4
5
6
7
#include <stdio.h>
int a[10240] = {1}; //~ 显式初始化a[0]为1的全局数组,编译后,可执行文件的大小为49.0K
int
main()
{
    return 0;
}

  局部对象是在运行时栈上分配的,编译后不占用磁盘空间。未显式初始化或者初始化为0的全局数组,编译器会做相应的记录,以便在程序装入时分配空间。做显式初始化的全局数组,由于初始化的元素个数及元素值不便记录,编译器便在编译时就分配了空间,因此需要占用磁盘空间。

Tags: .

程序输出:
o.o.o.o.o.o.o.o..o.o.oo.o.o.o.o.o.o.o.o.21
为什么呢?
加上代码中注释掉的互斥锁1、2、3后,输出没有大的变化,只是global变成了20。再注释掉$1、#2,输出结果就正常了。

Tags: .

  管道是Linux支持的 IPC形式之一,具有以下特点:

  • 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;
  • 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);
  • 单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。
  • 数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。
Tags: ,,,.

实现了动态加载标准库中的数学函数库,输入函数名及参数,返回计算结果。

1
2
3
4
5
6
7
8
9
10
11
/*
 * #include <dlfcn.h>
 * void *dlopen( const char *file, int mode );
 * void *dlsym( void *restrict handle, const char *restrict name );
 * char *dlerror();
 * char *dlclose( void *handle );
 * dlopen	使对象文件可被程序访问
 * dlsym	获取执行了 dlopen 函数的对象文件中的符号的地址
 * dlerror	返回上一次出现错误的字符串错误
 * dlclose	关闭目标文件
 */
Tags: ,.
1
void (*signal (int sigNum, void (*sigHandler)(int))) (int);

乍一看这个函数原型就被唬住了,跟个指针似的。仔细分析一下,
*signal (int sigNum, void (*sigHandler)(int))
部分里面
(int sigNum, void (*sigHandler)(int))
优先级高于signal前面的*,所以这是个函数,*只是返回值的一部分,即返回的应该是一个指针。这个指针式什么类型的取决于
(*signal (int sigNum, void (*sigHandler)(int)))
外面的部分,前面是void,后面是(int),可见signal函数返回的是一个函数指针,其函数原型为void f(int)。返回的这个指针与signal的第二个参数的类型是一样的。于是乎,这个bt的函数原型声明还可以这样来定义:

1
2
typedef void sigHandler(int);
sigHandler *signal(int, sigHandler *);

抑或是

1
2
typedef void (*psigHandler)(int);
psigHandler signal(int, psigHandler);

这种声明看起来真累!

Tags: ,.
0x4000a963 <_dl_runtime_resolve+3>:     movl   0x10(%esp,1),%edx
0x4000a967 <_dl_runtime_resolve+7>:     movl   0xc(%esp,1),%eax
0x4000a96b <_dl_runtime_resolve+11>:    call   0x4000a740 

  指令xchgl %eax,(%esp,1)将printf的地址放入栈顶。最精彩的一条指令当属ret $0×8,它将栈顶元素即printf的地址弹出至程序计数器PC,作为下一条将执行的指令地址,同时,清除堆栈中的0×10和0×8049560。此时堆栈中的情形,就如同直接调用了printf函数,似乎什么都没发生过。

  此外还做了一件重要的工作,就是把前面提到的addr2替换为printf的地址。从而当再次调用printf时jmp *addr1就直接将程序定位到printf,不需要再次加载库libc.so了。

  不知道我说清楚了没有,感觉说的很乱,文字也很乱。:-)

Tags: ,,.

没啥好说的,只是要注意防止m = (r + l) / 2;时m会发生上溢。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
template <class T>
int BSearch(T *a, size_t n, T v)
{
    assert(a != NULL && n > 0); //! a == NULL || n <= 0 就坏了:-(
    size_t l = 0,
           r = n -1,
           m = l + (r - l) / 2; //! 勿用m=(l+r)/2;
    while(l <= r)
    {
        if(a[m] == v)
            return (int)m;
        else if(a[m] < v)
            l = m + 1;
        else
            r = m - 1;
    }
    return -1;
}
Tags: ,.

convert是imagemagick软件包中的一个命令。它可以读取、转换、写入多种格式的图片。图片切割、颜色替换、各种效果的应用,图片的旋转、组合,文本,直线,多边形,椭圆,曲线,附加到图片伸展旋转。这里介绍几个简单的命令,具体的用法请参考man手册或者其官方网站。在Ubuntu中用命令

1
sudo apt-get install imagemagick

来安装,windows下的使用可以到官方网站下载。

Tags: ,,,.

归并排序算法(mergesort)是将一个序列划分为同样大小的两个子序列,然后对两个子序列分别进行排序,最后进行合并操作,将两个子序列合成有序的序列。在合成的过程中,一般的实现都需要开辟一块与原序列大小相同的空间,以进行合并操作。这里介绍一种不需要开辟新的空间就可以进行归并操作的原地归并算法。代码来说事儿:

1
2
3
4
5
6
7
8
9
//~ 原地归并
while(l < r && r < size) //~ 核心算法
{
	while(v[l] <= v[r] && l < r) ++l; //~ 找到左面第一个比v[r]大的v[l]
	size_t to_mv = 0; //~ 计数器
	while(v[r] < v[l] && r < size) ++r, ++to_mv; //~ 右侧找到to_mv个比v[l]小的数
	Exchange(v+l, r-l-to_mv, r-l);//~ 将此to_mv个数移到v[l]前面
	l += to_mv; //~ 改变l,继续下一轮循环
}//~ l >-r 或者 r >= size时归并结束
Tags: ,,.