过客

路过!
posts - 133, comments - 31, trackbacks - 2, articles - 3
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

公告

2008年6月6日

在网上看见下面的有趣的题目:
short s=0;
s+=1;
 
short s=0;
 s=s+1;
两个有什么区别?

posted @ 2008-06-06 07:34 过客路过 阅读(372) 评论(0) 编辑

1. 要看书。在成千上万的编程图书中,可能很大一部分根本毫无用处。 但是仍然有很多图书对你的(编程)能力有很大的提升。我一直坚持,相比在网络上查找很多有用信息,在同类图书中查找要来得更容易更快捷。阅读技术图书可心 更好地抓住核心内容。对于理论,架构和设计等方面来说,图书也一样很有帮助。只不过要找到高质量的、权威的信息,就不如网络上可供查找的范围广。

2. 读代码。这也是我很喜欢的一种方式。如果我并没有几年的专业编程 工作经验,在学习之初我并不会去读很多复杂的代码。如果我要是早些开始学习,我将是一个比现在更好的程序员。但是,开始时我会从一些开源项目里,去学习那 些源代码(当然,这些代码不能与我的工作有关,也不是我自己写的)。要从自己会使用到的,或者自己感兴趣的程序开始这项工作。我是从 Paint.net 这个网站里开始学习的,而且已经积累了很多关于 .NET 的编程技术。

读别人的代码可以为你提供更多不同的工作思路,这比你完全凭自己思考得到的工作方式要多。

3. 写代码。谨记,要写大量的代码。从根本上来讲,最好的学习方法就 是实践。如果不写代码,你根本不能把(某种语言中)内在的东西学习透彻。学习之初,可以从一些技术指南和图书中照搬一些尽量简单的程序。当然,这种照搬是 要自己完全手工输入,而不是复制和粘贴,这两种之间的效果差别是很大的。这种方法的主旨就在于,使知识内在化,并思考其用法,而不是盲目照抄。学习过程 中,要时常查找新的 API 调用方法,这其实是简单的事情。

最重要的是,要写一个你自己的程序,不管它是一个简单的游戏,或者是一个参与开源项目的程序,还是一个公为 你自己使用的简单插件。用不同的方式来写程序,尽量尝试使用新的技术,新的技巧,新的设计方式。一定要让现在的项目比以往的项目更好。想要成一个优秀的开 发者,这一点是核心。

4. 与其他开发者交流。像 Apple,微软,Google 等大公司一样的新闻描述的一样,(与其他开发者交流)可以让你解决一些复杂的问题。虽然这并不能让你感觉到自己已经成一个团队或是社区的成员,但是这种方法可以让你接触到更多不同的想法。

不同类型的项目要求不同的设计方法,代码技术,开发流程和设计思想。如果你工作在一个小团队里,你不必与太多的人接触,只要在用户群会议中找到一些人(来讨论)即可。如果这样还不行的话,参与到在线论坛中与其他人讨论(这时你需要更努力地寻找高质量的讨论内容)。

6. 教会别人。相对于仅仅读代码之类的工作,教其他人学习可以让你更深入地学习某个技术,这种方法有着非凡的效果。教会别人某个技术,同样也会让你更专注于这种技术,并且可以比别人更深层次地理解它。同样你也会面对一些问题。

“如果你不能向一个六岁的儿童解析清楚一个概念,那么其实是你并没有完全理解它。”Albert Einstein说。

教学场景可以是无穷无尽的:与工作搭档一对一交流,休息碰面,非正式周会,学习茶会,教室,讨论发表会,等 等。每周在相同理念开发者之间举办一次30分钟的非正式会议怎么样?每周,让几个人来就他们想要更深入了解话题,向大家传授这些技术知识,并且展开讨论。 如果你知道你将要向团队成员们传授正学学习的知识,你是不是更想要了解这项技术的每个细节呢?

6. 收听网络电台。如果你有空闲的时间,可以订阅网络电台节目。我现在最喜欢的编程节目就是 .Net Rocks。这个节目还会做一些视频节目,叫做 dnrTV 。这样会让你即时捕捉到最新最好的技术。一个人是不能学习到所有知识的,而网络电台刚是一个学习了解广泛知识的途径。

7. 阅读博客。博客远远比阅读者要多,但是其中有一些却是极其优秀的。我这里不并不是要推荐博客,因为网络上已经有了足够多的博客。这是与真正在开发你所喜欢和使用的软件的开者联系的好方法。

8. 学习新的语言。即使你已经在 C(++,#) / JAVA 等语言上有很好的造诣,仍然有很多其他可以解决问题的编程语言。学习新的语言,是对你已有思维方式的挑战。这不仅仅是另一种语言,更是对思维的重新架构。 的确,所有的语言最后都会被编译成汇编程序,但是这并不意味着高级语言毫无价值。

9. 学习不正确的方式。除了 要学习应该怎么做,还要学习不应该怎么做。经常阅读 Dailywtf.com ,学习你并不知道的经验与教训。学习适当的面向对象设计方式,代码写作方式,和必须要写的代码等,是很好的方式,但是如果不细心的话也容易养成不良习惯。 学习认识不正确的思路是负责项目开发至关重要的一环。

维基百科对很多觉的不正确方式有十分透彻的分类。

10. 要谦虚。

学习,意味着:

  • 用更好的知识代替不完美的知识
  • 增长你所不知道的知识

只有承认自己有所不足,才能有学习的动力。归根到底,就是谦虚,不对吗?如果你开始认为你已经掌握了所有需要的知识,那么你就危险了。真正的学习是如饥似渴地追逐知识并使其内在化,这需要很大的努力。我们都知道这一点,但是要必须时常不断地提醒自己。

posted @ 2008-06-06 07:08 过客路过 阅读(70) 评论(0) 编辑

编写不安全代码

MSDN:"尽管实际上对 C 或 C++ 中的每种指针类型构造,C# 都设置了与之对应的引用类型,但仍然会有一些场合需要访问指针类型。例如,当需要与基础操作系统进行交互、访问内存映射设备,或实现一些以时间为关键的算法时,若没有访问指针的手段,就不可能或者至少很难完成。为了满足这样的需求,C# 提供了编写不安全代码的能力。

在不安全代码中,可以声明和操作指针,可以在指针和整型之间执行转换,还可以获取变量的地址,等等。在某种意义上,编写不安全代码很像在 C# 程序中编写 C 代码。"

不安全代码必须用修饰符 unsafe 明确地标记。

这里所谓的编写"不安全代码",就是要跳出.net CLR的限制,自己进行地址分配和垃圾回收.在C++里面,我们定义一个简单的指针,至少要做三件事,1.给他分配内存,2.保证类型转换正确3将内存空间释放,而在.net环境里,CLR将程序员从这些事情里解放出来,用户不再需要直接手工地进行内存操作。但是有时候,比如调用windows底层函数, 或是效率上的原因使我们需要自己去操作地址空间,本文主要是说明在c#里面指针的用法.

指针的使用、操作内存

1.& 和 *

    c++里面,我们很熟悉这两个东西.在c#里面他们也一样可以用,只不过含有他们的代码如果不在unsafe 标记下,编译器将会将它拒之门外.当然如果条件编译参数没有/unsafe 也是无法编译通过的(如果用vs.net集成编译环境,则在项目属性页-代码生成节将"允许不安全代码块"设置成true).

    &可以取得变量的地址,但是并不是所有的变量,托管类型,将无法取得其地址.c#里面,普通的值类型都是可以取得地址的,比如struct,int,long等,而class是无法取得其地址的,另外string是比较特殊的类型,虽然是值类型,但它也是受管的.这里插一下另一个运算符,sizeof,它也是仅可用于unsafe模式下.

看下面这段代码,里面简单的用到了*,&,sizeof,还有c#里很少见但c++里大家很熟的->:

class Class1
    
{
        
struct Point
        
{
            
public int x;
            
public int y;
        }

        
public static unsafe void Main() 
        
{
            Point pt 
= new Point();
            Point
* pt1 = &pt;
            
int* px = &(pt1->x);
            
int* py = &(pt1->y);
            Console.WriteLine(
"Address of pt is :0x{0:X} ",(uint)&pt);
            Console.WriteLine(
"size of the struct :{0} ",sizeof(Point));
            Console.WriteLine(
"Address of pt.x is :0x{0:X} ",(uint)&(pt.x));
            Console.WriteLine(
"Address of pt.y is :0x{0:X} ",(uint)&(pt.y));
            Console.WriteLine(
"Address of px is :0x{0:X} ",(uint)&(*px));
            Console.WriteLine(
"Address of py is :0x{0:X} ",(uint)&(*py));
            Console.ReadLine();
        }

    }

我这里运行的输出结果是:

Address of pt is :0x12F698
size of the struct :8
Address of pt.x is :0x12F698
Address of pt.y is :0x12F69C
Address of px is :0x12F698
Address of py is :0x12F69C

可以看出struct的首地址与第一个成员变量的地址相同,而这个struct的长度是8个字节(=4+4).

2.fixed

虽然在unsafe模式下可以使用指针,但是unsafe的代码仍然是受管代码.CLR会对它的对象进行管理以及垃圾回收,CLR在这个过程中就会对内存进行重定位,可能过一段时间后,根据指针指向的地址就找不到原来的对象了,岂不是说指针在c#里没有什么实际的作用?别急,还有fixed.

fixed 语句设置指向托管变量的指针并在fixed里的语句块执行期间“锁定”该变量(或者是几个变量)。如果没有 fixed 语句,则指向托管变量的指针将作用很小,因为垃圾回收可能不可预知地重定位变量。(实际上,除非在 fixed 语句中,否则 C# 不允许设置指向托管变量的指针。)

看一段与刚才类似的代码,不同的地方是这里的输出地址的语句都在fixed块里.为什么不直接像第一个例子那样直接输出呢?这是因为我们对 Point进行了一个小小的改动,它不再是struct了,它现在是class!它是托管类型了,它的成员都没有固定的地址.但是在fixed块里,它的地址是固定的.

class Point
        
{    
            
public static int x;
            
public int y;
        }

        
public static unsafe void Main() 
        
{
            Point pt 
= new Point();
            
int[] arr = new int[10];
            
//如果不用fixed语句,无论是静态成员还是实例成员,都将无法取得其地址。
            
//int* ps = &CPoint.StaticField;
            
//PrintAddress(ps);
            fixed (int* p = &Point.x)
                Console.WriteLine(
"Address is 0x{0:X}",(int)p);
            
fixed (int* p = &pt.y) 
                Console.WriteLine(
"Address is 0x{0:X}",(int)p);
            
fixed (int* p1 = &arr[0],p2 = arr)
            
{
                Console.WriteLine(
"Address is 0x{0:X}",(int)p1);
                Console.WriteLine(
"Address is 0x{0:X}",(int)p2);
            }

            Console.ReadLine();
        }

我这里运行的输出结果是:

Address is 0x3D5404
Address is 0x4BF1968
Address is 0x4BF1978
Address is 0x4BF1978

3.分配内存

在堆栈上分配内存

c#提供stackalloc ,在堆栈上而不是在堆上分配一个内存块,语句为 type * ptr = stackalloc type [ expr ];它的大小足以包含 type 类型的 expr 元素;该块的地址存储在 ptr 指针中。此内存不受垃圾回收的制约,因此不必使用fixed将其固定。此内存块的生存期仅限于定义该内存块的方法的生存期。如果内存空间不足,将会抛出System.StackOverflowException异常.

以下是一段示例程序(form msdn),

public static unsafe void Main() 
{
    int* fib = stackalloc int[100];
    int* p = fib;
    *p++ = *p++ = 1;    //fib[0]=fib[1]=1
    for (int i=2; i<100; ++i, ++p)
        *p = p[-1] + p[-2];//fib[i]=fib[i-1]+fib[i-2];
    for (int i=0; i<10; ++i)
        Console.WriteLine (fib[i]);
    Console.ReadLine();
}
在堆上分配内存

既然有stackalloc,有没有heapalloc呢?答案是没有,c#没有提供这样的语法.想在堆上动态分配内存,只能靠自己想办法了.通过Kernel32.dll里的HeapAlloc()和HeapFree()可以达到这个目的.看下面的代码:

using System;
using System.Runtime.InteropServices;
public unsafe class Memory
{
    const int HEAP_ZERO_MEMORY = 0x00000008;//内存起始地址
    //获得进程堆的句柄
    [DllImport("kernel32")]
    static extern int GetProcessHeap();
    //内存分配
    [DllImport("kernel32")]
    static extern void* HeapAlloc(int hHeap, int flags, int size);
    //内存释放
    [DllImport("kernel32")]
    static extern bool HeapFree(int hHeap, int flags, void* block);
    static int ph = GetProcessHeap();//获得进程堆的句柄
    private Memory() {}
    public static void* Alloc(int size) //内存分配
    {
        void* result = HeapAlloc(ph, HEAP_ZERO_MEMORY, size);
        if (result == null) throw new OutOfMemoryException();
        return result;
    }
    public static void Free(void* block) //内存释放
    {
        if (!HeapFree(ph, 0, block)) throw new InvalidOperationException();
    }
}
class Test
{
    unsafe static void Main() 
    {
        byte* buffer = (byte*)Memory.Alloc(256);
        for (int i = 0; i < 256; i++) 
            buffer[i] = (byte)i;
        for (int i = 0; i < 256; i++) 
            Console.WriteLine(buffer[i]);
        Memory.Free(buffer);
        Console.ReadLine();
    }
}

posted @ 2008-06-06 06:34 过客路过 阅读(461) 评论(2) 编辑