年终了,今天是今年最后一天上班,下午开了"纳会",前几天项目组还开了"忘年会",前者是领导总结一下一年的工作,给大家拜个年,大家聊聊天。后者纯粹是吃吃喝喝了。
接下来一周时间,准备在家编程序。每天觉得时间不够用,这下子有一周假期,还真不适应呢。前几天一个电视节目里问:如果出了某种变故,你被告知仅有一天的寿命了,你会利用这一天做些什么?这真是一个无聊的问题,面对这样的问题,每个人有自己的回答。我想我仍然会利用这最后一天编程序的。有过太多IDEA,真正实现的寥寥无几。如果只有最后一天,还是希望实现自己的价值。
总结一下上一周:
1,
No! Flash 2.0 release了,在屏蔽flash的基础上,增加了block spyware的功能。除了新浪,网易这样的新闻网站,3721,Baidu也成了
No!Flash屏蔽的对象。对那种毫不尊重浏览者选择权的粗暴广告方式实在忍无可忍。特别是对播号上网和机器型号比较老的用户而言,直接占用了他们有限的带宽和CPU。没有No!Flash的时候真想大骂这些恶德业者。
2,写了一篇对话形式的小文章。"
MM与GG的对话---关与printf和字符数组"给C语言的初学者。
3,看了大怪兽
关与const的随笔,我想有必要总结一下const在C++里的各种用法,开始写一篇文章,现在写了一半,还在blogger的draft里,希望下周release它。
4,用了这么多年C++,真的没有好好总结一下,没有一个好得hint,也不会去想对于初学者会遇到些什么问题。今天在dev-club里,为了回答一个问题,总结了一下作为函数的参数时,什么情况下必须用"引用":
(1)引用保证了你传入的确实是一个对象的引用并且在函数里不会像指针一样改变用途。
void fun( X *px )和void fun( X &x )的不同在于
你可不可以这么用
fun(0);
用指针就可以传入一个0(或者其他整数),也许是一个非法的地址,而且在fun函数内还可以改变px指向的地址。例如
void fun( X *px )
{
X aaa;
px=&aaa;
}
用引用就不会有这些问题。传入0是违法的。引用在传入的时候被初始化以后,绝对不会再被改变成为另外一个对象的引用。
(2),作为重栽运算符时的参数方式。
如果你写成
X operator+( X *a, X *b )
用的时候就只能
&val1+&val2
了。
使用引用不会有这样的尴尬。
声明的时候这样
X operator+( X &a, X &b )
使用的时候这样
val1+val2
[13:42:43] MM: hi
[13:42:43] MM: #include
?????????? #include
?????????? void main()
?????????? {?????????? ?????????????? char str[41];
?????????? ?????????????? strcpy(str,"abcde");
?????????? ?????????????? printf(str);
?????????? }
[13:42:53] MM: #include
????? ?????#include
?????????? void main()
?????????? {?????????? ?????????????? char str[41];
?????????? ?????????????? strcpy(str,"abcde");
?????????? ?????????????? printf("%s\n",str);?????????? }
[13:43:04] MM: 都是表示一个意思?
[13:44:04] GG: yes
[13:44:22] GG: 但是第2个多了一个\n
[13:45:56] MM: printf(str);同普通的printf()函数有什么不同?
[13:47:31] GG: printf就是一个函数
[13:47:45] GG: 它的第一个参数是模板字符串
[13:48:34] MM: 模板是什么?
[13:48:58] GG: 这个字符串里面的一些符号可以用第2个参数(包括第2个)以后的参数代替
[13:49:14] GG: 比如:"%s\n"
[13:49:29] GG: 这个字符串就是一个模板字符串
[13:49:47] GG: 他里面的%s会被第2个参数替换
[13:50:05] GG: 类似的,还有%d,%c
[13:50:26] GG: 分别表示被整数,被字符代替
[13:50:44] MM: 那printf(str);这个是省略了第一个参数?
[13:50:54] GG: 这个模板字符串里面也可以没有替代字符
[13:51:03] GG: 这样他就直接输出了
[13:51:11] GG: 比如printf(str);
[13:51:32] MM: 这个是省略了第一个参数?
[13:51:46] GG: no
[13:52:02] GG: str就是模板字符串
[13:52:36] GG: 因为里面没有可替换的部分,所以只有一个参数
[13:53:28] MM: 没听明白
[13:54:09] MM: printf("%s",str);不是省略了第一个参数变成了printf(str);吗?[13:54:31] GG: 不对
[13:55:01] GG: printf(str)里的str的位置相当于"%s"
[13:55:43] GG: printf()的原型是int printf(
????????????? const char *format [,
????????????? argument]...
???????????);
[13:56:08] GG: 第一个参数一定是一个字符串
[13:56:24] GG: 这样既可以用"%s"做第一个参数
[13:56:34] GG: 也可以用str做第一个参数
[13:57:42] GG: 这第一个参数里面如果用可替换部分,就是以%打头的替换字符的话,就要有第2个第3个参数了
[13:58:30] MM: 不然的话不需要第二个参数?
[13:58:36] GG: yes
[13:58:58] GG: 现在你把你的程序改一下
[13:59:30] GG: #include
?????????? #include
?????????? void main()
?????????? {?????????? ?????????????? char str[41];
?????????? ?????????????? strcpy(str,"abc%sde");
?????????? ?????????????? printf(str);
?????????? }
[13:59:44] GG: #include
?????????? #include
?????????? void main()
?????????? {?????????? ?????????????? char str[41];
?????????? ?????????????? strcpy(str,"abc%sde");
?????????? ?????????????? printf("%s\n",str);?????????? }
[14:00:00] MM: 那什么样的东西可以直接作第一个参数呢?
[14:00:12] GG: 这两个改法,编译一下,看看效果
[14:00:38] GG: 第一个参数一定是一个字符串
[14:01:08] GG: 后面的参数要根据第一个字符串里的模板决定
[14:02:10] MM: 那因为str本身可以表示一个字符串,所以可以直接作第一个参数,ok?
[14:02:45] GG: yes
[14:02:56] MM: 如果要是定义了一个 int a=5; 然后printf( a );这样就不对了,是把?
[14:03:06] GG: 聪明
[14:03:48] MM: 要是定义 char abc='a';然后printf(abc); 这个对吧?
[14:04:05] GG: 你试一下?
[14:04:26] MM: 字符行不行?不是字符串
[14:04:44] GG: 不行
[14:05:36] MM: #include
?????????? #include
?????????? void main()
?????????? {?????????? ?????????????? char str[41]={'q','w','e','r','t','y','u'};?????????? ?????????????? strcpy(str,"abc%sde");
?????????? ?????????????? printf(str);
?????????? }
[14:05:36] MM: 这个程序不对
[14:05:56] GG: 
[14:05:57] MM: 但是#include
?????????? #include
?????????? void main()
?????????? {?????????? ?????????????? char str[41];
??? ????????????????????? strcpy(str,"abc%sde");
?????????? ?????????????? printf("%s\n",str);?????????? }
[14:05:57] MM: 这个对
[14:06:20] MM: 第一个怎么不对呢?
[14:06:39] GG: 你知道为什么不对吗?
[14:06:57] GG: strcpy(str,"abc%sde");
?????????? printf(str);
[14:07:04] GG: 为什么他不对呢?
[14:07:15] MM: 不知道
[14:07:21] GG: 
[14:07:41] GG: printf的第一个参数是个字符串对吧
[14:07:51] MM: 编译通过了
[14:07:51] MM: 对呀
[14:08:00] GG: printf(str)里的str是一个字符串
[14:08:10] MM: "abc%sde"这不是字符串吗?
[14:08:32] GG: 如果这个str里没有%s的话,你刚才已经正常输出了
[14:08:44] GG: 但是现在有%s了
[14:09:02] GG: 他就会找第2个参数,来代替%s
[14:09:09] GG: 但是没有找到啊
[14:09:22] GG: 所以,就出错了
[14:09:25] MM: 可是用引号引上的时候不都是字符串吗?
[14:09:31] GG: 我说得对不对?
[14:09:51] GG: 当然是字符串
[14:10:02] MM: oh
[14:10:19] GG: 你要是能用linux实验一下就好了
[14:10:43] GG: gcc好像可以发现printf(str)少了一个参数
[14:11:46] MM:
?????????? printf(str);时他先是printf("abc%sde");然后就识别%s应该是后面带另一个参数的,所以错了[14:12:10] GG: 
[14:12:30] MM: 也就是"abc%de"也是不对的
[14:13:01] MM: 加上任意一个带第2个引数的都不对,是把?
[14:13:44] GG: "abc%de"真是奇妙的问题
[14:13:52] GG: 你实验一下
[14:14:00] GG: 看看输出了什么
[14:14:11] MM: 这个是能执行的
[14:14:21] MM: abc627270241e
[14:14:57] GG: 
[14:15:16] MM: "ab%cde"这个也是能执行的,你知道结果是什么吗?
[14:15:42] MM: 为什么就加上%s不行呢?
[14:15:57] GG: 这说明printf(str);是不安全的
[14:16:14] GG: %c,%d的结果只是碰巧了
[14:16:30] GG: 很可能在linux上的结果是另外一回事
[14:16:36] MM: ok
[14:17:04] GG: 所以,如果用print的话,最好是用printf("%s",str);[14:17:16] MM: ok
[14:17:21] MM: 还有一个问题
[14:17:24] GG: 这个实验说明C语言是危险的
[14:17:45] MM: void main()
?????????? {?????????? ?????????????? char str[41]={'q','w','e','r','t','y','u'};?????????? ?????????????? strcpy(str,"abcde");
?????????? ?????????????? printf(str);
?????????? }
[14:18:36] MM: 为什么前面无论付了多少值,结果都是"abcde"呢?
[14:19:33] GG: 因为strcpy(str,"abcde");把str原来的值覆盖了啊
[14:20:35] MM: 可是数组长41,abcde只有5个字符
[14:21:48] MM: 他也只能覆盖前5个把?或者再加个终止符也就6个,怎么会后面的都覆盖了呢?
[14:22:04] GG: good question
[14:22:31] GG: 实际上呢,他并没有覆盖6以后的字符
[14:24:57] MM: 
[14:25:49] GG: #include
?????????? #include
?????????? void main()
?????????? {?????????? ?????????????? char str[41]={'q','w','e','r','t','y','u'};?????????? ?????????????? strcpy(str,"abc");
?????????? ?????????????? printf("%s\n",str);?????????? ?????????????? printf("%s\n",str+0);?????????? ?????????????? printf("%s\n",str+1);?????????? ?????????????? printf("%s\n",str+2);?????????? ?????????????? printf("%s\n",str+3);?????????? ?????????????? printf("%s\n",str+4);?????????? ?????????????? printf("%s\n",str+5);?????????? ?????????????? printf("%s\n",str+6);?????????? }
[14:25:58] GG: 你这样实验一下
[14:26:16] GG: 就很清楚了
[14:27:07] MM: 我估计可能是他读到那个终止符的时候就不读后面的了,对吧?
[14:27:08] GG: 你看到了什么?
[14:27:16] GG: 
[14:27:21] GG: 对了
[14:29:16] MM: str+0,str+1这些是什么意思?
[14:30:21] GG: 你一开始定义了char str[41]
[14:30:40] GG: 这说明str是一个数组
[14:31:27] GG: str就是这个数组的头指针,也就是指向这一组字符的第一个
[14:31:48] GG: str+1就,指向了第2个
[14:33:00] MM: abc
?????????? abc
?????????? bc
?????????? c
??????????
???????????tyu
?????????? yu
?????????? u
[14:33:11] MM: 结果怎么是这些呢?
[14:33:35] GG: 你分析一下
[14:34:28] MM: ?????????????? printf("%s\n",str);应该是abc [14:35:40] MM: 指向这一组字符的第一个字符,但输出还应该是字符串吧?
[14:36:24] GG: 输出的时候print函数会顺次输出,知道他看到\0为止
[14:36:34] MM: ?????????????? printf("%s\n",str+3);是那个终止付的位置吧[14:36:34] MM: 是null
[14:36:53] GG: 
[14:37:01] GG: null就是\0
[14:37:32] MM: 可是他把tyu也输出了
[14:37:56] GG: 那是什么原因呢?
[14:38:17] MM: 就是没覆盖备
[14:38:26] GG: 对
[14:38:38] MM: 可是输出结果在u的后面又出现了一个空行
[14:38:45] GG: 也就是他只覆盖了4个字符
[14:38:55] MM: 这是怎么输出的?
[14:39:15] GG: 那说明u后面还有一个null
[14:39:36] MM: 可是我们并没输入啊?
[14:39:58] GG: 是的,这是非常微妙的地方
[14:40:07] GG: ?????????????? char str[41]={'q','w','e','r','t','y','u'};[14:40:13] MM: 还有也没有打印?????????????? printf("%s\n",str+7);[14:40:20] GG: 这句里面并没有\0
[14:40:24] MM: 怎么他就自动输出了呢?
[14:40:33] MM: yes
[14:40:40] GG: 但是,为什么后面就都是\0了呢
[14:41:25] GG: 其实,这是由不同的编译器和执行环境决定的
[14:42:47] GG: 先在的环境使得str这个字符数组里没有付值得位置自动为\0了
[14:43:09] MM: 那要是我正好输入41个字符,没留\0的空间怎么办呢?
[14:43:13] GG: 但是换一个编译器可能结果就不同了
[14:43:41] GG: 那样就会出现不可预料的结果
[14:43:42] MM: 
?