肖高辉's blog

up everyday,just a little.

2007年12月1日 #

[内部推荐]微软中国成都(PRC West Service Team)招聘BizTalk Engineer

这是内部邮件,有兴趣的朋友可以发邮件到 v-zhlia@microsoft.com

大家好,

我们现在因为项目和团队发展需要,因此希望培养一个专门研究BizTalk工程师,主要专注EAISOA方面技术专家

如果有人有兴趣或者有推荐的请邮件和我联系,谢谢!

      

具体要求:
1.
有良好的沟通能力和团队合作能力;

2. 强烈的学习和工作激情;
3.
熟练使用Visual Studio 2005开发环境,熟悉C#语言,熟悉.Net 2.0的原理和开发,熟悉Microsoft SQL Server 2005的设计开发;
4.
如果具备下列条件优先:
   4.1
具备BizTalk Server 2006的设计开发能力,掌握Orchestration, Adapter, Pipeline等的开发步骤.

   4.2参与过一个基于BizTalk系统的设计开发全过程.

   4.3具备良好的英文文档阅读能力;

未来工作内容介绍:

1,快速熟悉Biztalk 2006构建多系统的EAI,要求2周内要求完成,我们提供学习材料。

2,学习微软基于Biztalk 2006ESB框架。

3,学习Biztalk 2006所有功能:EDI电子数据交换、RIFDWCF Support等等。

我们将提供BizTalk相关自学资源和培训资源和项目实践机会,帮助成长为西区服务团队BizTalk专业专家。

 

posted @ 2007-12-01 10:37 肖高辉(MSCD) 阅读(122) | 评论 (0)编辑

2007年11月18日 #

继续前一篇文章的分页,发布一个ASP.NET 2.0的分页控件

     摘要: //usingDirectives#region//usingDirectivesusingSystem;usingSystem.ComponentModel;usingSystem.Web.UI;usingSystem.Web.UI.WebControls;usingSystem.Text;usingSystem.Collections.Generic;usingSystem.Collectio... 阅读全文

posted @ 2007-11-18 15:50 肖高辉(MSCD) 阅读(227) | 评论 (0)编辑

2007年11月17日 #

发一个我所经手的一个项目中的SQL 2005 分页通用存储过程,支持列排序

废话不说,直接帖代码。
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go

-- =============================================
--
 Author:        GOFI XIAO
--
 Create date: 2007-07-28
--
 Description:    通用分页存储过程增强版,增加对列的排序功能。
--
使用注意:@sql语句中不加入 Order by 语句
--
 =============================================
ALTER PROCEDURE [dbo].[spSplitPageEx]
(
    
--组合后的查询语句
    @SQL varchar(8000),
    
--排序字段
    @ColumnName Nvarchar(100),  
    
--排序类型倒序或正序
    @OrderBy Nvarchar(10)='ASC'
    
--第几页
    @CurrentPage int=1,
    
--页大小
    @PageSize int =10,
    
--总页数
    @PageCount int =0 OUTPUT,
    
--总记录数
    @TotalRecord int=0 OUTPUT


)
AS
BEGIN
    
-- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    
declare @ExceSQL nvarchar(4000)    
    
--设置开始行号
    declare  @start_row_num AS int
    
declare  @end_row_num AS int
    
if(@CurrentPage=1)
      
begin
        
SET @start_row_num = (@CurrentPage - 1* @PageSize
        
SET @end_row_num = @start_row_num+@PageSize
      
end
    
else
      
begin
        
SET @start_row_num = ((@CurrentPage - 1* @PageSize)+1
        
SET @end_row_num = (@start_row_num+@PageSize)-1
      
end
   
    
--设置表示
    declare @RowNumber nvarchar(1000)
    
set @RowNumber = ', ROW_NUMBER() OVER(ORDER BY ' + @ColumnName +' '+ @OrderBy + ') as RowNumber from '

    
set @SQL = Replace(@SQL,'from',@RowNumber)

    
--获得总记录数
    set @ExceSQL = 'WITH tmp AS (' + @SQL + '
        select @TotalRecord=max(RowNumber),@PageCount=CEILING((max(RowNumber)+0.0)/
'+cast(@pageSize as varchar(50))+') from tmp'
    
PRINT @ExceSQL
    
execute sp_executesql @ExceSQL,N'@TotalRecord int output,@PageCount int output',@TotalRecord output,@PageCount OUTPUT

    
--查询语句
    set @ExceSQL = 'WITH tmp AS (' + @SQL + '
        select * from tmp where RowNumber between 
' + Convert(nvarchar,@start_row_num)
        
+ ' And ' + Convert(nvarchar,@end_row_num)

    
execute(@ExceSQL)
PRINT @TotalRecord
PRINT @PageCount
    
--防止为NULL fixed GOFI XIAO 2007-08-12
    IF @TotalRecord IS NULL 
        
SET @TotalRecord=0
    
IF @PageCount IS NULL 
        
SET @PageCount=0
END




如何调用呢?下面给出一个示例:

set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go



-- =============================================
--
 Author:        GOFI XIAO
--
 Create date: 2007-09-10
--
 Description:    分页查询合同
--
SelectContract.aspx?ContractCode=&ContractName=
--
 =============================================
ALTER PROCEDURE [dbo].[spGetPagedList_Contract]
    
-- Add the parameters for the stored procedure here
    (    
    
@CurrentPage int=1,
    
@PageSize int=10,    
    
--按列排序
    @ColumnName varchar(50)='ContractName',
    
--升序降序
    @OrderBy varchar(10)='DESC'    ,
    
--查询条件
    @ContractCode varchar(50),
    
@ContractName varchar(50)

)
AS
BEGIN
    
-- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;
--组合查询语句
    declare @str varchar(8000)
set @str=N'SELECT 
c.*
 from Contract c 
WHERE 1=1 
'
--查询条件
IF @ContractCode<>''
    
SET @str=@str+' AND c.ContractCode like ''%'+@ContractCode+'%'''
IF @ContractName<>''
    
SET @str=@str+' AND c.ContractName like ''%'+@ContractName+'%'''

--*/
/*排序*/
declare @OrderColumnName varchar(50)
--设置一个默认的排序
set @OrderColumnName=' c.ContractName '
IF @ColumnName='ContractName'
    
SET @OrderColumnName=' c.ContractName '
ELSE IF @ColumnName='ContractCode'
    
SET @OrderColumnName=' c.ContractCode '



--return
declare @PageCount int
declare @TotalRecord int

exec spSplitPageEx @str,@OrderColumnName,@OrderBy,@CurrentPage,@PageSize,@PageCount OUTPUT,@TotalRecord OUTPUT
select @TotalRecord as TotalRecord,@PageCount as PageCount

END

前台页面如何调用呢?啊,这个还要问我?
我会在下篇文章中配合自定义分页控件来阐述。

posted @ 2007-11-17 12:46 肖高辉(MSCD) 阅读(213) | 评论 (1)编辑

2007年11月16日 #

Sql Server 2005 中的try catch

在sql2000里判断执行是否成功用@@ERROR   判断
这个东西多少还是有点问题,sql2005了可以用try。。catch了,不管那块错都能捕捉,然后在回滚,很方便,贴出来晾晾

BEGIN   TRANSACTION;

BEGIN   TRY       --try   执行sql
       
      SELECT   1/0;

END   TRY
BEGIN   CATCH     --cache抓错
        SELECT  
                ERROR_NUMBER()   AS   ErrorNumber,--返回错误号
                ERROR_SEVERITY()   AS   ErrorSeverity,--返回严重性
                ERROR_STATE()   as   ErrorState, --返回错误状态号
                ERROR_PROCEDURE()   as   ErrorProcedure,--返回出现错误的存储过程或触发器的名称
                ERROR_LINE()   as   ErrorLine, --返回导致错误的例程中的行号
                ERROR_MESSAGE()   as   ErrorMessage;--返回错误消息的完整文本。该文本可包括任何可替换参数所提供的值,如长度、对象名或时间

        IF   @@TRANCOUNT   >   0       ---   返回当前连接的活动事务数。
                ROLLBACK   TRANSACTION;   --错误中的事务要回滚
END   CATCH;

IF   @@TRANCOUNT   >   0             ---   返回当前连接的活动事务数。
        COMMIT   TRANSACTION;--正确的sql,提交事务
GO

posted @ 2007-11-16 11:57 肖高辉(MSCD) 阅读(84) | 评论 (0)编辑

自定义数据库分割函数

CREATE   FUNCTION fn_Split(@sText nvarchar(4000), @sDelim varchar(20) = ' ')
RETURNS @retArray TABLE (idx smallint Primary Key, value varchar(8000))
AS
BEGIN
DECLARE @idx smallint,
@value nvarchar(4000),
@bcontinue bit,
@iStrike smallint,
@iDelimlength tinyint
IF @sDelim = 'Space'
BEGIN
SET @sDelim = ' '
END
SET @idx = 0
SET @sText = LTrim(RTrim(@sText))
SET @iDelimlength = DATALENGTH(@sDelim)
SET @bcontinue = 1
IF NOT ((@iDelimlength = 0) or (@sDelim = 'Empty'))
BEGIN
WHILE @bcontinue = 1
 
BEGIN
--If you can find the delimiter in the text, retrieve the first element and
--
insert it with its index into the return table.

 
IF CHARINDEX(@sDelim, @sText)>0
 
BEGIN
 
SET @value = SUBSTRING(@sText,1, CHARINDEX(@sDelim,@sText)-1)
   
BEGIN
   
INSERT @retArray (idx, value)
   
VALUES (@idx, @value)
   
END
 
--Trim the element and its delimiter from the front of the string.
  --Increment the index and loop.
SET @iStrike = DATALENGTH(@value) + @iDelimlength
 
SET @idx = @idx + 1
 
SET @sText = LTrim(Right(@sText,DATALENGTH(@sText) - @iStrike))
 
 
END
 
ELSE
 
BEGIN
--If you can抰 find the delimiter in the text, @sText is the last value in
--
@retArray.
SET @value = @sText
   
BEGIN
   
INSERT @retArray (idx, value)
   
VALUES (@idx, @value)
   
END
 
--Exit the WHILE loop.
SET @bcontinue = 0
 
END
 
END
END
ELSE
BEGIN
WHILE @bcontinue=1
 
BEGIN
 
--If the delimiter is an empty string, check for remaining text
  --instead of a delimiter. Insert the first character into the
  --retArray table. Trim the character from the front of the string.
--
Increment the index and loop.
  IF DATALENGTH(@sText)>1
 
BEGIN
 
SET @value = SUBSTRING(@sText,1,1)
   
BEGIN
   
INSERT @retArray (idx, value)
   
VALUES (@idx, @value)
   
END
 
SET @idx = @idx+1
 
SET @sText = SUBSTRING(@sText,2,DATALENGTH(@sText)-1)
 
 
END
 
ELSE
 
BEGIN
 
--One character remains.
  --Insert the character, and exit the WHILE loop.
  INSERT @retArray (idx, value)
 
VALUES (@idx, @sText)
 
SET @bcontinue = 0
 
END
END
END
RETURN
END

posted @ 2007-11-16 11:39 肖高辉(MSCD) 阅读(79) | 评论 (0)编辑

2007年11月1日 #

Win32环境下dll编程原理(转载)

 

比较大应用程序都由很多模块组成,这些模块分别完成相对独立的功能,它们彼此协作来完成整个软件系统的工作。其中可能存在一些模块的功能较为通用,在构造其它软件系统时仍会被使用。在构造软件系统时,如果将所有模块的源代码都静态编译到整个应用程序EXE文件中,会产生一些问题:一个缺点是增加了应用程序的大小,它会占用更多的磁盘空间,程序运行时也会消耗较大的内存空间,造成系统资源的浪费;另一个缺点是,在编写大的EXE程序时,在每次修改重建时都必须调整编译所有源代码,增加了编译过程的复杂性,也不利于阶段性的单元测试。

  Windows系统平台上提供了一种完全不同的较有效的编程和运行环境,你可以将独立的程序模块创建为较小的DLL(Dynamic Linkable Library)文件,并可对它们单独编译和测试。在运行时,只有当EXE程序确实要调用这些DLL模块的情况下,系统才会将它们装载到内存空间中。这种方式不仅减少了EXE文件的大小和对内存空间的需求,而且使这些DLL模块可以同时被多个应用程序使用。Microsoft Windows自己就将一些主要的系统功能以DLL模块的形式实现。例如IE中的一些基本功能就是由DLL文件实现的,它可以被其它应用程序调用和集成。

  一般来说,DLL是一种磁盘文件(通常带有DLL扩展名),它由全局数据、服务函数和资源组成,在运行时被系统加载到进程的虚拟空间中,成为调用进程的一部分。如果与其它DLL之间没有冲突,该文件通常映射到进程虚拟空间的同一地址上。DLL模块中包含各种导出函数,用于向外界提供服务。Windows在加载DLL模块时将进程函数调用与DLL文件的导出函数相匹配。

  在Win32环境中,每个进程都复制了自己的读/写全局变量。如果想要与其它进程共享内存,必须使用内存映射文件或者声明一个共享数据段。DLL模块需要的堆栈内存都是从运行进程的堆栈中分配出来的。

DLL现在越来越容易编写。Win32已经大大简化了其编程模式,并有许多来自AppWizard和MFC类库的支持。

一、导出和导入函数的匹配

  DLL 文件中包含一个导出函数表。这些导出函数由它们的符号名和称为标识号的整数与外界联系起来。函数表中还包含了DLL中函数的地址。当应用程序加载DLL模块时时,它并不知道调用函数的实际地址,但它知道函数的符号名和标识号。动态链接过程在加载的DLL模块时动态建立一个函数调用与函数地址的对应表。如果重新编译和重建DLL文件,并不需要修改应用程序,除非你改变了导出函数的符号名和参数序列。

简单的DLL文件只为应用程序提供导出函数,比较复杂的DLL文件除了提供导出函数以外,还调用其它DLL文件中的函数。这样,一个特殊的DLL可以既有导入函数,又有导入函数。这并不是一个问题,因为动态链接过程可以处理交叉相关的情况。

  在DLL代码中,必须像下面这样明确声明导出函数:

__declspec(dllexport) int MyFunction(int n);

但也可以在模块定义(DEF)文件中列出导出函数,不过这样做常常引起更多的麻烦。在应用程序方面,要求像下面这样明确声明相应的输入函数:

__declspec(dllimport) int MyFuncition(int n);

仅有导入和导出声明并不能使应用程序内部的函数调用链接到相应的DLL文件上。应用程序的项目必须为链接程序指定所需的输入库(LIB文件)。而且应用程序事实上必须至少包含一个对DLL函数的调用。

二、与DLL模块建立链接

  应用程序导入函数与DLL文件中的导出函数进行链接有两种方式:隐式链接和显式链接。所谓的隐式链接是指在应用程序中不需指明DLL文件的实际存储路径,程序员不需关心DLL文件的实际装载。而显式链接与此相反。

   采用隐式链接方式,程序员在建立一个DLL文件时,链接程序会自动生成一个与之对应的LIB导入文件。该文件包含了每一个DLL导出函数的符号名和可选的标识号,但是并不含有实际的代码。LIB文件作为DLL的替代文件被编译到应用程序项目中。当程序员通过静态链接方式编译生成应用程序时,应用程序中的调用函数与LIB文件中导出符号相匹配,这些符号或标识号进入到生成的EXE文件中。LIB文件中也包含了对应的DLL文件名(但不是完全的路径名),链接程序将其存储在EXE文件内部。当应用程序运行过程中需要加载DLL文件时,Windows根据这些信息发现并加载DLL,然后通过符号名或标识号实现对 DLL函数的动态链接。

  显式链接方式对于集成化的开发语言(例如VB)比较适合。有了显式链接,程序员就不必再使用导入文件,而是直接调用Win32 的LoadLibary函数,并指定DLL的路径作为参数。LoadLibary返回HINSTANCE参数,应用程序在调用 GetProcAddress函数时使用这一参数。GetProcAddress函数将符号名或标识号转换为DLL内部的地址。假设有一个导出如下函数的 DLL文件:

extern "C" __declspec(dllexport) double SquareRoot(double d);

下面是应用程序对该导出函数的显式链接的例子:

typedef double(SQRTPROC)(double);

HINSTANCE hInstance;

SQRTPROC* pFunction;

VERIFY(hInstance=::LoadLibrary("c:\\winnt\\system32\\mydll.dll"));

VERIFY(pFunction=(SQRTPROC*)::GetProcAddress(hInstance,"SquareRoot"));

double d=(*pFunction)(81.0);//调用该DLL函数

在隐式链接方式中,所有被应用程序调用的DLL文件都会在应用程序EXE文件加载时被加载在到内存中;但如果采用显式链接方式,程序员可以决定DLL文件何时加载或不加载。显式链接在运行时决定加载哪个DLL文件。例如,可以将一个带有字符串资源的DLL模块以英语加载,而另一个以西班牙语加载。应用程序在用户选择了合适的语种后再加载与之对应的DLL文件。

三、使用符号名链接与标识号链接

  在Win16环境中,符号名链接效率较低,所有那时标识号链接是主要的链接方式。在Win32环境中,符号名链接的效率得到了改善。Microsoft现在推荐使用符号名链接。但在MFC库中的DLL版本仍然采用的是标识号链接。一个典型的MFC程序可能会链接到数百个MFC DLL函数上。采用标识号链接的应用程序的EXE文件体相对较小,因为它不必包含导入函数的长字符串符号名。

四、编写DllMain函数

  DllMain 函数是DLL模块的默认入口点。当Windows加载DLL模块时调用这一函数。系统首先调用全局对象的构造函数,然后调用全局函数DLLMain。 DLLMain函数不仅在将DLL链接加载到进程时被调用,在DLL模块与进程分离时(以及其它时候)也被调用。下面是一个框架DLLMain函数的例子。

HINSTANCE g_hInstance;

extern "C" int APIENTRY DllMain(HINSTANCE hInstance,DWORD dwReason,LPVOID lpReserved)

{

if(dwReason==DLL_PROCESS_ATTACH)

{

TRACE0("EX22A.DLL Initializing!\n");

//在这里进行初始化

}

else if(dwReason=DLL_PROCESS_DETACH)

{

TRACE0("EX22A.DLL Terminating!\n");

//在这里进行清除工作

}

return 1;//成功

}

  如果程序员没有为DLL模块编写一个DLLMain函数,系统会从其它运行库中引入一个不做任何操作的缺省DLLMain函数版本。在单个线程启动和终止时,DLLMain函数也被调用。正如由dwReason参数所表明的那样。

五、模块句柄

   进程中的每个DLL模块被全局唯一的32字节的HINSTANCE句柄标识。进程自己还有一个HINSTANCE句柄。所有这些模块句柄都只有在特定的进程内部有效,它们代表了DLL或EXE模块在进程虚拟空间中的起始地址。在Win32中,HINSTANCE和HMODULE的值是相同的,这个两种类型可以替换使用。进程模块句柄几乎总是等于0x400000,而DLL模块的加载地址的缺省句柄是0x10000000。如果程序同时使用了几个DLL模块,每一个都会有不同的HINSTANCE值。这是因为在创建DLL文件时指定了不同的基地址,或者是因为加载程序对DLL代码进行了重定位。

模块句柄对于加载资源特别重要。Win32 的FindResource函数中带有一个HINSTANCE参数。EXE和DLL都有其自己的资源。如果应用程序需要来自于DLL的资源,就将此参数指定为DLL的模块句柄。如果需要EXE文件中包含的资源,就指定EXE的模块句柄。

但是在使用这些句柄之前存在一个问题,你怎样得到它们呢?如果需要得到EXE模块句柄,调用带有Null参数的Win32函数GetModuleHandle;如果需要DLL模块句柄,就调用以DLL文件名为参数的Win32函数GetModuleHandle。

六、应用程序怎样找到DLL文件

  如果应用程序使用LoadLibrary显式链接,那么在这个函数的参数中可以指定DLL文件的完整路径。如果不指定路径,或是进行隐式链接,Windows将遵循下面的搜索顺序来定位DLL:

1. 包含EXE文件的目录,

2. 进程的当前工作目录,

3. Windows系统目录,

4. Windows目录,

5. 列在Path环境变量中的一系列目录。

   这里有一个很容易发生错误的陷阱。如果你使用VC++进行项目开发,并且为DLL模块专门创建了一个项目,然后将生成的DLL文件拷贝到系统目录下,从应用程序中调用DLL模块。到目前为止,一切正常。接下来对DLL模块做了一些修改后重新生成了新的DLL文件,但你忘记将新的DLL文件拷贝到系统目录下。下一次当你运行应用程序时,它仍加载了老版本的DLL文件,这可要当心!

七、调试DLL程序

  Microsoft 的VC++是开发和测试DLL的有效工具,只需从DLL项目中运行调试程序即可。当你第一次这样操作时,调试程序会向你询问EXE文件的路径。此后每次在调试程序中运行DLL时,调试程序会自动加载该EXE文件。然后该EXE文件用上面的搜索序列发现DLL文件,这意味着你必须设置Path环境变量让其包含DLL文件的磁盘路径,或者也可以将DLL文件拷贝到搜索序列中的目录路径下。

posted @ 2007-11-01 23:49 肖高辉(MSCD) 阅读(135) | 评论 (0)编辑

2007年10月21日 #

[转]成为嵌入式程序员应知道的0x10个问题——笔试常考

C语言测试是招聘嵌入式系统程序员过程中必须而且有效的方法。这些年,我既参加也组织了许多这种测试,在这过程中我意识到这些测试能为面试者和被面试者提供许多有用信息,此外,撇开面试的压力不谈,这种测试也是相当有趣的。

  从被面试者的角度来讲,你能了解许多关于出题者或监考者的情况。这个测试只是出题者为显示其对ANSI标准细节的知识而不是技术技巧而设计吗?这是个愚蠢的问题吗?如要你答出某个字符的ASCII值。这些问题着重考察你的系统调用和内存分配策略方面的能力吗?这标志着出题者也许花时间在微机上而不是在嵌入式系统上。如果上述任何问题的答案是"是"的话,那么我知道我得认真考虑我是否应该去做这份工作。

  从面试者的角度来讲,一个测试也许能从多方面揭示应试者的素质:最基本的,你能了解应试者C语言的水平。不管怎么样,看一下这人如何回答他不会的问题也是满有趣。应试者是以好的直觉做出明智的选择,还是只是瞎蒙呢?当应试者在某个问题上卡住时是找借口呢,还是表现出对问题的真正的好奇心,把这看成学习的机会呢?我发现这些信息与他们的测试成绩一样有用。

  有了这些想法,我决定出一些真正针对嵌入式系统的考题,希望这些令人头痛的考题能给正在找工作的人一点帮助。这些问题都是我这些年实际碰到的。其中有些题很难,但它们应该都能给你一点启迪。

  这个测试适于不同水平的应试者,大多数初级水平的应试者的成绩会很差,经验丰富的程序员应该有很好的成绩。为了让你能自己决定某些问题的偏好,每个问题没有分配分数,如果选择这些考题为你所用,请自行按你的意思分配分数。

  预处理器(Preprocessor)

  1 . 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)

#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL

  我在这想看到几件事情:

  1) #define 语法的基本知识(例如:不能以分号结束,括号的使用,等等)

  2)懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少秒而不是计算出实际的值,是更清晰而没有代价的。

  3) 意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。

  4) 如果你在你的表达式中用到UL(表示无符号长整型),那么你有了一个好的起点。记住,第一印象很重要。

  2 . 写一个"标准"宏MIN ,这个宏输入两个参数并返回较小的一个。

#define MIN(A,B) ((A) <= (B) ? (A) : (B)) 

  这个测试是为下面的目的而设的:

  1) 标识#define在宏中应用的基本知识。这是很重要的。因为在 嵌入(inline)操作符 变为标准C的一部分之前,宏是方便产生嵌入代码的唯一方法,对于嵌入式系统来说,为了能达到要求的性能,嵌入代码经常是必须的方法。

  2) 三重条件操作符的知识。这个操作符存在C语言中的原因是它使得编译器能产生比if-then-else更优化的代码,了解这个用法是很重要的。

  3) 懂得在宏中小心地把参数用括号括起来

  4) 我也用这个问题开始讨论宏的副作用,例如:当你写下面的代码时会发生什么事?

least = MIN(*p++, b);

  3. 预处理器标识#error的目的是什么?

  如果你不知道答案,请看参考文献1。这问题对区分一个正常的伙计和一个书呆子是很有用的。只有书呆子才会读C语言课本的附录去找出象这种问题的答案。当然如果你不是在找一个书呆子,那么应试者最好希望自己不要知道答案。

  死循环(Infinite loops)

  4. 嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢?

  这个问题用几个解决方案。我首选的方案是:

while(1){}

  一些程序员更喜欢如下方案:

for(;;)
{}

  这个实现方式让我为难,因为这个语法没有确切表达到底怎么回事。如果一个应试者给出这个作为方案,我将用这个作为一个机会去探究他们这样做的基本原理。如果他们的基本答案是:"我被教着这样做,但从没有想到过为什么。"这会给我留下一个坏印象。

  第三个方案是用 goto

Loop:
...
goto Loop;

  应试者如给出上面的方案,这说明或者他是一个汇编语言程序员(这也许是好事)或者他是一个想进入新领域的BASIC/FORTRAN程序员。

  数据声明(Data declarations)

  5. 用变量a给出下面的定义

  a) 一个整型数(An integer)

  b)一个指向整型数的指针( A pointer to an integer)

  c)一个指向指针的的指针,它指向的指针是指向一个整型数( A pointer to a pointer to an integer)

  d)一个有10个整型数的数组( An array of 10 integers)

  e) 一个有10个指针的数组,该指针是指向一个整型数的。(An array of 10 pointers to integers)

  f) 一个指向有10个整型数数组的指针( A pointer to an array of 10 integers)

  g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer)

  h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数( An array of ten pointers to functions that take an integer argument and return an integer )

  答案是:

  a) int a; // An integer
  b) int *a; // A pointer to an integer
  c) int **a; // A pointer to a pointer to an integer
  d) int a[10]; // An array of 10 integers
  e) int *a[10]; // An array of 10 pointers to integers
  f) int (*a)[10]; // A pointer to an array of 10 integers
  g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer
  h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer

  人们经常声称这里有几个问题是那种要翻一下书才能回答的问题,我同意这种说法。当我写这篇文章时,为了确定语法的正确性,我的确查了一下书。但是当我被面试的时候,我期望被问到这个问题(或者相近的问题)。因为在被面试的这段时间里,我确定我知道这个问题的答案。应试者如果不知道所有的答案(或至少大部分答案),那么也就没有为这次面试做准备,如果该面试者没有为这次面试做准备,那么他又能为什么出准备呢?

  Static

  6. 关键字static的作用是什么?

  这个简单的问题很少有人能回答完全。在C语言中,关键字static有三个明显的作用:

  1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。

  2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。

  3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。

  大多数应试者能正确回答第一部分,一部分能正确回答第二部分,同是很少的人能懂得第三部分。这是一个应试者的严重的缺点,因为他显然不懂得本地化数据和代码范围的好处和重要性。

  Const

  7.关键字const有什么含意?

  我只要一听到被面试者说:"const意味着常数",我就知道我正在和一个业余者打交道。去年Dan Saks已经在他的文章里完全概括了const的所有用法,因此ESP(译者:Embedded Systems Programming)的每一位读者应该非常熟悉const能做什么和不能做什么.如果你从没有读到那篇文章,只要能说出const意味着"只读"就可以了。尽管这个答案不是完全的答案,但我接受它作为一个正确的答案。

  如果应试者能正确回答这个问题,我将问他一个附加的问题:

  下面的声明都是什么意思?

const int a;
int const a;
const int *a;
int * const a;
int const * a const;

/******/

  前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。如果应试者能正确回答这些问题,那么他就给我留下了一个好印象。顺带提一句,也许你可能会问,即使不用关键字 const,也还是能很容易写出功能正确的程序,那么我为什么还要如此看重关键字const呢?我也如下的几下理由:

  1) 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。)

  2) 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。

  3) 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。

  Volatile

  8. 关键字volatile有什么含意?并给出三个不同的例子。

  一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:

  1) 并行设备的硬件寄存器(如:状态寄存器)

  2) 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)

  3) 多线程应用中被几个任务共享的变量

  回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。搞嵌入式的家伙们经常同硬件、中断、RTOS等等打交道,所有这些都要求用到volatile变量。不懂得volatile的内容将会带来灾难。

  假设被面试者正确地回答了这是问题(嗯,怀疑是否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。

  1)一个参数既可以是const还可以是volatile吗?解释为什么。

  2); 一个指针可以是volatile 吗?解释为什么。

  3); 下面的函数有什么错误:

int square(volatile int *ptr)
{
return *ptr * *ptr;
}

  下面是答案:

  1) 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。

  2) 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。

  3) 这段代码有点变态。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:

int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}

  由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:

long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}

  位操作(Bit manipulation)

  9. 嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a,写两段代码,第一个设置a的bit 3,第二个清除a 的bit 3。在以上两个操作中,要保持其它位不变。

  对这个问题有三种基本的反应

  1)不知道如何下手。该被面者从没做过任何嵌入式系统的工作。

  2) 用bit fields。Bit fields是被扔到C语言死角的东西,它保证你的代码在不同编译器之间是不可移植的,同时也保证了的你的代码是不可重用的。我最近不幸看到 Infineon为其较复杂的通信芯片写的驱动程序,它用到了bit fields因此完全对我无用,因为我的编译器用其它的方式来实现bit fields的。从道德讲:永远不要让一个非嵌入式的家伙粘实际硬件的边。

  3) 用 #defines 和 bit masks 操作。这是一个有极高可移植性的方法,是应该被用到的方法。最佳的解决方案如下:

#define BIT3 (0x1 << 3)
static int a;

void set_bit3(void)
{
a |= BIT3;
}
void clear_bit3(void)
{
a &= ~BIT3;
}

  一些人喜欢为设置和清除值而定义一个掩码同时定义一些说明常数,这也是可以接受的。我希望看到几个要点:说明常数、|=和&=~操作。

  访问固定的内存位置(Accessing fixed memory locations)

  10. 嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。

  这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换(typecast)为一指针是合法的。这一问题的实现方式随着个人风格不同而不同。典型的类似代码如下:

int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa55;

A more obscure approach is: 

  一个较晦涩的方法是:

*(int * const)(0x67a9) = 0xaa55;

  即使你的品味更接近第二种方案,但我建议你在面试时使用第一种方案。

  中断(Interrupts)

  11. 中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展—让标准C支持中断。具代表事实是,产生了一个新的关键字 __interrupt。下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。

__interrupt double compute_area (double radius)
{
double area = PI * radius * radius;
printf("\nArea = %f", area);
return area;
}

  这个函数有太多的错误了,以至让人不知从何说起了:

  1)ISR 不能返回一个值。如果你不懂这个,那么你不会被雇用的。

  2) ISR 不能传递参数。如果你没有看到这一点,你被雇用的机会等同第一项。

  3) 在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额处的寄存器入栈,有些处理器/编译器就是不允许在ISR中做浮点运算。此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的。

  4) 与第三点一脉相承,printf()经常有重入和性能上的问题。如果你丢掉了第三和第四点,我不会太为难你的。不用说,如果你能得到后两点,那么你的被雇用前景越来越光明了。

  代码例子(Code examples)

  12 . 下面的代码输出是什么,为什么?

void foo(void)
{
unsigned int a = 6;
int b = -20;
(a+b > 6) ? puts("> 6") : puts("<= 6");
}

  这个问题测试你是否懂得C语言中的整数自动转换原则,我发现有些开发者懂得极少这些东西。不管如何,这无符号整型问题的答案是输出是 ">6"。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。如果你答错了这个问题,你也就到了得不到这份工作的边缘。

  13. 评价下面的代码片断:

unsigned int zero = 0;
unsigned int compzero = 0xFFFF;
/*1's complement of zero */

  对于一个int型不是16位的处理器为说,上面的代码是不正确的。应编写如下:

unsigned int compzero = ~0;

  这一问题真正能揭露出应试者是否懂得处理器字长的重要性。在我的经验里,好的嵌入式程序员非常准确地明白硬件的细节和它的局限,然而PC机程序往往把硬件作为一个无法避免的烦恼。

  到了这个阶段,应试者或者完全垂头丧气了或者信心满满志在必得。如果显然应试者不是很好,那么这个测试就在这里结束了。但如果显然应试者做得不错,那么我就扔出下面的追加问题,这些问题是比较难的,我想仅仅非常优秀的应试者能做得不错。提出这些问题,我希望更多看到应试者应付问题的方法,而不是答案。不管如何,你就当是这个娱乐吧...

  动态内存分配(Dynamic memory allocation)

  14. 尽管不像非嵌入式计算机那么常见,嵌入式系统还是有从堆(heap)中动态分配内存的过程的。那么嵌入式系统中,动态分配内存可能发生的问题是什么?

  这里,我期望应试者能提到内存碎片,碎片收集的问题,变量的持行时间等等。这个主题已经在ESP杂志中被广泛地讨论过了(主要是 P.J. Plauger, 他的解释远远超过我这里能提到的任何解释),所有回过头看一下这些杂志吧!让应试者进入一种虚假的安全感觉后,我拿出这么一个小节目:

  下面的代码片段的输出是什么,为什么?

char *ptr;
if ((ptr = (char *)malloc(0)) == NULL)
puts("Got a null pointer");
else
puts("Got a valid pointer");

  这是一个有趣的问题。最近在我的一个同事不经意把0值传给了函数malloc,得到了一个合法的指针之后,我才想到这个问题。这就是上面的代码,该代码的输出是"Got a valid pointer"。我用这个来开始讨论这样的一问题,看看被面试者是否想到库例程这样做是正确。得到正确的答案固然重要,但解决问题的方法和你做决定的基本原理更重要些。

  Typedef

  15 Typedef 在C语言中频繁用以声明一个已经存在的数据类型的同义字。也可以用预处理器做类似的事。例如,思考一下下面的例子:

#define dPS struct s *
typedef struct s * tPS;

  以上两种情况的意图都是要定义dPS 和 tPS 作为一个指向结构s指针。哪种方法更好呢?(如果有的话)为什么?

  这是一个非常微妙的问题,任何人答对这个问题(正当的原因)是应当被恭喜的。答案是:typedef更好。思考下面的例子:

dPS p1,p2;
tPS p3,p4;

  第一个扩展为

struct s * p1, p2;

  上面的代码定义p1为一个指向结构的指,p2为一个实际的结构,这也许不是你想要的。第二个例子正确地定义了p3 和p4 两个指针。

  晦涩的语法

  16 . C语言同意一些令人震惊的结构,下面的结构是合法的吗,如果是它做些什么?

int a = 5, b = 7, c;
c = a+++b;

  这个问题将做为这个测验的一个愉快的结尾。不管你相不相信,上面的例子是完全合乎语法的。问题是编译器如何处理它?水平不高的编译作者实际上会争论这个问题,根据最处理原则,编译器应当能处理尽可能所有合法的用法。因此,上面的代码被处理成:

c = a++ + b;

  因此, 这段代码持行后a = 6, b = 7, c = 12。

  如果你知道答案,或猜出正确答案,做得好。如果你不知道答案,我也不把这个当作问题。我发现这个问题的最大好处是这是一个关于代码编写风格,代码的可读性,代码的可修改性的好的话题。

  好了,伙计们,你现在已经做完所有的测试了。这就是我出的C语言测试题,我怀着愉快的心情写完它,希望你以同样的心情读完它。如果是认为这是一个好的测试,那么尽量都用到你的找工作的过程中去吧。天知道也许过个一两年,我就不做现在的工作,也需要找一个。 

posted @ 2007-10-21 10:38 肖高辉(MSCD) 阅读(286) | 评论 (0)编辑

[转]tencent腾讯——笔试题

基本都是基础题目,看来腾讯不准备放弃那些有思想但是还没有开始苦练基本功的人,只涉及到语言问题和简单的 
数据结构,其他的操作系统,编译原理,离散数学,软件工程,计算机原理,体系结构等等无一涉及,题目很多,
题号不与原试题相符,经过几天的冥思苦想,现已补全 希望师弟师妹可以探讨探讨答案,从中学到笔试的经验 
声明:以下问题仅供本校园网校内师弟师妹为了考察自己学习的参考,不要传播
1 计算 a^b << 2 (运算符优先级问题)
int a = 2;
int b = 1;
int c = 2;
cout << (a ^ b << c);
6
2 根据先序中序求后序
 
3 a[3][4]哪个不能表示 a[1][1]: *(&a[0][0]) *(*(a+1)+1) *(&a[1]+1) *(&a[0][0]+4)
cout << *(&a[1][1]) << endl
       <<*(*(a+1)+1) << endl
       << *(&a[1]+1) << endl
       << *(&a[0][0]+4);
6
6
0012FEC8
5
4 for(int i...)
    for(int j...)
      printf(i,j);
      printf(j)
 会出现什么问题
J没有定义,不在其作用域
5 for(i=0;i<10;++i,sum+=i);的运行结果
    int i, sum = 0;
for(i=0;i<10;++i,sum+=i);
cout << sum;
55
6 10个数顺序插入查找二叉树,元素62的比较次数
 
7 10个数放入模10hash链表,最大长度是多少
 
8 fun((exp1,exp2),(exp3,exp4,exp5))有几个实参
2个,逗号运算符,
9 希尔 冒泡 快速 插入 哪个平均速度最快
 
10 二分查找是 顺序存储 链存储 按value有序中的哪些
 
11 顺序查找的平均时间
 
12 *p=NULL *p=new char[100] sizeof(p)各为多少
都是4
13 频繁的插入删除操作使用什么结构比较合适,链表还是数组
链表
14 enum的声明方式
enum A{
a1,
a2,
a3,
};
15 1-20的两个数把和告诉A,积告诉B,A说不知道是多少,
B也说不知道,这时A说我知道了,B接着说我也知道了,问这两个数是多少
 
 
大题:
 
1 把字符串转换为小写,不成功返回NULL,成功返回新串
 
char* toLower(char* sSrcStr)
{
    char* sDest= NULL;
    if( __1___)
    {
        int j;
        sLen = strlen(sSrcStr);
        sDest = new [_______2_____];
        if(*sDest == NULL)
            return NULL;
        sDest[sLen] = '\0';
        while(_____3____)
           sDest[sLen] = toLowerChar(sSrcStr[sLen]);
    }
    return sDest;
}
char toLowerChar(char a)
{
    if(a >= 'A' && a <= 'Z')
       return a - 'A' + 'a';
}
 
char* toLower(char* sSrcStr)
{
    char* sDest= NULL;
    if(sSrcStr)
    {
       int j;
       int sLen = strlen(sSrcStr);
       sDest = new char[sLen + 1];
       if(*sDest == NULL)
           return NULL;
       sDest[sLen] = '\0';
       while(sLen--)
           sDest[sLen] = toLowerChar(sSrcStr[sLen]);
    }
    return sDest;
}
 
2 把字符串转换为整数 例如:"-123" -> -123
 
main()
{
    .....
    if( *string == '-' )
        n = ____1______; -1* num(string+1)
    else
        n = num(string);
    .....
}
 
int num(char* string)
{
    for(;!(*string==0);string++)
    {
        int k;
        k = __2_____; *string
        j = --sLen;
        while( __3__) j--
            k = k * 10;
        num = num + k;
    }
    return num;
}
 
附加题:
 
1 linux下调试core的命令,察看堆栈状态命令
 
2 写出socks套接字 服务端 客户端 通讯程序
 
3 填空补全程序,按照我的理解是添入:win32调入dll的函数名
 查找函数入口的函数名 找到函数的调用形式
 把formView加到singledoc的声明 将singledoc加到app的声明
 
4 有关系 s(sno,sname) c(cno,cname) sc(sno,cno,grade)
 1 问上课程 "db"的学生no
 2 成绩最高的学生号
 3 每科大于90分的人数
 
2005年腾讯招聘
选择题(60)
 c/c++ os linux 方面的基础知识 c的Sizeof函数有好几个!
程序填空(40)
1.(20) 4空x5
 不使用额外空间,将 A,B两链表的元素交叉归并
2.(20) 4空x5
MFC 将树序列化 转存在数组或 链表中!
 
发信人: ChinaOfey (勇敢的傻,傻得勇敢), 信区: Career_Plaza
标 题: 腾讯笔试题
发信站: 水木社区 (Sat Nov 5 16:25:25 2005), 站内
 
 希望明年的兄弟有用!(B卷)
 
 第一部分:基本都是数据结构题+1,2道操作系统和数据库相关的(比较基本)
 
 有印象的:
 
 1,为节省空间,两个栈共用一个空间,栈底在两边,问什么时候表明空间用完
    答案:栈顶相遇时
    这道题就是很基础的一个题目,因为是第一道题,所以印象比较深^_^
 
 2,char A[5]; char* B ="abcdefg"; void * C; C = new char[100];
    sizeof(A) sizeof(B) size(C)
5,4,4
 3,爸爸,妈妈,妹妹,小强,至少两个人同一生肖的概率是多少
    1- 12*11*10*9/12*12*12*12 = 43% ,我忘用1减了....
 
 然后还有几个看程序给结果的题,考察了类,指针的内容
 
 第二部分:程序填空
 
   主要要能看出他的思路
 
 第三部分:写代码
 
   1,关于mfc的,一个控件,显示时间,1s钟刷新一次
 
   2,SQL语言进行简单的数据库操作,建表,查询,求平均工资等
 
     不记得语言了,因此只好自创....ft
 
   3,Unix进程通信有哪些方式,各有什么特点?
 
   (其中A卷给的是道网络编程题目)
 
 第四部分:主观题
 
 考rp的,比较无聊,手都写酸了....
 
程序4说明]
设 M 叉树采用列表法表示,即每棵子树对应一个列表,列表的结构为:子树根结点的值部分
 (设为一个字符) 和用“( )”,括起来的各子树的列表 (如有子树的话) ,各子列表间用“
,”,分隔。例如下面的三叉树可用列表 a( b( c,d ),e,f( g,h,i ))表示。
 
 
本程序输入列表,生成一棵 M 叉树,并由 M 叉树输出列表。假定输入无错误。
[程序4]
#include〈stdio.h〉
#include〈stdlib.h〉
#define M 3
typedef struct node{ char val;
struct node *subTree[M];
} NODE;
char buf[255] ,*str = buf
NODE *d = NULL
NODE *mackTree( ) /*由列表生成M叉树*/
{ int k; NODE *s ;
s = __(1)__
s -> val = *str++ ;
for ( k = 0 ; k < M ; k++ ) s-> subTree[k] = NULL ;
if(*str='( '){
k = 0;
do { str++;
s -> subTree[k] = __(2)__ ;
if ( *str == ')' ) { str++; break ; }
k = k+l ;
} while ( __(3)__ );
}
return s ;
}
void walkTree( NODE *t ) /*由 M 叉树输出列表*/
{ int i ;
if( t != NULL ) {
__(4)__
if ( t -> subTree[0] == NULL ) return ;
putchar ( '( ' ) ;
for ( i = 0 ; i < m ; i++) {
__(5)__
if ( i! = M - l && t -> subTree[i+l] != NULL )
putchar ( ', ' ) ;
}
putchar ( ') ' ) ;
}
}
 
void main( )
{ printf( "Enter exp:" ) ;
scanf( "%S" , str ) ;
d = makeTree() ;
walkTree( d ) ; putchar( '\n') ;
}
有两个集合  
 集合A{1,7,19,21,55,100。。。}  
 集合B{7,22,100。。。}  
 两个集合都是10万个数据(已排序),要求写一个算法,判断B是不是A的子集,算法时间复杂度为Q(N)

posted @ 2007-10-21 10:11 肖高辉(MSCD) 阅读(2729) | 评论 (0)编辑

2007年10月20日 #

成都目前比较可以的软件公司

阿尔卡特研究所
  贝尔阿尔卡特研究所
  intel(生产)
        nokia研究所
  西门子研究所
  爱立信研究所
  微软技术中心
        Oracle技术支持中心
  SAP
  IBM研发中心
  中兴
  华为
        gameloft
  美国新蛋
        美国科胜讯
        美国安捷轮
        美国synnex
  esmertec
  凹凸科技股份有限公司
        NEC
        Synnex
        汉略
  金蝶研发中心
        金山
  用友软件开发中心
  联想全球研发中心
        大唐研发中心
        神州数码研发中心
  盛大研发中心
  腾讯研发中心
  迈普
  康佳集团成都研发中心
  长虹成都IC设计中心
        摩托罗拉成都研究所
        赛门铁克(刚设立)
        平安保险集团成都研发中心

posted @ 2007-10-20 15:41 肖高辉(MSCD) 阅读(215) | 评论 (1)编辑

2007年10月17日 #

[转]vi编辑器学习

 ex是底层的编辑器,而vi只是它的“可视模式”,因此在使用vi时ex也是可用的。

  vi有两种工作模式:命令模式和插入模式。
一、
  保存和退出文件
    命令ZZ,注意是大写。
    或使用ex命令,:w保存文件但不退出vi,如果没做修改可以用:q退出,:wq保存并退出。
  不保存而直接退出
    :e!
      放弃本次会话中所做的任何编辑,返到初始文本。重新进行编辑。
    :q!
      放弃编辑并退出vi。
  保存时
    :w! filename 如果filename已经存在,则强制覆盖。
二、
  移动光标
    有许多移动光标的vi命令:
    ×上、下、左或右每次移动一个字符
    ×按照单词、句子或段落那样的文本块前移或后移
    ×在文件中前移或后移,每次移动一屏

    单一移动
      h 左移,一个空格
      j 下移,一行
      k 上移,一行
      l 右移,一个空格
    数字参数
      可以在移动命令的前面添加数字。
    行内移动
      0 移动到行首
      $ 移动到行尾
    按文本块移动
      w 向前移动一个单词,并把符号和标点作为单词处理。
      W 也是向前移动一个单词,但不把符号和标点作为单词。
      b 与w相反
      B 与W相反
三、
  简单编辑
    i 插入命令,在当前光标位置开始。
    a 把文本插入到光标之后。
    c和移动命令,修改。
    cc 替换整个当前行。先删除原有文本,并留出一个空白行插入文本。
    C 相当于c$,替换从当前光标位置到行尾的所有字符。
    r 替换命令,用单个字符替换原光标位置的一个字符。
    s 可替换多个字符。3s删除其后的3个字符然后进入插入模式。
    S 删除整行,进入插入模式。
    R 进入改写模式,你输入的字符逐个替换屏幕上的文本。最多改写一行。
    ~ 改变字符的大写小。把光标位置的大小写进行转换。可以加数字前缀。
    d加移动命令,删除文本。
      dw总是删除本行中下一个单词前面的空格,如果不想删除那个空格,可使用de,只删除到单词的尾部。
      dE将删除到单词的尾部,并且包括标点符号。
    dd 删除整行。
    D 相当于d$,删除从光标位置到行尾的文本。
    x 删除字符,X删除光标前的字符。
    p 粘贴命令,P把文本粘贴到光标的前面。
        调换两个字母的顺序
    mvoe -> move  把光标移到v上,然后用xp命令。
    y加移动命令,复制本文。经常用于一行或多行文本的操作。
    yy 与 Y 相同,都是复制一行。
    . 重复最后的命令
    u 撤消上次的操作
    U 撤消对一行进行的任何修改
四、
  插入文本的常用方法
    A 在当前行的尾部添加文本
    I 在行首插入文本
    o 在光标所在行的下面新建一行,等待输入文本。
    O 在光标所在行的上面新建一行,等待输入文本。
    s
    S
    R
    所有这些命令都使你处于插入模式,在插入文本后,要按下ESC键返回命令模式。
五、
  使用J合并两行
    当要把两行合并为一行时,将光标定位在第一行上,然后按下J键来合并这两行。
    带数字参数可以合并多行。

六、快速移动
  并不只是使用vi创建新文件,还会把许多时间花在用vi编辑现有的文件上。在处理文件时,需要直接到达文件的指定位置,然后开始工作。
  按屏幕移动
    ^F 向前滚动一屏。
    ^B 向后滚动一屏。
    ^D 向前滚动半屏。
    ^U 向后滚动半屏。
  使用z重新定位屏幕
    z 把当前行移动到屏幕顶部并滚动。
    z. 把当前行移动到屏幕中央并滚动。
    z- 把当前行移动到屏幕底部并滚动。
  刷新屏幕
    ^L
  在屏幕内移动
    H 移动到起始点,屏幕首行。
    M 移动到屏幕中间行。
    L 移动到屏幕中间行。
    nH 移动到屏幕首行下面的第n行。
    nL 移动到屏幕上面的第n行。
  按行移动
    return 移动到下一行的首字符
    + 移动到下一行的首字符
    - 移动到前一行的首字符
    与j和k不同的是,这三个命令会忽略任何空格或制表符。
  在当前行内移动
    ^ 移动到当前行第一个非空格的字符。
    n| 移动到当前行的第n列。
  按文本块移动
    e 移动到词尾
    E 移动到词尾(忽略标点)
    ( 移动到当前句子的开始
    ) 移动到下一句子的开始
    { 移动到当前段落的开始
    } 移动到下一段落的开始
    [[ 移动到当前节的开始
    ]] 移动到下一节的开始
  按搜索移动
    /pattern 这里暂时只把模式看成单词或短语,以后讨论更有用的模式匹配语法。
    ?pattern 向后搜索
    在这两种情况下,如果需要,都将从文件的首部或尾部开始搜索。
  重复搜索
    n  同向重复搜索
    N  反向重复搜索
    /  向前重复搜索
    ?  向后重复搜索
    由于上次的搜索模式是可用的,因此可以在搜索某个模式后做些编辑工作,然后通过使用n,N,/或?对同一
    模式进行再次搜索,而不用再次输入搜索命令。
    可以设置wrapscan来控制是否需要折回搜索。
      :set nowrapscan
    这样向前搜索失败时,不会折回到文件前部。
  Ctrl-G命令使下列信息显示在屏幕底部:当前行号、文件总行数和当前行在总行中的百分比。

  G(转移)命令
    G命令使用行号作为数字参数而直接移动到那行,不再行号的G命令把光标移动到文件的最后一行。
    如果在这段时期没有进行编辑,那么输入两个反引号(``)可使你返回原来的位置(上次调用G命令的位置)。如果已进行了编辑,然后使用了不同于G的命令移动过光标,那么``将把光标返回到上次编辑的位置。如果已经调用了搜索命令(/或?),则``将把光标返回到开始进行搜索的位置。一对单引号('')除了把光标返回到行首而不是光标以前所在的确切位置外,它与两个反引号的作用很相似。

posted @ 2007-10-17 10:27 肖高辉(MSCD) 阅读(119) | 评论 (0)编辑