2009年4月11日星期六
C++宏展开(zt)
宏参数的prescan,
当一个宏参数被放进宏体时,这个宏参数会首先被全部展开(有例外,见下文)。当展开后的宏参数被放进宏体时,
预处理器对新展开的宏体进行第二次扫描,并继续展开。例如:
#define PARAM( x ) x
#define ADDPARAM( x ) INT_##x
PARAM( ADDPARAM( 1 ) );
因为ADDPARAM( 1 ) 是作为PARAM的宏参数,所以先将ADDPARAM( 1 )展开为INT_1,然后再将INT_1放进PARAM。
例外情况是,如果PARAM宏里对宏参数使用了#或##,那么宏参数不会被展开:
#define PARAM( x ) #x
#define ADDPARAM( x ) INT_##x
PARAM( ADDPARAM( 1 ) ); 将被展开为"ADDPARAM( 1 )"。
使用这么一个规则,可以创建一个很有趣的技术:打印出一个宏被展开后的样子,这样可以方便你分析代码:
#define TO_STRING( x ) TO_STRING1( x )
#define TO_STRING1( x ) #x
TO_STRING首先会将x全部展开(如果x也是一个宏的话),然后再传给TO_STRING1转换为字符串,现在你可以这样:
const char *str = TO_STRING( PARAM( ADDPARAM( 1 ) ) );去一探PARAM展开后的样子。
2009年3月8日星期日
lib和dll中的导出变量可见性
而链接到同一个dll时,导出变量是共享的,在各个模块之间只有一份拷贝,一处修改处处可见。
2009年2月25日星期三
2009年2月4日星期三
explicit initialization的新知
explicit
struct foo {
explicit foo( int a )
: a_( a )
{ }
int a_;
};
int bar( const foo & f ) {
return f.a_;
}
bar( 1 ); // fails because an implicit conversion from int to foo
// is forbidden by explicit.
bar( foo( 1 ) ); // works -- explicit call to explicit constructor.
bar( static_cast
bar( foo( 1.0 ) ); // works -- explicit call to explicit constructor
// with automatic conversion from float to int.
2009年2月2日星期一
Effective STL学习
永不建立auto_ptr的容器,也可以说auto_ptr同STL结合并不良好。
值传递或返回时,需要确保两件事:对象应该小;必须单态。
2009年1月31日星期六
stream学习
经典的方法是借助一个指定大小的Buffer,调用fopen,fread,fwrite,fclose。
通过STL的i/o stream可以简洁的实现文件复制:
ifstream ifs("infilename");
ofstream ofs("outfilename");
ofs << inf.rdbuf();
原来是有很多书还没有看过Effective STL就是其中之一。其中的Item17就是之前在DevX上看到通过swap来去除vector过剩容量的内容。而Item29就是这里的ifstreambuf_iterator
ifstream instream("txt.txt");
string input(
istreambuf_iterator
istreambuf_iterator
);
又引出istreambuf_iterator的一个特殊之处:如果一个istreambuf_iterator对象到达stream的末尾或者由默认构造函数构造生成,则它的值为end-of-stream,类似于文件操作中的EOF。上面的string构造函数的第二个参数就是利用了这一点。
这里用到的是string的构造函数:template
std::find学习
SGI的说明中函数原型为:
templateInputIterator, class EqualityComparable>
InputIterator find(InputIterator first, InputIterator last,
const EqualityComparable& value);
函数返回值为[first, last)范围内,第一个值等于value的迭代器。如果不存在,则返回last。前提是first与
last之间是一个有效地范围。没有提到first位于last之后的情况。但就实际情况来说,如果出现这种情况,仍
然返回last。环境为Linux,libstdc++.so.6。
原先的代码
|
可能是原作者也存在和我同样的误解,武断的认为如果不存在满足查找条件的对象,std::find会返回空指针。
修改后的代码如下,也就是多加了一项对于p是否为objs+count的判断。
|
STL map的一些注意
特别是取下标操作符data_type& operator[](const key_type& k)。在sgi的文档里清楚的写着:由于operator[]可能向map插入新元素,所以不能是const成员函数。而m[k]实际上相当于((((m.insert(value_type(k, data_type()))).first)).second。严格来说,这个成员函数不是必需的,只是为了简便使用。(也带来了一些困扰和开销)。
typedef map
INT_MAP iMap;
iMap[3] = "three";
插入3时,先在iMap中查找主键为3的项,没发现,然后将一个新的对象插入iMap,键是3,值是一个空字符串,插入完成后,将字符串赋为"three"; 该方法会将每个值都赋为缺省值,然后再赋为显示的值,如果元素是类对象,则开销比较大。可以用以下方法来避免开销:
enumMap.insert(map
同样,以string tmp = iMap[3];的操作来获取一个键值的对应值也存在问题。只有当map中有这个键值才成立,否则自动插入一个实例,值为默认初始值。
crtDbgFlag
_CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG));
以下来自msdn
_crtDbgFlag 标志包含下列位域:
| 位域 | 默认值 | 说明 |
|---|---|---|
| _CRTDBG_ALLOC_MEM_DF | On | 打开调试分配。当该位为 off 时,分配仍链接在一起,但它们的块类型为 _IGNORE_BLOCK。 |
| _CRTDBG_DELAY_FREE_MEM_DF | Off | 防止实际释放内存,与模拟内存不足情况相同。当该位为 on 时,已释放块保留在调试堆的链接列表中,但标记为 _FREE_BLOCK,并用特殊字节值填充。 |
| _CRTDBG_CHECK_ALWAYS_DF | Off | 导致每次分配和释放时均调用 _CrtCheckMemory。这将减慢执行,但可快速捕捉错误。 |
| _CRTDBG_CHECK_CRT_DF | Off | 导致将标记为 _CRT_BLOCK 类型的块包括在泄漏检测和状态差异操作中。当该位为 off 时,在这些操作期间将忽略由运行时库内部使用的内存。 |
| _CRTDBG_LEAK_CHECK_DF | Off | 导致在程序退出时通过调用 _CrtDumpMemoryLeaks 来执行泄漏检查。如果应用程序未能释放其所分配的所有内存,将生成错误报告。 |
2009年1月29日星期四
real, effective, saved UID
Linux系统下与进程相关的ID有:
setresuid(), setresgid()
首先需要明确一点,这几个概念都是和进程相关的.
real user ID表示的是实际上进程的执行者是谁,effective user ID主要用于校验该进程在执行时所获得的文件访问权限,也就是说当进程访问文件时检查权限时实际上检查的该进程的"effective user ID",saved set-user-ID 仅在effective user ID发生改变时保存.
一般情况下,real user ID就是进程的effective user ID,但是当要运行的可执行程序设置了"set-user-ID" 位之后,进程的effective user ID变成该文件的属主用户id,同时该进程的"saved set-user-ID"变成此时进程的"effective user ID",也就是该可执行程序的属主用户ID,该进程在执行一些与文件访问权限相关的操作时系统检查的是进程的effective user ID.
为什么需要一个"saved set-user-ID"?因为当进程没有超级用户权限的时候,进程在设置"effective user ID"时需要将需要设置的ID和该进程的"real user ID"或者"saved set-user-ID"进行比较.
APUE2中进行的解释是:
1)If the process has superuser privileges, the setuid function sets the real user ID, effective user ID, and saved set-user-ID to uid.
2)If the process does not have superuser privileges, but uid equals either the real user ID or the saved set-user-ID, setuid sets only the effective user ID to uid. The real user ID and the saved set-user-ID are not changed.
3)If neither of these two conditions is true, errno is set to EPERM, and 1 is returned
也就是说:
1)当用户具有超级用户权限的时候,setuid 函数设置的id对三者都起效.
2)否则,仅当该id为real user ID 或者saved set-user-ID时,该id对effective user ID起效.
3)否则,setuid函数调用失败.
也就是说,这个saved set-user-ID更多的作用是在进程切换自己的effective user ID起作用.
需要特别提醒的是:并没有任何的API可以获取到进程的saved set-user-ID,它仅仅是系统在调用setuid函数时进行比较而起作用的.
APUE2中关于此事的原话如下:
Note that we can obtain only the current value of the real user ID and the effective user ID with the functions getuid and geteuid from Section 8.2. We can't obtain the current value of the saved set-user-ID.
举一个例子说明问题,假设这样的一种情况,系统中有两个用户A,B,还有一个由B创建的可执行程序proc,该可执行程序的set-
user-id位已经进行了设置.
当A用户执行程序proc时,
程序的real user ID = A的用户ID,effective user ID = B的用户ID, saved set-user-ID=B的用户ID.
假如在该进程结束了对某些限制只能由用户B访问的文件操作后,程序将effective user ID设置回A,也就是说此时:
程序的real user ID = A的用户ID,effective user ID = A的用户ID, saved set-user-ID=B的用户ID.
这个改动之所以能成功,原因在于上面列举出的情况2):该ID为进程的real user ID.
最后,假设由于种种原因进程需要再次切换effective user ID为B,可是因为不能通过API获取进程的saved set-user-ID(该值为B的用户ID),所以只能通过两种途径获得(可能还有别的途径):
a)在设置effective user ID变回A之前保存effective user ID,它的值为B的用户ID.
b)调用函数getpwnam( "B"),在返回的struct passwd *指针中成员pw_uid存放的就是用户B的ID.
这样,这个调用setuid(B的用户ID)就会成功,原因也在于上面说的情况2):该ID与进程的saved set-user-ID相同.
APUE2中关于这几个值的相关解释在section4.4和section8.11中都有涉及.
