Sep
6
前一阵重装过XP,IE6中文版这一点很不爽,今天终于决定改了。
搜了一下具体的方法,发现有个巨麻烦的要自己hack browselc.dll,不爽。
然后看到了这个解决方法,很不错,贴一下:
单击“开始/运行”,输入“regedit”并回车打注册表,
依次展开到:HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Toolbar,
新建项QuickComplete,再在QuickComplete项下新建字符串值QuickComplete,值为“http://www.%s.com”
如果想改成cn 就值为“http://www.%s.cn”
搜了一下具体的方法,发现有个巨麻烦的要自己hack browselc.dll,不爽。
然后看到了这个解决方法,很不错,贴一下:
单击“开始/运行”,输入“regedit”并回车打注册表,
依次展开到:HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Toolbar,
新建项QuickComplete,再在QuickComplete项下新建字符串值QuickComplete,值为“http://www.%s.com”
如果想改成cn 就值为“http://www.%s.cn”
Aug
17
前两天在Dutor(或者是Ivan?)的Blog看到了一篇文章:
关于free和delete http://www.dutor.net/index.php/2009/08/free-delete/
以前没有仔细考虑过这个问题,因此和Sandy讨论了一下,得出了初步结果,总结一下放在这里吧。
---传说中的分割线---
先看一段代码:
这段代码的前面还是比较好理解的,关键在于,这最后的 delete (char *)a; 它究竟做了什么事情?
1. 它释放了多少内存——是释放了一个char*指向的内存,还是当初new分配的那些内存?
2. 它是否调用了class T的析构函数?
下面是我和Sandy的讨论结果,针对以上两个问题的分析,以及一点延伸的探讨。
1. 释放了多少内存。
回头想想C提供的malloc/free函数,这小俩口处理的都是void*类型的指针,而且当free时只要告诉其指针即可。
也就是说,malloc函数多做了一些事情,让程序在某处记住了分配的内存大小,当free时,对应找到这个大小进行释放。
因此我们有理由认为,new/delete这对黑白脸做法也是类似的(没有小心求证,如果不对,请大牛指出):
-- new从堆上申请内存,是先进行记录,包括新内存的地址以及申请的长度,然后才把地址返回给申请者。
-- delete根据释放者提交的地址,在记录中查找这个地址,然后找到其长度,然后回收内存。。
故此我们认为,它释放的内存是new分配的那些内存,与传入的指针类型无关。
2. 是否调用了相应的析构函数。
这个问题很容易验证,把上面的程序跑一遍就可以了。
结果就是,输出了 "T()" 这一行,说明只调用了构造函数而没有调用析构函数。
仔细想想,其实这很容易理解。
隐约记得STL之父(Or C++之父?)说过一句话,你有很多事情做,但是编译器没有。
C++编译器被设计成帮你完成了很多琐碎的事情,很典型的一件事就是,编译期类型推导。
因此你在cout的时候也不需要指定输出类型(%d%x%c...)
new一个东西以后不需要像malloc那样进行强制类型转换了,还自动调用class 的构造函数,
delete一个指向class的指针时,也根据指针的类型自动添加了调用该class析构函数的代码。
而上面给出的代码,最终交给delete的是一个char *类型的指针
因此编译器添加的代码就不会涉及到class T的析构函数。
如果你是转换成另一个class Q的指针,那么就会调用Q的析构函数。有兴趣的话可以自己写程序证实一下。
3. 它会导致严重的后果吗?
答案是会。
比如上面给出的程序,造成了内存泄漏:预期在析构函数中释放的 T::a 指向的内存,但是析构函数最终没有被执行。
4. 它会导致更严重的后果吗?
答案还是会。
这一点是Sandy提出来的:在C++中,operator new/delete是可以被重载的。
如果一个class的operator new/delete被重载了,它将以它自己的方式去管理(分配/回收)内存。
比如说以内存池的方式;或者是在栈上进行分配的内存。
如果你使用这种方式去delete一个改变了类型的指针,那么结果就是,采用了不同的管理方式去回收这个内存。
——就好比你malloc出来的内存被delete掉了
传说中的换妻啊。。。后果就是对方崩溃了。。。嗯。
--------传说中的分割线--------
这个问题到这里差不多了,实际使用中应该不会有谁这么脑残的吧,嗯。
进行这样的一些分析,主要是希望能够更深入地理解C++这一块的内容。
还没有确切解决的疑问就是第一个——
new/delete 释放的内存块大小是否关乎指针类型?希望有大牛释疑。
另外顺便提一下,我觉得当在C++中需要使用对class进行强制类型转换的时候,
应该好好考虑一下,这程序是不是在设计上出了什么问题,因为这么做会增加程序的耦合性。
当然,在某些时候你的确会需要用到这种做法,因为这样可以增加程序的动态性(许多Java程序依赖于此)。
关于free和delete http://www.dutor.net/index.php/2009/08/free-delete/
以前没有仔细考虑过这个问题,因此和Sandy讨论了一下,得出了初步结果,总结一下放在这里吧。
---传说中的分割线---
先看一段代码:
#include<iostream>
class T{
public:
int *a;
T() {
a = new int[1048576];
std::cout << "T()" << endl;
}
~T() {
delete[] a;
std::cout << "~T()" << endl;
}
};
int main() {
T *a = new T;
delete (char*)a;
}
class T{
public:
int *a;
T() {
a = new int[1048576];
std::cout << "T()" << endl;
}
~T() {
delete[] a;
std::cout << "~T()" << endl;
}
};
int main() {
T *a = new T;
delete (char*)a;
}
这段代码的前面还是比较好理解的,关键在于,这最后的 delete (char *)a; 它究竟做了什么事情?
1. 它释放了多少内存——是释放了一个char*指向的内存,还是当初new分配的那些内存?
2. 它是否调用了class T的析构函数?
下面是我和Sandy的讨论结果,针对以上两个问题的分析,以及一点延伸的探讨。
1. 释放了多少内存。
回头想想C提供的malloc/free函数,这小俩口处理的都是void*类型的指针,而且当free时只要告诉其指针即可。
也就是说,malloc函数多做了一些事情,让程序在某处记住了分配的内存大小,当free时,对应找到这个大小进行释放。
因此我们有理由认为,new/delete这对黑白脸做法也是类似的(没有小心求证,如果不对,请大牛指出):
-- new从堆上申请内存,是先进行记录,包括新内存的地址以及申请的长度,然后才把地址返回给申请者。
-- delete根据释放者提交的地址,在记录中查找这个地址,然后找到其长度,然后回收内存。。
故此我们认为,它释放的内存是new分配的那些内存,与传入的指针类型无关。
2. 是否调用了相应的析构函数。
这个问题很容易验证,把上面的程序跑一遍就可以了。
结果就是,输出了 "T()" 这一行,说明只调用了构造函数而没有调用析构函数。
仔细想想,其实这很容易理解。
隐约记得STL之父(Or C++之父?)说过一句话,你有很多事情做,但是编译器没有。
C++编译器被设计成帮你完成了很多琐碎的事情,很典型的一件事就是,编译期类型推导。
因此你在cout的时候也不需要指定输出类型(%d%x%c...)
new一个东西以后不需要像malloc那样进行强制类型转换了,还自动调用class 的构造函数,
delete一个指向class的指针时,也根据指针的类型自动添加了调用该class析构函数的代码。
而上面给出的代码,最终交给delete的是一个char *类型的指针
因此编译器添加的代码就不会涉及到class T的析构函数。
如果你是转换成另一个class Q的指针,那么就会调用Q的析构函数。有兴趣的话可以自己写程序证实一下。
3. 它会导致严重的后果吗?
答案是会。
比如上面给出的程序,造成了内存泄漏:预期在析构函数中释放的 T::a 指向的内存,但是析构函数最终没有被执行。
4. 它会导致更严重的后果吗?
答案还是会。
这一点是Sandy提出来的:在C++中,operator new/delete是可以被重载的。
如果一个class的operator new/delete被重载了,它将以它自己的方式去管理(分配/回收)内存。
比如说以内存池的方式;或者是在栈上进行分配的内存。
如果你使用这种方式去delete一个改变了类型的指针,那么结果就是,采用了不同的管理方式去回收这个内存。
——就好比你malloc出来的内存被delete掉了
传说中的换妻啊。。。后果就是对方崩溃了。。。嗯。
--------传说中的分割线--------
这个问题到这里差不多了,实际使用中应该不会有谁这么脑残的吧,嗯。
进行这样的一些分析,主要是希望能够更深入地理解C++这一块的内容。
还没有确切解决的疑问就是第一个——
new/delete 释放的内存块大小是否关乎指针类型?希望有大牛释疑。
另外顺便提一下,我觉得当在C++中需要使用对class进行强制类型转换的时候,
应该好好考虑一下,这程序是不是在设计上出了什么问题,因为这么做会增加程序的耦合性。
当然,在某些时候你的确会需要用到这种做法,因为这样可以增加程序的动态性(许多Java程序依赖于此)。
Aug
11
9.04以前的ubuntu要安装gstreamer的时候,只需要简单地
$ sudo apt-get install gstreamer0.10-*
然后就OK了。
从9.04开始,这选中的所有包有冲突了,经过筛选,发现最后安装这些包即可:
gstreamer0.10-alsa
gstreamer0.10-doc
gstreamer0.10-esd
gstreamer0.10-ffmpeg
gstreamer0.10-fluendo-mp3
$ sudo apt-get install gstreamer0.10-*
然后就OK了。
从9.04开始,这选中的所有包有冲突了,经过筛选,发现最后安装这些包即可:
gstreamer0.10-alsa
gstreamer0.10-doc
gstreamer0.10-esd
gstreamer0.10-ffmpeg
gstreamer0.10-fluendo-mp3
Aug
10
前两天发现迅雷针对其会员推出了一个“离线下载”业务,
能够让迅雷的服务器帮你下东西,甚至挂BT挂电驴。
然后提供150KB以上的稳定下载速度让你下回来。
虽然我觉得很好,但是在百度知道看了一下,很多人却觉得不靠谱。
由于迅雷提供3天的免费试用(600MB空间,单任务),于是决定试试。
前天本来想下一个Ice Age III的特别短片Surviving Sid,540.8MB
但是由于下载速度过慢(只有两三KB)而我又不想挂机,所以还是放弃了。
昨晚在迅雷的离线任务添加了这个任务,速度其实也差不多,不过有的时候快些。
今天早上来看,下了70%+,接近中午的时候80%+。午饭回来再一看,下好了。
点击下载到本地,弹出firefox自带的下载管理器,直接就有200KB以上的稳定速度(教育网)。
感觉很不错,用来挂不太容易下的东西还是比较靠谱的。
不过。。如果你的网络本身够快且有公网IP(适合挂BT电驴),那就不用考虑了。
顺便提一句,迅雷会员跟QQ一样,10米/月,网银八八折。
迅雷离线下载的官方网站是 http://lixian.vip.xunlei.com
能够让迅雷的服务器帮你下东西,甚至挂BT挂电驴。
然后提供150KB以上的稳定下载速度让你下回来。
虽然我觉得很好,但是在百度知道看了一下,很多人却觉得不靠谱。
由于迅雷提供3天的免费试用(600MB空间,单任务),于是决定试试。
前天本来想下一个Ice Age III的特别短片Surviving Sid,540.8MB
但是由于下载速度过慢(只有两三KB)而我又不想挂机,所以还是放弃了。
昨晚在迅雷的离线任务添加了这个任务,速度其实也差不多,不过有的时候快些。
今天早上来看,下了70%+,接近中午的时候80%+。午饭回来再一看,下好了。
点击下载到本地,弹出firefox自带的下载管理器,直接就有200KB以上的稳定速度(教育网)。
感觉很不错,用来挂不太容易下的东西还是比较靠谱的。
不过。。如果你的网络本身够快且有公网IP(适合挂BT电驴),那就不用考虑了。
顺便提一句,迅雷会员跟QQ一样,10米/月,网银八八折。
迅雷离线下载的官方网站是 http://lixian.vip.xunlei.com
Jul
26
1. 选中收件箱和发件箱
2. 菜单->操作->导出文本
3. 把导出的短信都放在一个目录下面
4. 在目录下放一个convertencoding.php,内容为:
5. 另一个php脚本,用于提取和指定的人的聊天记录
2. 菜单->操作->导出文本
3. 把导出的短信都放在一个目录下面
4. 在目录下放一个convertencoding.php,内容为:
<?php
$dir = scandir(".");
foreach($dir as $file){
if(substr($file, -3) != "txt") continue;
echo $file, "\n";
$str = file_get_contents($file);
$str = iconv("GB18030", "UTF-8", $str);
file_put_contents($file, $str);
}
?>
然后$ php convertencoding.php 运行之。$dir = scandir(".");
foreach($dir as $file){
if(substr($file, -3) != "txt") continue;
echo $file, "\n";
$str = file_get_contents($file);
$str = iconv("GB18030", "UTF-8", $str);
file_put_contents($file, $str);
}
?>
5. 另一个php脚本,用于提取和指定的人的聊天记录
Jul
26
遇到一个问题:
有一个数组,其中每个元素都包含time和content两个子元素,需要按time字段排序。
记得以前是搞过这个东西的,有个专门的函数,貌似就是array_multisort来搞。
查了一下mannual,发现例子实在太晦涩,看起来很不爽。
于是干脆自己实现一个,只需要提供一个排序函数即可。
效率可能低一些,但是用起来舒服多了。。
有一个数组,其中每个元素都包含time和content两个子元素,需要按time字段排序。
记得以前是搞过这个东西的,有个专门的函数,貌似就是array_multisort来搞。
查了一下mannual,发现例子实在太晦涩,看起来很不爽。
于是干脆自己实现一个,只需要提供一个排序函数即可。
效率可能低一些,但是用起来舒服多了。。
//调用例子(cmp函数可以自己重写):
mysort($array_name, 0, count($array_name) - 1, cmp);
function cmp($i, $j){
return $i['time'] < $j['time'];
}
function mysort(&$arr, $s, $e, $func){
$i = $s; $j = $e; $tmp = $arr[$s];
if($s < $e){
while($i != $j){
while($i < $j && $func($tmp, $arr[$j])) $j--;
if($i < $j){
$arr[$i] = $arr[$j];
$i++;
}
while($i < $j && $func($arr[$i], $tmp)) $i++;
if($i < $j){
$arr[$j] = $arr[$i];
$j--;
}
}
$arr[$i] = $tmp;
mysort($arr, $s, $i-1, $func);
mysort($arr, $i+1, $e, $func);
}
}
mysort($array_name, 0, count($array_name) - 1, cmp);
function cmp($i, $j){
return $i['time'] < $j['time'];
}
function mysort(&$arr, $s, $e, $func){
$i = $s; $j = $e; $tmp = $arr[$s];
if($s < $e){
while($i != $j){
while($i < $j && $func($tmp, $arr[$j])) $j--;
if($i < $j){
$arr[$i] = $arr[$j];
$i++;
}
while($i < $j && $func($arr[$i], $tmp)) $i++;
if($i < $j){
$arr[$j] = $arr[$i];
$j--;
}
}
$arr[$i] = $tmp;
mysort($arr, $s, $i-1, $func);
mysort($arr, $i+1, $e, $func);
}
}
Jul
8
咱们来对比一下这5个东西:
——天哪,这都是什么东西?用5个小程序来解释一下。。或许你可以看得懂?
如果你急着和mm去约会,直接跳到文末,有比较简洁的说明。
OK,其实有一种很简单的阅读方法:从右向左读定义,结果就是——
char ** a;
const char ** b;
char * const * c;
const char * const * d;
const char * const * const e;
const char ** b;
char * const * c;
const char * const * d;
const char * const * const e;
——天哪,这都是什么东西?用5个小程序来解释一下。。或许你可以看得懂?
如果你急着和mm去约会,直接跳到文末,有比较简洁的说明。
#include <iostream>
#include <cstring>
using namespace std;
void test1(){
char **s; //s是数组的指针;
s = NULL;
s = new char*[4];
for (int i = 0; i < 4; ++i){
s[i] = new char[10];
strcpy(s[i], "test");
}
for (int i = 0; i < 4; ++i){
printf("%s\n", s[i]);
}
for (int i = 0; i < 4; ++i){
delete[] s[i];
}
delete[] s;
}
void test2(){
const char **s; //s是指向字符串常量的指针
s = NULL;
char b[4][10] = {"a","b","c","d"};
s = new const char*[4];
for (int i = 0; i < 4; ++i){
s[i] = b[i]; // OK
//s[i][0] = 'd'; //这句要报错,因为s[i]指向的是字符串常量
//即使b[i]字符串本不是常量(编译期间添加的属性)
}
for (int i = 0; i < 4; ++i){
printf("%s\n", s[i]);
}
delete[] s;
}
void test3(){
char * const * s; //s指向常量数组,数组的每一个元素是字符指针常量。
//数组的元素不可改,但数组元素指向的字符串可修改
s = NULL;// s不是常量
char a[4][10] = {"aa", "bb", "cc", "dd"};
char * const(b[4]) = {a[0], a[1], a[2], a[3]};
s = b;
for (int i = 0; i < 4; ++i){
s[i][1] = 'd'; //OK
//s[i] = NULL; //报错,因为s[i]是常量
printf("%s\n", s[i]);
}
}
void test4(){
const char * const * s; //s指向一个常量指针数组
//数组的每一个元素是字符指针常量,指向字符串常量(绕口令阿这是。。。)
s = NULL;// s不是常量
char a[4][10] = {"aa", "bb", "cc", "dd"};
char * const(b[4]) = {a[0], a[1], a[2], a[3]};
s = b;
for (int i = 0; i < 4; ++i){
//s[i][1] = 'd'; //报错,因为s[i][j]是常量
//s[i] = NULL; //报错,因为s[i]是常量
printf("%s\n", s[i]);
}
}
void test5(){
char a[4][10] = {"aa", "bb", "cc", "dd"};
const char * const(b[4]) = {a[0], a[1], a[2], a[3]};
const char * const * const s = b;
//s是一个常量指针,指向一个常量指针数组
//数组的每一个元素是字符指针常量,指向字符串常量(这才是绕口令!)
//s = NULL; //Error, s是常量
for (int i = 0; i < 4; ++i){
//s[i][1] = 'd'; //报错,因为s[i][j]是常量
//s[i] = NULL; //报错,因为s[i]是常量
printf("%s\n", s[i]);
}
}
int main(){
test5();
return 0;
}
#include <cstring>
using namespace std;
void test1(){
char **s; //s是数组的指针;
s = NULL;
s = new char*[4];
for (int i = 0; i < 4; ++i){
s[i] = new char[10];
strcpy(s[i], "test");
}
for (int i = 0; i < 4; ++i){
printf("%s\n", s[i]);
}
for (int i = 0; i < 4; ++i){
delete[] s[i];
}
delete[] s;
}
void test2(){
const char **s; //s是指向字符串常量的指针
s = NULL;
char b[4][10] = {"a","b","c","d"};
s = new const char*[4];
for (int i = 0; i < 4; ++i){
s[i] = b[i]; // OK
//s[i][0] = 'd'; //这句要报错,因为s[i]指向的是字符串常量
//即使b[i]字符串本不是常量(编译期间添加的属性)
}
for (int i = 0; i < 4; ++i){
printf("%s\n", s[i]);
}
delete[] s;
}
void test3(){
char * const * s; //s指向常量数组,数组的每一个元素是字符指针常量。
//数组的元素不可改,但数组元素指向的字符串可修改
s = NULL;// s不是常量
char a[4][10] = {"aa", "bb", "cc", "dd"};
char * const(b[4]) = {a[0], a[1], a[2], a[3]};
s = b;
for (int i = 0; i < 4; ++i){
s[i][1] = 'd'; //OK
//s[i] = NULL; //报错,因为s[i]是常量
printf("%s\n", s[i]);
}
}
void test4(){
const char * const * s; //s指向一个常量指针数组
//数组的每一个元素是字符指针常量,指向字符串常量(绕口令阿这是。。。)
s = NULL;// s不是常量
char a[4][10] = {"aa", "bb", "cc", "dd"};
char * const(b[4]) = {a[0], a[1], a[2], a[3]};
s = b;
for (int i = 0; i < 4; ++i){
//s[i][1] = 'd'; //报错,因为s[i][j]是常量
//s[i] = NULL; //报错,因为s[i]是常量
printf("%s\n", s[i]);
}
}
void test5(){
char a[4][10] = {"aa", "bb", "cc", "dd"};
const char * const(b[4]) = {a[0], a[1], a[2], a[3]};
const char * const * const s = b;
//s是一个常量指针,指向一个常量指针数组
//数组的每一个元素是字符指针常量,指向字符串常量(这才是绕口令!)
//s = NULL; //Error, s是常量
for (int i = 0; i < 4; ++i){
//s[i][1] = 'd'; //报错,因为s[i][j]是常量
//s[i] = NULL; //报错,因为s[i]是常量
printf("%s\n", s[i]);
}
}
int main(){
test5();
return 0;
}
OK,其实有一种很简单的阅读方法:从右向左读定义,结果就是——
char ** s;
s是一个指针1,指向一个指针2, 指针2指向char
const char ** s;
s是一个指针1,指向一个指针2,指针2指向char,char是常数
char * const * s;
s是一个指针1,指向一个常量1,常量1是个指针2,指针2指向char
const char * const * s;
s是一个指针1,指向一个常量1,常量1是个指针2,指针2指向char,char是常数
const char * const * const s;
s是一个常量1,常量1是一个指针1,指针1指向一个常量2,常量2是个指针2,指针2指向char,char是常数
s是一个指针1,指向一个指针2, 指针2指向char
const char ** s;
s是一个指针1,指向一个指针2,指针2指向char,char是常数
char * const * s;
s是一个指针1,指向一个常量1,常量1是个指针2,指针2指向char
const char * const * s;
s是一个指针1,指向一个常量1,常量1是个指针2,指针2指向char,char是常数
const char * const * const s;
s是一个常量1,常量1是一个指针1,指针1指向一个常量2,常量2是个指针2,指针2指向char,char是常数
Jul
1
本文档分析了xen启动时创建dom0的基本过程,并进行了一些简单的测试,记录了测试结果。
@ xen3.3.0/xen/arch/x86/setup.c
---- line 408 __start_xen(unsigned long mbi_p)
@ line 414, multiboot_info_t *mbi = __va(mbi_p);
mbi = multiboot_info, 类型multiboot_info_t, 位于include/xen/multiboot.h
@ line 415, modile_t *mod = (module_t *)__va(mbi->mods_addr);
module_t是一个位于multiboot.h中的struct,包含mod_start, mod_end, string, reserved等4个成员,其中mod_start, mod_end是地址, string是module [file] [para]这里的para字段(参数, 如果没有的话,就是NULL),比如kernel的参数就是这么搞出来的cmdline(大概在997行的位置)。
这个mod是一个指针,取值于mbi_p的成员mods_addr,其实就对应了一个数组,这数组的每一项应该就是顺序地对应着grub启动xen的时候,每一个module行对应的文件载入内存后的信息,包括起始和结束地址,还有附加的参数。
@ xen3.3.0/xen/arch/x86/setup.c
---- line 408 __start_xen(unsigned long mbi_p)
@ line 414, multiboot_info_t *mbi = __va(mbi_p);
mbi = multiboot_info, 类型multiboot_info_t, 位于include/xen/multiboot.h
@ line 415, modile_t *mod = (module_t *)__va(mbi->mods_addr);
module_t是一个位于multiboot.h中的struct,包含mod_start, mod_end, string, reserved等4个成员,其中mod_start, mod_end是地址, string是module [file] [para]这里的para字段(参数, 如果没有的话,就是NULL),比如kernel的参数就是这么搞出来的cmdline(大概在997行的位置)。
这个mod是一个指针,取值于mbi_p的成员mods_addr,其实就对应了一个数组,这数组的每一项应该就是顺序地对应着grub启动xen的时候,每一个module行对应的文件载入内存后的信息,包括起始和结束地址,还有附加的参数。