posts - 16,  comments - 34,  trackbacks - 0
共10页: 1 2 3 4 5 6 7 8 9 Last 
@溪流

template<typename R>
R call( R (*f)() ) { return f(); }

void __cdecl f() { printf("cdecl\n"); }
void __stdcall g() { printf("stdcall\n"); }

VC8默认情况: call(f) 是可以的, 而 call(g)不行。
因为 call 中的 f 参数没有指定调用约定。
如果显式使用 /Gr , 那就call(f)不行, call(g) 可以。



如果使用重载:
template<typename R>
R call( R ( __cdecl *f)() ) { return f(); }


template<typename R>
R call( R ( __stdcall *f)() ) { return f(); }

两者都可以, 但在g++下, 只要两者被同时使用, 就是重定义。
@溪流
你会C++还怕C么……

说是教, 也就是以前的死党指点指点。
都是灰常基础的东西……
@溪流
嗯, 理想是一回事。 有时候还是得向现实屈服……
作为个人的话, 还是可以学学python(perl, ruby) 什么的, 日常编程工作就可以摆脱C/C++了……
@溪流
嗯嗯, 这个确实符合破窗效应。

但15个api与45、 75个api应该用什么效应解释呢?
反正就我的感觉, 15个会很有耐心的去看; 而45个就没耐心了; 75个就绝对不想去看了, 并给自己一个心理安慰:这玩意太复杂, 没设计好……
@溪流
>> 前两天我在犹豫框架要不要帮用户解析 WPARAM、LPARAM,你的例子让我坚定了想法,解析之!

解析WPARAM和LPARAM确实无比无比蛋疼啊……
当然, 最初的那些C程序员也不是没办法了, 有个 windowx.h 里面的宏有一些帮助。

我也觉得, 作为一个有责任的库, 应该做之。
如果能提供没有解析的原始层次, 供那些有经验的程序员使用, 似乎也可以?


似乎程序员都有实现gui的冲动?
我也冲动过的…… 但就是被WPARAM, LPARAM击败了……
本想以此当作熟悉各种消息的方式, 最终还是没坚持下来……
没毅力的人飘过……
@溪流
>> 但一般实现代价一般很小的情况下才会出现这种冲动,想要顺手做了。

以前是不管代价如何就很冲动去顺手了……
现在想来……
也算是练手吧……
@溪流
貌似你误会了。

cexer所说的应该是这个情况:
比如需要实现bind(或者是别的功能, 这个不是重点, 只是用于举例)。
重点是: 库的用户会用不同的调用约定的函数传入。

还是上面的伪代码:
binder bind( f , arg ) { ... }

R0 __cdecl F0_of_client ( ... );
R1 __stdcall F1_of_client ( ... );

bind(F0_of_client, ... );
bind(F1_of_client, ... );

只有两者之一会成功。
@yrj
感谢~
其实我困惑的不是"如何实现", 而是"是否需要在C++中实现"。
举个实际例子 (我就一话痨……) :
最近在教人C语言……

条件包含部分: #if, #elif, #else, #endif, defined 就完全ok了。
所有功能都可以实现。
但C语言还提供了 #ifdef, #ifndef…… 而且人们通常用的就是这个方便的接口……
而且很多时候连#elif, #else都没有 —— 比如头文件保护符, 我就是在给别人解释这个事情。

为什么有#ifdef, #ifndef后, 还要有 #if 与 defined?
若不给初学者解释 #elif , 是肯定理解不了的。


如果C语言一开始就只为条件包含提供一种方法: #if, #elif, #else, #endif, defined。
大家也就只能这么写。
那介绍头文件保护符时, 就先介绍#if, defined, #endif就完了, 暂时不需要牵扯出其他部分, 其他部分可以慢慢了解。
人脑没法一下子容纳太多信息的。



再声明一下, 以上都是个人观点, 话痨犯了, 聊聊天而已。
完全没有"其他人也必须这样", "这就是事实"的意思……
@cexer
同时, 我还发现C++程序员很容易产生一种倾向:
嗯, 我要设计一个程序库, 这个库很smart, 无论怎么使用, 都是可以的。

想怎么用, 就怎么用 —— 以前我也是这样想的。


就举这个帖里出现的一个例子: 某处理器需要一个返回值。 是否可以让程序员在需要默认返回时, 编写一个返回void的处理器?

就我个人而言 ( 仅仅是个人的看法, 与技术无关了, 两者都是对的), 类似WTL那样也是"完全可用"的。
... frame work ...
case WM_XXX :
R result = default_result;
user_handler(&result, ... );
return result;

签名必须写为:
(R* response, ... ); // 其实是个引用, 只是指针的话, 上面的frame work中的代码意图更明显。

"有且仅有这一种做法" , 没有第二选项。
若用户不关心, 不去修改response的值即可。


其实我也觉得传出参数是很难看的设计。
但它可工作, 也可以避免去编写一堆meta programming 代码, 而且这堆代码可能又需要同C++的各种阴暗作斗争。
例如boost, 若不需要"照顾" 这么多编译器的话, 其实代码还是可读的。
但需要照顾这么多编译器, 那无数的workaround, 使得代码变得……
不是天书也是烂泥……



"嗯, T是一个原始指针可以, 当它是个smart pointer时也应该要被支持"。
很多时候, C++ 程序员不是去考虑这种需求是否存在, 实现这种需求的复杂度如何, 维护代价如何。
仅仅是因为他们能, 所以他们就要这样做……

其实有时候, 这些需求仅仅是美学上的, 而不是技术上的。
比如上面那个返回值的例子。
再比如:

f(char const* s, ... ); // primary function
f(std::string const& s, ... ) { return f(s.c_str(), ...); }
f(CString const& s, ... ) { return f(static_cast<char const*>(s), ...); }
...

如果不要后两者, 任何工作依然可以完成。
后两者仅仅是提供一种方便, 而不是"核心"接口。

也不是说这种方便不好…… 我的观点是一定需要有某种方式告之用户, 哪部分是核心接口。
用户优先去掌握这部分, 然后整个库的功能他就可以完全掌握了。
其他的, 学得越多, 使用越方便。

否则, 若没有这种通告, 一个f不要紧, 若全体函数都这样搞, 就会增加学习成本。
本来15个核心函数, 如果扩展为45个, 75个, 就不仅仅是量上的变化, 而是质上的变化 —— 让人失去耐心。
这同为什么要抑制warning一样, 不是说不可以产生warning, 而是说当warning多到一定程度时, 会超过人的处理极限, 会不自觉的将他们全体忽略掉。
所以发现warning就要干掉, 让新的warning能引起人注意。


至于通告方式…… 感觉手写文档是最靠谱的……
docxgen 什么的…… 产生的文档太死气沉沉, 无法感受到当中的轻重缓急。
@cexer
来说一个具体的C++问题吧, 就是这个:
>> 你还需要考虑调用约定的问题,因为 __stdcall,__cdecl,__fastcall的不同,都会使函数签名不同。有多少种调用约定,functor的绑定函数的数量就需要乘以多少,这是个体力活,可以用预处理元和文件包含来减少体力消耗。

绝对的体力活啊! 干过一次绝对不想干第二次……

而且, 你有试过g++么?
g++中这问题很麻烦。
函数重载目前是通过mangling实现的, 而g++对一个函数(free or member)指针类型的mangling只包含普通的signature, 不包含调用约定。

伪代码:
binder bind( __cdecl f, arg )
binder bind( __stdcall f, arg )

俩f在g++中是不同类型, boost::is_same是false。
但这俩bind函数"不构成重载" , 是bind的重复定义……
原因就是上面提到的, f被mangling时, 它的调用约定信息被忽略了。
俩bind最终产生的符号是相同的。


用C++, 就要和这些许多细枝末节的问题作斗争……
懒了…… 想逃避之……
@cexer
>> 同时支持静态和动态的映射方式,关键是库要实现得好,在没有使用动态映射的时候,这个动态映射的功能就好像不存在一样,这样大多数时候既能够享受空间优势,在需要的时候又能发挥它的动态优势。免得用户要在在空间优势和灵活性之间做出痛苦的选择。


说得我口水都流了…… (其实是开饭了~)
有没有机会能先睹为快呢~


假设, 这部分能做到上述所说。
但还是有别的问题。

比如参数绑定(boost.bind), 在其他范畴里叫partial apply。
其他语言要实现这个简直太容易了, 语言直接支持, 不需要一个额外的库。
也不需要关心被bind的参数的生命周期问题。

比如匿名函数, boost.lambda应该也是有不少限制的, C++0x目前也不算太成熟 —— 还有这么多人用VC6呢…… 能让他们进化到VC8就很感谢了, VC10感觉是奢望。


还有你上面提到"给handler一个名字, 以方便从handler们中删除" —— 这完全就是动态类型语言常用的方式嘛……
比如lua:
handlers = {}
添加:
handlers[name] = handler
调用:
for _,v in pairs(handlers) do v( ... ) end
删除:
handlers[name] = nil


还有楼主提到的typelist, 其实是无法展开为参数列表的。
这功能在动态类型语言里也太容易了……
最简单的元编程方式, 就是产生所需代码的字符串, 然后加载之……


总之呢, 当需要灵活性时, C++就缺这缺那的, 什么都要自己去造……
造的时候, 又有各种问题。


造的时候肯定是很有成就感的, 编程的乐趣啊!
但就我个人而言, 已经懒惰了……
@cexer
上面所说的"一个类只需要产生一个instance"是非常极端的情况。
实际上是否真的会产生这样的情况?


比如那个动态产生button的问题。
如果依然遵守静态类型的方式, 既然"预见" 会产生多个button, 是否可以先在on_command里留下stub, 然后把on_command这部分动态化。
每个button被按下的行为不同, 但这些行为是否依然是"可预见"的?
是否依然可以将它们归入到同一类型中, 比如使用类多态机制?


"A完全动态" 的方案给我的感觉是: 这已经不是在用C++, 而是在C++中实现一个微型动态类型系统, 然后用动态类型的方式编程。
如果是我个人作出选择的话, 当需要这种灵活性时, 直接把C++丢了, 选其他动态类型语言来编程-_-
@cexer
感谢感谢~ 果然是经验丰富的大神。

我想说的就是对象模型不同导致的"空间和效率"上的差异。
C++ 其实不是"面向对象"语言, 而是"面向类" 语言。
真正的面向对象语言, 每个instance都是相互独立的。
而C++,每一个type的所属instance共享一部分行为。


C++其实就是将"可能拥有一组相同行为"的instance们, 归属到同一个类里,
共享许多信息, 并提高空间与时间效率。
但是, 当instance们的行为复杂(灵活)到一定层次时, 极端情况, 就是一个class, 只需要一个instance。


这时候, 如果依然遵守静态类型的桎梏, 为每种行为组合编写一个类, 就会非常繁琐, 远不如动态类型语言来得方便。
借宝地请教一下~ 对于handler, 是否需要如此的动态?

A. 完全动态

比如, 一个具体的 window 类。
window w; // 具体的一个类
w.add_handler( ON_CREATE, my_on_create, ... ); // 动态的添加一些handler。

具体语法不重要, w.add_handler<ON_CREATE>(my_on_create, ...);
关键点是, window是一个具体类, 动态的(运行时而非编译时)添加一些handler。

if (some_condition)
/*run time*/w.add_handler( ON_CREATE, my_on_create, ... );


B. 完全静态

与之相对的, 是编译时就决定一个类型, 比如 my_window。
然后让它处理 ON_CREATE。 如何让WndProc分派到这个handler同样不是重点。
重点是my_window的每个instance肯定都会处理这个event, 没有例外。


C. 两者结合, 动态部分自己实现。

两者的折中可能是这样:
1. 依然是一个具体的, 编译时决定处理哪些event的window类, 比如叫y_window
2. 在设计y_window时, 认为有"部分"(而非window那样的全部)行为需要在运行时定制
3. 在event中留一些stub, 用于运行时添加。

比如 y_window::on_create( param ){
/*同样, wndproc 如何分派到这里不重要*/
/**/for (it=this->on_create_handlers.begin(); it!=this->on_create_handlers.end(); ++it) *it(param);
}
y_window::add_on_create( f , ... ) {
/**/this->on_create_handlers.push_back(f, ... );
}


如上, 需要A那种灵活性么? 缺之不可?
因为我自己确实没写过几个gui程序, "作为一个库的用户", 没有足够的这方面的使用经验。
typelist 不给力的, 它没办法展开为一个参数列表。
最终还是需要boost.pp 那样的东西。
我记得cnblogs有一个功能, 只许注册用户评论。

如果cppblog也有这个功能, 就打开呗。
cppblog上的注册用户素质都还不错。
re: C++关键字mutable的思考 OwnWaterloo 2010-10-30 11:50
逻辑上是const, 但又必须修改成员。 比如缓式求值什么的。
造轮子是可以的。

同时也建议尝试一下其他语言, 比如python或者perl。
有趣的东西很多, 但不是什么都适合用C++去写。
比如metablog api, python也就分分钟的事情。

总之嘛, 解决这些"程序员日常杂活", 学点C++之外的语言是很方便的。
@天下
I see. 谢谢~
@天下
能详细说说是哪一章节, 以及哪一版本吗?
中文版至少有3个版本, 侯捷的简体与繁体、 还有另一个人翻译的简体。
再次感谢~


@溪流
我最开始是听一个学弟在论坛上这么说, 以为是他自己造的一个词。
后来偶尔也有文章出现这样的词。
今个儿终于逮到机会问问。
是哪本大学教科书啊?
re: 学习下 WTL 的 thunk OwnWaterloo 2010-10-26 19:35

@溪流
>> 路过同学的例子确实也指出了一个用bind拐个弯设置到原始C函数指针上去的方法

bind生成的可调用体 —— 一个instance —— 依然需要通过某种方式传递到callback中
而现在的问题就是如何将这个context传递进去
 
所以我一直说, bind根本就不是解决这个问题的方法, 它本身需要这个问题解决之后才能使用
re: 学习下 WTL 的 thunk OwnWaterloo 2010-10-26 19:30
@路过
最烦这种瞅住机会就迫不及待的秀一下自己似是而非的理解, 离题万里
浮躁
re: 学习下 WTL 的 thunk OwnWaterloo 2010-10-26 19:27
>>照这种方法法不能推导出wndproc的用法?
>>还是“如果”,有意义?
 
你有点起码的逻辑分析能力吗?
说threadproc是帮助你理解这两者的不同, 既然不同, 那就是不可导出的。
既然好心被当作驴肝肺, 那别什么"如果"了, 你自己动手导出wndproc试试。
 
然后, 摸着自己的良心说: wndproc缺少的参数是靠什么传入的
是bind吗? 是function吗? 是华丽的语法吗?
 
还是我在前面提到的那些东西: table, cbExtra, GWL_USERDATA, Set/GetProp, thunk?
 
re: 学习下 WTL 的 thunk OwnWaterloo 2010-10-26 19:15
@路过
lz的标题是 WTL thunk。
thunk你懂吗? thunk解决的是华丽的语法, 还是缺少的context参数?
请教一下, "静态联编"和"动态联编"这两个术语的出处在哪?
re: 学习下 WTL 的 thunk OwnWaterloo 2010-10-26 00:10
@路过
作者讨论的是“如何开启一个线程”吗?
不, 作者讨论的是如何给WndProc塞入一个context。

哪谁在讨论ThreadProc?
是你, 将讨论从WndProc转移到ThreadProc。

我怕你不懂WndProc和ThreadProc究竟有什么重大区别,
以及, 也许你不熟悉WndProc,
所以想就近从你熟悉的ThreadProc中给你一点启发 —— 就是移除ThreadProc的context参数。
设想一下ThreadProc如果没有void*参数, 编程会是个什么样子, api有多难扩展。
以及, 问题的关键究竟是什么?

你是不明白啊, 还是不明白啊, 还是不明白?


多说一句, GWL_USERDATA我在上面已经提到过, 是谁回帖不看贴的?
再多提醒一句, GWL_USERDATA并不是你想象的那么简单。
究竟是什么, 请继续看我在楼上的回复。
re: 学习下 WTL 的 thunk OwnWaterloo 2010-10-25 23:09
@路过
喔唷! 好华丽, 好耀眼哦!
class, thread, callable, run什么的, 我看不懂也!

我就问一句, 还望赐教赐教啊~

如果CreateThread使用的线程函数的签名是这个样子:
unsigned threadproc(void); // 注意, 是void, 而不是void*

你用上面那些华丽的东西给我看看?
全局变量什么的也丑的, 也不许用哦!


WndProc就是这样一个场景 —— 回调的签名没有void* context。
您, 明白了吗?
re: 学习下 WTL 的 thunk OwnWaterloo 2010-10-25 09:33
@waiting4you
有。
Windows支持的体系结构里, ATL的stdthunk都有实现。
编译switch也不用自己操心, 都写好了的。
re: 学习下 WTL 的 thunk OwnWaterloo 2010-10-24 22:10
@溪流
因为用的人少啊
re: 学习下 WTL 的 thunk OwnWaterloo 2010-10-24 21:53
@陈梓瀚(vczh)
这问题用bind解决不了。

bind生成的对象, 需要template配合。
比如: for_each(begin, end, bind( ... ) );
for_each是一个函数模板。

而WndProc必须是一个函数指针。
lpWndProc = bind( ... ); 是不行的。
re: 学习下 WTL 的 thunk OwnWaterloo 2010-10-24 18:13
@溪流
WTL我不熟, 深入研究的也就是和thunk相关的东西……

给人的感觉是模板使用过度。
比如, 居然用一个模板参数去传递两个窗口风格参数。
感觉是C++的混沌年代, 大家都还不知道如何合理使用模板的情况下, 产生的东西……
所以也不愿意多看……

想研究gui的话, cppblog有个 cexer, 看过很多gui的框架, 可以向他咨询。
re: 学习下 WTL 的 thunk OwnWaterloo 2010-10-24 18:08
@溪流
没什么好惭愧的呀……
实验、 玩具代码这很正常。 为了突出重点(机器代码的构造)而非内存管理。
造轮子也是很好的练习。

只是, 轮子在投入正规使用前, 一定要谨慎。
玩具代码中忽略的非重点, 恰恰可能占了很多代码, 繁琐、 无趣、 bug丛生……
如果真有恒心把这些问题处理好, 再和现有的轮子客观的比较权衡之后, 再考虑使用。


因为看过许多把玩具代码 —— 而且是一些相当没有技术含量, 仅仅是满足个人审美观而已 —— 的代码放入实际工程中, 还很自鸣得意的……
惨不忍睹啊……
re: 学习下 WTL 的 thunk OwnWaterloo 2010-10-24 17:43
virtualalloc不是这样用的啊, 同学……
virtualalloc的粒度很大(保留按64k, 提交按页)。
也就是说, 如果直接用virtualalloc, 每个window instance, 要占用64k地址空间, 并使用至少4k内存。


ATL不同版本处理这个问题好像采取的策略不同。
有你提到的使用virtualalloc分配, 然后再划分。
还有直接使用HeapCreate创建一个分配可执行内存的win32 heap。


这些技术很炫, 研究起来也很有成就感。
玩具代码当然可以随便用, 但成熟代码尽可能选取成熟的技术。

比如全局table。它的效率问题真的无法忍受吗? 或者全局带来的其他问题(多线程相关)很要紧吗?

如果窗口类是自己注册的, 可以用cbExtra。
如果不是, 而且GWL_USERDATA是开放的(对话框和MDI是保留给自己用的), 那可以用GWL_USERDATA。

还可以用Set/GetWindowProp。

如果thunk机器相关的缺陷不要紧, 而且上述的这些成熟的方案都不合适, 也不一定非要自己写内存池, HeapCreate在windows上总是可用的。
re: 问一个语法问题 OwnWaterloo 2010-10-15 18:56
@溪流
哦, 对, 你这么一说想起来了。
确实有歧义。 问题差不多: 一个隐式转换到T*, 一个[]操作符。
o[x] 究竟是 o.operator T*()[x] 还是 o.operator[](x)。
看这里:
http://www.glenmccl.com/ansi_022.htm

但究竟是怎么规定的, 忘了…………………………
re: 问一个语法问题 OwnWaterloo 2010-10-15 15:32
歧义在哪?
re: Visual Studio 历史简介(转) OwnWaterloo 2010-08-21 14:11
@溪流
1. 具体我也不清楚……

不过这应该都有据可查……

2.

String& operate=(String const& other)
{
size_t len = strlen(other.p);
char* p = new char[len+1];
strcpy(p, other.p); // 风险在哪?
delete [] this->p;
this->p = p;
}

int i = 0;
scanf("%d", &i); // 风险在哪?


char buf[numeric_limits<int>::digit10+2];
sprintf(buf, "%d", i); // 风险在哪?


上面的警告还可以通过一个宏定义 _CRT_SECURE_NO_DEPRECATE全部干掉。

std::copy就更搞笑了, 要抑制警告, 必须#pragma…… 先push, 再disable, 再pop……
非常非常干扰源文件的表达。

如果不在源文件里, 而是在构建脚本里, 那更复杂。
.vcproject手工改吗? cmake如何加入自定义编译选项?


std::copy(src.begin(), src.end(), back_inserter(dst) ); // 风险在哪?

还有那个/Wp64。

VS2005的这些新东西, 全他妈是傻逼设计。
re: Visual Studio 历史简介(转) OwnWaterloo 2010-08-21 08:56
@溪流
VC6情有可原?

好吧, 那VC8加入的所谓的security crt怎么解释?
硬生生的将scanf, std::copy等函数列为废弃?
硬生生的将open, read的函数列为废弃, 而且是以莫名其妙的理由?

非不能, 乃不愿也。
re: 一个大整数类的实现 OwnWaterloo 2010-08-21 08:26
"任意精度的整数类 *模板* " —— 这事情我也干过……
现在如果有必要再造轮子, 感觉应该没有模板的必要……
re: 关于C++之“复杂” OwnWaterloo 2010-07-08 19:29
@陈梓瀚(vczh)
控制for中的变量的scope?
还是禁用异常?
还有其他的么?
re: 关于C++之“复杂” OwnWaterloo 2010-07-07 21:53
@陈梓瀚(vczh)
如果C++再提供一个"特性": 可以禁用某些方面的另外的特性。
团队合作时会不会好一些?

比如看异常不爽的团队, 直接在构建脚本里, 通过编译选项把异常的使用给禁了。
看xxx不爽的, 也可以。。。
re: 关于C++之“复杂” OwnWaterloo 2010-07-07 19:26
@陈梓瀚(vczh)
讨论, review什么的, 有一些用处, 但不能全防住。
特别是"团队换人"的时候。

这些讨论不一定会被良好的记录。
新成员可能就乱来了。

如果是团队大换血, 可能新的团队就会觉得现有代码是坨屎。


>> 至于说选择多的东西交给一群没经验的人是会出问题的,那这不就证明他们要是能力不足或者不想学这么多东西,就应该去用C#和Java而不应该用C++吗。

没有经验、 能力不足、 不思进取、 混口饭吃的, 恰恰占大多数。
所以, C++也只是少数人手中的利刃, 不是谁都玩得转的。
一部分玩不转的人, 就把原因归咎到C++过于复杂。
推卸责任是很常见的心理。
re: 关于C++之“复杂” OwnWaterloo 2010-07-07 16:55
我觉得只是复杂, 问题还不大。

C++的问题出在给出的特性分了岔。
从留言中就已经可以看出有(曾经)崇尚OO的,和不主张OO的两派。
当团队中人数再增加时, 肯定还会出现更多派系。
如果没能很好的控制, 就很难和谐, 造成四不像的设计。

相比而言, C和java都没这么容易分岔。
尤其是java, 团队中基本不会出现不兼容的设计, 因为没有什么其他的好的选择。
re: 2D场景节点实现(空间关系) OwnWaterloo 2010-07-07 00:45
评论通知啊……
通知啊……
啊……
……
re: [C++]内存管理(1) OwnWaterloo 2010-07-07 00:08
@volnet
详细设计部分在cppblog上调整过样式吧?
我见过一个白色的页面, 过段时间刷新后, 就变黄色了。
很用心~
re: [C++]内存管理(1) OwnWaterloo 2010-07-06 23:29
请教一下: 这个排版是怎么做的? live writter?
re: C++的流设计很糟糕 OwnWaterloo 2010-07-06 14:16
鲜明的对比:

学会了errno就学不会exception。
学会了prints就学不会streams。
学会了C就学不会C++。
。。。

学会了errno继续学exception。
学会了printfs继续学streams。
学会了C继续学C++。
。。。
re: size_t的倒序遍历问题 OwnWaterloo 2010-07-01 17:33
for (size_t i=n; i-- /*!=0*/; )
...
re: C++实用技巧(二) OwnWaterloo 2010-06-24 14:12
@陈梓瀚(vczh)
http://linuxdude.com/Steve_Sumit/C-faq/q5.17.html
整个第5章都是讲null pointer的。
re: C++实用技巧(二) OwnWaterloo 2010-06-24 11:25
@陈梓瀚(vczh)
无论是C还是C++, 说的都是"编译时可求出0值的常数是空指针常量"。
可以将空指针赋值给任意指针类型的变量。
但并不保证赋值之后指针变量的二进制表示也是全0。


从语法上讲:
void* p = 0;
void* p=(void*)(int)0;
都是对的, 就像:

float f = 0;
float f = (float)(int)0;
而通常f的二进制表示并不是全0。
这是因为编译器是能看到这里的转型动作, 并加入适当的操作。


但这样写就是没有保证的:
void* p;
memset(&p, 0, sizeof p);
memset函数中已经不知道p的类型, 只能将p当作byte 数组。
就没有机会为指针类型作适当的调整。


再举个例子:
class A {};
class B {};
class C : public A, public B {};

C c;
C* pc = &c;
A* pa = pc;
B* pb = pc;
assert( pc==pa && pc==pb);

因为编译器知道pc,pa,pb的类型。
所以无论是赋值还是比较, 都会插入适当的操作。

而如果使用memset或者memcmp, 就挂了。


最后, 标准中有句话, 在calloc的脚注里。
The calloc function allocates space for an array
of nmemb objects, each of whose size is size.
The space is initialized to all bits zero.

Note that this need not be the same as the representation
of floating-point zero or a null pointer constant.
共10页: 1 2 3 4 5 6 7 8 9 Last 
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

常用链接

留言簿(8)

随笔档案(16)

链接

搜索

  •  

积分与排名

  • 积分 - 196688
  • 排名 - 132

最新随笔

最新评论

阅读排行榜

评论排行榜