记录

来源:互联网 发布:淘宝阿里妈妈推广 编辑:程序博客网 时间:2024/06/11 12:45

今天在C版看到一个代码,如下

Q:编写一个程序,把输入作为字符流处理,直至遇到EOF。令其报告每个单词的平均字母数和单词数。不要将空白字符和标点符号记为单词的字母,,做好用一下ctype.h中的函数。

A:

#include<stdio.h>#include<stdlib.h>#include<ctype.h>int main(void){  int ch,nwd=0,nwdnum=0,state=0;  while (EOF!=(ch = getchar()) && (((isalpha(ch) || isdigit(ch)) && !ispunct(ch) && ++nwd && (!state?(state=1),++nwdnum:1)?0:(state=0)),1));  printf("\nNow this time input avger is : %f\n",(double)nwd/nwdnum);  system("pause");  return EXIT_SUCCESS;}

肢解下:

EOF!=(ch = getchar()) 

(((isalpha(ch) || isdigit(ch)) && !ispunct(ch) && ++nwd && (!state?(state=1),++nwdnum:1)?0:(state=0)),1)
((isalpha(ch) || isdigit(ch)) && !ispunct(ch) && ++nwd && (!state?(state=1),++nwdnum:1)?0:(state=0))
后面有个", 1"即这个逗号表达式的值为1即这个表达式的值为true,这个表达式很乱,如果是非标点的数字或字符state = 1, 单词数+1,?:表达式返回0,后面有个1,即为true,继续,如果是个空格,
(isalpha(ch) || isdigit(ch))这个为false,不执行后面的的,直接执行?:表达式,即为false,得到state = 0,同上,后面一个1,即为true,直到输入EOF

===================================分割线====================================
二维数组的指针形式
Array[2][3]:
1> *(*(Array + i) + j)
2> *(Array + i * 行长 + j)
详见《C专家编程》
===================================分割线====================================
const的后面如果紧随着一个类型说明符,const作用于类型说明符
其他情况下,const作用于它左边紧邻的指针的*号
《C专家编程》P64

===================================分割线====================================

互斥对象内部包含有一个线程ID,调用ReleaseMutex时会检查线程ID是否和当前的线程ID相符,不符会返回失败,如果主线程拥有互斥对象,在别的线程中调用ReleaseMutex会失败的,因为线程ID不符,所以互斥对象需要谁拥有谁释放,参考孙鑫VC视频第15课,时间:63:20 <windows核心编程>P255

===================================分割线====================================

new 只要你系统的内存还够用,都是在内存里的,不够的话 依标准会抛异常

操作系统如果发现物理内存不够用,会将部分内存调到硬盘上,使用的时候再调回来(换页)
virtualalloc 分配的则统统先保存到硬盘上,使用的时候调入内存

<Windows核心编程>P362

===================================分割线====================================

使用通用打开对话框打开文件时,会改变当前目录,变为被打开文件的那个目录

===================================分割线====================================

关于<Windows核心编程>第19章的P539的死锁问题,根据http://blog.csdn.net/breaksoftware/article/details/8159088所介绍,
线程调用大概是这样的:LdrpInitialize -->LdrpInitializeThread -->LdrpCallInitRoutine,进而调用DllMain
在LdrpInitialize中进入关键段,也就是书中所说的每个进程的锁
,LdrpInitializeThread在关键段中包括着,至于调不调用DisableThreadLibCalls是管不着这个关键段的,也就说调用了它,只不过是不掉用DllMain了,但是任然还是会运行LdrpInitializeThread的,但这个函数被包含在关键段中~当前线程获得这个关键段,然后在DllMain中创建线程,然后根据书中所说,新线程会获得锁,但此时当前线程还在关键段中(因为有WaigForSingleObject),所以新线程还是会被关键段给阻塞等待,死锁依然存在~

===================================分割线====================================

休眠挂起睡眠阻塞,似乎是我们一开始学习linux最郁闷的四个概念。

其实简单地解释应该是这样:

阻塞操作是指在执行设备操作的时候若不能获得资源将挂起进程,直到满足该操作的条件后再进行操作。被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等到的条件被满足。

因为阻塞的进程会进入休眠状态,因此,必须确保有一个地方能够唤醒休眠的进程,唤醒进程的地方最大可能发生在中断里面,因为硬件资源的获得的同时往往伴随着一个终端。

所以,阻塞是一种统称,而挂起是它的具体行为,休眠是一种状态。

至于睡眠,在调度制度里,睡眠是指被放在等待队列里的进程的状态。


操作系统中睡眠、阻塞、挂起的区别形象解释
首先这些术语都是对于线程来说的。对线程的控制就好比你控制了一个雇工为你干活。你对雇工的控制是通过编程来实现的。
挂起线程的意思就是你对主动对雇工说:“你睡觉去吧,用着你的时候我主动去叫你,然后接着干活”。
使线程睡眠的意思就是你主动对雇工说:“你睡觉去吧,某时某刻过来报到,然后接着干活”。
线程阻塞的意思就是,你突然发现,你的雇工不知道在什么时候没经过你允许,自己睡觉呢,但是你不能怪雇工,肯定你这个雇主没注意,本来你让雇工扫地,结果扫帚被偷了或被邻居家借去了,你又没让雇工继续干别的活,他就只好睡觉了。至于扫帚回来后,雇工会不会知道,会不会继续干活,你不用担心,雇工一旦发现扫帚回来了,他就会自己去干活的。因为雇工受过良好的培训。这个培训机构就是操作系统

===================================分割线====================================

记一下关于SEH全局展开,只有在EXCEPTION_EXECUTE_HANDLER时才进行全局展开,全局展开时,如果异常发生在__try __finally块中,先找到能够处理这个异常的__except,但不执行异常处理代码,然后如果异常过滤值是EXCEPTION_EXECUTE_HANDLER,进行全局展开,首先执行最内层的__finally,然后执行刚刚找到的能够处理这个异常的__except,具体详情查看<Windows核心编程>第24章的全局展开,<Windows核心编程>第24章,P647,全局展开这节的最后一段,<Windows核心编程>第25章25.4节第一段

ps: 如果在__finally的异常处理程序中再次发生异常,将不会执行产生异常的指令后面的指令,如果在__except的异常处理块中发生异常,同样不会执行产生异常的指令后面的指令,而是寻找上一个匹配的__except。如果是异常过滤程序中发生异常,调试时貌似会出现死循环,貌似是如果异常过滤程序发生异常,这个__except会捕获,捕获再进入异常过滤程序,然后再发异常,__except再捕获……

void foo(){__try {int v = 0;int n = 5 / v;}__finally{       // 如果__finally里发生一个异常,则异常指令后的指令将不会执行MessageBox(NULL, L"finally2", NULL, 0);         // 先执行foo的finally和func的finally的原因就是发生了异常,需要执行test中的异常处理代码块,}                                                       // 跳出了finally的try的代码块,所以要执行finally}void func(){__try {foo();}__finally{MessageBox(NULL, L"finally1", NULL, 0);}}void test(){__try {func();}__except (EXCEPTION_EXECUTE_HANDLER){       // 如果这里产生一个异常,将不会执行产生异常的指令的后面的指令,继续寻找上一个__exceptMessageBox(NULL, L"except2", NULL, 0);}}int _tmain(int argc, _TCHAR* argv[]){__try {test();}__except (EXCEPTION_EXECUTE_HANDLER){MessageBox(NULL, L"except1", NULL, 0);}return 0;}执行结果:finally2finally1except2=========================================================void foo(){__try{*(PBYTE)NULL = 0;}__except (EXCEPTION_EXECUTE_HANDLER)       // 如果是这样的,应为执行异常处理程序不用跳出finally的try,所以先执行except后执行finally{                MessageBox(NULL, TEXT("finally"), NULL, MB_OK);}}void test(){__try{foo();}__finally{MessageBox(NULL, TEXT("finally"), NULL, MB_OK);}} 


===================================分割线====================================

TrackPopupMenu函数是同步的,也就是说弹出菜单后,不选择是不会返回的,必须等到菜单弹出后,然后选择了菜单,如果这个菜单的功能也是同步的,必须等到这个菜单处理完,比如弹出一个对话框,等这个对话框关闭了,这个函数才返回.

===================================分割线====================================

关于LoadMenu和GetMenu返回的句柄之所以不同,是因为GetMenu返回的是当前窗口已加载的菜单的句柄,而LoadMenu则是新加载一个菜单资源,然后返回其句柄

ps:SetMenu可以把LoadMenu新加载的菜单资源设置为当前菜单

===================================分割线====================================

关于CreateFileMapping:

关于使用CreateFileMapping把PE文件映射到进程地址空间中的问题

用CreateFileMapping为一个PE文件创建内存映射并且第三个参数包含SEC_IMAGE标志时,映射后的内存文件会按内存粒度和节中指定的VA对齐~ 此时内存中的布局和磁盘上的通常不一样,因为一般情况下内存对齐粒度比磁盘对齐粒度大~

===================================分割线====================================

MEMORY_BASIC_INFORMATION结构一些成员备忘
BaseAddress >> 块(一些具有相同保护属性连续的页面)的起始地址, 将指定地址向下取整到页面大小.(同<Windows内核情景分析P54的区块>)
AllocationBase >> 当前内存区域的起始地址, 一个内存区域可以包含多个块, 该区域包含指定地址, 就一个模块而言, 这个地址是模块的基地址. 指定地址为MEM_FREE状态时,此成员为0.(同<Windows内核情景分析P54的区间>)
RegionSize >> 当前块的长度

===================================分割线====================================

关于MmCreateHyperspaceMapping的一些理解备忘:

在MmCopyMmInfo中, 本进程页目录的HYPERSPACE目录项被赋值为一个物理页号pfn[1], 可以理解为一个页表4kb, 1024个表项. MmCreateHyperspaceMapping将指定的物理页映射到自己的一个空白表项, 然后返回该表项的地址. 返回时Hyperspace基地址 + i * PAGE_SIZE是为了形成地址, Hyperspace是形成页目录号, i * PAGE_SIZE是形成页表号(页表号和页表号之间间隔4096字节, 因为访问偏移超过1页(4kb)才会换页), 下面用MmGetPageTableForProcess中创建指定进程页目录的临时映射为例, 假如, Hyperspace的基地址为0xc0400000, 空白项的索引为1, 返回0xc0401000, 也就PageDir的值, 设PdeOffset为2, 当执行PageDir[PdeOffset](展开为*(0xc0401000 + 8))时, 将地址0xc0401008分为页目录号, 页表号和偏移3部分, 用页目录号从当前进程的页目录中获取Hyperspace的物理地址, 用页表号在Hyperspace中就可以获得MmCreateHyperspaceMapping中保存的目标进程的页目录的PTE(物理地址)了, 然后用偏移加上目标进程页目录的物理地址, 即可获得指定的PDE:

===================================分割线====================================

查找窗口的WndProc(如果本地调试时无法查看win32k的符号, .reload下):

参考, ValidateHwnd反汇编, win32子系统之三:ValidateHwnd函数分析

具体原理: (win32k!gSharedInfo(win32k!tagSHAREDINFO)+0x08) * LOWORD(hwnd) + win32k!gSharedInfo+0x04获得一个win32k!_HANDLEENTRY结构指针,该结构的第一个成员phead是结构win32k!tagWND结构的地址, 该结构0x60处为lpWndProc


原创粉丝点击