精华内容
下载资源
问答
  • 距离上一次博客好像过去了很久的样子,虽然博客没有更新但学习上却不敢有丝毫松懈,为了能够记录自己每日的学习进展,也是决定坚持,每周两篇,将自己所学习到的新知识,和对于一些概念的整理都通过这样的方式来...

    新年新气象

    哪有什么岁月静好,只不过是有人替我们负重前行——致敬那些逆行在疫情第一线的英雄们,待春暖花开之时,愿你们平安归来。
    距离上一次写博客好像过去了很久的样子,虽然博客没有更新但学习上却不敢有丝毫松懈,为了能够记录自己每日的学习进展,也是决定坚持写,每周两篇,将自己所学习到的新知识,和对于一些概念的整理都通过这样的方式来进行记录包括每个月的GitHub的记录,口说无凭,附上自己的:GitHub记录

    33行书写俄罗斯方块(学习到的)

    言归正传今天通过其他的途径也是学习到了一个学长所书写出来的俄罗斯方块,说实话还没有全方位的搞懂,因此发表出来也是希望能够有更多的大神能够指点一二,对于其中存在的一系列问题大家展开讨论,对于学长这样的功底还是非常的佩服得,虽说他在书写时候挺规范的,但是对于我们这种一行一行代码敲出来的人还是能够感觉到他的语言有着一些混乱,可能也是因为要达到所谓的33行这个噱头吧,所以在大家学习的时候也是尽可能地用自己的思维将他的解读以下在进行敲写。
    对于我们众多的朋友来说,既然是学习到的那就是学习到的,就像我在前面所说道的,在开始时候更多的学习其实也是为了更好的了解别人的编程思维和一些所存在的习惯,这样也是能够更好的方便和提升我们自己。

    代码段

    #include <stdio.h>
    #include <windows.h>
    #include <conio.h>
    
    int T, X, Y, c, i, j, k, map[250] = { 0 }, node[28][4] = {
    	//形状,根坐标,输入&循环变量,ijk循环变量,map地图,node节点,下面为7种形状*4种方向*4个节点包含宽*Y+X的数据
    	-1, 0, 1, -11, 10, 0, -10, -9, 11, -1, 0, 1, 9, 10, 0, -10, -1, 0, 1, -9, 10, 11, 0, -10, 9, -1, 0, 1,
    	10, 0, -10, -11, 9, 10, 0, 1, 11, 0, 1, -10, 9, 10, 0, 1, 11, 0, 1, -10, 10, 11, -1, 0, 10, 0, 1, -9,
    	10, 11, -1, 0, 10, 0, 1, -9, -1, 0, 1, -10, 10, 0, 1, -10, 10, -1, 0, 1, 10, -1, 0, -10, 20, 10, 0, -10,
    	-1, 0, 1, 2, 20, 10, 0, -10, -1, 0, 1, 2, 10, 11, 0, 1, 10, 11, 0, 1, 10, 11, 0, 1, 10, 11, 0, 1 };
    //移动
    int move(int* v, int l) {
    	for (*v += l, i = 0; i < 4 && (j = (node[T][i] + 11) % 10 - 1 + X, 1); i++)
    		//先移动,遍历4个节点,j=节点X坐标
    	if ((j < 0 || 9 < j || 24 < (node[T][i] + 11) / 10 - 1 + Y ||
    		//越界判断
    		map[node[T][i] + Y * 10 + X]) && (*v -= l, 1))return 0;
    	//判断当前节点是否有方块,如果前面有为真,则回到移动之前的位置,并且返回0
    	return 1;
    }
    //下落
    void down() {
    	if (move(&Y, 1) || Y < 2 && (exit(!_getch()), 0))return;
    	//向下移动,如果为真直接返回,为假则再判定当前Y坐标,如果<2则退出并阻塞
    	for (i = 0; i < 4 && (map[node[T][i] + Y * 10 + X] = 1); i++);
    	//4个节点在地图对应的位置赋值
    	for (i = 250, k = 0; i >= 10 || (c = 0); i % 10 == 0 && (k = 0))
    		//i从最后一行的行尾向前遍历,直到第一行的行尾,每遇到行头则k复位,结束时c=0
    	if (--i, (k += map[i]) == 10)
    		//k累计当前行数的方块,如果为10,则执行消行,消行原理,上面的数据覆盖下面的数据
    	for (j = i + 9; j > 9 || (i += 10, 0); map[j] = map[j - 10], j--);
    	//j从当前行尾开始,当前的数据等于上面的数据,直到第一行行尾结束,结束时i回到行尾
    }
    //主函数
    int main() {
    	srand((unsigned)malloc(!system("mode con: cols=20 lines=25")));
    	//初始化随机种子,并设置窗口大小
    	for (; c || (X = 4, Y = 1, T = rand() % 7 * 4, c = 1); down(), Sleep(150)) {
    		//开始循环;c=0时重置根坐标和形状及本身;固定的下落和延时
    		if (_kbhit() && (c = _getch())) {
    			//检测是否有输入,有则获取并判断
    			if (c == 'w' || c == 'W')move(&T, (T % 4) < 3 ? 1 : -3);
    			//w旋转
    			else if (c == 'd' || c == 'D')X < 9 && move(&X, 1);
    			//d右移
    			else if (c == 'a' || c == 'A')X > 0 && move(&X, -1);
    			//a左移
    			else if (c == 's' || c == 'S')down();
    			//s下落
    		}
    		for (i = system("cls"); i < 4 && (map[node[T][i] + Y * 10 + X] = -1); i++);
    		//清屏,节点赋值为-1
    		for (i = 0; i < 250; i++)_cputs(map[i] ? "[]" : "  "), map[i] += map[i] < 0;
    		//打印,并复位节点
    	}
    }
    

    成果图
    俄罗斯方块GitHub链接 点击其中第一个Eluosi就可以了,当然了下面也是我自己编写出来的其他的一些小游戏,大家也是可以借鉴的。

    多交流 多沟通 和我一样在自己学习的朋友可以一起进行交流讨论哦!

    展开全文
  • 消息循环

    千次阅读 2007-08-16 09:23:00
     从消息的发送途径来看,消息又可以分成2种:队列消息和非队列消息。消息队列由可以分成系统消息队列和线程消息队列。系统消息队列由Windows维护,线程消息队列则由每个GUI线程自己进行维护,为避免给non-GUI现成...
    首先 我们知道dos糸统是基于过程驱动的,从开始到结束按顺序执行。而windows则是基于事件驱动的,不由事件的顺序来控制,而是由事件的发生来控制,事件的发生是无序的。事件驱动程序设计就是密切围绕消息的产生与处理而展开的,一条消息是关于发生的事件的消息。
    Windows为应用程序创建一个"消息队列(message queue)",用以存放发给应用程序的消息。消息队列中消息的结构(MSG)为:
    typedef struct tagMsg
    {
           HWND    hwnd;       接受该消息的窗口句柄
           UINT    message;    消息常量标识符,也就是我们通常所说的消息号
           WPARAM  wParam;     32位消息的特定附加信息,确切含义依赖于消息值
           LPARAM  lParam;     32位消息的特定附加信息,确切含义依赖于消息值
           DWORD   time;       消息创建时的时间
           POINT   pt;         消息创建时的鼠标/光标在屏幕坐标系中的位置
    }MSG;
    通常应用程序中有一段称作"消息循环"的代码,用来从消息队列中检索这些消息并把它们分发到相应的窗口函数中。 

    while(GetMessage(&&msg,NULL,NULL,NULL)) 
      { //从消息队列中取得消息 
       TranslateMessage(&&msg); 
       //它从键盘接受原始按键消息,将扫描码转化成ascii码并解释成字符消息WM_CHAR,如不需要可不用
       DispatchMessage(&&msg); 
       //将消息发送给相应的窗口过程函数 
       }  

    把消息传送到应用程序有两种方法:一种是由系统将消息发送到应用程序的"消息队列"这是"进队消息"Win32 API有对应的函数:  PostMessage(),此函数不等待该消息处理完就返回;而另一种则是由系统在直接调用窗口函数时将消息"发送(send)"给应用程序的窗口函数,属于"不进队消息"对应的函数是SendMessage()其必须等待该消息处理完后方可返回。
    Windows 应用程序创建的每个窗口都在系统核心注册一个相应的窗口函数(也称窗口过程),这个函数是由windows调用的也称回调函数。我们先调用DispatchMessage函数,然后由其调用窗口函数。窗口函数程序代码形式上是一个巨大的switch 语句,用以处理由消息循环发送到该窗口的消息,窗口函数由Windows 采用消息驱动的形式直接调用,而不是由应用程序显示调用的,窗口函数处理完消息后又将控制权返回给Windows。
    窗口过程函数类似于下面程序:
    LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
         HDC         hdc ;
         PAINTSTRUCT ps ;
         RECT        rect ;
         
         switch (message)
         {
        
         case WM_PAINT:
              hdc = BeginPaint (hwnd, &ps) ;
              
              GetClientRect (hwnd, &rect) ;
              
              DrawText (hdc, TEXT ("树欲静而风不止"), -1, &rect,
                        DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
              EndPaint (hwnd, &ps) ;
              return 0 ;
              
         case WM_DESTROY:
              PostQuitMessage (0) ;
              return 0 ;
         }
         return DefWindowProc (hwnd, message, wParam, lParam) ;//缺省的窗口处理函数
    }
    检查传递过来的消息,处理完我们需要的消息,其它的丢给DefWindowProc就可以了。所有消息中必须处理的是 WM_DESTROY,它是在窗体清除后,通知程序做一些清理工作,清理后必须调用 PostQuitMessage,该函数会把 WM_QUIT 消息传回您的应用程序,而该消息会使得  GetMessage 返回,并在 eax 寄存器中放入 0,然后会结束消息循环并退回 WINDOWS。但是WM_DESTROY无法阻止退出应用程序。如果你想这么做,可以处理 WM_CLOSE 消息。在WM_CLOSE 调用 DestroyWindow 函数,它会清除窗口,然后发送一个  WM_DESTROY 消息给您自己的应用程序,从而使程序退出。
    如常用的退出却认:
    case WM_CLOSE:
          m=MessageBox (NULL, TEXT ("真的要退出么?"), TEXT ("警告"), MB_YESNO|MB_ICONWARNING);     
          if(m==IDYES)DestroyWindow(hwnd);
          return 0;
    再者DefWindowProc函数也是必须的,系统保留消息标识符的值在0x0000在0x03ff(WM_USER-1)范围。这些值被系统定义消息使用。DefWindowProc以默认方式帮我们处理了大量的消息,这也是为什么windows的窗口行为看起来大同小异,如果你想自已处理几百个消息的话可以不用它。当然如果你想让你的程序与众不同的话,那可以处已处理部分消息,如改变窗口样式(有空再写~)。
    我们来进一步分析消息,本来想写个分析例程,但无意中看到罗老大的测试程序,结~又省下不少脑细胞~
    代码如下:
    http://www.blogbus.com/blogbus/bl ... 23643/1091516880.txt
    说实话汇编使用了大量宏之后,除了提高输入效率外(包括减少输入错误),没什么好处,反而使代码变的难懂,除非是想用它来代替c来写程序。但我还是觉的c好~
    把消息发到记事本来监视消息-
    启动程序:
    Creating Window...
    WndProc: [0024]WM_GETMINMAXINFO          00000000 0012fda4
    WndProc: [0081]WM_NCCREATE               00000000 0012fd8c
    WndProc: [0083]WM_NCCALCSIZE             00000000 0012fdc4
    WndProc: [0001]WM_CREATE                 00000000 0012fd68
    CreateWindow end
    Showing Window...
    WndProc: [0018]WM_SHOWWINDOW             00000001 00000000
    WndProc: [0046]WM_WINDOWPOSCHANGING      00000000 0012fec0
    WndProc: [0046]WM_WINDOWPOSCHANGING      00000000 0012fec0
    WndProc: [001c]WM_ACTIVATEAPP            00000001 00000440
    WndProc: [0086]WM_NCACTIVATE             00000001 00000000
    WndProc: [000d]WM_GETTEXT                000001fe 0012f580
    WndProc: [0006]WM_ACTIVATE               00000001 00000000
    WndProc: [0007]WM_SETFOCUS               00000000 00000000
    WndProc: [0085]WM_NCPAINT                00000001 00000000
    WndProc: [000d]WM_GETTEXT                000001fe 0012f580
    WndProc: [0014]WM_ERASEBKGND             01010058 00000000
    WndProc: [0047]WM_WINDOWPOSCHANGED       00000000 0012fec0
    WndProc: [0005]WM_SIZE                   00000000 00450064
    WndProc: [0003]WM_MOVE                   00000000 004b0038
    ShowWindow end
    Updating Window...
    WndProc: [000f]WM_PAINT                  00000000 00000000
    UpdateWindow end
    Getting Message...
    WndProc: [0086]WM_NCACTIVATE             00000000 00000000
    WndProc: [000d]WM_GETTEXT                000001fe 0012f554
    WndProc: [0006]WM_ACTIVATE               00000000 00000000
    WndProc: [001c]WM_ACTIVATEAPP            00000000 00000258
    WndProc: [0008]WM_KILLFOCUS              00000000 00000000
    却实是够多的,其大体可分为三类:窗口消息、命令消息和控件通知消息。
       窗口消息是系统中最常见的消息,如窗口的刨建、移动、释放、鼠标、键盘等消息都是窗口消息。
       命令消息,这是一种特殊的窗口消息,他用来处理从一个窗口发送到另一个窗口的用户请求,包括子窗口。
       控件通知消息,子控件发生事件,需要通知父窗口。通知消息只适用于标准的窗口控件如按钮、列表框、组合框、编辑框,以及Windows公共控件如树状视图、列表视图等。如单击按钮事件。
        从消息的发送途径来看,消息又可以分成2种:队列消息和非队列消息。消息队列由可以分成系统消息队列和线程消息队列。系统消息队列由Windows维护,线程消息队列则由每个GUI线程自己进行维护,为避免给non-GUI现成创建消息队列,所有线程产生时并没有消息队列,仅当线程第一次调用GDI函数数系统给线程创建一个消息队列。队列消息送到系统消息队列,然后到线程消息队列;非队列消息直接送给目的窗口过程。
         对于队列消息,最常见的是鼠标和键盘触发的消息,例如WM_MOUSERMOVE,WM_CHAR等消息,还有一些其它的消息,例如:WM_PAINT、 WM_TIMER和 WM_QUIT。当鼠标、键盘事件被触发后,相应的鼠标或键盘驱动程序就会把这些事件转换成相应的消息,然后输送到系统消息队列,由 Windows系统去进行处理。Windows系统则在适当的时机,从系统消息队列中取出一个消息,根据前面我们所说的MSG消息结构确定消息是要被送往那个窗口,然后把取出的消息送往创建窗口的线程的相应队列,下面的事情就该由线程消息队列操心了,Windows开始忙自己的事情去了。线程看到自己的消息队列中有消息,就从队列中取出来,通过操作系统发送到合适的窗口过程去处理。
    对于非队列消息验证,可把GetMessage函数删除~可发现窗口过程依然会被调用。
    剩下的就是你对哪些消息感兴趣就处理它好了~~
    WM_NULL = $0000;
    WM_CREATE = $0001;
    应用程序创建一个窗口
    WM_DESTROY = $0002;
    一个窗口被销毁
    WM_MOVE = $0003;
    移动一个窗口
    WM_SIZE = $0005;
    改变一个窗口的大小
    WM_ACTIVATE = $0006;
    一个窗口被激活或失去激活状态;
    WM_SETFOCUS = $0007;
    获得焦点后
    WM_KILLFOCUS = $0008;
    失去焦点
    WM_ENABLE = $000A;
    改变enable状态
    WM_SETREDRAW = $000B;
    设置窗口是否能重画
    WM_SETTEXT = $000C;
    应用程序发送此消息来设置一个窗口的文本
    WM_GETTEXT = $000D;
    应用程序发送此消息来复制对应窗口的文本到缓冲区
    WM_GETTEXTLENGTH = $000E;
    得到与一个窗口有关的文本的长度(不包含空字符)
    WM_PAINT = $000F;
    要求一个窗口重画自己
    WM_CLOSE = $0010;
    当一个窗口或应用程序要关闭时发送一个信号
    WM_QUERYENDSESSION = $0011;
    当用户选择结束对话框或程序自己调用ExitWindows函数
    WM_QUIT = $0012;
    用来结束程序运行或当程序调用postquitmessage函数
    WM_QUERYOPEN = $0013;
    当用户窗口恢复以前的大小位置时,把此消息发送给某个图标
    WM_ERASEBKGND = $0014;
    当窗口背景必须被擦除时(例在窗口改变大小时)
    WM_SYSCOLORCHANGE = $0015;
    当系统颜色改变时,发送此消息给所有顶级窗口
    WM_ENDSESSION = $0016;
    当系统进程发出WM_QUERYENDSESSION消息后,此消息发送给应用程序,
    通知它对话是否结束
    WM_SYSTEMERROR = $0017;
    WM_SHOWWINDOW = $0018;
    当隐藏或显示窗口是发送此消息给这个窗口
    WM_ACTIVATEAPP = $001C;
    发此消息给应用程序哪个窗口是激活的,哪个是非激活的;
    WM_FONTCHANGE = $001D;
    当系统的字体资源库变化时发送此消息给所有顶级窗口
    WM_TIMECHANGE = $001E;
    当系统的时间变化时发送此消息给所有顶级窗口
    WM_CANCELMODE = $001F;
    发送此消息来取消某种正在进行的摸态(操作)
    WM_SETCURSOR = $0020;
    如果鼠标引起光标在某个窗口中移动且鼠标输入没有被捕获时,就发消息给某个窗口
    WM_MOUSEACTIVATE = $0021;
    当光标在某个非激活的窗口中而用户正按着鼠标的某个键发送此消息给当前窗口
    WM_CHILDACTIVATE = $0022;
    发送此消息给MDI子窗口当用户点击此窗口的标题栏,或当窗口被激活,移动,改变大小
    WM_QUEUESYNC = $0023;
    此消息由基于计算机的训练程序发送,通过WH_JOURNALPALYBACK的hook程序
    分离出用户输入消息
    WM_GETMINMAXINFO = $0024;
    此消息发送给窗口当它将要改变大小或位置;
    WM_PAINTICON = $0026;
    发送给最小化窗口当它图标将要被重画
    WM_ICONERASEBKGND = $0027;
    此消息发送给某个最小化窗口,仅当它在画图标前它的背景必须被重画
    WM_NEXTDLGCTL = $0028;
    发送此消息给一个对话框程序去更改焦点位置
    WM_SPOOLERSTATUS = $002A;
    每当打印管理列队增加或减少一条作业时发出此消息
    WM_DRAWITEM = $002B;
    当button,combobox,listbox,menu的可视外观改变时发送
    此消息给这些空件的所有者
    WM_MEASUREITEM = $002C;
    当button, combo box, list box, list view control, or menu item 被创建时
    发送此消息给控件的所有者
    WM_DELETEITEM = $002D;
    当the list box 或 combo box 被销毁 或 当 某些项被删除通过LB_DELETESTRING, LB_RESETCONTENT, CB_DELETESTRING, or CB_RESETCONTENT 消息
    WM_VKEYTOITEM = $002E;
    此消息有一个LBS_WANTKEYBOARDINPUT风格的发出给它的所有者来响应WM_KEYDOWN消息
    WM_CHARTOITEM = $002F;
    此消息由一个LBS_WANTKEYBOARDINPUT风格的列表框发送给他的所有者来响应WM_CHAR消息
    WM_SETFONT = $0030;
    当绘制文本时程序发送此消息得到控件要用的颜色
    WM_GETFONT = $0031;
    应用程序发送此消息得到当前控件绘制文本的字体
    WM_SETHOTKEY = $0032;
    应用程序发送此消息让一个窗口与一个热键相关连
    WM_GETHOTKEY = $0033;
    应用程序发送此消息来判断热键与某个窗口是否有关联
    WM_QUERYDRAGICON = $0037;
    此消息发送给最小化窗口,当此窗口将要被拖放而它的类中没有定义图标,应用程序能返回一个图标或光标的句柄,当用户拖放图标时系统显示这个图标或光标
    WM_COMPAREITEM = $0039;
    发送此消息来判定combobox或listbox新增加的项的相对位置
    WM_GETOBJECT = $003D;
    WM_COMPACTING = $0041;
    显示内存已经很少了
    WM_WINDOWPOSCHANGING = $0046;
    发送此消息给那个窗口的大小和位置将要被改变时,来调用setwindowpos函数或其它窗口管理函数
    WM_WINDOWPOSCHANGED = $0047;
    发送此消息给那个窗口的大小和位置已经被改变时,来调用setwindowpos函数或其它窗口管理函数
    WM_POWER = $0048;(适用于16位的windows)
    当系统将要进入暂停状态时发送此消息
    WM_COPYDATA = $004A;
    当一个应用程序传递数据给另一个应用程序时发送此消息
    WM_CANCELJOURNAL = $004B;
    当某个用户取消程序日志激活状态,提交此消息给程序
    WM_NOTIFY = $004E;
    当某个控件的某个事件已经发生或这个控件需要得到一些信息时,发送此消息给它的父窗口
    WM_INPUTLANGCHANGEREQUEST = $0050;
    当用户选择某种输入语言,或输入语言的热键改变
    WM_INPUTLANGCHANGE = $0051;
    当平台现场已经被改变后发送此消息给受影响的最顶级窗口
    WM_TCARD = $0052;
    当程序已经初始化windows帮助例程时发送此消息给应用程序
    WM_HELP = $0053;
    此消息显示用户按下了F1,如果某个菜单是激活的,就发送此消息个此窗口关联的菜单,否则就
    发送给有焦点的窗口,如果当前都没有焦点,就把此消息发送给当前激活的窗口
    WM_USERCHANGED = $0054;
    当用户已经登入或退出后发送此消息给所有的窗口,当用户登入或退出时系统更新用户的具体
    设置信息,在用户更新设置时系统马上发送此消息;
    WM_NOTIFYFORMAT = $0055;
    公用控件,自定义控件和他们的父窗口通过此消息来判断控件是使用ANSI还是UNICODE结构
    在WM_NOTIFY消息,使用此控件能使某个控件与它的父控件之间进行相互通信
    WM_CONTEXTMENU = $007B;
    当用户某个窗口中点击了一下右键就发送此消息给这个窗口
    WM_STYLECHANGING = $007C;
    当调用SETWINDOWLONG函数将要改变一个或多个 窗口的风格时发送此消息给那个窗口
    WM_STYLECHANGED = $007D;
    当调用SETWINDOWLONG函数一个或多个 窗口的风格后发送此消息给那个窗口
    WM_DISPLAYCHANGE = $007E;
    当显示器的分辨率改变后发送此消息给所有的窗口
    WM_GETICON = $007F;
    此消息发送给某个窗口来返回与某个窗口有关连的大图标或小图标的句柄;
    WM_SETICON = $0080;
    程序发送此消息让一个新的大图标或小图标与某个窗口关联;
    WM_NCCREATE = $0081;
    当某个窗口第一次被创建时,此消息在WM_CREATE消息发送前发送;
    WM_NCDESTROY = $0082;
    此消息通知某个窗口,非客户区正在销毁
    WM_NCCALCSIZE = $0083;
    当某个窗口的客户区域必须被核算时发送此消息
    WM_NCHITTEST = $0084;//移动鼠标,按住或释放鼠标时发生
    WM_NCPAINT = $0085;
    程序发送此消息给某个窗口当它(窗口)的框架必须被绘制时;
    WM_NCACTIVATE = $0086;
    此消息发送给某个窗口 仅当它的非客户区需要被改变来显示是激活还是非激活状态;
    WM_GETDLGCODE = $0087;
    发送此消息给某个与对话框程序关联的控件,widdows控制方位键和TAB键使输入进入此控件
    通过响应WM_GETDLGCODE消息,应用程序可以把他当成一个特殊的输入控件并能处理它
    WM_NCMOUSEMOVE = $00A0;
    当光标在一个窗口的非客户区内移动时发送此消息给这个窗口 file://非客户区为:窗体的标题栏及窗
    的边框体
    WM_NCLBUTTONDOWN = $00A1;
    当光标在一个窗口的非客户区同时按下鼠标左键时提交此消息
    WM_NCLBUTTONUP = $00A2;
    当用户释放鼠标左键同时光标某个窗口在非客户区十发送此消息;
    WM_NCLBUTTONDBLCLK = $00A3;
    当用户双击鼠标左键同时光标某个窗口在非客户区十发送此消息
    WM_NCRBUTTONDOWN = $00A4;
    当用户按下鼠标右键同时光标又在窗口的非客户区时发送此消息
    WM_NCRBUTTONUP = $00A5;
    当用户释放鼠标右键同时光标又在窗口的非客户区时发送此消息
    WM_NCRBUTTONDBLCLK = $00A6;
    当用户双击鼠标右键同时光标某个窗口在非客户区十发送此消息
    WM_NCMBUTTONDOWN = $00A7;
    当用户按下鼠标中键同时光标又在窗口的非客户区时发送此消息
    WM_NCMBUTTONUP = $00A8;
    当用户释放鼠标中键同时光标又在窗口的非客户区时发送此消息
    WM_NCMBUTTONDBLCLK = $00A9;
    当用户双击鼠标中键同时光标又在窗口的非客户区时发送此消息
    WM_KEYFIRST = $0100;
    WM_KEYDOWN = $0100;
    file://按下一个键
    WM_KEYUP = $0101;
    file://释放一个键
    WM_CHAR = $0102;
    file://按下某键,并已发出WM_KEYDOWN, WM_KEYUP消息
    WM_DEADCHAR = $0103;
    当用translatemessage函数翻译WM_KEYUP消息时发送此消息给拥有焦点的窗口
    WM_SYSKEYDOWN = $0104;
    当用户按住ALT键同时按下其它键时提交此消息给拥有焦点的窗口;
    WM_SYSKEYUP = $0105;
    当用户释放一个键同时ALT 键还按着时提交此消息给拥有焦点的窗口
    WM_SYSCHAR = $0106;
    当WM_SYSKEYDOWN消息被TRANSLATEMESSAGE函数翻译后提交此消息给拥有焦点的窗口
    WM_SYSDEADCHAR = $0107;
    当WM_SYSKEYDOWN消息被TRANSLATEMESSAGE函数翻译后发送此消息给拥有焦点的窗口
    WM_KEYLAST = $0108;
    WM_INITDIALOG = $0110;
    在一个对话框程序被显示前发送此消息给它,通常用此消息初始化控件和执行其它任务
    WM_COMMAND = $0111;
    当用户选择一条菜单命令项或当某个控件发送一条消息给它的父窗口,一个快捷键被翻译
    WM_SYSCOMMAND = $0112;
    当用户选择窗口菜单的一条命令或当用户选择最大化或最小化时那个窗口会收到此消息
    WM_TIMER = $0113; file://发生了定时器事件
    WM_HSCROLL = $0114;
    当一个窗口标准水平滚动条产生一个滚动事件时发送此消息给那个窗口,也发送给拥有它的控件
    WM_VSCROLL = $0115;
    当一个窗口标准垂直滚动条产生一个滚动事件时发送此消息给那个窗口也,发送给拥有它的控件 WM_INITMENU = $0116;
    当一个菜单将要被激活时发送此消息,它发生在用户菜单条中的某项或按下某个菜单键,它允许程序在显示前更改菜单
    WM_INITMENUPOPUP = $0117;
    当一个下拉菜单或子菜单将要被激活时发送此消息,它允许程序在它显示前更改菜单,而不要改变全部
    WM_MENUSELECT = $011F;
    当用户选择一条菜单项时发送此消息给菜单的所有者(一般是窗口)
    WM_MENUCHAR = $0120;
    当菜单已被激活用户按下了某个键(不同于加速键),发送此消息给菜单的所有者;
    WM_ENTERIDLE = $0121;
    当一个模态对话框或菜单进入空载状态时发送此消息给它的所有者,一个模态对话框或菜单进入空载状态就是在处理完一条或几条先前的消息后没有消息它的列队中等待
    WM_MENURBUTTONUP = $0122;
    WM_MENUDRAG = $0123;
    WM_MENUGETOBJECT = $0124;
    WM_UNINITMENUPOPUP = $0125;
    WM_MENUCOMMAND = $0126;
    WM_CHANGEUISTATE = $0127;
    WM_UPDATEUISTATE = $0128;
    WM_QUERYUISTATE = $0129;
    WM_CTLCOLORMSGBOX = $0132;
    在windows绘制消息框前发送此消息给消息框的所有者窗口,通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置消息框的文本和背景颜色
    WM_CTLCOLOREDIT = $0133;
    当一个编辑型控件将要被绘制时发送此消息给它的父窗口;通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置编辑框的文本和背景颜色
    WM_CTLCOLORLISTBOX = $0134;
    当一个列表框控件将要被绘制前发送此消息给它的父窗口;通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置列表框的文本和背景颜色
    WM_CTLCOLORBTN = $0135;
    当一个按钮控件将要被绘制时发送此消息给它的父窗口;通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置按纽的文本和背景颜色
    WM_CTLCOLORDLG = $0136;
    当一个对话框控件将要被绘制前发送此消息给它的父窗口;通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置对话框的文本背景颜色
    WM_CTLCOLORSCROLLBAR= $0137;
    当一个滚动条控件将要被绘制时发送此消息给它的父窗口;通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置滚动条的背景颜色
    WM_CTLCOLORSTATIC = $0138;
    当一个静态控件将要被绘制时发送此消息给它的父窗口;通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置静态控件的文本和背景颜色
    WM_MOUSEFIRST = $0200;
    WM_MOUSEMOVE = $0200;
    // 移动鼠标
    WM_LBUTTONDOWN = $0201;
    file://按下鼠标左键
    WM_LBUTTONUP = $0202;
    file://释放鼠标左键
    WM_LBUTTONDBLCLK = $0203;
    file://双击鼠标左键
    WM_RBUTTONDOWN = $0204;
    file://按下鼠标右键
    WM_RBUTTONUP = $0205;
    file://释放鼠标右键
    WM_RBUTTONDBLCLK = $0206;
    file://双击鼠标右键
    WM_MBUTTONDOWN = $0207;
    file://按下鼠标中键
    WM_MBUTTONUP = $0208;
    file://释放鼠标中键
    WM_MBUTTONDBLCLK = $0209;
    file://双击鼠标中键
    WM_MOUSEWHEEL = $020A;
    当鼠标轮子转动时发送此消息个当前有焦点的控件
    WM_MOUSELAST = $020A;
    WM_PARENTNOTIFY = $0210;
    当MDI子窗口被创建或被销毁,或用户按了一下鼠标键而光标在子窗口上时发送此消息给它的父窗口
    WM_ENTERMENULOOP = $0211;
    发送此消息通知应用程序的主窗口that已经进入了菜单循环模式
    WM_EXITMENULOOP = $0212;
    发送此消息通知应用程序的主窗口that已退出了菜单循环模式
    WM_NEXTMENU = $0213;
    WM_SIZING = 532;
    当用户正在调整窗口大小时发送此消息给窗口;通过此消息应用程序可以监视窗口大小和位置也可以修改他们
    WM_CAPTURECHANGED = 533;
    发送此消息 给窗口当它失去捕获的鼠标时;
    WM_MOVING = 534;
    当用户在移动窗口时发送此消息,通过此消息应用程序可以监视窗口大小和位置也可以修改他们;
    WM_POWERBROADCAST = 536;
    此消息发送给应用程序来通知它有关电源管理事件;
    WM_DEVICECHANGE = 537;
    当设备的硬件配置改变时发送此消息给应用程序或设备驱动程序
    WM_IME_STARTCOMPOSITION = $010D;
    WM_IME_ENDCOMPOSITION = $010E;
    WM_IME_COMPOSITION = $010F;
    WM_IME_KEYLAST = $010F;
    WM_IME_SETCONTEXT = $0281;
    WM_IME_NOTIFY = $0282;
    WM_IME_CONTROL = $0283;
    WM_IME_COMPOSITIONFULL = $0284;
    WM_IME_SELECT = $0285;
    WM_IME_CHAR = $0286;
    WM_IME_REQUEST = $0288;
    WM_IME_KEYDOWN = $0290;
    WM_IME_KEYUP = $0291;
    WM_MDICREATE = $0220;
    应用程序发送此消息给多文档的客户窗口来创建一个MDI 子窗口
    WM_MDIDESTROY = $0221;
    应用程序发送此消息给多文档的客户窗口来关闭一个MDI 子窗口
    WM_MDIACTIVATE = $0222;
    应用程序发送此消息给多文档的客户窗口通知客户窗口激活另一个MDI子窗口,当客户窗口收到此消息后,它发出WM_MDIACTIVE消息给MDI子窗口(未激活)激活它;
    WM_MDIRESTORE = $0223;
    程序 发送此消息给MDI客户窗口让子窗口从最大最小化恢复到原来大小
    WM_MDINEXT = $0224;
    程序 发送此消息给MDI客户窗口激活下一个或前一个窗口
    WM_MDIMAXIMIZE = $0225;
    程序发送此消息给MDI客户窗口来最大化一个MDI子窗口;
    WM_MDITILE = $0226;
    程序 发送此消息给MDI客户窗口以平铺方式重新排列所有MDI子窗口
    WM_MDICASCADE = $0227;
    程序 发送此消息给MDI客户窗口以层叠方式重新排列所有MDI子窗口
    WM_MDIICONARRANGE = $0228;
    程序 发送此消息给MDI客户窗口重新排列所有最小化的MDI子窗口
    WM_MDIGETACTIVE = $0229;
    程序 发送此消息给MDI客户窗口来找到激活的子窗口的句柄
    WM_MDISETMENU = $0230;
    程序 发送此消息给MDI客户窗口用MDI菜单代替子窗口的菜单
    WM_ENTERSIZEMOVE = $0231;
    WM_EXITSIZEMOVE = $0232;
    WM_DROPFILES = $0233;
    WM_MDIREFRESHMENU = $0234;
    WM_MOUSEHOVER = $02A1;
    WM_MOUSELEAVE = $02A3;
    WM_CUT = $0300;
    程序发送此消息给一个编辑框或combobox来删除当前选择的文本
    WM_COPY = $0301;
    程序发送此消息给一个编辑框或combobox来复制当前选择的文本到剪贴板
    WM_PASTE = $0302;
    程序发送此消息给editcontrol或combobox从剪贴板中得到数据
    WM_CLEAR = $0303;
    程序发送此消息给editcontrol或combobox清除当前选择的内容;
    WM_UNDO = $0304;
    程序发送此消息给editcontrol或combobox撤消最后一次操作
    WM_RENDERFORMAT = $0305;

    WM_RENDERALLFORMATS = $0306;
    WM_DESTROYCLIPBOARD = $0307;
    当调用ENPTYCLIPBOARD函数时 发送此消息给剪贴板的所有者
    WM_DRAWCLIPBOARD = $0308;
    当剪贴板的内容变化时发送此消息给剪贴板观察链的第一个窗口;它允许用剪贴板观察窗口来
    显示剪贴板的新内容;
    WM_PAINTCLIPBOARD = $0309;
    当剪贴板包含CF_OWNERDIPLAY格式的数据并且剪贴板观察窗口的客户区需要重画;
    WM_VSCROLLCLIPBOARD = $030A;
    WM_SIZECLIPBOARD = $030B;
    当剪贴板包含CF_OWNERDIPLAY格式的数据并且剪贴板观察窗口的客户区域的大小已经改变是此消息通过剪贴板观察窗口发送给剪贴板的所有者;
    WM_ASKCBFORMATNAME = $030C;
    通过剪贴板观察窗口发送此消息给剪贴板的所有者来请求一个CF_OWNERDISPLAY格式的剪贴板的名字
    WM_CHANGECBCHAIN = $030D;
    当一个窗口从剪贴板观察链中移去时发送此消息给剪贴板观察链的第一个窗口;
    WM_HSCROLLCLIPBOARD = $030E;
    此消息通过一个剪贴板观察窗口发送给剪贴板的所有者 ;它发生在当剪贴板包含CFOWNERDISPALY格式的数据并且有个事件在剪贴板观察窗的水平滚动条上;所有者应滚动剪贴板图象并更新滚动条的值;
    WM_QUERYNEWPALETTE = $030F;
    此消息发送给将要收到焦点的窗口,此消息能使窗口在收到焦点时同时有机会实现他的逻辑调色板
    WM_PALETTEISCHANGING= $0310;
    当一个应用程序正要实现它的逻辑调色板时发此消息通知所有的应用程序
    WM_PALETTECHANGED = $0311;
    此消息在一个拥有焦点的窗口实现它的逻辑调色板后发送此消息给所有顶级并重叠的窗口,以此来改变系统调色板
    WM_HOTKEY = $0312;
    当用户按下由REGISTERHOTKEY函数注册的热键时提交此消息
    WM_PRINT = 791;
    应用程序发送此消息仅当WINDOWS或其它应用程序发出一个请求要求绘制一个应用程序的一部分;
    WM_PRINTCLIENT = 792;
    WM_HANDHELDFIRST = 856;
    WM_HANDHELDLAST = 863;
    WM_PENWINFIRST = $0380;
    WM_PENWINLAST = $038F;
    WM_COALESCE_FIRST = $0390;
    WM_COALESCE_LAST = $039F;
    WM_DDE_FIRST = $03E0;
    WM_DDE_INITIATE = WM_DDE_FIRST + 0;
    一个DDE客户程序提交此消息开始一个与服务器程序的会话来响应那个指定的程序和主题名;
    WM_DDE_TERMINATE = WM_DDE_FIRST + 1;
    一个DDE应用程序(无论是客户还是服务器)提交此消息来终止一个会话;
    WM_DDE_ADVISE = WM_DDE_FIRST + 2;
    一个DDE客户程序提交此消息给一个DDE服务程序来请求服务器每当数据项改变时更新它
    WM_DDE_UNADVISE = WM_DDE_FIRST + 3;
    一个DDE客户程序通过此消息通知一个DDE服务程序不更新指定的项或一个特殊的剪贴板格式的项
    WM_DDE_ACK = WM_DDE_FIRST + 4;
    此消息通知一个DDE(动态数据交换)程序已收到并正在处理WM_DDE_POKE, WM_DDE_EXECUTE, WM_DDE_DATA, WM_DDE_ADVISE, WM_DDE_UNADVISE, or WM_DDE_INITIAT消息
    WM_DDE_DATA = WM_DDE_FIRST + 5;
    一个DDE服务程序提交此消息给DDE客户程序来传递个一数据项给客户或通知客户的一条可用数据项
    WM_DDE_REQUEST = WM_DDE_FIRST + 6;
    一个DDE客户程序提交此消息给一个DDE服务程序来请求一个数据项的值;
    WM_DDE_POKE = WM_DDE_FIRST + 7;
    一个DDE客户程序提交此消息给一个DDE服务程序,客户使用此消息来请求服务器接收一个未经同意的数据项;服务器通过答复WM_DDE_ACK消息提示是否它接收这个数据项;
    WM_DDE_EXECUTE = WM_DDE_FIRST + 8;
    一个DDE客户程序提交此消息给一个DDE服务程序来发送一个字符串给服务器让它象串行命令一样被处理,服务器通过提交WM_DDE_ACK消息来作回应;
    WM_DDE_LAST = WM_DDE_FIRST + 8;
    WM_APP = $8000;
    WM_USER = $0400;
    此消息能帮助应用程序自定义私有消息;
    /
    通知消息(Notificationmessage)是指这样一种消息,一个窗口内的子控件发生了一些事情,需要通知父窗口。通知消息只适用于标准的窗口控件如按钮、列表框、组合框、编辑框,以及Windows95公共控件如树状视图、列表视图等。例如,单击或双击一个控件、在控件中选择部分文本、操作控件的滚动条都会产生通知消息。
    按扭
    BN_CLICKEDfile://用户单击了按钮
    BN_DISABLEfile://按钮被禁止
    BN_DOUBLECLICKEDfile://用户双击了按钮
    BN_HILITEfile://用户加亮了按钮
    BN_PAINT按钮应当重画
    BN_UNHILITE加亮应当去掉
    组合框
    CBN_CLOSEUP组合框的列表框被关闭
    CBN_DBLCLK用户双击了一个字符串
    CBN_DROPDOWN组合框的列表框被拉出
    CBN_EDITCHANGE用户修改了编辑框中的文本
    CBN_EDITUPDATE编辑框内的文本即将更新
    CBN_ERRSPACE组合框内存不足
    CBN_KILLFOCUS组合框失去输入焦点
    CBN_SELCHANGE在组合框中选择了一项
    CBN_SELENDCANCEL用户的选择应当被取消
    CBN_SELENDOK用户的选择是合法的
    CBN_SETFOCUS组合框获得输入焦点
    编辑框
    EN_CHANGE编辑框中的文本己更新
    EN_ERRSPACE编辑框内存不足
    EN_HSCROLL用户点击了水平滚动条
    EN_KILLFOCUS编辑框正在失去输入焦点
    EN_MAXTEXT插入的内容被截断
    EN_SETFOCUS编辑框获得输入焦点
    EN_UPDATE编辑框中的文本将要更新
    EN_VSCROLL用户点击了垂直滚动条消息含义
    列表框
    LBN_DBLCLK用户双击了一项
    LBN_ERRSPACE列表框内存不够
    LBN_KILLFOCUS列表框正在失去输入焦点
    LBN_SELCANCEL选择被取消
    LBN_SELCHANGE选择了另一项
    LBN_SETFOCUS列表框获得输入焦点
    展开全文
  • 如何在Bash中编写循环? 使用for循环和find命令自动对多个文件执行一组操作。 人们想要学习Unix shell的一个常见原因是释放批处理的功能。如果要对许多文件执行某些操作,一种方法是构造一个遍历这些文件的命令来...

    如何在Bash中编写循环?

    使用for循环和find命令自动对多个文件执行一组操作。

    人们想要学习Unix shell的一个常见原因是释放批处理的功能。如果要对许多文件执行某些操作,一种方法是构造一个遍历这些文件的命令来实现。在编程术语中,这称为执行控制,最常见的示例之一是for循环。

    for循环是一个配方,详细说明了您希望计算机对指定的每个数据对象(例如文件)执行什么操作。

    经典的循环

    Linux终端适用于Linux的7大终端仿真器用于Linux中进行数据分析的10个命令行工具立即下载:SSH备忘单高级Linux命令备忘单Linux命令行教程一个简单的循环是分析文件集合的循环。这本身可能不是一个有用的循环,但它是一种安全的方法,可以向您证明自己有能力分别处理目录中的每个文件。首先,通过创建目录并将一些文件的某些副本放入其中来创建一个简单的测试环境。一开始的时候使用任何文件都可以,但是以后的示例需要图形文件(例如JPEG、PNG或类似文件)。您可以使用文件管理器或在终端中创建文件夹并将文件复制到其中:

    •  
    •  
    $ mkdir example        $ cp ~/Pictures/vacation/*.{png,jpg} example

    将目录更改为新文件夹,然后列出其中的文件以确认测试环境符合您的期望:

    •  
    •  
    •  
    •  
    •  
    •  
    $ cd example$ ls -1cat.jpgdesign_maori.pngotago.jpgwaterfall.png

    在一个循环中逐个遍历每个文件的语法是:创建一个变量。然后定义您要变量循环通过的数据集。在这种情况下,请使用通配符循环浏览当前目录中的所有文件(通配符匹配所有内容)。然后以分号(;)终止此介绍性子句。

    •  
    $ for f in * ;

    根据您的喜好,您可以选择按此处返回。在语法上完成之前,shell不会尝试执行循环。

    接下来,定义您希望在每次循环迭代中发生的事情。为简单起见,请使用file命令获取有关每个文件的少量数据,这些数据由f变量表示(但是以$开头,告诉shell将变量的值替换为当前包含的变量):

    •  
    do file $f ;

    用另一个分号终止子句并关闭循环:

    •  
    done

    做完了按Return键可启动Shell循环遍历当前目录中的所有内容。for循环将每个文件一个一个地分配给变量f,然后运行命令:

    •  
    •  
    •  
    •  
    •  
    •  
    •  
    $ for f in * ; do        > file $f ;        > done        cat.jpg: JPEG image data, EXIF standard 2.2        design_maori.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced        otago.jpg: JPEG image data, EXIF standard 2.2        waterfall.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced

    您也可以这样写:

    •  
    •  
    •  
    •  
    •  
    $ for f in *; do file $f; done        cat.jpg: JPEG image data, EXIF standard 2.2        design_maori.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced        otago.jpg: JPEG image data, EXIF standard 2.2        waterfall.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced

    多行和单行格式对于您的外壳都是相同的,并且产生完全相同的结果。

    一个实际的例子

     

    这是一个循环如何对日常计算有用的实际示例。假设您有要发送给朋友的度假照片集。您的照片文件很大,太大而无法通过电子邮件发送,并且不便上传到您的照片共享服务。您想为照片创建较小的网络版本,但是您有100张照片,不想浪费时间一张一张地缩小每张照片。

    首先,在Linux,BSD或Mac上使用包管理器安装ImageMagick命令。例如,在Fedora和RHEL上:

    •  
    $ sudo dnf install ImageMagick

    在Ubuntu或Debian上:

    •  
    $ sudo apt install ImageMagick

    在BSD上,使用端口或pkgsrc。在Mac上,使用Homebrew或MacPorts。

    安装ImageMagick后,您将拥有一组用于对照片进行操作的新命令。

    为您要创建的文件创建目标目录:

    •  
    $ mkdir tmp

    要将每张照片缩小到其原始大小的33%,请尝试以下循环:

    •  
    $ for f in * ; do convert $f -scale 33% tmp/$f ; done

    然后在tmp文件夹中查看缩放后的照片。

    您可以在循环中使用任意数量的命令,因此,如果您需要对一批文件执行复杂的操作,则可以将整个工作流放在for循环的do和done语句之间。例如,假设您要将每张处理过的照片直接复制到Web主机上的共享照片目录,并从本地系统中删除照片文件:

    •  
    •  
    •  
    •  
    •  
    $ for f in * ; do    convert $f -scale 33% tmp/$f    scp -i seth_web tmp/$f seth@example.com:~/public_html    trash tmp/$f ;  done

    做完了对于for循环处理的每个文件,您的计算机将自动运行三个命令。这意味着,如果您仅以这种方式处理10张照片,则可以为自己节省30条命令,还会节省同样多的时间。

    限制循环

    并不一定总是要查看每个文件。您可能只想处理示例目录中的JPEG文件:

    •  
    •  
    •  
    $ for f in *.jpg ; do convert $f -scale 33% tmp/$f ; done$ ls -m tmpcat.jpg, otago.jpg

    做完了 ls -m tmpcat.jpg,otago.jpg或者,您可能需要重复执行特定次数的操作,而不是处理文件。for循环的变量由您提供的任何数据定义,因此您可以创建一个循环访问迭代数字而不是文件的循环:

    •  
    •  
    •  
    •  
    •  
    •  
    $ for n in {0..4}; do echo $n ; done01234

    更多的循环

    您现在已经足够了解创建自己的循环了。在对循环感到满意之前,请在要处理的文件副本上使用它们,并尽可能多地使用带有内置保护措施的命令,以防止您破坏数据并造成不可弥补的错误,例如意外重命名整个文件,相同名称的文件目录,彼此覆盖。

    有关高级for循环主题,请继续阅读。

    并非所有的shell都是Bash

    for关键字内置在Bash shell中。许多相似的shell使用相同的关键字和语法,但是某些shell(例如tcsh)使用不同的关键字(例如foreach)来代替。

    在tcsh中,语法本质上相似,但比Bash严格。在以下代码示例中,是否不键入字符串foreach?在第2行和第3行中。它是辅助提示,提醒您仍在构建循环的过程中。

    •  
    •  
    •  
    •  
    •  
    •  
    •  
    $ foreach f (*)foreach? file $fforeach? endcat.jpg: JPEG image data, EXIF standard 2.2design_maori.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlacedotago.jpg: JPEG image data, EXIF standard 2.2waterfall.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced

    在tcsh中,foreach和end都必须单独出现在单独的行中,因此不能像使用Bash和类似的shell那样在一行上创建for循环。

    使用find命令执行for循环

    从理论上讲,您可能会发现一个不提供for循环函数的shell,或者您可能只是更喜欢使用带有附加功能的其他命令。

    find命令是实现for循环功能的另一种方法,因为它提供了几种方法来定义要包含在循环中的文件范围以及并行处理选项。

    find命令旨在帮助您在硬盘驱动器上查找文件。它的语法很简单:您提供要搜索的位置的路径,并找到所有文件和目录:

    •  
    •  
    •  
    •  
    •  
    •  
    $ find .../cat.jpg./design_maori.png./otago.jpg./waterfall.png

    你可以通过添加name的一部分来过滤搜索结果:

    •  
    •  
    •  
    $ find . -name "*jpg"./cat.jpg./otago.jpg

    find的优点在于,可以使用-exec标志将找到的每个文件输入到循环中。例如,要仅缩小示例目录中的PNG照片,请执行以下操作:

    •  
    •  
    •  
    $ find . -name "*png" -exec convert {} -scale 33% tmp/{} \;$ ls -m tmpdesign_maori.png, waterfall.png

    在-exec子句中,括号字符{}代表正在处理的任何项(换句话说,已定位的任何以PNG结尾的文件,一次一个)。-exec子句必须以分号终止,但是Bash通常尝试自行使用分号。使用反斜杠(\;)“转义”分号,以便find知道将分号视为其终止字符。

    find命令非常擅长于其功能,有时它可能太好了。例如,如果重复使用它来查找另一个照片处理的PNG文件,则会出现一些错误:

    •  
    •  
    •  
    •  
    $ find . -name "*png" -exec convert {} -flip -flop tmp/{} \;   convert: unable to open image `tmp/./tmp/design_maori.png':No such file or directory @ error/blob.c/OpenBlob/2643....

    似乎find找到了所有的PNG文件-不仅是当前目录(。)中的文件,还包括您之前处理过并放在tmp子目录中的文件。在某些情况下,您可能想要搜索当前目录以及其中的所有其他目录(以及其中的所有目录)。它可以是功能强大的递归处理工具,尤其是在复杂的文件结构中(例如,音乐艺术家的目录中包含充满音乐文件的专辑目录),但是您可以使用-maxdepth选项对其进行限制。

    只查找当前目录下的PNG文件(不包括子目录):

    •  
    $ find . -maxdepth 1 -name "*png"

    要在当前目录以及其他子目录级别中查找和处理文件,请将最大深度增加1:

    •  
    $ find . -maxdepth 2 -name "*png"

    它的默认值是进入所有子目录。

    小延伸

    使用循环的次数越多,节省的时间和精力就越多,可以处理的任务也就越大。您只是一个用户,但是经过深思熟虑的循环,您可以使计算机完成艰苦的工作。

    您可以并且应该像对待其他任何命令一样对待循环,以便在需要对多个文件重复执行一个或两个操作时可以将其放在手边。但是,它也是进行认真编程的合法途径,因此,如果您必须对任意数量的文件执行复杂的任务,请抽出一些时间来计划工作流程。如果您可以在一个文件上实现目标,那么将该可重复过程包装在for循环中是相对简单的,并且唯一需要的“编程”是了解变量的工作方式以及足够的组织以将未处理的文件与已处理的文件分开。只需做一些练习,您就可以从一个Linux用户转移到知道如何编写循环的Linux用户!

     

    Shell脚本关于循环的一些总结

    不管是哪一门计算机语言,循环都是不可绕开的一个话题,Shell 当然也不是例外。下面总结一些 Shell 脚本里常用的循环相关的知识点,新手朋友可以参考。

    for 循环

    Shell 脚本里最简单的循环当属 for 循环,有编程基础的朋友应该都有使用过 for 循环。最简单的 for 循环如下所示,你只需将变量值依次写在 in 后面即可:

    #!/bin/bash
    
    for num in 1 2 3 4
    do
        echo $num
    done
    

    如果要循环的内容是字母表里的连续字母或连续数字,那么就可以按以下语法来写脚本:

    #!/bin/bash
    
    for x in {a..z}
    do
        echo $x
    done

    while 循环

    除了 for 循环,Shell 同样提供了 while 循环。对于其它语言,如果你见过 for 循环却没见过 while 循环,那么你一定是学了个假语言。

    在 while 循环里,每进行一次循环,条件都会被判断一次,来确定本次循环是否该继续。其实在循环次数比较少的情况下,for 循环与 while 循环效果差不多,但如果循环次数比较多,比如 10 万次,那么 while 循环的优势就体现出来了。

    #!/bin/bash
    
    n=1
    
    while [ $n -le 4 ]
    do
        echo $n
        ((n++))
    done

    循环套循环

    像其它高级语言一样,循环是可以互相嵌套的。比如下面这个例子,我们在 while 循环里再套入一个 for 循环:

    #!/bin/bash
    
    n=1
    
    while [ $n -lt 6 ]
    do
        for l in {a..d}
        do
            echo $n$l
        done
        ((n++))
    done
    

    这个脚本执行的结果应该是 1a, 1b, 1c, 1d, 2a, 2b … 5d。

    循环的内容是变化的

    我们上面提到的 for 循环,循环变量要赋的值都列在了 in 后面的列表里了。但这样灵活性太差,因为在很多情况下,循环变量要获得的值是不固定的。

    就比如,有个变量要获得当前系统上所有用户,但因为每台电脑用户都不一样,我们根本就没办法将这个变量写死。

    在这种情况下,我们可以使用 ls 命令将 /home 目录下所有用户都列出来,然后用循环变量依次获取它们。完整代码如下:

    #!/bin/bash
    
    for user in `ls /home`
    do
        echo $user
    done
    

    当然,除了 ls ,Shell 还支持其它命令。比如我们可以使用 date 命令获取当前系统时间,再依次打印出来:

    $ for word in `date`
    > do
    >     echo $word
    > done
    Thu
    Apr
    9
    08:12:09
    CST
    2020

    变量值检查

    我们在使用 while 循环时,经常需要判断一个变量的值是否大于或者小于某个数。有时候这个数也是用另一个变量来表示,那么我们就需要判断这个变量的值是否是数字。有三种判断方法:

    #!/bin/bash
    
    echo -n "How many times should I say hello? "
    read ans
    
    if [ "$ans" -eq "$ans" ]; then
        echo ok1
    fi
    
    if [[ $ans = *[[:digit:]]* ]]; then
        echo ok2
    fi
    
    if [[ "$ans" =~ ^[0-9]+$ ]]; then
        echo ok3
    fi
    

    第一种方法看起来似乎是个废话,但实际上,-eq 只能用于数值间判断,如果是字符串则判断不通过,所以这就保证了 ans 是个数值型变量。

    第二种方法是直接使用 Shell 的通配符对变量进行判断。

    第三种方法就更直接了,使用正则表达式对变量进行判断。

    我们直接来看一个例子:

    #!/bin/bash
    
    echo -n "How many times should I say hello? "
    read ans
    
    if [ "$ans" -eq "$ans" ]; then
      n=1
      while [ $n -le $ans ]
      do
        echo hello
        ((n++))
      done
    fi
    

    在这个脚本里,我将要循环的次数传入到 ans 变量,然后脚本就具体打印几次 hello 。为了保证我们传入的内容是数字,我们使用了 if [ "$ans" -eq "$ans" ] 语句来判断。如果我们传入的不是数字,则不会进入 while 循环。

    循环输出文本文件内容

    如果你想按行依次循环输出文本文件的内容,可以这样操作:

    #!/bin/bash
    
    echo -n "File> "
    read file
    n=0
    
    while read line; do
      ((n++))
      echo "$n: $line"
    done < $file
    

    在这里,我们使用 read 命令将文本文件的内容读取存入 file 变量,然后再使用重定向(上述脚本最后一行)将 file 内容依次传入 while 循环处理再打印出来。

    死循环

    有时候我们需要一直永远循环做某件事,那么我们就可以使用死循环。达到这个目的很简单,只需使用while true 即可。

    #!/bin/bash
    
    while true
    do
        echo -n "Still running at "
        date
        sleep 1
    done
    

    在以上这个脚本里,将每隔 1 秒打印一次 Still running at 具体时间 ,直到你按 Ctrl + C 终止这个脚本。

    END

    展开全文
  • 栈、队列及循环队列

    千次阅读 2018-07-21 22:18:37
    栈,队列及循环队列,是数据结构中的重要的且常用的内容,最近在学习数据结构,于是作一篇笔记记录一下,希望以后复习有帮助  1.栈:  栈是限定仅在表尾进行插入或删除操作的线性表。我们把表尾称作栈顶(top),...

      栈,队列及循环队列,是数据结构中的重要的且常用的内容,最近在学习数据结构,于是作一篇笔记记录一下,希望以后复习有帮助

      1.栈:

        栈是限定仅在表尾进行插入或删除操作的线性表。我们把表尾称作栈顶(top),相应的,表头称作栈底(bottom),由栈的特性可知,栈是一种后进先出(LIFO)的线性表,下面对栈的建立、基本操作及注意事项做一个说明:

    栈的图示

        上图详细的勾勒出了栈的四种基本形态,通过观察我们知道,非空栈的top指针永远指向栈顶的后一位,并且base指针的位置保持不变,事实上,base指针除了在创建栈、扩容栈的时候被直接调用外,它都是不变的,这是因为我们不能直接对栈底元素进行操作,所以改变bese的指向之类的行为是毫无意义的。当我们注意看第四个图,发现栈已经满了,top指针溢出有效位置,实际上这种情况是不被允许的,对计算机随机位置的内存空间进行调用是不被推荐的,因此我们为了让栈保持功能,选择了对其进行扩容处理,就是在栈满时使用realloc()对其重新进行空间分配,确保不会有指针溢出的情况。

        基于上面的原理,我们不难想到实现思路:

        1.规定top、base指针,并确定栈的初始大小stacksize,将这三者封装为一个整体MyStack;

        2.初始化一个栈,使用malloc()动态分配空间,注意空间大小是stacksize × sizeof(ElemType),即元素类型大小×元素个数,并      将top和base指针同时指向这片空间的存储首地址;

        3.元素入栈先判断是否满栈,是就扩容,否就让元素入栈,并且让top指向下一个位置;

        4.元素出栈先判断是否空栈,是就退出,否就让元素出栈,并且让top指向上一个位置;

        5.元素空栈的充要条件是top = base; 满栈是top >= base+stacksize;

        6.清空栈即是让top = base; 销毁栈则是free(base),top = base =NULL;

        7.可以通过top指针来遍历栈;

        8.top - base的值即是栈长度;

      下面给出代码:

    #include<iostream>
    #include<cstdlib>
    #define InitSize 100    //初始大小;
    #define DLC 10  //增量;
    using namespace std;
    
    typedef struct  Stack
    {
        char *base;
        char *top;
        int stacksize;
    }MyStack;
    
    bool InitStack(MyStack &S)  //初始化;
    {
        S.base = (char *)malloc(InitSize*sizeof (char));
        if(!S.base) return false;   //内存分配失败;
        S.top = S.base;
        S.stacksize = InitSize;
        return true;
    }
    
    bool IsEmpty(MyStack &S)    //判空;
    {
        if(S.base == S.top) return true;
        return false;
    }
    
    bool IsFull(MyStack &S)    //判满;
    {
        if(S.top >= S.base + S.stacksize) return true;
        else return false;
    }
    
    bool GetTop(MyStack &S,char &e)  //取栈顶;
    {
        if(IsEmpty(S))   return false;
        e = *(S.top - 1);
        return true;
    }
    
    bool Push(MyStack &S,char e)   //入栈;
    {
        if(IsFull(S))
        {
            S.base = (char *)realloc(S.base,(S.stacksize + DLC)*sizeof(char));
            if(!S.base) return false;
            S.stacksize += DLC;
        }
        *(S.top++) = e;
        return true;
    }
    
    bool Pop(MyStack &S,char &e)    //出栈;
    {
        if(IsEmpty(S))  return false;
        e = *(--S.top);
        return true;
    }
    
    bool DestoryStack(MyStack S)    //销毁;
    {
    	free(S.base);
    	S.top = S.base = NULL;
    	S.stacksize = 0;
    	return true;
    }
    
    bool ClearStack(MyStack &S)    //清空;
    {
        S.top = S.base;
        return true;
    }
    
    bool Traverse(MyStack S)    //遍历栈;
    {
        while(S.top > S.base)
        {
            cout<<*(--S.top)<<endl;
        }
        return true;
    }
    
    int GetLength(MyStack S)    //取栈长;
    {
        return S.top - S.base;
    }
    
    int main()
    {
        int n;
        char e;
        MyStack S;
        InitStack(S);
        cout<<"Enter the number of Elem(s) you want to Push: "<<endl;
        cin>>n;
        cout<<"Enter each Elem: "<<endl;
        for(int i = 0; i < n; i++)
        {
            cin>>e;
            Push(S,e);
        }
        cout<<"Here is what they look like: "<<endl;
        Traverse(S);
        cout<<"Size: "<<GetLength(S)<<endl;
        cout<<"The top is: ";
        GetTop(S,e);
        cout<<e<<endl;
        cout<<"How many Elem(s) do you want to Pop: "<<endl;
        cin>>n;
        for(int i = 0; i < n; i++)
        {
            if(Pop(S,e))
                continue;
            else
            {
                cout<<"The stack is empty now!"<<endl;
                break;
            }
        }
        if(!IsEmpty(S))
        {
            cout<<"Here is what's left: "<<endl;
            Traverse(S);
            cout<<"Size: "<<GetLength(S)<<endl;
            cout<<"The top is: ";
            GetTop(S,e);
            cout<<e<<endl;
        }
        if(ClearStack(S))   cout<<"The stack has been cleared."<<endl;
        Traverse(S);    //验证是否清空;
        if(DestoryStack(S)) cout<<"The stack has been destoryed."<<endl;
        Traverse(S);
        return 0;
    }
    
    

      以上代码DLC是每次栈满时增加的容量,代码中的栈包括初始化、判空、判满、清空、销毁、入栈、出栈、遍历、取栈长、取      栈顶等方法,不是很难写,关键是要理解栈的原理,剩下的都是体力活了。

      2.队列:

          和栈相反,队列是一种先进先出(FIFO)的线性表,它只允许在一边插入,而在另一边删除元素,我们把删除的一端称为队首(front),插入的一端称为队尾(rear),下面对队列做一个说明:

    链式队列

          上图很好的反映了队列的几种状态,实际上,形如上图所示的队列叫做链式队列,这是最常用的一种队列,除此之外还有循环队列双端队列,但是双端队列由于其局限性一般推荐不使用,因此不做说明,这里先介绍链式队列:

          显然,跟链表一样,链式队列的基本结构是数据域+指针域,不过多了两个指针——Front和Rear,有了这两个指针的限制,队列就远远不如链表那么灵活,在操作上也有别于链表,下面根据上图,给出实现的思路:

          1.首先,将数据域和指针域封装成Queue,再将两个Queue类型的指针Front和Rear封装在一起;

          2.用malloc()动态分配空间,并用Front和Rear同时指向它;

          3.插入元素的时候,Rear向后指向插入的元素,Front不动;

          4.删除元素的时候,Front向后指向被删除元素的后一个元素,Rear不动;

          5.以上两条均应该注意是否有越界;

          6.队列为空的充要条件是Front = Rear;

        下面根据以上思路写出代码:

    #include<iostream>
    #include<cstdlib>
    using namespace std;
    
    typedef struct QNode
    {
        int data;
        struct QNode *next;
    }QNode,*QueuePtr;
    
    typedef struct
    {
        QueuePtr Front;
        QueuePtr Rear;
    }LinkQueue;
    
    bool InitQueue(LinkQueue &Q)    //初始化队列;
    {
        Q.Front = Q.Rear = (QueuePtr)malloc(sizeof(QNode));
        if(!Q.Front)    return false;   //内存分配失败;
        Q.Front->next = NULL;
        return true;
    }
    
    bool DestoryQueue(LinkQueue &Q)    //销毁;
    {
         while(Q.Front->next != NULL)
         {
            Q.Rear = Q.Front->next;
            free(Q.Front);
            Q.Front = Q.Rear;
         }
         return true;
    }
    
    bool ClearQueue(LinkQueue &Q)   //清空;
    {
        Q.Rear = Q.Front;
        return true;
    }
    
    bool Push(LinkQueue &Q,int e)   //入队;
    {
        QueuePtr p = (QueuePtr)malloc(sizeof(QNode));
        if(!p) return false;
        p->data = e;
        p->next = NULL;
        Q.Rear->next = p;
        Q.Rear = p;
        p = NULL;
        return true;
    }
    
    bool Pop(LinkQueue &Q,int &e)   //出队;
    {
        if(Q.Front == Q.Rear)   return false;
        e = Q.Front->next->data;
        Q.Front->next = Q.Front->next->next;
        if(Q.Rear == Q.Front->next) Q.Front = Q.Rear;   //空队列;
        return true;
    }
    
    bool GetFront(LinkQueue Q,int &e)   //取队首;
    {
        if(Q.Front == Q.Rear)  return false;
        e = Q.Front->next->data;
        return true;
    }
    
    bool GetLength(LinkQueue Q,int &l)  //取队长;
    {
        int i = 0;
        while(Q.Front->next != NULL)
        {
            Q.Rear = Q.Front->next;
            Q.Front = Q.Rear;
            i++;
        }
        l = i;
    }
    
    bool IsEmpty(LinkQueue Q)   //判空;
    {
        if(Q.Front == Q.Rear) return true;
        return false;
    }
    
    bool Traverse(LinkQueue Q)  //判满;
    {
        if(IsEmpty(Q))   return false;
        while(Q.Front->next != NULL)
        {
            Q.Rear = Q.Front->next;
            cout<<Q.Rear->data<<" ";
            Q.Front = Q.Rear;
        }
        cout<<endl;
        return true;
    }
    
    int main()
    {
        int l,n,e;
        LinkQueue Q;
        InitQueue(Q);
        cout<<"How many elem(s) do you want to push: "<<endl;
        cin>>n;
        cout<<"Enter "<<n<<" Elem(s): "<<endl;
        for(int i = 0; i < n; i++)
        {
            cin>>e;
            Push(Q,e);
        }
        cout<<"Here is what they look like: "<<endl;
        Traverse(Q);
        GetLength(Q,l);
        GetFront(Q,e);
        cout<<"Length: "<<l<<endl;
        cout<<"The head is: "<<e<<endl;
        cout<<"How many elem(s) do you want to pop: "<<endl;
        cin>>n;
        for(int i = 0; i < n; i++)
        {
            if(!IsEmpty(Q))
                Pop(Q,e);
            else break;
        }
        cout<<"Here is what they look like: "<<endl;
        Traverse(Q);
        GetLength(Q,l);
        cout<<"Length: "<<l<<endl;
        if(GetFront(Q,e))
            cout<<"The head is: "<<e<<endl;
        if(ClearQueue(Q)) cout<<"Queue Cleared!"<<endl;
        Traverse(Q);    //验证是否清空;
        if(DestoryQueue(Q)) cout<<"Queue Destoryed!"<<endl;
        Traverse(Q);
        return 0;
    }
    

        相比链表,链式队列的写法或许显得稍微简单一些,关键还是掌握原理而不要死扣细节,细节是在不断修改的痛苦之上建立起来的,而正确理解原理,才是使代码从一开始就正确的唯一途径。

      3.循环队列:

        通过以上代码的剖析,不难发现链式队列是(理论上)可以开辟无限空间的,这些空间是动态分配的,而稍不注意就有可能导致空指针、野指针问题出现,为了避免以上问题,我们可以使用循环队列,即,将队列的大小定死,插入和删除在一个封闭的空间内执行,一旦队列满则不再进行插入,一旦有空闲位置就可以重新插入别的元素,这样可以让空间的使用更加有节制,而不会出现无法控制的情况,但是它的缺点是:空间有限,使用完不能再动态分配空间。这样的队列适合知道具体问题中最大元素个数的时候使用,下面来说明循环队列的原理:

    空循环队列

    非空未满循环队列

    满循环队列

          通过上图,不难发现,循环队列是一个(抽象意义上)闭合的内存空间,Rear每次指向队尾的下一个位置,0位置为其首地址,而循环队列的Front却不一定要指向其首地址(哪怕是在队列空或者是满的时候),因为随着不断有元素入队、出队,Front和Rear的位置在不断发生变化,并不能确保每一次队列空/满的时候Front都在首地址上,并且图中画的是满队列和空队列状态一致(Front = Rear),仅仅是出于好理解的意图,实际上这样是不好分辨队列是满或是空的,一般的做法是,认为Front = Rear时队列为空,Rear 的下一个循环位置(等下会解释)等于Front时为满。下面给出实现思路:

        1.将ElemType型的指针base、int 型的Front、Rear封装成CQueue;

        2.用base指向malloc()新开辟的空间首地址,Front = Rear = 0;

        3.为什么要这么做?因为base引用[]符号,如base[Rear],可以直接调用这一片空间内的第Rear个位置,因此Front和Rear不必是指针类型(封闭空间的好处,和数组是一样的用法);

        4.每次插入元素时,判断队列是否为满,不满则插入,并让Rear指向队尾下一个位置;

        5.Rear指向队尾下一个位置的原因是:每次插入后Rear的值都要+1;空队列时Rear = 0,插入后base[0] = Elem;如果Rear指向插入元素,就不能+1,这样会使得后续步骤受影响/也可以单独处理第一个元素的插入,但很麻烦,并且Rear指向下一个位置还有利于判断队列是否满,如果Rear的下一个位置为Front,则认定它满了,因为Rear已经不能再+1了,否则和Front就相等了,这样循环队列最多存MAX-1个元素(MAX为队列最大长度);

      6.每次删除元素时,判断队列是否为空,不空则删除,并让Front向前移一位;

      7.其余事项和链式队列大致相当;         

    下面写出代码:

    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #define MAX 101  //最多存MAX-1个数据元素;
    using namespace std;
    
    typedef struct CQueue
    {
        char *base;
        int Front;
        int Rear;
    }CQueue;
    
    bool InitCQueue(CQueue &Q)  //初始化;
    {
        Q.base = (char *)malloc(MAX*sizeof(char));
        if(!Q.base) return false;
        memset(Q.base,'@',sizeof Q.base);       //定义'@'代表此处为空;
        Q.Front = Q.Rear = 0;
        return true;
    }
    
    bool DestoryCQueue(CQueue &Q)   //销毁;
    {
        free(Q.base);
        Q.Front = Q.Rear = 0;
        return true;
    }
    
    bool ClearCQueue(CQueue &Q)   //清空;
    {
        memset(Q.base,'@',sizeof Q.base);
        Q.Front = Q.Rear = 0;
        return true;
    }
    
    bool IsEmpty(CQueue Q)  //判空;
    {
        if(Q.Front == Q.Rear) return true;
        return false;
    }
    
    bool IsFull(CQueue Q)   //判满;
    {
        if((Q.Rear+1)%MAX == Q.Front)   return true;
        return false;
    }
    
    bool Push(CQueue &Q,char e)  //入队;
    {
        if(IsFull(Q) || Q.base[Q.Rear] != '@') return false;
        Q.base[Q.Rear] = e;
        Q.Rear = (Q.Rear+1)%MAX;
        return true;
    }
    
    bool Pop(CQueue &Q,char &e)  //出队;
    {
        if(IsEmpty(Q)) return false;
        e = Q.base[Q.Front];
        Q.Front = (Q.Front+1)%MAX;
        return true;
    }
    
    bool GetHead(CQueue Q,char &e)   //取队首;
    {
        if(IsEmpty(Q))  return false;
        e = Q.base[Q.Front];
        return true;
    }
    
    int GetLength(CQueue Q)    //取队长;
    {
        return (Q.Rear-Q.Front+MAX)%MAX;
    }
    
    bool Traverse(CQueue Q)    //遍历队列;
    {
        if(IsEmpty(Q)) return false;
        while(!IsEmpty(Q))
        {
            cout<<Q.base[Q.Front]<<" ";
            Q.Front = (Q.Front+1)%MAX;
        }
        cout<<endl;
        return true;
    }
    
    int main()
    {
        char e;
        CQueue Q;
        int n,l;
        InitCQueue(Q);
        cout<<"How many elem(s) do you want to push: "<<endl;
        cin>>n;
        cout<<"Enter "<<n<<" Elem(s): "<<endl;
        for(int i = 0; i < n; i++)
        {
            cin>>e;
            if(!IsFull(Q))
                Push(Q,e);
            else continue;
        }
        cout<<"Here is what they look like: "<<endl;
        Traverse(Q);
        GetHead(Q,e);
        cout<<"Length: "<<GetLength(Q)<<endl;
        cout<<"The Head is: "<<e<<endl;
        cout<<"How many elem(s) do you want to pop: "<<endl;
        cin>>n;
        for(int i = 0; i < n; i++)
        {
            if(!IsEmpty(Q))
                Pop(Q,e);
            else break;
        }
        cout<<"Here is what they look like: "<<endl;
        Traverse(Q);
        cout<<"Length: "<<GetLength(Q)<<endl;
        if(GetHead(Q,e))
            cout<<"The head is: "<<e<<endl;
        if(ClearCQueue(Q)) cout<<"Queue Cleared!"<<endl;
        Traverse(Q);    //验证是否清空;
        if(DestoryCQueue(Q)) cout<<"Queue Destoryed!"<<endl;
        Traverse(Q);
        return 0;
    }
    

        这里有一个需要注意的地方:虽然图画成一个圈,但是其存储的物理结构仍然是一条链,不难看出图上内存空间编号是0-8的连续片断,那么如何让它们围成一个圈呢?显然%运算是关键,可以根据初等数论的内容,将此过程与模运算联系起来。

    请思考下面的几句代码:

    if((Q.Rear+1)%MAX == Q.Front)   return true;
    
    
    Q.Front = (Q.Front+1)%MAX;
    
    
    Q.Rear = (Q.Rear+1)%MAX;

        上面的三句代码正是解决这个问题最核心的东西,我不能保证Front、Rear加1以后不超过MAX的范围,但是我能保证它们的结果对MAX取模以后一定小于MAX,而我们知道取模以后的数,刚好是一个循环,比如0-9分别对3取模,得到序列:

    0%3  1%3  2%3  3%3  4%3  5%3  6%3  7%3  8%3  9%3   ...
    
    0    1    2    0    1    2    0    1    2    0     ...

        这样可得到永远小于MAX的序列,也就实现了循环而不会使指针越界

        其他便没有什么需要注意的了,其实循环队列是最好写也是最好理解的队列了,跟一个数组基本没什么区别,相信只要语言基本功扎实,便不成什么问题。

       以上就是今天要写的全部了,Coder之路,路遥而险,还是接着写代码吧...

     

    展开全文
  • Nature综述:微生物构成的氮循环网络(必读)

    千次阅读 多人点赞 2019-04-21 16:07:05
    文章目录微生物构成的氮循环网络大纲摘要亮点名词解析背景和内容概述Box 1 | 氮全球生化循环圈:全球的氮库存、氮转化过程、氮通量图1 | 微生物转化氮化合物图2 | 催化氮循环中关键的四种反应的酶氮转化反应固氮作用...
  • 2.氮全球生化循环圈:全球的氮库存、氮转化过程、氮通量 3.微生物构成的氮化合物的转化 4.固氮作用 5.羟胺氧化成一氧化氮以及进一步氧化成亚硝酸盐 6.亚硝酸盐向硝酸盐的氧化 7.硝酸盐还原为亚硝酸盐的过程 8....
  • 建立企业体系结构的更佳途径

    千次阅读 2006-11-13 22:34:00
    show toc 欢迎来到 MSDN > 体系结构 建立企业体系结构的更佳途径 发布日期: 2006-6-13 | 更新日期: 2006-6-13
  • 众所周知,这几个词分别翻译为:循环、迭代、遍历和递归。乍一看,这几个词好像都与重复(repeat)有关,但有的又好像不完全是重复的意思。那么这几个词到底各是什么含义,有什么区别和联系呢?下面就试着解释一下。...
  • 为什么你要博客?

    万次阅读 多人点赞 2015-09-23 13:55:08
    一个选择我知道现在可能说这话有点...判断一件事情值不值得去做有一个方法:在一张白纸的左边不值得做的原因,然后在右边值得做的原因,完一比较,一权衡,自然能够得出结果。大家都成年人了,你会觉得...
  • 本文从简单实用的角度出发,给几个提高Oracle处理大量数据效率的有效途径。 <br /> 一、配置数据库初始化参数     数据库的全部初始化参数可以在OEM中看到。参见下图: <br /> ...
  • gravatar的服务,不过这玩意儿不时会被墙,还是拉回来靠谱,第2个途径是qq邮箱,通过分析数据发现,这几十万 用户里面居然有一半以上是qq邮箱,so 要想办法通过不用oauth的方式拿到. 爬虫 目录[-] ...
  • 例如,一个快速排序函数供他人调 用,其中必包含比较大小。麻烦来了:此时并不知要比较的是何类数据--整数、浮点数、字符串?于是只好为每类数据制作一个不同的排序函数。更通行的办法是 在函数参数中列一个回调...
  • 接下来还要在外面的for循环中添加如下代码来预测这幅图像的标号:  knn.find_nearest(currentTest,5, currentLabel);  testLabels->data.fl =currentLabel->data.fl[0];  if(currentLabel->data.fl[0...
  •  使用对象前必须将它创建和销毁的方式和可能的途径确定下来,并作为规则严格遵守。这些规则包括: new/delete和malloc/free分别配对使用 尽可能减少手工调用delete或释放资源的地方 如果有可能,尽量让一个对象的...
  • 给大忙人看的操作系统

    万次阅读 多人点赞 2020-02-28 12:34:12
    重复该循环直到程序运行完毕。 每个 CPU 都有一组可以执行的特定指令集。因此,x86 的 CPU 不能执行 ARM 的程序并且 ARM 的 CPU 也不能执行 x86 的程序。由于访问内存获取执行或数据要比执行指令花费的时间长,因此...
  • [Cocoa]深入浅 Cocoa 之消息

    万次阅读 2011-08-15 17:44:04
    深入浅 Cocoa 之消息  罗朝辉(http://blog.csdn.net/kesalin) 转载请注明出处 在入门级别的ObjC 教程中,我们常对从C++或Java 或其他面向对象语言转过来的程序员说,ObjC 中的方法调用(ObjC中的术语为...
  • 华为C语言编程规范(精华总结)

    万次阅读 多人点赞 2020-03-24 09:48:55
    注:没有在宏最前面加上单下划线"_",是因为一般以单下划线"_"和双下划线"__"开头的标识符为ANSIC等使用,在有些静态检查工具中,若全局可见的标识符以"_"开头会给告警。 定义包含保护符时,应该遵守如下规则: ...
  • 二级C语言考试知识点(很全)

    万次阅读 多人点赞 2018-11-30 01:38:14
    二级公共基础知识作为必考内容出现,出题形式为选择题前10道,占考试总分的10%。 考试其它比重: ...4、循环结构 占比分的5% 5、数组的定义和引用 占比分的5% 6、函数 占比分的5% 7、编译预处理 占比分1...
  • 1993年IEEE对软件工程的定义:软件工程是将系统化的、规范化的、可度量的途径应用于软件的开发、运行和维护的过程,即将工程化应用于软件的方法的研究。 软件工程原则: 抽象与自顶向下,逐层细化 信息隐蔽和数据...
  • 10个python办公黑科技,助你办公效率提高100倍

    万次阅读 多人点赞 2021-06-02 11:46:55
    save_res(res,save_path) 在以上代码中,我们为了函数功能与名称对应,修改了部分函数名以及必要的正则信息,在此我们就已经知道,如果从一个文本中提取常用信息只需要修改对应的正则即可,不会正则我们可以搜索...
  • SSD固态硬盘相比传统HDD机械硬盘的优点是性能强、体积小、无噪音,但是特殊的工作原理也让它的使用次数有限,而且这个次数还会随着制程工艺的升级而降低,编程/擦写循环次数从之前的5000+降低到3000次甚至1000次。...
  • 这与前面代码中写出的”x4=image(101:200,1:100,:);%拼图块4矩阵数据“作用一致。  当index=0时则执行第9行else下的代码部分,你可能会奇怪,为什么拼图块0不是从imge矩阵中分割而是放到else中了?这是因为0这个...
  • C#变长数组

    千次阅读 2018-10-30 22:24:49
    首先声明,这不是我的,只是我看对你有用才贴上来的  1、什么是ArrayList   ArrayList就是传说中的动态数组,用MSDN中的说法,就是Array的复杂版本,它提供了如下一些好处:  动态的增加和...
  • 看这玩意复习你还会挂科?《软件工程2篇》

    千次阅读 多人点赞 2020-01-23 11:16:45
    第一章: ...1993年IEEE对软件工程的定义:软件工程是将系统化的、规范化的、可度量的途径应用于软件的开发、运行和维护的过程,即将工程化应用于软件的方法的研究。 软件工程原则: 抽象与自顶向下,...
  • 巨好的入门精通

    千次阅读 2012-04-11 17:33:29
    我们为本书提供了网页,其中列了堪误表、示例和其它信息。你可以访问: http://oreilly.com/catalog/9781449390501/ 如有意见或者技术问题,请发邮件至: (a) oreilly com> 要了解我们的图书、...
  • 文章目录什么是ANR,如何避免主线程中的Looper.loop()一直无限循环为什么不会造成ANR?ListView原理与优化ContentProvider实现原理如何使用ContentProvider进行批量操作?为什么要使用通过`Con...
  • C/C++超级大火锅

    千次阅读 多人点赞 2016-03-15 17:06:46
    在前面最近接触到一些基础知识,平时遇到的编程困惑也加入其中。准确说是给自己看的,但是如果...1.malloc/free是C/C++语言的标准库函数,new/delete是C++的运算符2.new能够自动分配空间大小3.对于用户自定义的对
  • 推荐一本给IT项目经理的好书

    万次阅读 2018-03-21 11:46:34
    原文地址:http://www.cnblogs.com/cbook/archive/2011/01/19/1939060.html(防止原文作者删除、只能拷贝一份了)推荐一本给IT项目经理的好书...内容简介这个世界上给项目经理的书很多,给IT项目经理的书也不...
  • OS --written test1

    千次阅读 2011-09-22 18:10:05
    241.在提供虚拟存储的系统中...内存空闲块的大小 B.外存的大小 C.计算机编址范围 D.页表大小 答案:C 242.在分时系统中,时间片一定,( ),响应时间越长。 A.内存越多 B.用户数越多 C.内存越少 D.用户数越少

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 22,423
精华内容 8,969
关键字:

写出大小循环的途径