Ted's Blog

Happy coding

设置往vim中粘贴数据时不自动缩进

在.vimrc中添加:

"Paste toggle - when pasting something in, don't indent.
set pastetoggle=<F3>

当粘贴时,按一下F3即可。

sprintf详解

在将各种类型的数据构造成字符串时,sprintf 的强大功能很少会让你失望。由于sprintf 跟printf 在用法上几乎一样,只是打印的目的地不同而已,前者打印到字符串中,后者则直接在命令行上输出。这也导致sprintf 比printf 有用得多。

sprintf 是个变参函数,定义如下:
int sprintf( char *buffer, const char *format [, argument] ... );
除了前两个参数类型固定外,后面可以接任意多个参数。而它的精华,显然就在第二个参数:
格式化字符串上。


printf 和sprintf 都使用格式化字符串来指定串的格式,在格式串内部使用一些以“%”开头的格式说明符(format specifications)来占据一个位置,在后边的变参列表中提供相应的变量,最终函数就会用相应位置的变量来替代那个说明符,产生一个调用者想要 的字符串。

格式化数字字符串
sprintf 最常见的应用之一莫过于把整数打印到字符串中,所以,spritnf 在大多数场合可以替代
itoa。

如:
//把整数123 打印成一个字符串保存在s 中。
sprintf(s, "%d", 123); //产生"123"
可以指定宽度,不足的左边补空格:
sprintf(s, "%8d%8d", 123, 4567); //产生:" 123 4567"
当然也可以左对齐:
sprintf(s, "%-8d%8d", 123, 4567); //产生:"123 4567"
也可以按照16 进制打印:
sprintf(s, "%8x", 4567); //小写16 进制,宽度占8 个位置,右对齐
sprintf(s, "%-8X", 4568); //大写16 进制,宽度占8 个位置,左对齐

这样,一个整数的16 进制字符串就很容易得到,但我们在打印16 进制内容时,通常想要一种左边补0 的等宽格式,那该怎么做呢?很简单,在表示宽度的数字前面加个0 就可以了。
sprintf(s, "%08X", 4567); //产生:"000011D7"
上面以”%d”进行的10 进制打印同样也可以使用这种左边补0 的方式。


这里要注意一个符号扩展的问题:比如,假如我们想打印短整数(short)-1 的内存16 进制表示形式,在Win32 平台上,一个short 型占2 个字节,所以我们自然希望用4 个16 进制数字来打印它:
short si = -1;
sprintf(s, "%04X", si);
产 生“FFFFFFFF”,怎么回事?因为spritnf 是个变参函数,除了前面两个参数之外,后面的参数都不是类型安全的,函数更没有办法仅仅通过一个“%X”就能得知当初函数调用前参数压栈时被压进来的到底 是个4 字节的整数还是个2 字节的短整数,所以采取了统一4 字节的处理方式,导致参数压栈时做了符号扩展,扩展成了32 位的整数-1,打印时4 个位置不够了,就把32 位整数-1 的8 位16 进制都打印出来了。

如果你想看si 的本来面目,那么就应该让编译器做0 扩展而不是符号扩展(扩展时二进制左边补0 而不是补符号位):
sprintf(s, "%04X", (unsigned short)si);
就可以了。或者:
unsigned short si = -1;
sprintf(s, "%04X", si);


sprintf 和printf 还可以按8 进制打印整数字符串,使用”%o”。注意8 进制和16 进制都不会打
印出负数,都是无符号的,实际上也就是变量的内部编码的直接的16 进制或8 进制表示。

控制浮点数打印格式
浮点数的打印和格式控制是sprintf 的又一大常用功能,浮点数使用格式符”%f”控制,默认保
留小数点后6 位数字,比如:
sprintf(s, "%f", 3.1415926); //产生"3.141593"
但有时我们希望自己控制打印的宽度和小数位数,这时就应该使用:”%m.nf”格式,其中m 表
示打印的宽度,n 表示小数点后的位数。比如:
sprintf(s, "%10.3f", 3.1415626); //产生:" 3.142"
sprintf(s, "%-10.3f", 3.1415626); //产生:"3.142 "
sprintf(s, "%.3f", 3.1415626); //不指定总宽度,产生:"3.142"

注意一个问题,你猜
int i = 100;
sprintf(s, "%.2f", i);
会打出什么东东来?“100.00”?对吗?自己试试就知道了,同时也试试下面这个:
sprintf(s, "%.2f", (double)i);
第 一个打出来的肯定不是正确结果,原因跟前面提到的一样,参数压栈时调用者并不知道跟i相对应的格式控制符是个”%f”。而函数执行时函数本身则并不知道当 年被压入栈里的是个整数,于是可怜的保存整数i 的那4 个字节就被不由分说地强行作为浮点数格式来解释了,整个乱套了。不过,如果有人有兴趣使用手工编码一个浮点数,那么倒可以使用这种方法来检验一下你手工编 排的结果是否正确。

字符/Ascii 码对照
我们知道,在C/C++语言中,char 也是一种普通的scalable 类型,除了字长之外,它与short,
int,long 这些类型没有本质区别,只不过被大家习惯用来表示字符和字符串而已。(或许当年该把
这 个类型叫做“byte”,然后现在就可以根据实际情况,使用byte 或short 来把char 通过typedef 定义出来,这样更合适些)于是,使用”%d”或者”%x”打印一个字符,便能得出它的10 进制或16 进制的ASCII 码;反过来,使用”%c”打印一个整数,便可以看到它所对应的ASCII 字符。以下程序段把所有可见字符的ASCII 码对照表打印到屏幕上(这里采用printf,注意”#”与”%X”合用时自动为16 进制数增加”0X”前缀):
for(int i = 32; i < 127; i++) {
printf("[ %c ]: %3d 0x%#04X\n", i, i, i);
}


连接字符串
sprintf 的格式控制串中既然可以插入各种东西,并最终把它们“连成一串”,自然也就能够连
接字符串,从而在许多场合可以替代strcat,但sprintf 能够一次连接多个字符串(自然也可以同时
在它们中间插入别的内容,总之非常灵活)。比如:
char* who = "I";
char* whom = "CSDN";
sprintf(s, "%s love %s.", who, whom); //产生:"I love CSDN. "
strcat 只能连接字符串(一段以’’结尾的字符数组或叫做字符缓冲,null-terminated-string),但有时我们有两段字符缓冲区,他们并不是以 ’’结尾。比如许多从第三方库函数中返回的字符数组,从硬件或者网络传输中读进来的字符流,它们未必每一段字符序列后面都有个相应的’’来结尾。如果直接 连接,不管是sprintf 还是strcat 肯定会导致非法内存操作,而strncat 也至少要求第一个参数是个null-terminated-string,那该怎么办呢?我们自然会想起前面介绍打印整数和浮点数时可以指定宽度,字符串 也一样的。比如:
char a1[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
char a2[] = {'H', 'I', 'J', 'K', 'L', 'M', 'N'};
如果:
sprintf(s, "%s%s", a1, a2); //Don't do that!
十有八九要出问题了。是否可以改成:
sprintf(s, "%7s%7s", a1, a2);
也没好到哪儿去,正确的应该是:
sprintf(s, "%.7s%.7s", a1, a2);//产生:"ABCDEFGHIJKLMN"
这 可以类比打印浮点数的”%m.nf”,在”%m.ns”中,m 表示占用宽度(字符串长度不足时补空格,超出了则按照实际宽度打印),n 才表示从相应的字符串中最多取用的字符数。通常在打印字符串时m 没什么大用,还是点号后面的n 用的多。自然,也可以前后都只取部分字符:
sprintf(s, "%.6s%.5s", a1, a2);//产生:"ABCDEFHIJKL"
在 许多时候,我们或许还希望这些格式控制符中用以指定长度信息的数字是动态的,而不是静态指定的,因为许多时候,程序要到运行时才会清楚到底需要取字符数组 中的几个字符,这种动态的宽度/精度设置功能在sprintf 的实现中也被考虑到了,sprintf 采用”*”来占用一个本来需要一个指定宽度或精度的常数数字的位置,同样,而实际的宽度或精度就可以和其它被打印的变量一样被提供出来,于是,上面的例子 可以变成:
sprintf(s, "%.*s%.*s", 7, a1, 7, a2);
或者:
sprintf(s, "%.*s%.*s", sizeof(a1), a1, sizeof(a2), a2);
实际上,前面介绍的打印字符、整数、浮点数等都可以动态指定那些常量值,比如:
sprintf(s, "%-*d", 4, 'A'); //产生"65 "
sprintf(s, "%#0*X", 8, 128); //产生"0X000080","#"产生0X
sprintf(s, "%*.*f", 10, 2, 3.1415926); //产生" 3.14"


打印地址信息
有时调试程序时,我们可能想查看某些变量或者成员的地址,由于地址或者指针也不过是个32 位的数,你完全可以使用打印无符号整数的”%u”把他们打印出来:
sprintf(s, "%u", &i);
不过通常人们还是喜欢使用16 进制而不是10 进制来显示一个地址:
sprintf(s, "%08X", &i);
然而,这些都是间接的方法,对于地址打印,sprintf 提供了专门的”%p”:
sprintf(s, "%p", &i);
我觉得它实际上就相当于:
sprintf(s, "%0*x", 2 * sizeof(void *), &i);
利用sprintf 的返回值
较少有人注意printf/sprintf 函数的返回值,但有时它却是有用的,spritnf 返回了本次函数调用
最终打印到字符缓冲区中的字符数目。也就是说每当一次sprinf 调用结束以后,你无须再调用一次
strlen 便已经知道了结果字符串的长度。如:
int len = sprintf(s, "%d", i);
对于正整数来说,len 便等于整数i 的10 进制位数。
下面的是个完整的例子,产生10 个[0, 100)之间的随机数,并将他们打印到一个字符数组s 中,
以逗号分隔开。
#include
#include
#include
int main() {
srand(time(0));
char s[64];
int offset = 0;
for(int i = 0; i < 10; i++) {
offset += sprintf(s + offset, "%d,", rand() % 100);
}
s[offset - 1] = '\n';//将最后一个逗号换成换行符。
printf(s);
return 0;
}
设想当你从数据库中取出一条记录,然后希望把他们的各个字段按照某种规则连接成一个字
符串时,就可以使用这种方法,从理论上讲,他应该比不断的strcat 效率高,因为strcat 每次调用
都需要先找到最后的那个’’的位置,而在上面给出的例子中,我们每次都利用sprintf 返回值把这
个位置直接记下来了。


使用sprintf 的常见问题
sprintf 是个变参函数,使用时经常出问题,而且只要出问题通常就是能导致程序崩溃的内存访
问错误,但好在由sprintf 误用导致的问题虽然严重,却很容易找出,无非就是那么几种情况,通
常用眼睛再把出错的代码多看几眼就看出来了。


?? 缓冲区溢出
第一个参数的长度太短了,没的说,给个大点的地方吧。当然也可能是后面的参数的问
题,建议变参对应一定要细心,而打印字符串时,尽量使用”%.ns”的形式指定最大字符数。


?? 忘记了第一个参数
低级得不能再低级问题,用printf 用得太惯了。//偶就常犯。:。(


?? 变参对应出问题
通常是忘记了提供对应某个格式符的变参,导致以后的参数统统错位,检查检查吧。尤
其是对应”*”的那些参数,都提供了吗?不要把一个整数对应一个”%s”,编译器会觉得你
欺她太甚了(编译器是obj 和exe 的妈妈,应该是个女的,:P)。

strftime
sprnitf 还有个不错的表妹:strftime,专门用于格式化时间字符串的,用法跟她表哥很像,也
是一大堆格式控制符,只是毕竟小姑娘家心细,她还要调用者指定缓冲区的最大长度,可能是为
了在出现问题时可以推卸责任吧。这里举个例子:
time_t t = time(0);
//产生"YYYY-MM-DD hh:mm:ss"格式的字符串。
char s[32];
strftime(s, sizeof(s), "%Y-%m-%d %H:%M:%S", localtime(&t));
sprintf 在MFC 中也能找到他的知音:CString::Format,strftime 在MFC 中自然也有她的同道:
CTime::Format,这一对由于从面向对象哪里得到了赞助,用以写出的代码更觉优雅。

sprintf详解

在将各种类型的数据构造成字符串时,sprintf 的强大功能很少会让你失望。由于sprintf 跟printf 在用法上几乎一样,只是打印的目的地不同而已,前者打印到字符串中,后者则直接在命令行上输出。这也导致sprintf 比printf 有用得多。

sprintf 是个变参函数,定义如下:
int sprintf( char *buffer, const char *format [, argument] ... );
除了前两个参数类型固定外,后面可以接任意多个参数。而它的精华,显然就在第二个参数:
格式化字符串上。


printf 和sprintf 都使用格式化字符串来指定串的格式,在格式串内部使用一些以“%”开头的格式说明符(format specifications)来占据一个位置,在后边的变参列表中提供相应的变量,最终函数就会用相应位置的变量来替代那个说明符,产生一个调用者想要 的字符串。

格式化数字字符串
sprintf 最常见的应用之一莫过于把整数打印到字符串中,所以,spritnf 在大多数场合可以替代
itoa。

如:
//把整数123 打印成一个字符串保存在s 中。
sprintf(s, "%d", 123); //产生"123"
可以指定宽度,不足的左边补空格:
sprintf(s, "%8d%8d", 123, 4567); //产生:" 123 4567"
当然也可以左对齐:
sprintf(s, "%-8d%8d", 123, 4567); //产生:"123 4567"
也可以按照16 进制打印:
sprintf(s, "%8x", 4567); //小写16 进制,宽度占8 个位置,右对齐
sprintf(s, "%-8X", 4568); //大写16 进制,宽度占8 个位置,左对齐

这样,一个整数的16 进制字符串就很容易得到,但我们在打印16 进制内容时,通常想要一种左边补0 的等宽格式,那该怎么做呢?很简单,在表示宽度的数字前面加个0 就可以了。
sprintf(s, "%08X", 4567); //产生:"000011D7"
上面以”%d”进行的10 进制打印同样也可以使用这种左边补0 的方式。


这里要注意一个符号扩展的问题:比如,假如我们想打印短整数(short)-1 的内存16 进制表示形式,在Win32 平台上,一个short 型占2 个字节,所以我们自然希望用4 个16 进制数字来打印它:
short si = -1;
sprintf(s, "%04X", si);
产 生“FFFFFFFF”,怎么回事?因为spritnf 是个变参函数,除了前面两个参数之外,后面的参数都不是类型安全的,函数更没有办法仅仅通过一个“%X”就能得知当初函数调用前参数压栈时被压进来的到底 是个4 字节的整数还是个2 字节的短整数,所以采取了统一4 字节的处理方式,导致参数压栈时做了符号扩展,扩展成了32 位的整数-1,打印时4 个位置不够了,就把32 位整数-1 的8 位16 进制都打印出来了。

如果你想看si 的本来面目,那么就应该让编译器做0 扩展而不是符号扩展(扩展时二进制左边补0 而不是补符号位):
sprintf(s, "%04X", (unsigned short)si);
就可以了。或者:
unsigned short si = -1;
sprintf(s, "%04X", si);


sprintf 和printf 还可以按8 进制打印整数字符串,使用”%o”。注意8 进制和16 进制都不会打
印出负数,都是无符号的,实际上也就是变量的内部编码的直接的16 进制或8 进制表示。

控制浮点数打印格式
浮点数的打印和格式控制是sprintf 的又一大常用功能,浮点数使用格式符”%f”控制,默认保
留小数点后6 位数字,比如:
sprintf(s, "%f", 3.1415926); //产生"3.141593"
但有时我们希望自己控制打印的宽度和小数位数,这时就应该使用:”%m.nf”格式,其中m 表
示打印的宽度,n 表示小数点后的位数。比如:
sprintf(s, "%10.3f", 3.1415626); //产生:" 3.142"
sprintf(s, "%-10.3f", 3.1415626); //产生:"3.142 "
sprintf(s, "%.3f", 3.1415626); //不指定总宽度,产生:"3.142"

注意一个问题,你猜
int i = 100;
sprintf(s, "%.2f", i);
会打出什么东东来?“100.00”?对吗?自己试试就知道了,同时也试试下面这个:
sprintf(s, "%.2f", (double)i);
第 一个打出来的肯定不是正确结果,原因跟前面提到的一样,参数压栈时调用者并不知道跟i相对应的格式控制符是个”%f”。而函数执行时函数本身则并不知道当 年被压入栈里的是个整数,于是可怜的保存整数i 的那4 个字节就被不由分说地强行作为浮点数格式来解释了,整个乱套了。不过,如果有人有兴趣使用手工编码一个浮点数,那么倒可以使用这种方法来检验一下你手工编 排的结果是否正确。

字符/Ascii 码对照
我们知道,在C/C++语言中,char 也是一种普通的scalable 类型,除了字长之外,它与short,
int,long 这些类型没有本质区别,只不过被大家习惯用来表示字符和字符串而已。(或许当年该把
这 个类型叫做“byte”,然后现在就可以根据实际情况,使用byte 或short 来把char 通过typedef 定义出来,这样更合适些)于是,使用”%d”或者”%x”打印一个字符,便能得出它的10 进制或16 进制的ASCII 码;反过来,使用”%c”打印一个整数,便可以看到它所对应的ASCII 字符。以下程序段把所有可见字符的ASCII 码对照表打印到屏幕上(这里采用printf,注意”#”与”%X”合用时自动为16 进制数增加”0X”前缀):
for(int i = 32; i < 127; i++) {
printf("[ %c ]: %3d 0x%#04X\n", i, i, i);
}


连接字符串
sprintf 的格式控制串中既然可以插入各种东西,并最终把它们“连成一串”,自然也就能够连
接字符串,从而在许多场合可以替代strcat,但sprintf 能够一次连接多个字符串(自然也可以同时
在它们中间插入别的内容,总之非常灵活)。比如:
char* who = "I";
char* whom = "CSDN";
sprintf(s, "%s love %s.", who, whom); //产生:"I love CSDN. "
strcat 只能连接字符串(一段以’’结尾的字符数组或叫做字符缓冲,null-terminated-string),但有时我们有两段字符缓冲区,他们并不是以 ’’结尾。比如许多从第三方库函数中返回的字符数组,从硬件或者网络传输中读进来的字符流,它们未必每一段字符序列后面都有个相应的’’来结尾。如果直接 连接,不管是sprintf 还是strcat 肯定会导致非法内存操作,而strncat 也至少要求第一个参数是个null-terminated-string,那该怎么办呢?我们自然会想起前面介绍打印整数和浮点数时可以指定宽度,字符串 也一样的。比如:
char a1[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
char a2[] = {'H', 'I', 'J', 'K', 'L', 'M', 'N'};
如果:
sprintf(s, "%s%s", a1, a2); //Don't do that!
十有八九要出问题了。是否可以改成:
sprintf(s, "%7s%7s", a1, a2);
也没好到哪儿去,正确的应该是:
sprintf(s, "%.7s%.7s", a1, a2);//产生:"ABCDEFGHIJKLMN"
这 可以类比打印浮点数的”%m.nf”,在”%m.ns”中,m 表示占用宽度(字符串长度不足时补空格,超出了则按照实际宽度打印),n 才表示从相应的字符串中最多取用的字符数。通常在打印字符串时m 没什么大用,还是点号后面的n 用的多。自然,也可以前后都只取部分字符:
sprintf(s, "%.6s%.5s", a1, a2);//产生:"ABCDEFHIJKL"
在 许多时候,我们或许还希望这些格式控制符中用以指定长度信息的数字是动态的,而不是静态指定的,因为许多时候,程序要到运行时才会清楚到底需要取字符数组 中的几个字符,这种动态的宽度/精度设置功能在sprintf 的实现中也被考虑到了,sprintf 采用”*”来占用一个本来需要一个指定宽度或精度的常数数字的位置,同样,而实际的宽度或精度就可以和其它被打印的变量一样被提供出来,于是,上面的例子 可以变成:
sprintf(s, "%.*s%.*s", 7, a1, 7, a2);
或者:
sprintf(s, "%.*s%.*s", sizeof(a1), a1, sizeof(a2), a2);
实际上,前面介绍的打印字符、整数、浮点数等都可以动态指定那些常量值,比如:
sprintf(s, "%-*d", 4, 'A'); //产生"65 "
sprintf(s, "%#0*X", 8, 128); //产生"0X000080","#"产生0X
sprintf(s, "%*.*f", 10, 2, 3.1415926); //产生" 3.14"


打印地址信息
有时调试程序时,我们可能想查看某些变量或者成员的地址,由于地址或者指针也不过是个32 位的数,你完全可以使用打印无符号整数的”%u”把他们打印出来:
sprintf(s, "%u", &i);
不过通常人们还是喜欢使用16 进制而不是10 进制来显示一个地址:
sprintf(s, "%08X", &i);
然而,这些都是间接的方法,对于地址打印,sprintf 提供了专门的”%p”:
sprintf(s, "%p", &i);
我觉得它实际上就相当于:
sprintf(s, "%0*x", 2 * sizeof(void *), &i);
利用sprintf 的返回值
较少有人注意printf/sprintf 函数的返回值,但有时它却是有用的,spritnf 返回了本次函数调用
最终打印到字符缓冲区中的字符数目。也就是说每当一次sprinf 调用结束以后,你无须再调用一次
strlen 便已经知道了结果字符串的长度。如:
int len = sprintf(s, "%d", i);
对于正整数来说,len 便等于整数i 的10 进制位数。
下面的是个完整的例子,产生10 个[0, 100)之间的随机数,并将他们打印到一个字符数组s 中,
以逗号分隔开。
#include
#include
#include
int main() {
srand(time(0));
char s[64];
int offset = 0;
for(int i = 0; i < 10; i++) {
offset += sprintf(s + offset, "%d,", rand() % 100);
}
s[offset - 1] = '\n';//将最后一个逗号换成换行符。
printf(s);
return 0;
}
设想当你从数据库中取出一条记录,然后希望把他们的各个字段按照某种规则连接成一个字
符串时,就可以使用这种方法,从理论上讲,他应该比不断的strcat 效率高,因为strcat 每次调用
都需要先找到最后的那个’’的位置,而在上面给出的例子中,我们每次都利用sprintf 返回值把这
个位置直接记下来了。


使用sprintf 的常见问题
sprintf 是个变参函数,使用时经常出问题,而且只要出问题通常就是能导致程序崩溃的内存访
问错误,但好在由sprintf 误用导致的问题虽然严重,却很容易找出,无非就是那么几种情况,通
常用眼睛再把出错的代码多看几眼就看出来了。


?? 缓冲区溢出
第一个参数的长度太短了,没的说,给个大点的地方吧。当然也可能是后面的参数的问
题,建议变参对应一定要细心,而打印字符串时,尽量使用”%.ns”的形式指定最大字符数。


?? 忘记了第一个参数
低级得不能再低级问题,用printf 用得太惯了。//偶就常犯。:。(


?? 变参对应出问题
通常是忘记了提供对应某个格式符的变参,导致以后的参数统统错位,检查检查吧。尤
其是对应”*”的那些参数,都提供了吗?不要把一个整数对应一个”%s”,编译器会觉得你
欺她太甚了(编译器是obj 和exe 的妈妈,应该是个女的,:P)。

strftime
sprnitf 还有个不错的表妹:strftime,专门用于格式化时间字符串的,用法跟她表哥很像,也
是一大堆格式控制符,只是毕竟小姑娘家心细,她还要调用者指定缓冲区的最大长度,可能是为
了在出现问题时可以推卸责任吧。这里举个例子:
time_t t = time(0);
//产生"YYYY-MM-DD hh:mm:ss"格式的字符串。
char s[32];
strftime(s, sizeof(s), "%Y-%m-%d %H:%M:%S", localtime(&t));
sprintf 在MFC 中也能找到他的知音:CString::Format,strftime 在MFC 中自然也有她的同道:
CTime::Format,这一对由于从面向对象哪里得到了赞助,用以写出的代码更觉优雅。

GtkRadioButton示例

#include<gtk/gtk.h>

GtkWidget *window,*hbox,*button1,*button2,*button3;
void toggled(GtkWidget *widget, gpointer data)
{
    if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
    {
        g_print ("%s\n", data);
    }
}
void destroy(GtkWidget *widget, gpointer data)
{
    gtk_main_quit();
}

int main(int argc,char** argv)
{
    gtk_init(&argc,&argv);

    window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window),"Radio button");
    gtk_container_set_border_width(GTK_CONTAINER(window),3);
    g_signal_connect(window,"destroy",G_CALLBACK(gtk_main_quit),0);

    hbox=gtk_hbox_new(TRUE,3);
    /* 单选按钮的创建 */

    button1=gtk_radio_button_new_with_label(NULL,"orange");
    g_signal_connect(G_OBJECT(button1),"toggled",G_CALLBACK(toggled),"orange");
    /* 每创建一个单选按钮时,都必须调用一次gtk_radio_button_get_group,否则出错*/
    button2=gtk_radio_button_new_with_label(gtk_radio_button_get_group(GTK_RADIO_BUTTON(button1)),"apple");
    g_signal_connect(G_OBJECT(button2),"toggled",G_CALLBACK(toggled),"apple");
    button3=gtk_radio_button_new_with_label(gtk_radio_button_get_group(GTK_RADIO_BUTTON(button1)),"banana");
    g_signal_connect(G_OBJECT(button3),"toggled",G_CALLBACK(toggled),"banana");
    /* 设置第三个按钮为激活状态 */
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button3),TRUE);

    /* 将单选按钮添加到hbox内 */
    gtk_box_pack_start(GTK_BOX(hbox),button1,TRUE,TRUE,3);
    gtk_box_pack_start(GTK_BOX(hbox),button2,TRUE,TRUE,3);
    gtk_box_pack_start(GTK_BOX(hbox),button3,TRUE,TRUE,3);

    gtk_container_add(GTK_CONTAINER(window),hbox);
    gtk_widget_show_all(window);
    gtk_main();
    return 0;
}

shell用到ANSI控制码汇总

我们在编写shell脚本时,如果要实现某种特殊功能(只对当前shell起作用),就需要ANSI的控制码。

比如我要让终端下字符颜色改换为绿色的,可以用
  root$ echo -e "\033[40;32"
要偷懒,将当前光标移动到第3行第1列的地方,用到
  root$ echo -ne "\33[3;1H"

上面这些都是实际的例子,要应用好还要学好ANSI控制码的相关代码,如下:

\33[30m -- \33[37m  设置前景色
\33[40m -- \33[47m  设置背景色
\33[nA  光标上移n行
\33[nB  光标下移n行
\33[nC  光标右移n行
\33[nD  光标左移n行
\33[K   清除从光标到行尾的内容
\33[s   保存光标位置
\33[u   恢复光标位置
\33[y;xH设置光标位置
\33[?25l  隐藏光标
\33[?25h  显示光标
\33[2J  清屏
\33[0m  关闭所有属性
\33[1m  设置高亮度
\33[4m  下划线
\33[5m  闪烁
\33[7m  反显
\33[8m  消隐

注意:使用不同的shell,也会用到不同的转义的,看下面的例子:

/home/lee#echo "\\033[32mddd"
ddd
/home/lee#
/home/lee#bash
/home/lee#echo "\\033[33maddd"
\033[33maddd
/home/lee#exit
/home/lee#echo "\\033[34maddd"
addd
/home/lee#echo $0
-ksh
/home/lee#

GtkStatusicon

#include <gtk/gtk.h>

int main(int argc, char **argv) {
    GtkStatusIcon *tray_icon;

    gtk_init(&argc, &argv);
    tray_icon = gtk_status_icon_new();
    gtk_status_icon_set_from_file(tray_icon, "/tmp/trayicon.png");
    gtk_status_icon_set_tooltip(tray_icon, "Hello Tray Icon!!");
    gtk_status_icon_set_visible(tray_icon, TRUE);
    gtk_main();

    return 0;
}

indent

indent -kr -i8 -ts8 -sob -l80 -ss -bs

GTK+学习笔记-树型视图与模型(GtkTreeView)

GtkWidget *mytree = NULL;
GtkTreeStore 
*lefttree = NULL;



树型模型的建立:

void setupTreeStore()
{
    lefttree = gtk_tree_store_new(1,G_TYPE_STRING);    
    GtkTreeIter   iter ;
    GtkTreeIter   child;
    gtk_tree_store_append (lefttree, 
&iter, NULL);//建立一个空行
    gtk_tree_store_set (lefttree, &iter ,0,"root1",-1);//为此行设置值

    gtk_tree_store_append (lefttree, 
&iter, NULL);//建立另一个空行
    gtk_tree_store_set (lefttree, &iter ,0,"root2",-1);//为此行设置值
    
    gtk_tree_store_append (lefttree, 
&child, &iter);//为上一行建立一个子行
    gtk_tree_store_set (lefttree, &child ,0,"child1",-1);//为此子行设置值
    

//更多行依此类推..........

}

 

树型视图的建立以及与数据模型的关联:

//要想让视图显示出数据,必须建立GtkCellRenderer与GtkTreeViewColumn
GtkCellRenderer *renderer;
GtkTreeViewColumn 
*column;

//关联视图与模型
gtk_tree_view_set_model(GTK_TREE_VIEW(mytree),GTK_TREE_MODEL(lefttree));

//将数据模型交给视图管理,视图销毁时数据会被一同销毁
g_object_unref (G_OBJECT (lefttree));

//建立一个GtkCellRenderer 
renderer = gtk_cell_renderer_text_new ();

//设置GtkCellRenderer的渲染方式
g_object_set (G_OBJECT (renderer),
                 
"foreground""red",
                 NULL);

//建立一个带标题的列 并且将renderer放入其中使其能显示内容
column = gtk_tree_view_column_new_with_attributes ("Family", renderer,
                                                                                               
"text"0,
                                                                                               NULL);
//将列设置为可以手动改变大小
 gtk_tree_view_column_set_resizable(column,TRUE);

//将列加入gtk_tree_view
gtk_tree_view_append_column (GTK_TREE_VIEW (mytree), column);
    

gtk_widget_show_all(mytree);


下面为建立好的树型视图实现一个功能:
      当用户选中一个有子行的行时按Enter或Space键展开或收起子行,当选中一个没有子行的行时按Enter或Space键后台打印一些信息。

/*
TreeView的"row-activated"信号产生条件:
    --在gtk_tree_view_row_activated()被调用时
    -- 一个行被双击时
    -- 选中一个不可编辑的行,按下Space, Shift+Space, Return,Enter键时
*/

//此回调函数与 TreeView的"row-activated"信号相连接
void on_lefttree_row_activated  (GtkTreeView       *tree_view,
                                 GtkTreePath       
*path,
                                 GtkTreeViewColumn 
*column,
                                 gpointer           user_data)    
...
{
    GtkTreeIter   iter ;
    
//根据path获得iter
     if (gtk_tree_model_get_iter(GTK_TREE_MODEL(lefttree), &iter, path))
    ...
{
        
//得到iter,判断iter指向的行是否有子行
        if ( gtk_tree_model_iter_has_child (GTK_TREE_MODEL(lefttree), &iter) )
        ...
{
            
//有子行,展开本行
            if ( gtk_tree_view_expand_row(tree_view,path,FALSE) )
            ...
{
                
//展开本行成功
                g_print("expand child ");
            }

            
else //本行已经被展开
            ...{
                
//收起本行
                gtk_tree_view_collapse_row(tree_view,path);
                g_print(
"collapse child ");
            }

        }

        
else
        ...
{
            
//没有子行,直接执行规定的功能
            g_print("do some ");
        }

    }


    

}


获得treeview中选中的一行的GtkTreeIter或path

GtkTreeSelection* select = gtk_tree_view_get_selection( tree_view );
gtk_tree_selection_select_path( select, path );
gtk_tree_selection_select_iter( select, 
&iter );


要想更改GtkTreeIter指向的数据的值,使用:

gtk_tree_store_set  (lefttree&iter,CHECKED_COLUMN ,data);



GtkTreeView本身是不带横纵滚动条的,把他放入GtkScrolledWindow中就可以了,滚动条会随内容自动调整。

Linux pthread_create 如何设置 线程的detach 状态

1 引言
线程(thread)技术早在60年代就被提出,但真正应用多线程到操作系统中去,是在80年代中期,solaris是这方面的佼佼者。传统的 Unix也支持线程的概念,但是在一个进程(process)中只允许有一个线程,这样多线程就意味着多进程。现在,多线程技术已经被许多操作系统所支 持,包括Windows/NT,当然,也包括Linux。
为什么有了进程的概念后,还要再引入线程呢?使用多线程到底有哪些好处?什么的系统应该选用多线程?我们首先必须回答这些问题。
使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式。我们知道,在Linux系统下,启动一个新的进程必须分配给它独立的 地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同 的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的 时间。据统计,总的说来,一个进程的开销大约是一个线程开销的30倍左右,当然,在具体的系统上,这个数据可能会有较大的区别。
使用多线程的理由之二是线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅 费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数据 的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编 写多线程程序时最需要注意的地方。
除了以上所说的优点外,不和进程比较,多线程程序作为一种多任务、并发的工作方式,当然有以下的优点:
1) 提高应用程序响应。这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time consuming)置于一个新的线程,可以避免这种尴尬的情况。
2) 使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。
3) 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。
下面我们先来尝试编写一个简单的多线程程序。

2 简单的多线程编程
Linux系统下的多线程遵循POSIX线程接口,称为pthread。编写Linux下的多线程程序,需要使用头文件pthread.h,连接 时需要使用库libpthread.a。顺便说一下,Linux下pthread的实现是通过系统调用clone()来实现的。clone()是 Linux所特有的系统调用,它的使用方式类似fork,关于clone()的详细情况,有兴趣的读者可以去查看有关文档说明。下面我们展示一个最简单的 多线程程序example1.c。

/* example.c*/
#include
#include
void thread(void)
{
int i;
for(i=0;i<3;i++)
printf("This is a pthread.n");
}

int main(void)
{
pthread_t id;
int i,ret;
ret=pthread_create(&id,NULL,(void *) thread,NULL);
if(ret!=0){
printf ("Create pthread error!n");
exit (1);
}
for(i=0;i<3;i++)
printf("This is the main process.n");
pthread_join(id,NULL);
return (0);
}

我们编译此程序:
gcc example1.c -lpthread -o example1
运行example1,我们得到如下结果:
This is the main process.
This is a pthread.
This is the main process.
This is the main process.
This is a pthread.
This is a pthread.
再次运行,我们可能得到如下结果:
This is a pthread.
This is the main process.
This is a pthread.
This is the main process.
This is a pthread.
This is the main process.

前后两次结果不一样,这是两个线程争夺CPU资源的结果。上面的示例中,我们使用到了两个函数,  pthread_create和pthread_join,并声明了一个pthread_t型的变量。
pthread_t在头文件/usr/include/bits/pthreadtypes.h中定义:
typedef unsigned long int pthread_t;
它是一个线程的标识符。函数pthread_create用来创建一个线程,它的原型为:
extern int pthread_create __P ((pthread_t *__thread, __const pthread_attr_t *__attr,
void *(*__start_routine) (void *), void *__arg));
第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数。这里,我 们的函数thread不需要参数,所以最后一个参数设为空指针。第二个参数我们也设为空指针,这样将生成默认属性的线程。对线程属性的设定和修改我们将在 下一节阐述。当创建线程成功时,函数返回0,若不为0则说明创建线程失败,常见的错误返回代码为EAGAIN和EINVAL。前者表示系统限制创建新的线 程,例如线程数目过多了;后者表示第二个参数代表的线程属性值非法。创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行 下一行代码。
函数pthread_join用来等待一个线程的结束。函数原型为:
extern int pthread_join __P ((pthread_t __th, void **__thread_return));
第一个参数为被等待的线程标识符,第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数,调用它的 函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。一个线程的结束有两种途径,一种是象我们上面的例子一样,函数结束了,调 用它的线程也就结束了;另一种方式是通过函数pthread_exit来实现。它的函数原型为:
extern void pthread_exit __P ((void *__retval)) __attribute__ ((__noreturn__));
唯一的参数是函数的返回代码,只要pthread_join中的第二个参数thread_return不是NULL,这个值将被传递给 thread_return。最后要说明的是,一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用pthread_join的线 程则返回错误代码ESRCH。
在这一节里,我们编写了一个最简单的线程,并掌握了最常用的三个函数pthread_create,pthread_join和pthread_exit。下面,我们来了解线程的一些常用属性以及如何设置这些属性。

3 修改线程的属性
在上一节的例子里,我们用pthread_create函数创建了一个线程,在这个线程中,我们使用了默认参数,即将该函数的第二个参数设为NULL。的确,对大多数程序来说,使用默认属性就够了,但我们还是有必要来了解一下线程的有关属性。
属性结构为pthread_attr_t,它同样在头文件/usr/include/pthread.h中定义,喜欢追根问底的人可以自己去查 看。属性值不能直接设置,须使用相关函数进行操作,初始化的函数为pthread_attr_init,这个函数必须在pthread_create函数 之前调用。属性对象主要包括是否绑定、是否分离、堆栈地址、堆栈大小、优先级。默认的属性为非绑定、非分离、缺省1M的堆栈、与父进程同样级别的优先级。
关于线程的绑定,牵涉到另外一个概念:轻进程(LWP:Light Weight Process)。轻进程可以理解为内核线程,它位于用户层和系统层之间。系统对线程资源的分配、对线程的控制是通过轻进程来实现的,一个轻进程可以控制 一个或多个线程。默认状况下,启动多少轻进程、哪些轻进程来控制哪些线程是由系统来控制的,这种状况即称为非绑定的。绑定状况下,则顾名思义,即某个线程 固定的"绑"在一个轻进程之上。被绑定的线程具有较高的响应速度,这是因为CPU时间片的调度是面向轻进程的,绑定的线程可以保证在需要的时候它总有一个 轻进程可用。通过设置被绑定的轻进程的优先级和调度级可以使得绑定的线程满足诸如实时反应之类的要求。
设置线程绑定状态的函数为pthread_attr_setscope,它有两个参数,第一个是指向属性结构的指针,第二个是绑定类型,它有两个 取值:PTHREAD_SCOPE_SYSTEM(绑定的)和PTHREAD_SCOPE_PROCESS(非绑定的)。下面的代码即创建了一个绑定的线 程。
#include
pthread_attr_t attr;
pthread_t tid;

/*初始化属性值,均设为默认值*/
pthread_attr_init(&attr);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);

pthread_create(&tid, &attr, (void *) my_function, NULL);

线程的分离状态决定一个线程以什么样的方式来终止自己。在上面的例子中,我们采用了线程的默认属性,即为非分离状态,这种情况下,原有的线程等待 创建的线程结束。只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。而分离线程不是这样子的,它没有被其 他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。设置线程分离状态的函数为pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)。第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程)。

gdb使用

1.编译的时候, 比如编译源码是 test.c, 则编译命令是:  gcc -g -O0 test.c
   -g表示生成的a.out可以gdb调试,-O0是不让gcc优化,按照程序设计顺序执行.

2.调试.
   出错以后,比如提示segmentation fault(据说 segmentation fault这种错误很常见,一般就是因为访问越界造成的).那么输入  gdb a.out 就可以调试.
   一般会出现一个统一的gdb调试环境.gdb的提示符是(gdb).此时就可以输入命令进行一步步的debug.(后面列出常用命令)
   help 可以打印出帮助.看看gdb到底怎么用.
   list 列出程序的代码(大概每次10行左右).按回车可以直接执行上一次输入的命令.因此不必每次输入list.
   break 在认为可能出错的地方设置断点.比如45行,就 break 45.执行run以后,在45行以前如果没有错误就不会停止直到这个断点.然后返回(gdb)提示符,此时可以继续执行或者查看需要检查的一些变量值.
   print 可以显示出想要了解的值. 比如变量a, 就 print a, 甚至可以打印出结构的具体内容.
   ptype 可以显示结构原型! 比如用了一个网络结构sockaddr_in thisadd, 可以ptype thisadd 将sockaddr_in的结构打印出来.
   step 单步执行.
   next 但不执行.区别与step在于,step会跟踪到子程序中,而next只在本层单步.
   until 如果认为最近一些行都没有问题,可以 untill 87, 表示继续执行到87行然后停下来.
   quit 表示退出gdb.

   上面是最基本的命令.据说gdb的功能还很强大.不过有了刚才这些命令,就可以开始上路排错了.