随笔-57  评论-12  文章-0  trackbacks-0
  2008年10月20日

直接用Writer发表试试。

posted @ 2008-10-20 15:25 Martin 阅读(342) | 评论 (0)编辑 收藏
  2006年11月16日
很久不写了,留点痕迹吧
posted @ 2006-11-16 18:01 Martin 阅读(358) | 评论 (0)编辑 收藏
  2006年8月25日
美国人挂在嘴边的句子

作者: Jerry Cat
时间: 2006/07/09
链接:
http://www.cppblog.com/jerysun0818/archive/2006/07/09/9611.html

1-----You can't argue with success

Though you may tell someone they are doing something in a wrong way, you have to stop criticizing them if their method works.


2-----Nothing succeeds like success

Like money in a savings account, success seems to compound itself. A person who has a small success expects it to be followed by a bigger one.


3-----The end justifies the means

It does not matter how you succeed. Any method is all right if it works for you. This proverb is not always accepted. Often one hears the opposite, " the end doesn't justifies the means."


4-----If you can't beat them, join them

If you cannot compete successfully with a person or a group, stop competing and go join them. Then you can share in their success.


5-----Always a day late and a dollar short

A criticism of a person who never succeeds. The failures may not be drastic but the pattern of small failures is annoying to others, especially in two areas that Americans value as highly as time and money.


6-----One thing at a time

Concentration leads to success. The person who tires to do too many things at once may fail at all of them.


7-----We'll cross that bridge when we come to it

We will not discuss or worry about a future problem. If and when the problem becomes urgent, we will deal with it.


8-----Don't put the cart before the hourse

Do things in a sensible order. For example, do something to impress your boss before you ask for a raise in pay. Don't ask for the raise first.


9-----All's fair in love and war

Do not trust a rival or an enemy at all. His or her desire to succeed will overrule everything else. Such a people may lie, cheat, attack or do many other things far more terrible than he or she would normally do.


10-----When in Rome do as the romans do

Flexibility leads to success in unfamiliar circumstances. People may changes their normal way of doing things in order to fit in better with those they are visiting.


11------Easy does it

Some things require gentleness for success. One should not push too hard. Force may cause damage

12-----The bigger they are, the harder they fall

Do not be discouraged by the size of your problem or the fame of your competitor. (the saying is traced to a boxer preparing to fight an opponent much larger than he was.)


13-----All is well that ends well

If we can come to a successful conclusion, let us forget about the pain and mistakes along the way.


14-----Go for it

Go ahead and try to achieve your goal. Don't worry about failing. Similar to this proverb--God provides food to every bird, but he won't cast to its nest, you got to earn it.


15-----Every little bit helps

If someone apologizes for giving only a small gift, the recipient may encourage the giver by quoting this proverb. This is especially true if there is a huge job to do or a huge amount of money to be raised from many people.


16-----Look before you leap

Don't jump into a situation carelessly. You may land in difficulty.


17-----All that glitters is not gold

Do not be fooled by worthless things that seem valuable or look attractive.

"Fool's gold " is the popular name for a kind of stone that glitters like gold but contains no gold at all.


18------Curiosity killed the cat

Do not ask too many questions or become too curious about things. You may get yourself into trouble like a cat that does not see the danger in a new situation.


19-----Don't put all your eggs in one basket

Do not risk everything in one place or on one project. Divide your sources that one failure will not ruin you. Similar to this proverb---Do not use both feet to test the depth of the river.


20-----Don't bite off more than you can chew

Do not attempt something too large for you to handle.


21-----Too good to be ture

This is often used to warn about advertising. An offer looks good but turns out to be misleading.


22-----Flattery will get you nowhere

If flattery (insincere praise) is recognized, it will not succeed. One sometimes hears the opposite, Flattery will get you everywhere.


23-----There is no such thing as a free lunch

If someone you do not know offers you a free lunch or other gift, watch out. The gift may be a method of getting something from you.


24------Pride goes before a fall

Do not be over-confident and careless. Your circumstances may change quickly and the emptiness of your bragging will be exposed.


25------Better safe than sorry

It is better not to take a risk than to take a risk foolishly.


26-----Winning isn't everything

One may enjoy the process of playing a game whether one wins or not. This traditional proverb is now often heard in the opposite form, Winning isn't everything. It's the only thing!


27------Time will tell

Wait and see how something will work out. Don't trust a person or thing too much right now.


28-----The burned child shuns fire

Some of life's lessons are learned the hard way. The person who has had to pay for a mistake will be careful not to make that mistake again.


29-----There are other fish in the sea

There are opportunities! For example, this may be used to encourage someone who has just broken off a relationship with a sweetheart.


30-----Don't count your chickens before they are hatched

Do not become too confident of success before you can see that progress is happening.


31-----Rome wasn't built in a day

Do not be discouraged if your goal still seems far away. On a big project, success takes time!


32-----There is more here than meets the eye

Be careful. People are hiding something in order to protect themselves or take advantage of you.


33-----The customer is always right

Businesses instruct their clerks not to do anything that might threaten a customer's self-esteem, no matter how unreasonable the customer is. Any complaint of any customer must be carefully and politely heard by any employee. The customer must not be made to feel ignorant.


34-----Be true to yourself

Do not compromise yourself or your beliefs in order to please other people or fit in with a system. Know who you are. Let other people know what you stand for. Do not give in to pressure to conform or to do things you do not agree with.


35-----Once bitten, twice shy

This may be used to explain the behavior of a person who is overly cautious about something. The wise person makes mistakes but does not usually make the same mistake twice.


36-----It isn't over till the fat lady sings

I refuse to admit defeat yet. Though right now it looks like I may fall, there is still time for things to change. (The "fat lady" is an insulting reference to an opera soloist, opera often end with a female solo.)


37-----To each his own

Each individual should be allowed to have his or her own preferences. people will naturally choose different activities, goals, life-s, etc.


38-----Looking out for number one

Looking out for one's own personal welfare more than anything else. This is sometimes used to criticize a person who has abused or taken advantage of someone else, "All he was doing was looking out for number one." Or it may be used to defend himself or herself, "There is nothing wrong with what I did. I was just looking out for number one."


39-----When the going gets tough, the tough get going

When situation is difficult, only the determined people can handle it. They don't give up!


40-----Times flies when you are having fun

A day seems shot when it is full of enjoyable things but it seems like an eternity if one is idle or stuck with a boring job.


41-----If it feels good, do it

Live according to your desires at the moment. Forget about rules, regulations or consequences. Just have fun.


42-----Live and let live

Do not be judgmental. Do not try to control or punish other people.

You live as you like and let others live as they like.

43-----Variety is the spice of life

Variety is what makes life pleasureable. Boredom is seen as a threat and monotony is the surest route to boredom.

44------Money talks

Wealth has influence. People who make big contributions to political candidates are "talking" to the candidates and expecting them to listen.

45-----All work and no play makes Jack a dull boy

This is used to criticize someone who takes work too seriously. It affirms that play is as an essential part of human life.


46-----The more the merrier

This is a way of welcoming someone to participate. It means, the bigger the group, the bigger the party.


47------A bird in the hand is worth two in the bush

A thing you already have is worth twice as much as a thing you might get in the future.


48-----You can't have your cake and eat it too

Make up your mind. Sometimes one must choose between two very desirable things, giving up one thing in order to enjoy the other.


49-----There is more than one way to skin a cat

Any of several methods may bring about the desired result. Each person may choose the method that best suits her. If one method does not work, choose a different one and try again.


50-----One man's meat is another man's poison

Since the same thing may affect different people in different ways, each person must choose the things that will work in his or her particular case. 

51-----Love finds a way

Love is considered one of the most powerful and determined forces in the world. Two people in love will "find a way" to get together.


52-----Absence makes the heart grow fonder

When lovers are separated, they may think about each other constantly. their love may deepen.


53-----Out of sight, out of mind

This is said of someone not considered important or worthy of loyalty. One forgets about that person as soon as he or she is out of sight.


54------Love makes the world go around

Love is the driving force in all of life. Love makes life worth living. If you understand love,you understand everything about life.


55-----Marry in haste and repent at leisure

Do not rush into any long-term commitment. In the past this meant, "take your time before you commit yourself to a marriage partner." Now it means, "Do not commit yourself to anyone you will be sorry." or it may mean, "Live with a person for a while before marrying him or her." It may also apply to any long-term partnership, such as a joint business venture.


56------Talk is cheap

Talking is not doing, It is easy to talk about a plan, but this gets little respect untill one takes a risk and puts the plan into action.


57-----Love is blind

When people are in love, they do not see each other's faults.


58-----Time is money

Time can be converted to money, that is, wages are often paid per hour of work. Managers want employees to do things quickly because "time is money." If employees waste time, the company loses money.


59-----If you're so smart, why ain't you rich

This question implies that intelligence, like time, can be converted into money. It is used to cut down an ordinary person (not a rich one) who has expressed an opinion as if it is the final word on the subject.


60-----Beggars can't be choosers

Beggars have to accept whatever they get. A poor person has very few choices in life. This harsh reply may be used if people complain about the size or quality of a gift.


61-----The love of money is the root of all evil

Greed will lead a person into many other evils such as cheating, oppression and violence. (one also hears, money is the root of all evil, which places the blame more on money than on greed.)


62-----A fool and his money are soon parted

Folly will always show up in the way a fool handles money. Wise people are the ones smart enough to guard money once they have it.


63-----It isn't whether you win or lose, it's how you play the game

Playing well and fairly is more important than succeeding. This saying is sometimes used to console someone who has lost a game. The ideas is that in spite of losing, one may still feel like a success if one has played as well as possible.


64-----Money can't buy happiness

There are some things money just can't buy. This reminds people that money is not an ultimate value although it often is treated as one. The saying may be used as a comment when a wealthy but lonely and wretched person commits suicide.


65-----Nice guys finish last

Success is valued more highly than kindness and politeness. If unkindness and impoliteness contribute to one's success, they are sometimes excused as "toughness" or "what it takes." This is more often applied to men than woman.


66-----The one who dies with the most toys wins

This recent, cynical saying makes fun of three common American desires-- competing, accumulating property, and having fun.


67-----Just do it

This proverb is actually a recently invented advertising slogan for the Nike Shoe company. It may also be the best three-word summary of American cultural values. It means, "quit being indecisive, Don't bother consulting a lot of people. Don't waste much times on planning. Just do it and do it now.


68-----You only go around once in life

Enjoy life, do what you want to do, experience everything you can.


69-----Put your money where your mouth is

Get serious about what you are saying. When you have spoken in favor of something, prove that you mean it by risking some money on it in a bet or an investment.


70-----The best defense is a good offense

Be pro-active and goal-oriented, not timid or conservative. Aim to conquer, not merely to protect yourself.


71-----The road to hell is paved with good intentions

Like talking, intending is not doing. Actions matter far more than intentions. Actions determine where a person ends up.


72-----Never put off till tomorrow what you can do today

Doing is better than waiting. If you wait, you might lose your opportunity


73-----Action speak louder than words

To know people's hearts, pay more attention to what they do than what they say.


74-----Idle hands are the devil's workshop

When people have nothing constructive to do, they get into mischief. The child who is bored may pick up a crayon and color on the wall, something that would not happen if the child were happily occupied in some other activity.


75-----You've got to take the bull by the horns

Grab directly and fearlessly for the toughnest part of a problem. Don't look for an easy way out.


76-----Money doesn't grow on trees

You have to work for things. Success does not come on its own.


77-----Easy come, easy go

If you have received something without working for it, it may soon be gone. You will not be as careful with it as you will be with something you earned.


78-----Too many cooks spoil the broth

Individual action or action by the smallest group possible is better than consulting a lot of people. Too much consultation will waste time and create problems.


79-----Stand on your own two feet

Grow up; act like an adult. Don't ask me to do something for you. This may be said to a young or immature person who depends too much on others. It is like a cow kicking a grown calf that wants to suckle.


80------If you want something done right, do it yourself

This discourages people from trusting anyone except themselves. It maybe said to someone who ask another person to do a job for him/her and then complains about it was done.


81------Necessity is the mother of invention

When a person really needs something, he or she figures out some new way to get it. This creative initiative to change one's situation is considered much better than giving up and accepting things as they are.


82------First come, first served.

This saying is used when there is a limited supply of something such as tickets to an event or food at a picnic. The idea is that no matter what your status, if you want some of what is being given away or sold, you must present yourself in person and in good time.


83-----No pain no gain

one must put forth an effort in order to succeed. This saying comes from physical fitness instructors. They say that if you do not exercise hard enough to make your muscles hurt, the exercise is not doing you much good.


84-----Give him an inch and he'll take a mile

Be careful of presumptuous people who take more initiative than they should. If you do someone a small favor or delegate a little permission or a little power, he or she may take advantage of you.


85-----If it isn't broke, don't fix it

Do not bother trying to improve something if it is already working satisfactorily. That is a waste of time and you run the risk of breaking the thing while you are trying to improve it.


86-----There is no harm in trying

A person expects to be respected for making an effort even if it does not succeed.


87-----The cure is worse than the disease

While trying to solve one problem, we create an even bigger problem. We would have been better off if we had done nothing.


88-----Count to ten before you lose your temper

Do not instantly let your feelings control your actions. Slow down. Think before you react.


89-----All things come to him who waits

Sometimes patience is better than initiative.


90-----There is many a slip between the cup and the lip

This refers to a plan that has gone wrong. When one is drinking from a cup, one intends to get all the drink into the mouth, but this does not always happen. The plan is good but it can still fail.


91-----Boys will be boys

People will act according to their nature, including some mischief. This is sometimes used to describe irresponsible but not too seriously wrong behavior by men. In other words, grown men will sometimes act like little boys.


92------One bad apple can spoil the whole barrel

Do not associate with bad people. They may spoil you as a rotting apple spoils the apples next to it in the barrel.


93-----Better the devil you know than the devil you don't

People are generally not trustworthy. It is therefore better to deal with a familiar person or situation than an unknown one. That way you know what to watch out for and you can protect yourself better.

94-----Nobody is perfect

Everyone has shortcomings. This is used as an excuse for a minor Mistake that has been made.


95------The grass is always greener on the other side of the fence

People are never satisfied with what they have. They always want what someone else has.


96-----A man is only as old as he feels

A person's energy level is more important than age.


97-----A man's home is his castle

At home a man is like a king, completely free to do as he wishes.


98-----Clothes make the man

The way a man dresses will affect the way he sees himself and the way other people see him. The implication is that if you want to succeed, dress for success.


99-----Two heads are better than one

Two people are more likely to succeed than one working or thinking alone. It is better to consult than to act alone.


100-----Beauty is only skin deep

One should admire a person, particularly a woman, for her deep, inner character and not simply for her superficial beauty.


101-----Many hands make light work

Cooperation makes a job much easier.


102-----There is safety in numbers

Do not take large risks all by yourself. Do not walk alone on a dangerous street at night.


103-----Charity begins at home

One should be kind to close relatives before doing good to the community in general.


104-----There is no place like home

Home is the best place in the world. The most relaxing and enjoyable place.


105-----Silence is consent

Do not stay silent when you see a problem or an injustice. Speak out against it. Say, "Enough is enough." or people will think you do not mind the situation.


106-----What goes around comes around

What you do to others, especially if it is bad, will eventually be done to you. As you seek your own success, do not treat other people badly. They may take revenge and spoil your success.


107-----Honesty is the best policy

When one is tempted to cheat, this proverb serves as a reminder of the importance of good character. Honesty brings trust and trust leads to success.


108-----If the shoe fits, wear it

If an accusation is true, accept the blame. This saying can also mean, " if a suitable opportunity comes to you, take it!"


109-----You can lead a horse to water, but you can't make him drink

Even when given an opportunity, some people will not use it. In that case the blame is all on him.


110-----You made the bed, you lie in it

You created a certain situation so you are now responsible to take the consequences.


111-----The pot calling the kettle black

Since the pot and the kettle are equally black from the cooking fire, the pot has not right to criticize the kettle. This might be used if someone known to be a gossip criticizes someone else for gossiping.


112-----Don't get mad, get even

When someone treats you badly, do not just get angry, express your anger in action.


113-----Get a life

Do not be so unreasonably critical about tiny things. Find something better to do with your time.


114-----My country, right or wrong

I am absolutely loyal to my country whether or not I agree with its policy in a certain area.


115------There are two sides to everything

Never assume that all the blame for a conflict lies on one side, especially if you have heard only one side of the story.


116-----Forgive and forget

Conflicts should be resolved and ended, not left alone to cause years of regret, bitterness and pain.


117------Business and pleasure don't mix

When you work, work. When you play, play. It is inefficient and therefore unwise to try to mix the two.


118-----Let a sleeping dog lie

Do not meddle in something that will cause you no trouble if you leave it alone. If you wake up the "dog," it may bite you.


119------No man can serve two masters

Divided loyalty will break down sooner or later.


120-----Make it short and sweet

Speak briefly and to the point. We don't have time for the details


121-----What will be will be

Some things cannot be changed or avoided.


122-----A friend in need is a friend indeed

My true friend is the one who show loyalty to me by helping me when I am in need.


123-----People who live in glass house should not throw stones

Do not attact anyone if you have no way to defend yourself when they attact you in the same way.

124-----Opposites attract

As positive and negative magnets attract each other, so people of very different types attract each other. For example, a quiet woman marries a very outgoing man. These differences have a mixed effect on the couple, partly making them interested in each other but also leading to miscommunication and conflict.


125-----The early bird catches the worm

Punctuality is important. If you want to succeed, arrive early, like the bird that hunts worms at dawn before they go into hiding for the day.


126-----Blood is thicker than water

Blood relationship is stronger than voluntarily chosen friendship. Brothers will stick up for each other against their own friends if they have to. This proverb is not quoted much anymore because kinship is not as highly valued as it used to be.


127-----You can't teach an old dog new tricks

Once habits are learned, it is very hard to change them.


128-----To err is human

It is normal for humans to make mistakes and it is godly (or God-like) to forgive them.


129-----Haste makes waste

Through acting quickly is generally a good thing, acting too quickly may cause troulbe. This proverb is used when someone has done a job carelessly and wrongly. For example, a carpenter in a hurry may cut a board too short and have to throw it away.


130-----Make hay while the sun shines

If you do not seize the opportunity, rain may come and spoil the hay before you get into the barn.


131-----If you can't stand the heat, get out of the kitchen

Withdraw from an activity if you don't like the conflict and criticism it brings. Let other people do it without you.


132-----Everyone puts their pants on one leg at a time

This is to tell us that we are all equal.


133-----Life is a pie

It's dished out in large and small pieces and when's gone, it's gone. If we ask for more than our share, we're just begging to be disappointed.


134-----Making every minute count

Doing something productive all the time, not letting any time "slip away."

135-----A pen is mightier than the sword

A conflict maybe resolved more deeply and effectively by writing the truth than by resorting to violence.


136-----A new broom sweeps clean

A new person in power will change many things and improve the situation. change is better than leaving things as they are.


137-----Tomorrow is another day (or, Tomorrow is a new day.)

No matter how bad things are right now, a person may hope for better prospects in the morning. New opportunities will come.


138-----Opportunity only knocks once

Opportunity is like an unexpected stranger passing by. It knocks on someone's door. If the person fails to answer the door, opportunity goes away and knocks on someone else's door. It does not return to the same person.


139-----Business is business

Do business with strangers as if they were brothers and with brothers as if they were strangers.


140-----History repeats itself

There are patterns in history. Nations rise and fall for similar reasons. Nations do not change their ways or learn from the mistakes of others. They repeat them.


141-----People are people

Their human nature is predictable. They respond to stimulus and incentives.


142-----Keep your friends close, keep your enemies closer

A man without enemy is a man without quality. Think not? Even Jesus Christ had many, many enemies.


143-----Better late than never

Though being on time is better than being late, doing something late is better than giving up or doing nothing.


144-----Punish one, teach a hundred

People problems sometimes must be dealt with harshly. When you make an example of someone, make sure that everyone knows what the lesson is.


145-----Attitude is everything

A pessimist sees a glass half-empty, An optimist sees it half-full.


146-----Like father, like son

A son will be like his father. This may be quoted whenever the son has done something that reminds people of the father. Whether it is good or bad.


147-----When the cat's away the mice will play

People will take advantage of a situation if they can. Employees will get lazy if the supervisor is not keeping an eye on them. A teenage son or daughter might hold a wild party at home during a weekend when the parents have gone out of town.


148-----Praise when praise is due (sometimes even when it isn't)

Do not criticise beyond necessity and seldom in public. And always precede criticism with a few words of praise.


149-----Turnabout is fair play

If a person is taking advantage of someone else, the situation may change. The one on top may end up on the bottom. Then the one who has turned the tables will say, "I am only doing to you what you did to me.


150-----Here today, gone tomorrow

Things may change quickly. This is said as a criticism of a person who changes quickly for no good reason. It may also be said of a thing or situation which does not last. Change is not good when it indicates unreliability.

posted @ 2006-08-25 16:15 Martin 阅读(553) | 评论 (0)编辑 收藏

tokenizer - Break of a string or other character sequence into a series of tokens, from John Bandela
tokenizer - 分解字串,提取内容.作者: John Bandela

例一:
// simple_example_1.cpp
#include<iostream>
#include<boost/tokenizer.hpp>
#include<string>

int main(){
   using namespace std;
   using namespace boost;
   string s = "This is,  a test";
   tokenizer<> tok(s);
   for(tokenizer<>::iterator beg=tok.begin(); beg!=tok.end();++beg){
       cout << *beg << "\n";
   }
}

输出
This
is
a
test

tokenizer默认将单词以空格和标点为边界分开.

例二:
#include<iostream>
#include<boost/tokenizer.hpp>
#include<string>

int main(){
   using namespace std;
   using namespace boost;
   string s = "Field 1,\"putting quotes around fields, allows commas\",Field 3";
   tokenizer<escaped_list_separator<char> > tok(s);
   for(tokenizer<escaped_list_separator<char> >::iterator beg=tok.begin(); beg!=tok.end();++beg){
       cout << *beg << "\n";
   }
}
输出
Field 1
putting quotes around fields, allows commas
Field 3

双引号之间可以有标点.


例三:
// simple_example_3.cpp
#include<iostream>
#include<boost/tokenizer.hpp>
#include<string>

int main(){
   using namespace std;
   using namespace boost;
   string s = "12252001";
   int offsets[] = {2,2,4};
   offset_separator f(offsets, offsets+3);
   tokenizer<offset_separator> tok(s,f);
   for(tokenizer<offset_separator>::iterator beg=tok.begin(); beg!=tok.end();++beg){
       cout << *beg << "\n";
   }
}

把12252001分解为
12
25
2001

例4:
// char_sep_example_1.cpp
#include <iostream>
#include <boost/tokenizer.hpp>
#include <string>

int main()
{
  std::string str = ";!!;Hello|world||-foo--bar;yow;baz|";
  typedef boost::tokenizer<boost::char_separator<char> >
    tokenizer;
  boost::char_separator<char> sep("-;|");
  tokenizer tokens(str, sep);
  for (tokenizer::iterator tok_iter = tokens.begin();
       tok_iter != tokens.end(); ++tok_iter)
    std::cout << "<" << *tok_iter << "> ";
  std::cout << "\n";
  return EXIT_SUCCESS;
}

输出
<!!> <Hello> <world> <foo> <bar> <yow> <baz>
自定义分隔的标点

例5:
    // char_sep_example_2.cpp
    #include <iostream>
    #include <boost/tokenizer.hpp>
    #include <string>

    int main()
    {
        std::string str = ";;Hello|world||-foo--bar;yow;baz|";
        typedef boost::tokenizer<boost::char_separator<char> >
            tokenizer;
        boost::char_separator<char> sep("-;", "|", boost::keep_empty_tokens);
        tokenizer tokens(str, sep);
        for (tokenizer::iterator tok_iter = tokens.begin();
             tok_iter != tokens.end(); ++tok_iter)
          std::cout << "<" << *tok_iter << "> ";
        std::cout << "\n";
        return EXIT_SUCCESS;
    }

The output is:

    <> <> <Hello> <|> <world> <|> <> <|> <> <foo> <> <bar> <yow> <baz> <|> <>
去除-; , 保留|但将它看作是分隔符,当两个分隔符相邻的时候会自动加空格

例6:
    // char_sep_example_3.cpp
    #include <iostream>
    #include <boost/tokenizer.hpp>
    #include <string>

    int main()
    {
       std::string str = "This is,  a test";
       typedef boost::tokenizer<boost::char_separator<char> > Tok;
       boost::char_separator<char> sep; // default constructed
       Tok tok(str, sep);
       for(Tok::iterator tok_iter = tok.begin(); tok_iter != tok.end(); ++tok_iter)
         std::cout << "<" << *tok_iter << "> ";
       std::cout << "\n";
       return EXIT_SUCCESS;
    }

The output is:

    <This> <is> <,> <a> <test>
保留标点但将它看作分隔符

posted @ 2006-08-25 11:11 Martin 阅读(550) | 评论 (0)编辑 收藏
  2006年5月19日

From: http://freeman.cnblogs.com/archive/2005/08/30/226128.html


1、什么是cpuid指令

    CPUID指令是intel IA32架构下获得CPU信息的汇编指令,可以得到CPU类型,型号,制造商信息,商标信息,序列号,缓存等一系列CPU相关的东西。

2、cpuid指令的使用

    cpuid使用eax作为输入参数,eax,ebx,ecx,edx作为输出参数,举个例子:

 __asm
 {
  mov eax, 1
  cpuid
  ...
 }

    以上代码以1为输入参数,执行cpuid后,所有寄存器的值都被返回值填充。针对不同的输入参数eax的值,输出参数的意义都不相同。
    为了更好的在C++中使用cpuid指令,可以使用类对指令进行封装,在类中定义一个专门的函数负责cpuid的执行,他需要一个输入参数。还需要定义四 个成员变量存储cpuid执行以后返回来的值。由于这四个寄存器都是32位长的,可以使用unsinged long 类型变量存储。

 typedef unsigned long DWORD

 class CPUID
 {
 public:
  ...
 private:
  void Executecpuid(DWORD eax); // 用来实现cpuid

  DWORD m_eax;   // 存储返回的eax
  DWORD m_ebx;   // 存储返回的ebx
  DWORD m_ecx;   // 存储返回的ecx
  DWORD m_edx;   // 存储返回的edx

  ...
 }

 void CPUID::Executecpuid(DWORD veax)
 {
  // 因为嵌入式的汇编代码不能识别 类成员变量
  // 所以定义四个临时变量作为过渡
  DWORD deax;
  DWORD debx;
  DWORD decx;
  DWORD dedx;

  __asm
  {
   mov eax, veax ;将输入参数移入eax
   cpuid  ;执行cpuid
   mov deax, eax ;以下四行代码把寄存器中的变量存入临时变量
   mov debx, ebx
   mov decx, ecx
   mov dedx, edx
  }

  m_eax = deax; // 把临时变量中的内容放入类成员变量
  m_ebx = debx;
  m_ecx = decx;
  m_edx = dedx;
 }

    这样就可以通过直接调用Executecupid()函数的方式来执行cpuid指令了,返回值存在类成员变量m_eax, m_ebx, m_ecx和m_edx中。

3、获得CPU的制造商信息(Vender ID String)

    把eax = 0作为输入参数,可以得到CPU的制造商信息。
    cpuid指令执行以后,会返回一个12字符的制造商信息,前四个字符的ASC码按低位到高位放在ebx,中间四个放在edx,最后四个字符放在ecx。比如说,对于intel的cpu,会返回一个“GenuineIntel”的字符串,返回值的存储格式为:

           31      23      15      07      00
        EBX| u (75)| n (6E)| e (65)| G (47)
        EDX| I (49)| e (65)| n (6E)| i (69)
        ECX| l (6C)| e (65)| t (74)| n (6E)

    因此可以这样实现他:

 string CPUID::GetVID()
 {
  char cVID[13];   // 字符串,用来存储制造商信息
  memset(cVID, 0, 13);  // 把数组清0
  Executecpuid(0);  // 执行cpuid指令,使用输入参数 eax = 0
  memcpy(cVID, &m_ebx, 4); // 复制前四个字符到数组
  memcpy(cVID+4, &m_edx, 4); // 复制中间四个字符到数组
  memcpy(cVID+8, &m_ecx, 4); // 复制最后四个字符到数组

  return string(cVID);  // 以string的形式返回
 }

4、获得CPU商标信息(Brand String)

    在我的电脑上点击右键,选择属性,可以在窗口的下面看到一条CPU的信息,这就是CPU的商标字符串。CPU的商标字符串也是通过cpuid得到的。由于 商标的字符串很长(48个字符),所以不能在一次cpuid指令执行时全部得到,所以intel把它分成了3个操作,eax的输入参数分别是 0x80000002,0x80000003,0x80000004,每次返回的16个字符,按照从低位到高位的顺序依次放在eax, ebx, ecx, edx。因此,可以用循环的方式,每次执行完以后保存结果,然后执行下一次cpuid。

 string CPUID::GetBrand()
 {
  const DWORD BRANDID = 0x80000002;  // 从0x80000002开始,到0x80000004结束
  char cBrand[49];    // 用来存储商标字符串,48个字符
  memset(cBrand, 0, 49);    // 初始化为0

  for (DWORD i = 0; i < 3; i++)   // 依次执行3个指令
  {
   Executecpuid(BRANDID + i);   
   memcpy(cBrand + i*16, &m_eax, 16); // 每次执行结束后,保存四个寄存器里的asc码到数组
  }      // 由于在内存中,m_eax, m_ebx, m_ecx, m_edx是连续排列
        // 所以可以直接以内存copy的方式进行保存
  return string(cBrand);  // 以string的形式返回
 }

5、检测CPU特性(CPU feature)

    我98年初买第一台电脑的时候,CPU能支持MMX就很了不起了。现在的intel CPU,台式机的好点的都支持Hyper-Threading了,移动的要支持Speed Sted。这些都是CPU的特性。CPU的特性可以通过cpuid获得,参数是eax = 1,返回值放在edx和ecx,通过验证edx或者ecx的某一个bit,可以获得CPU的一个特性是否被支持。比如说,edx的bit 32代表是否支持MMX,edx的bit 28代表是否支持Hyper-Threading,ecx的bit 7代表是否支持speed sted。下面就是获得CPU特性的例子:

 bool CPUID::IsHyperThreading()  // 判断是否支持hyper-threading
 {
  Executecpuid(1);  // 执行cpuid指令,使用输入参数 eax = 1

  return m_edx & (1<<28);  // 返回edx的bit 28
 }

 bool CPUID::IsEST()   // 判断是否支持speed step
 {
  Executecpuid(1);  // 执行cpuid指令,使用输入参数 eax = 1

  return m_ecx & (1<<7);  // 返回ecx的bit 7
 }

 bool CPUID::IsMMX()   // 判断是否支持MMX
 {
  Executecpuid(1);  // 执行cpuid指令,使用输入参数 eax = 1

  return m_edx & (1<<23);  // 返回edx的bit 23
 }

    CPU的特性还有很多,这只是平时我们听到比较多的三个,更多的特性请参考intel的资料。

6、获得CPU的缓存(cache)
   
    缓存,就是CACHE,已经成为判断CPU性能的一项大指标。缓存信息包括:第几级缓存(level),缓存大小(size),通道数(way),吞吐量(line size)。因此可以使用一个结构体来存储缓存信息。

 struct CacheInfo
 {
  int level;    // 第几级缓存
  int size;    // 缓存大小,单位KB
  int way;    // 通道数
  int linesize;    // 吞吐量

  CacheInfo()    // 构造函数
  {
   level = 0;
   size = 0;
   way = 0;
   linesize = 0;
  }

  CacheInfo(int clevel, int csize, int cway, int clinesize)  // 构造函数
  {
   level = clevel;
   size = csize;
   way = cway;
   linesize = clinesize;
  }
 };
   
    缓存信息可以通过eax = 2的cpuid来得到(得到的不光有cache信息,还有其他的一些信息),返回值在eax(高24位), ebx, ecx和edx,总共15个BYTE的信息,每个BYTE的值不同,代表的意义也不同,所以需要用一个哈希表存储各种不同BYTE的定义,可以定义一个 map类型的类成员存储这些资料。我把资料上和缓存有关的信息存储如下:

 m_cache[0x06] =  CacheInfo(1, 8, 4, 32);
 m_cache[0x08] =  CacheInfo(1, 16, 4, 32);
 m_cache[0x0a] =  CacheInfo(1, 8, 2, 32);
 m_cache[0x0c] =  CacheInfo(1, 16, 4, 32);
 m_cache[0x2c] =  CacheInfo(1, 32, 8, 64);
 m_cache[0x30] =  CacheInfo(1, 32, 8, 64);
 m_cache[0x60] =  CacheInfo(1, 16, 8, 64);
 m_cache[0x66] =  CacheInfo(1, 8, 4, 64);
 m_cache[0x67] =  CacheInfo(1, 16, 4, 64);
 m_cache[0x68] =  CacheInfo(1, 32, 4, 64);

 m_cache[0x39] =  CacheInfo(2, 128, 4, 64);
 m_cache[0x3b] =  CacheInfo(2, 128, 2, 64);
 m_cache[0x3c] =  CacheInfo(2, 256, 4, 64);
 m_cache[0x41] =  CacheInfo(2, 128, 4, 32);
 m_cache[0x42] =  CacheInfo(2, 256, 4, 32);
 m_cache[0x43] =  CacheInfo(2, 512, 4, 32);
 m_cache[0x44] =  CacheInfo(2, 1024, 4, 32);
 m_cache[0x45] =  CacheInfo(2, 2048, 4, 32);
 m_cache[0x79] =  CacheInfo(2, 128, 8, 64);
 m_cache[0x7a] =  CacheInfo(2, 256, 8, 64);
 m_cache[0x7b] =  CacheInfo(2, 512, 8, 64);
 m_cache[0x7c] =  CacheInfo(2, 1024, 8, 64);
 m_cache[0x82] =  CacheInfo(2, 256, 8, 32);
 m_cache[0x83] =  CacheInfo(2, 512, 8, 32);
 m_cache[0x84] =  CacheInfo(2, 1024, 8, 32);
 m_cache[0x85] =  CacheInfo(2, 2048, 8, 32);
 m_cache[0x86] =  CacheInfo(2, 512, 4, 64);
 m_cache[0x87] =  CacheInfo(2, 1024, 8, 64);

 m_cache[0x22] =  CacheInfo(3, 512, 4, 64);
 m_cache[0x23] =  CacheInfo(3, 1024, 8, 64);
 m_cache[0x25] =  CacheInfo(3, 2048, 8, 64);
 m_cache[0x29] =  CacheInfo(3, 4096, 8, 64);

    m_cache是类成员,定义如下:

 map<int, CacheInfo> m_cache; // Cache information table

    在得到返回值以后,只需要遍历每一个BYTE的值,找到在m_cache中存在的元素,就可以得到cache信息了。代码如下:

 typedef unsigned char BYTE;

 DWORD CPUID::GetCacheInfo(CacheInfo& L1, CacheInfo& L2, CacheInfo& L3)
 {
  BYTE cValues[16];      // 存储返回的16个byte值
  DWORD result = 0;      // 记录发现的缓存数量
  Executecpuid(2);      // 执行cpuid,参数为eax = 2
  memcpy(cValues, &m_eax, 16);     // 把m_eax, m_ebx, m_ecx和m_edx存储到cValue

  for (int i = 1; i < 16; i++)     // 开始遍历,注意eax的第一个byte没有意义,需要跳过
  {
   if (m_cache.find(cValues[i]) != m_cache.end())  // 从表中查找此信息是否代表缓存
   {
    switch (m_cache[cValues[i]].level)  // 对号入座,保存缓存信息
    {
    case 1:  // L1 cache
     L1 = m_cache[cValues[i]];
     break;
    case 2:  // L2 cache
     L2 = m_cache[cValues[i]];
     break;
    case 3:  // L3 cache
     L3 = m_cache[cValues[i]];
     break;
    default:
     break;
    }
    result++;
   }
  
  }

  return result;
 }

   
7、获得CPU的序列号

    序列号无处不在!!CPU的序列号用一个96bit的串表示,格式是连续的6个WORD值:XXXX-XXXX-XXXX-XXX-XXXX-XXXX。WORD是16个bit长的数据,可以用unsigned short模拟:

 typedef unsigned short WORD;

    获得序列号需要两个步骤,首先用eax = 1做参数,返回的eax中存储序列号的高两个WORD。用eax = 3做参数,返回ecx和edx按从低位到高位的顺序存储前4个WORD。实现如下:

 bool CPUID::GetSerialNumber(SerialNumber& serial)
 {
  Executecpuid(1); // 执行cpuid,参数为 eax = 1
  bool isSupport = m_edx & (1<<18); // edx是否为1代表CPU是否存在序列号
  if (false == isSupport) // 不支持,返回false
  {
   return false;
  }
  memcpy(&serial.nibble[4], &m_eax, 4); // eax为最高位的两个WORD

  Executecpuid(3); // 执行cpuid,参数为 eax = 3
  memcpy(&serial.nibble[0], &m_ecx, 8); // ecx 和 edx为低位的4个WORD

  return true;
 }

8、后记

    CPUID还能获得很多信息,以上实现的都是最常见的。完整的代码和有关cpuid的资料我会用附件的形式附在文章结尾。昨天代码写完后拿给朋友看,朋友 骂我使用了太多的memcpy()函数进行赤裸裸的内存操作...其实我这么做的目的是提高程序的性能,减少代码量,但是可读性就降了下来,不喜欢这种风 格的朋友可以自己改一下。还有,因为CPUID类只是提供了很多的接口,没有存储数据的功能,所以类以Singleton的方式设计,使用方法可以参考我 代码中的test2.cpp文件。

posted @ 2006-05-19 16:32 Martin 阅读(2392) | 评论 (0)编辑 收藏

From: http://freeman.cnblogs.com/articles/sizeof.html


前言

    50米的网站http://www.50mi.net开张了,所以50米邀请我给他写点什么。说实在的,作为一个资深的潜水员,我还真没动笔写过什么东西,所以绞尽脑汁也没想起来能写什么的。不过鉴于50米喜欢在小孩子面前臭屁的,就写一篇群里讨论很多的sizeof问题吧。

1、什么是sizeof

    首先看一下sizeof在msdn上的定义:

    The sizeof keyword gives the amount of storage, in bytes, associated with a variable or a type (including aggregate types). This keyword returns a value of type size_t.

    看到return这个字眼,是不是想到了函数?错了,sizeof不是一个函数,你见过给一个函数传参数,而不加括号的吗?sizeof可以,所以 sizeof不是函数。网上有人说sizeof是一元操作符,但是我并不这么认为,因为sizeof更像一个特殊的宏,它是在编译阶段求值的。举个例子:
 
 cout<<sizeof(int)<<endl; // 32位机上int长度为4
 cout<<sizeof(1==2)<<endl; // == 操作符返回bool类型,相当于 cout<<sizeof(bool)<<endl;

    在编译阶段已经被翻译为:

 cout<<4<<endl;
 cout<<1<<endl;

    这里有个陷阱,看下面的程序:

 int a = 0;
 cout<<sizeof(a=3)<<endl;
 cout<<a<<endl;

    输出为什么是4,0而不是期望中的4,3???就在于sizeof在编译阶段处理的特性。由于sizeof不能被编译成机器码,所以sizeof作用范围 内,也就是()里面的内容也不能被编译,而是被替换成类型。=操作符返回左操作数的类型,所以a=3相当于int,而代码也被替换为:

 int a = 0;
 cout<<4<<endl;
 cout<<a<<endl;

    所以,sizeof是不可能支持链式表达式的,这也是和一元操作符不一样的地方。

    结论:不要把sizeof当成函数,也不要看作一元操作符,把他当成一个特殊的编译预处理。

2、sizeof的用法

    sizeof有两种用法:
 
    (1)sizeof(object)
    也就是对对象使用sizeof,也可以写成sizeof object 的形式。例如:

    (2)sizeof(typename)
    也就是对类型使用sizeof,注意这种情况下写成sizeof typename是非法的。下面举几个例子说明一下:

 
 int i = 2;
 cout<<sizeof(i)<<endl; // sizeof(object)的用法,合理
 cout<<sizeof i<<endl; // sizeof object的用法,合理
 cout<<sizeof 2<<endl; // 2被解析成int类型的object, sizeof object的用法,合理
 cout<<sizeof(2)<<endl; // 2被解析成int类型的object, sizeof(object)的用法,合理
 cout<<sizeof(int)<<endl;// sizeof(typename)的用法,合理
 cout<<sizeof int<<endl; // 错误!对于操作符,一定要加()

    可以看出,加()是永远正确的选择。

    结论:不论sizeof要对谁取值,最好都加上()。


3、数据类型的sizeof

(1)C++固有数据类型

    32位C++中的基本数据类型,也就char,short int(short),int,long int(long),float,double, long double
大小分别是:1,2,4,4,4,8, 10。

    考虑下面的代码:

 cout<<sizeof(unsigned int) == sizeof(int)<<endl; // 相等,输出 1

    unsigned影响的只是最高位bit的意义,数据长度不会被改变的。

    结论:unsigned不能影响sizeof的取值。

(2)自定义数据类型

    typedef可以用来定义C++自定义类型。考虑下面的问题:

 typedef short WORD;
 typedef long DWORD;
 cout<<(sizeof(short) == sizeof(WORD))<<endl; // 相等,输出1
 cout<<(sizeof(long) == sizeof(DWORD))<<endl; // 相等,输出1

    结论:自定义类型的sizeof取值等同于它的类型原形。

(3)函数类型

    考虑下面的问题:

 int f1(){return 0;};
 double f2(){return 0.0;}
 void f3(){}

 cout<<sizeof(f1())<<endl; // f1()返回值为int,因此被认为是int
 cout<<sizeof(f2())<<endl; // f2()返回值为double,因此被认为是double
 cout<<sizeof(f3())<<endl; // 错误!无法对void类型使用sizeof
 cout<<sizeof(f1)<<endl;  // 错误!无法对函数指针使用sizeof   
 cout<<sizeof*f2<<endl;  // *f2,和f2()等价,因为可以看作object,所以括号不是必要的。被认为是double

    结论:对函数使用sizeof,在编译阶段会被函数返回值的类型取代,

4、指针问题

    考虑下面问题:
 
 cout<<sizeof(string*)<<endl; // 4
 cout<<sizeof(int*)<<endl; // 4
 cout<<sizof(char****)<<endl; // 4

    可以看到,不管是什么类型的指针,大小都是4的,因为指针就是32位的物理地址。

    结论:只要是指针,大小就是4。(64位机上要变成8也不一定)。

    顺便唧唧歪歪几句,C++中的指针表示实际内存的地址。和C不一样的是,C++中取消了模式之分,也就是不再有small,middle,big,取而代 之的是统一的flat。flat模式采用32位实地址寻址,而不再是c中的 segment:offset模式。举个例子,假如有一个指向地址 f000:8888的指针,如果是C类型则是8888(16位, 只存储位移,省略段),far类型的C指针是f0008888(32位,高位保留段地址,地位保留位移),C++类型的指针是f8888(32位,相当于 段地址*16 + 位移,但寻址范围要更大)。

5、数组问题

    考虑下面问题:

 char a[] = "abcdef";
 int b[20] = {3, 4};
 char c[2][3] = {"aa", "bb"};
 

 cout<<sizeof(a)<<endl; // 7
 cout<<sizeof(b)<<endl; // 20 (Wrong here, should be 20*4)
 cout<<sizeof(c)<<endl; // 6
 

    数组a的大小在定义时未指定,编译时给它分配的空间是按照初始化的值确定的,也就是7。c是多维数组,占用的空间大小是各维数的乘积,也就是6。可以看出,数组的大小就是他在编译时被分配的空间,也就是各维数的乘积*数组元素的大小。

    结论:数组的大小是各维数的乘积*数组元素的大小。

    这里有一个陷阱:

 int *d = new int[10];

 cout<<sizeof(d)<<endl; // 4

    d是我们常说的动态数组,但是他实质上还是一个指针,所以sizeof(d)的值是4。

    再考虑下面的问题:

 double* (*a)[3][6];
 
 cout<<sizeof(a)<<endl;  // 4
 cout<<sizeof(*a)<<endl;  // 72
 cout<<sizeof(**a)<<endl; // 24
 cout<<sizeof(***a)<<endl; // 4
 cout<<sizeof(****a)<<endl; // 8

    a是一个很奇怪的定义,他表示一个指向 double*[3][6]类型数组的指针。既然是指针,所以sizeof(a)就是4。

    既然a是执行double*[3][6]类型的指针,*a就表示一个double*[3][6]的多维数组类型,因此sizeof(*a)= 3*6*sizeof(double*)=72。同样的,**a表示一个double*[6]类型的数组,所以sizeof(**a)=6*sizeof (double*)=24。***a就表示其中的一个元素,也就是double*了,所以sizeof(***a)=4。至于****a,就是一个 double了,所以sizeof(****a)=sizeof(double)=8。


6、向函数传递数组的问题。

    考虑下面的问题:
#include <iostream>
using namespace std;

int Sum(int i[])
{
 int sumofi = 0;
 for (int j = 0; j < sizeof(i)/sizeof(int); j++) //实际上,sizeof(i) = 4
 {
  sumofi += i[j];
 }
 return sumofi;
}

int main()
{
 int allAges[6] = {21, 22, 22, 19, 34, 12};
 cout<<Sum(allAges)<<endl;
 system("pause");
 return 0;
}

    Sum的本意是用sizeof得到数组的大小,然后求和。但是实际上,传入自函数Sum的,只是一个int 类型的指针,所以sizeof(i)=4,而不是24,所以会产生错误的结果。解决这个问题的方法使是用指针或者引用。

    使用指针的情况:
int Sum(int (*i)[6])
{
 int sumofi = 0;
 for (int j = 0; j < sizeof(*i)/sizeof(int); j++) //sizeof(*i) = 24
 {
  sumofi += (*i)[j];
 }
 return sumofi;
}

int main()
{
 int allAges[] = {21, 22, 22, 19, 34, 12};
 cout<<Sum(&allAges)<<endl;
 system("pause");
 return 0;
}
    在这个Sum里,i是一个指向i[6]类型的指针,注意,这里不能用int Sum(int (*i)[])声明函数,而是必须指明要传入的数组的大小,不然sizeof(*i)无法计算。但是在这种情况下,再通过sizeof来计算数组大小已经 没有意义了,因为此时大小是指定为6的。
使用引用的情况和指针相似:

int Sum(int (&i)[6])
{
 int sumofi = 0;
 for (int j = 0; j < sizeof(i)/sizeof(int); j++)
 {
  sumofi += i[j];
 }
 return sumofi;
}

int main()
{
 int allAges[] = {21, 22, 22, 19, 34, 12};
 cout<<Sum(allAges)<<endl;
 system("pause");
 return 0;
}
    这种情况下sizeof的计算同样无意义,所以用数组做参数,而且需要遍历的时候,函数应该有一个参数来说明数组的大小,而数组的大小在数组定义的作用域内通过sizeof求值。因此上面的函数正确形式应该是:
#include <iostream>
using namespace std;

int Sum(int *i, unsigned int n)
{
 int sumofi = 0;
 for (int j = 0; j < n; j++)
 {
  sumofi += i[j];
 }
 return sumofi;
}

int main()
{
 int allAges[] = {21, 22, 22, 19, 34, 12};
 cout<<Sum(i, sizeof(allAges)/sizeof(int))<<endl;
 system("pause");
 return 0;
}

7、字符串的sizeof和strlen

    考虑下面的问题:

 char a[] = "abcdef";
 char b[20] = "abcdef";
 string s = "abcdef";

 cout<<strlen(a)<<endl;  // 6,字符串长度
 cout<<sizeof(a)<<endl;  // 7,字符串容量
 cout<<strlen(b)<<endl;  // 6,字符串长度
 cout<<strlen(b)<<endl;  // 20,字符串容量
 cout<<sizeof(s)<<endl;  // 12, 这里不代表字符串的长度,而是string类的大小
 cout<<strlen(s)<<endl;  // 错误!s不是一个字符指针。

 a[1] = '\0';
 cout<<strlen(a)<<endl;  // 1
 cout<<sizeof(a)<<endl;  // 7,sizeof是恒定的


    strlen是寻找从指定地址开始,到出现的第一个0之间的字符个数,他是在运行阶段执行的,而sizeof是得到数据的大小,在这里是得到字符串的容 量。所以对同一个对象而言,sizeof的值是恒定的。string是C++类型的字符串,他是一个类,所以sizeof(s)表示的并不是字符串的长 度,而是类string的大小。strlen(s)根本就是错误的,因为strlen的参数是一个字符指针,如果想用strlen得到s字符串的长度,应 该使用sizeof(s.c_str()),因为string的成员函数c_str()返回的是字符串的首地址。实际上,string类提供了自己的成员 函数来得到字符串的容量和长度,分别是Capacity()和Length()。string封装了常用了字符串操作,所以在C++开发过程中,最好使用 string代替C类型的字符串。


8、从union的sizeof问题看cpu的对界

    考虑下面问题:(默认对齐方式)

 union u
 {
  double a;
  int b;
 };

 union u2
 {
  char a[13];
  int b;
 };

 union u3
 {
  char a[13];
  char b;
 };

 cout<<sizeof(u)<<endl;  // 8
 cout<<sizeof(u2)<<endl;  // 16
 cout<<sizeof(u3)<<endl;  // 13

    都知道union的大小取决于它所有的成员中,占用空间最大的一个成员的大小。所以对于u来说,大小就是最大的double类型成员a了,所以 sizeof(u)=sizeof(double)=8。但是对于u2和u3,最大的空间都是char[13]类型的数组,为什么u3的大小是13,而 u2是16呢?关键在于u2中的成员int b。由于int类型成员的存在,使u2的对齐方式变成4,也就是说,u2的大小必须在4的对界上,所以占用的空间变成了16(最接近13的对界)。

    结论:复合数据类型,如union,struct,class的对齐方式为成员中对齐方式最大的成员的对齐方式。

    顺便提一下CPU对界问题,32的C++采用8位对界来提高运行速度,所以编译器会尽量把数据放在它的对界上以提高内存命中率。对界是可以更改的,使用 #pragma pack(x)宏可以改变编译器的对界方式,默认是8。C++固有类型的对界取编译器对界方式与自身大小中较小的一个。例如,指定编译器按2对界,int 类型的大小是4,则int的对界为2和4中较小的2。在默认的对界方式下,因为几乎所有的数据类型都不大于默认的对界方式8(除了long double),所以所有的固有类型的对界方式可以认为就是类型自身的大小。更改一下上面的程序:

 #pragma pack(2)
 union u2
 {
  char a[13];
  int b;
 };

 union u3
 {
  char a[13];
  char b;
 };
 #pragma pack(8)

 cout<<sizeof(u2)<<endl;  // 14
 cout<<sizeof(u3)<<endl;  // 13

    由于手动更改对界方式为2,所以int的对界也变成了2,u2的对界取成员中最大的对界,也是2了,所以此时sizeof(u2)=14。

    结论:C++固有类型的对界取编译器对界方式与自身大小中较小的一个。


9、struct的sizeof问题

    因为对齐问题使结构体的sizeof变得比较复杂,看下面的例子:(默认对齐方式下)

 struct s1
 {
  char a;
  double b;
  int c;
  char d; 
 };

 struct s2
 {
  char a;
  char b;
  int c;
  double d;
 };

 cout<<sizeof(s1)<<endl; // 24
 cout<<sizeof(s2)<<endl; // 16

    同样是两个char类型,一个int类型,一个double类型,但是因为对界问题,导致他们的大小不同。计算结构体大小可以采用元素摆放法,我举例子说 明一下:首先,CPU判断结构体的对界,根据上一节的结论,s1和s2的对界都取最大的元素类型,也就是double类型的对界8。然后开始摆放每个元 素。
    对于s1,首先把a放到8的对界,假定是0,此时下一个空闲的地址是1,但是下一个元素d是double类型,要放到8的对界上,离1最接近的地址是8 了,所以d被放在了8,此时下一个空闲地址变成了16,下一个元素c的对界是4,16可以满足,所以c放在了16,此时下一个空闲地址变成了20,下一个 元素d需要对界1,也正好落在对界上,所以d放在了20,结构体在地址21处结束。由于s1的大小需要是8的倍数,所以21-23的空间被保留,s1的大 小变成了24。
    对于s2,首先把a放到8的对界,假定是0,此时下一个空闲地址是1,下一个元素的对界也是1,所以b摆放在1,下一个空闲地址变成了2;下一个元素c的 对界是4,所以取离2最近的地址4摆放c,下一个空闲地址变成了8,下一个元素d的对界是8,所以d摆放在8,所有元素摆放完毕,结构体在15处结束,占 用总空间为16,正好是8的倍数。

    这里有个陷阱,对于结构体中的结构体成员,不要认为它的对齐方式就是他的大小,看下面的例子:

 struct s1
 {
  char a[8];
 };

 struct s2
 {
  double d;
 };

 struct s3
 {
  s1 s;
  char a;
 };

 struct s4
 {
  s2 s;
  char a; 
 };

 cout<<sizeof(s1)<<endl; // 8
 cout<<sizeof(s2)<<endl; // 8
 cout<<sizeof(s3)<<endl; // 9
 cout<<sizeof(s4)<<endl; // 16;

    s1和s2大小虽然都是8,但是s1的对齐方式是1,s2是8(double),所以在s3和s4中才有这样的差异。

    所以,在自己定义结构体的时候,如果空间紧张的话,最好考虑对齐因素来排列结构体里的元素。

10、不要让double干扰你的位域

    在结构体和类中,可以使用位域来规定某个成员所能占用的空间,所以使用位域能在一定程度上节省结构体占用的空间。不过考虑下面的代码:

 struct s1
 {
  int i: 8;
  int j: 4;
  double b;
  int a:3;
 };

 struct s2
 {
  int i;
  int j;
  double b;
  int a;
 };

 struct s3
 {
  int i;
  int j;
  int a;
  double b;
 };

 struct s4
 {
  int i: 8;
  int j: 4;
  int a:3;
  double b;
 };

 cout<<sizeof(s1)<<endl;  // 24
 cout<<sizeof(s2)<<endl;  // 24
 cout<<sizeof(s3)<<endl;  // 24
 cout<<sizeof(s4)<<endl;  // 16

    可以看到,有double存在会干涉到位域(sizeof的算法参考上一节),所以使用位域的的时候,最好把float类型和double类型放在程序的开始或者最后。

    第一次写东西,发现自己的表达能力太差了,知道的东西讲不出来,讲出来的东西别人也看不懂,呵呵。另外,C99标准的sizeof已经可以工作在运行时了,打算最近找个支持C99的编译器研究一下。

posted @ 2006-05-19 16:28 Martin 阅读(1855) | 评论 (4)编辑 收藏
[译著]在模板方法中的一些"反常"用法

作者: Jerry Cat
时间: 2006/05/19
链接: 
http://www.cppblog.com/jerysun0818/archive/2006/05/19/7393.html

-------------------------------------
I. Virtually Yours -- Template Method模式
我在研究Wendy写的一个类。那是她为这个项目写的一个抽象基类,而我的工作就是从中派生出一个具象类(concrete class)。这个类的public部分是这样的:

class Mountie {
public:
    void read( std::istream & );
    void write( std::ostream & ) const;
    virtual ~Mountie();

很正常,virtual destructor表明这个类打算被继承。那么再看看其protected部分:

protected:
    virtual void do_read( std::istream & );
    virtual void do_write( std::ostream & ) const;

也 不过就是一会儿的功夫,我识破了Wendy的把戏:她在使用template method模式。public成员函数read和write是非虚拟的,它们肯定是调用protected部分do_read/do_write虚拟成 员函数来完成实际的工作。啊,我简直为自己的进步而飘飘然了!哈,Wendy,这回你可难不住我,还有什么招数?尽管放马过来... 突然,笑容在我脸上凝固,因为我看到了其private部分:

private:
    virtual std::string classID() const = 0;

这是什么?一个private纯虚函数,能工作么?我站了起来,

“Wendy,你的Mountie类好像不能工作耶,它有一个private virtual function。”

“你试过了?”她连头都不抬。

“嗯,那倒是没有啦,可是想想也不行啊?我的派生类怎么能override你的private函数呢?” 我嘟囔着。

“嗬,你倒是很确定啊!”Wendy的声音很轻柔,“你怎么老是这也不行,那也不行的,这几个月跟着我你就没学到什么东西吗?小菜鸟。”

真是可恶啊...

“小菜鸟,你全都忘了,访问控制级别跟一个函数是不是虚拟的根本没关系。判断一个函数是动态绑定还是静态绑定是函数调用解析的最后一个步骤。好好读读标准的3.4和5.2.2节吧。”

我完全处于下风,只好采取干扰战术。“好吧,就算你说的不错,我也还是不明白,何必把它设为private?”

“我且问你,倘若你不想让一个类中的成员函数被其他的类调用,应当如何处理?”

“当然是把它设置为private的,” 我回答道。

“那么你去看看我的Mountie类实现,特别是write()函数的实现。”

我正巴不得逃开Wendy那刺人的目光,便转过头去在我的屏幕上搜索,很快,我找到了:

void Mountie::write(std::ostream &Dudley) const
{
    Dudley << classID() << std::endl;
    do_write(Dudley);
}

嗨,最近卡通片真是看得太多了,居然犯这样的低级失误。还是老是承认吧:“好了,我明白了。classID()是一个实现细节,用来在保存对象时指示具象类的类型,派生类必须覆盖它,所以必须是纯虚的。但是既然是实现细节,就应该设为private的。”

“这还差不多,小菜鸟。”大虾点了点头,“现在给我解释一下为什么do_read()和do_write()是protected的?”

这个问题并不难,我组织了一下就回答:“因为派生类对象需要调用这两个函数的实现来读写其中的基类对象。”

“很好很好,”大虾差不多满意了,“不过,你再解释解释为什么我不把它们设为public的?”

现在我感觉好多了:“因为调用它们的时候必须以一种特定的方式进行。比如do_write()函数,必须先把类型信息写入,再把对象信息写入,这样读取的时候,负责生成对象的模块首先能够知道要读出来的对象是什么类型的,然后才能正确地从流中读取对象信息。”

“聪明啊,我的小菜鸟!”Wendy停顿了一下,“就跟学习外国口语一样,学习C++也不光是掌握语法而已,还必须要掌握大量的惯用法。”

“是啊是啊,我正打算读Coplien的书...”

[译者注:就是James Coplien 1992年的经典著作Advanced C++ Programming Style and Idioms]

大 虾挥了挥她的手,“冷静,小菜鸟,我不是指先知Coplien的那本书,我是指某种结构背后隐含的惯用法。比如一个类有virtual destructor,相当于告诉你说:‘嗨,我是一个多态基类,来继承我吧!’ 而如果一个类的destructor不是虚拟的,则相当于是在说:‘我不能作为多态基类,看在老天的份上,别继承我。’”

“同 样的,virtual函数的访问控制级别也具有隐含的意义。一个protected virtual function告诉你:‘你写的派生类应该,哦,可是说是必须调用我的实现。’而一个private virtual function是在说:‘派生类可以覆盖,也可以不覆盖我,随你的便。但是你不可以调用我的实现。’”

我点点头,告诉她我懂了,然后追问道:“那么public virtual function呢?”

“尽可能不要使用public virtual function。”她拿起一支笔写下了以下代码:

class HardToExtend
{
public:
  virtual void f();
};
 void HardToExtend::f()
{
 // Perform a specific action
}

“假设你发布了这个类。在写第二版时,需求有所变化,你必须改用Template Method。可是这根本不可能,你知道为什么?”

“呃,这个...,不知道。”

“由两种可能的办法。其一,将f()的实现代码转移到一个新的函数中,然后将f()本身设为non-virtual的:

class HardToExtend
{
// possibly protected
    virtual void do_f();
public:
    void f();
};
void HardToExtend::f()
{
    // pre-processing
    do_f();
    // post-processing
}
void HardToExtend::do_f()
{
    // Perform a specific action
}

然 而你原来写的派生类都是企图override函数f()而不是do_f()的,你必须改变所有的派生类实现,只要你错过了一个类,你的类层次就会染上先知 Meyers所说的‘精神分裂的行径’。” [译者注:参见Scott Meyers,Effective C++, Item 37,绝对不要重新定义继承而来的非虚拟函数]

“另一种办法是将f()移到private区域,引入一个新的non-virtual函数:”

class HardToExtend
{
// possibly protected
    virtual void f();
public:
    void call_f();
};

“这会导致无数令人头痛的问题。首先,所有的客户都企图调用f()而不是call_f(),现在它们的代码都不能编译了。更有甚者,大部分派生类都回把f()放在public区域中,这样直接使用派生类的用户可以访问到你本来想保护的细节。”

“对待虚函数要象对待数据成员一样,把它们设为private的,直到设计上要求使用更宽松的访问控制再来调整。要知道由private入public易,由public入private难啊!”

[译 者注:这篇文章所表达的思想具有一定的颠覆性,因为我们太容易在基类中设置public virtual function了,Java中甚至专门为这种做法建立了interface机制,现在竟然说这不好!一时间真是接受不了。但是仔细体会作者的意思,他并 不是一般地反对public virtual function,只是在template method大背景下给出上述原则。虽然这个原则在一般的设计中也是值得考虑的,但是主要的应用领域还是在template method模式中。当然,template method是一种非常有用和常用的模式,因此也决定了本文提出的原则具有广泛的意义。]

posted @ 2006-05-19 12:18 Martin 阅读(491) | 评论 (0)编辑 收藏
  2006年5月15日
From: http://www.cnblogs.com/eastsurfer/archive/2006/05/14/400057.html


    一个人的成就取决于他的行动。同时我认为一个人的成就跟他时间管理的能力成正比。

    很多人时间管理做不好,因为他不够忙。时间管理好的人,第一个现象就是忙,整个人开始忙碌起来,不是瞎忙,而是很有效率的忙。

    时间管理的目的是为了要达成你的目标,所以假设一开始目标没有设定好,计划没有拟定详细,事实上时间管理的效率已经不理想了。

    成功就是每天进步1%。当你每天学习一点,行动一些,把计划做得越来越详细,不断地做检讨,你就会每天进步一点,慢慢步入成功。



    时间就是生命,掌握时间就是掌握生命!

    一个人的成就决定于他24小时做了哪些事情。时间管理的重点在于如何分配时间,在每一分每一秒都最有生产力的事情,在更短的时间达成更多的目标。书中同样用大量实践的心得分享了最棒的时间管理的十个关键。

    现在,我们来学习做好时间管理的十个关键:

    1、第一关键:要有明确的目标

    上次我们演习时,已经明确过每个人的目标。现在还是请你拿出纸和笔,在这张纸上,写出你明确的目标。

    如果你没有明确的目标,那时间是无法管理的。

    时间管理的目的,是让你在更短的时间达成更多你想要达成的目标。我们都知道成功等于目标,所以你愈能够把目标明确地设立好,依照我之前所分享的方法,你的时间管理就会愈好。

    2、第二关键:你必须要有一张“个人清单”

    也就是你必须要把今年所要做的每一件事情都列出来。现在就把你要完成的每一件目标列出来,不光是主要的目标,还有一些小的目标要达成,也要把它列出来。

    当你有“个人清单”之后,下一个你要做的是把目标切割。譬如为了达成今年的每一个目标,我上半年必须完成哪些事情?下一步就是把它切割成季目标。我这一季需要做哪些事情,全部列出来,如此再推出每一个月需要做哪些事情。

    假设你没有办法有一个全年的“个人清单”,你至少从现在开始必须要有每个月的“月清单”。当然我们都知道一日之计不是在于晨,而是在于昨夜,所以在前一天晚上要把第二天要做的事情列出来。记住,你永远没有时间做每一件事情,但你永远有时间做对你最重要的事情。

    当你列出来之后,把优先顺序排好,并且设定完成期限,这时你就已经迈向成功之路了。

    3、第三关键:也就是大家所熟悉的二十、八十定律,或八十、二十定律

    你要把时间管理好,一定要知道哪些事情对你是最重要的,它赋予你最高的生产力。假如这些事情你不是很清楚,不是很了解,那你的时间管理永远不会很好。所以每一天必须花最多时间做那一件事情。

    我个人一定会列出第二天要做的每一件事情,同时我会把这些事情分成小小的时段,这样我就可以百分之百地掌握我的时间了。

    还有一点,就是运用视觉的力量。导致时间管理不好的原因通常就是拖延。当“马上行动”摆在你前面,你很明确地看着它,它就会刺激你的潜意识,进入你的脑海里,迫使你马上行动。所以你应该在你的书桌前面贴一个“马上行动”四个大字。

    时间管理要做好,你就必须有一个明确而且详细的计划。计划愈详细愈容易管理,你也愈容易成功。

    4、第四关键:每天至少要有半小时到1小时的“不被干扰的时间”

    假如你能有1小时完全不受任何人干扰,自己关在自己的房间里面,开始思考一些事情,或是做一些你认为最重要的事情,这1个小时可以抵过你1天的工作效率, 甚至有时候这1小时比你3天工作的效率还要好。所以记住,不被干扰的时间至少要30分钟,最好的时间差不多是60分钟,也就是1个小时。

    一般来讲,需要花20分钟才能让自己的头脑冷静下来,心定下来。假设只有30分钟,效率并不会太好。所以给自己1个小时的不被干扰的时间是非常有效的方法。

    设定不被打扰的时间在早上,最好是起床的时候,5点到6点,这个时候,你一个人思考,尤其是你的头脑非常清楚,你会发挥非常非常大的力量。假如你这个时段 没有办法做到,还有一个时间你可以试试,就是在中午吃饭的时间。或是在下午3点到4点的时候。我自己设立的时间则是在晚上回家之后。

    5、第五关键:你的目标和你的价值观要吻合,不可以相互矛盾。

    你一定要确立你个人的价值观,假如价值观不明确,你就很难知道什么对你最重要。当价值观不明确的时候,你时间的分配一定不好。所以你一定要找一个时间把自己的价值观确定一下,什么对你才是最重要的?是健康、是事业、是家庭、是朋友,把它分配好。

    记住,“时间管理”的重点不在于管理时间,而在于如何分配时间。

    6、第六关键:每天静坐一小时

    你可以找一张椅子,就坐在那里,记住,一定要完全不受干扰,没有任何的音乐,没有任何的杂音,就一个人坐在椅子上。当然一开始的时候,你一定很想要动,那时候你就要鞭策自己不准动,直到满一小时。假设你每天能够静坐一小时,你工作的效率一定会提升。

    7、第七关键:所有的事情开始就把它做对

    开始就把它做到完美,就把它做到最好,这样你就不需要重复去做同一件事情。

    8、第八关键:你必须控制你的电话时间

    善于管理时间的人通常是由他的秘书帮他查询到底是谁打电话来,或是请他留言。留言时必须记住什么时间回电是最好的时机,不然你打电话过去,他又不在,徒劳无功。一般来讲,把电话积累到某一个时间,一次把它全部打完。

    9、第九关键:同一类的事情最好一次把它做完

    当你重复去做同一件事情,你会熟能生巧,因此你的效率一定会增加。

    10、第十关键:做“时间日志”

    你花了多少时间在做那些事情,把它详细抵记录下来,每天做了什么,一一记录下来。你会发现,哎呀!浪费那么多时间。当你找到浪费时间的根源,你才有办法改变。

    接下来,我们来学习做好时间管理最重要的四个秘诀:

    1、 第一秘诀:做最有生产力的事情

    我把这句话“在每一分,每一秒都要做最有生产力的事情”贴在我的书房里,我发现我的时间管理的确有进步,同时也因为这句话的影响,我在27岁时,也成了亿万富翁。所以,请你也和我一样,把这句话贴出来,时刻提醒自己每一分、每一秒都要做最有生产力的事情。

    2、第二秘诀:“时间大于金钱”

    用你的金钱去换取别人的成功经验,这是比较快的方式。

    3、第三秘诀:花最多时间做最重要可是不紧急的事情成功网

    我们常谈到时间管理,有所谓紧急的事情、重要的事情,然而到底应该先做哪些事情?当然第一个要做的一定是紧急又重要的事情。通常这些都是一些迫不及待要解决的问题。当你天天处理这些事情的时候,表示你的时间管理并不是那么的理想。

    成功者花最多的时间在做最重要、可是不紧急的事情,这些都是所谓的高生产力的事情。然而一般人是做紧急,但不重要的事情。你必须学会如何把重要的事情变得很紧急,这时你就会立刻开始做高生产力的事情了。

    4、第四秘诀:你一定要跟顶尖的人士学习

    每一个成功人士都是跟之前成功的人士学习,这几乎没有什么例外。你跟什么人接触,你的想法就会跟他接近,所以千万要仔细抵选择你所接触的对象,因为这会节省你很多时间。

    假如你跟一个成功者在一起,他花了40年成功,你跟10个这样的人在一起,你是不是就浓缩了400年的经验?这不是时间管理,那什么才叫做时间管理呢?

    一旦掌握了时间管理的秘诀,你会发现自己做事的效率竟然会这么好,你终于有了更多的时间来做自己真正想做的事情。这美好的一切都来自于你彻底的实践学到的每一个方法,从今天开始你将成为一个不折不扣的时间管理专家。
posted @ 2006-05-15 13:19 Martin 阅读(413) | 评论 (0)编辑 收藏
  2006年5月11日
2006德国世界杯6月.JPG
2006德国世界杯7月.JPG
posted @ 2006-05-11 08:44 Martin 阅读(694) | 评论 (0)编辑 收藏
  2006年5月9日
From: http://www.cppblog.com/oosky/archive/2006/01/21/2958.html

堆和栈的区别
一、预备知识—程序的内存分配
一个由c/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放 
4、文字常量区—常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。
二、例子程序 
这是一个前辈写的,非常详细 
//main.cpp 
int a = 0; 全局初始化区 
char *p1; 全局未初始化区 
main() 

int b; 栈 
char s[] = "abc"; 栈 
char *p2; 栈 
char *p3 = "123456"; 123456\0在常量区,p3在栈上。 
static int c =0; 全局(静态)初始化区 
p1 = (char *)malloc(10); 
p2 = (char *)malloc(20); 
分配得来得10和20字节的区域就在堆区。 
strcpy(p1, "123456"); 123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。 


二、堆和栈的理论知识 
2.1申请方式 
stack: 
由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间 
heap: 
需要程序员自己申请,并指明大小,在c中malloc函数 
如p1 = (char *)malloc(10); 
在C++中用new运算符 
如p2 = (char *)malloc(10); 
但是注意p1、p2本身是在栈中的。 


2.2 
申请后系统的响应 
栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。 
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时, 
会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。 

2.3申请大小的限制 
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。 
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。 


2.4申请效率的比较: 
栈由系统自动分配,速度较快。但程序员是无法控制的。 
堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便. 
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活。 

2.5堆和栈中的存储内容 
栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。 
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。 
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。 

2.6存取效率的比较 

char s1[] = "aaaaaaaaaaaaaaa"; 
char *s2 = "bbbbbbbbbbbbbbbbb"; 
aaaaaaaaaaa是在运行时刻赋值的; 
而bbbbbbbbbbb是在编译时就确定的; 
但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。 
比如: 
#include 
void main() 

char a = 1; 
char c[] = "1234567890"; 
char *p ="1234567890"; 
a = c[1]; 
a = p[1]; 
return; 

对应的汇编代码 
10: a = c[1]; 
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh] 
0040106A 88 4D FC mov byte ptr [ebp-4],cl 
11: a = p[1]; 
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h] 
00401070 8A 42 01 mov al,byte ptr [edx+1] 
00401073 88 45 FC mov byte ptr [ebp-4],al 
第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了。 


2.7小结: 
堆和栈的区别可以用如下的比喻来看出: 
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。 
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。 



windows进程中的内存结构


在阅读本文之前,如果你连堆栈是什么多不知道的话,请先阅读文章后面的基础知识。 

接触过编程的人都知道,高级语言都能通过变量名来访问内存中的数据。那么这些变量在内存中是如何存放的呢?程序又是如何使用这些变量的呢?下面就会对此进行深入的讨论。下文中的C语言代码如没有特别声明,默认都使用VC编译的release版。 

首先,来了解一下 C 语言的变量是如何在内存分部的。C 语言有全局变量(Global)、本地变量(Local),静态变量(Static)、寄存器变量(Regeister)。每种变量都有不同的分配方式。先来看下面这段代码: 

#include <stdio.h> 

int g1=0, g2=0, g3=0; 

int main() 

static int s1=0, s2=0, s3=0; 
int v1=0, v2=0, v3=0; 

//打印出各个变量的内存地址 

printf("0x%08x\n",&v1); //打印各本地变量的内存地址 
printf("0x%08x\n",&v2); 
printf("0x%08x\n\n",&v3); 
printf("0x%08x\n",&g1); //打印各全局变量的内存地址 
printf("0x%08x\n",&g2); 
printf("0x%08x\n\n",&g3); 
printf("0x%08x\n",&s1); //打印各静态变量的内存地址 
printf("0x%08x\n",&s2); 
printf("0x%08x\n\n",&s3); 
return 0; 

编译后的执行结果是: 

0x0012ff78 
0x0012ff7c 
0x0012ff80 

0x004068d0 
0x004068d4 
0x004068d8 

0x004068dc 
0x004068e0 
0x004068e4 

输出的结果就是变量的内存地址。其中v1,v2,v3是本地变量,g1,g2,g3是全局变量,s1,s2,s3是静态变量。你可以看到这些变量在内存是连续分布的,但是本地变量和全局变量分配的内存地址差了十万八千里,而全局变量和静态变量分配的内存是连续的。这是因为本地变量和全局/静态变量是分配在不同类型的内存区域中的结果。对于一个进程的内存空间而言,可以在逻辑上分成3个部份:代码区,静态数据区和动态数据区。动态数据区一般就是“堆栈”。“栈(stack)”和“堆(heap)”是两种不同的动态数据区,栈是一种线性结构,堆是一种链式结构。进程的每个线程都有私有的“栈”,所以每个线程虽然代码一样,但本地变量的数据都是互不干扰。一个堆栈可以通过“基地址”和“栈顶”地址来描述。全局变量和静态变量分配在静态数据区,本地变量分配在动态数据区,即堆栈中。程序通过堆栈的基地址和偏移量来访问本地变量。 


├———————┤低端内存区域 
│ …… │ 
├———————┤ 
│ 动态数据区 │ 
├———————┤ 
│ …… │ 
├———————┤ 
│ 代码区 │ 
├———————┤ 
│ 静态数据区 │ 
├———————┤ 
│ …… │ 
├———————┤高端内存区域 


堆栈是一个先进后出的数据结构,栈顶地址总是小于等于栈的基地址。我们可以先了解一下函数调用的过程,以便对堆栈在程序中的作用有更深入的了解。不同的语言有不同的函数调用规定,这些因素有参数的压入规则和堆栈的平衡。windows API的调用规则和ANSI C的函数调用规则是不一样的,前者由被调函数调整堆栈,后者由调用者调整堆栈。两者通过“__stdcall”和“__cdecl”前缀区分。先看下面这段代码: 

#include <stdio.h> 

void __stdcall func(int param1,int param2,int param3) 

int var1=param1; 
int var2=param2; 
int var3=param3; 
printf("0x%08x\n",¶m1); //打印出各个变量的内存地址 
printf("0x%08x\n",¶m2); 
printf("0x%08x\n\n",¶m3); 
printf("0x%08x\n",&var1); 
printf("0x%08x\n",&var2); 
printf("0x%08x\n\n",&var3); 
return; 

int main() 

func(1,2,3); 
return 0; 

编译后的执行结果是: 

0x0012ff78 
0x0012ff7c 
0x0012ff80 

0x0012ff68 
0x0012ff6c 
0x0012ff70 


├———————┤<—函数执行时的栈顶(ESP)、低端内存区域 
│ …… │ 
├———————┤ 
│ var 1 │ 
├———————┤ 
│ var 2 │ 
├———————┤ 
│ var 3 │ 
├———————┤ 
│ RET │ 
├———————┤<—“__cdecl”函数返回后的栈顶(ESP) 
│ parameter 1 │ 
├———————┤ 
│ parameter 2 │ 
├———————┤ 
│ parameter 3 │ 
├———————┤<—“__stdcall”函数返回后的栈顶(ESP) 
│ …… │ 
├———————┤<—栈底(基地址 EBP)、高端内存区域 


上图就是函数调用过程中堆栈的样子了。首先,三个参数以从又到左的次序压入堆栈,先压“param3”,再压“param2”,最后压入“param1”;然后压入函数的返回地址(RET),接着跳转到函数地址接着执行(这里要补充一点,介绍UNIX下的缓冲溢出原理的文章中都提到在压入RET后,继续压入当前EBP,然后用当前ESP代替EBP。然而,有一篇介绍windows下函数调用的文章中说,在windows下的函数调用也有这一步骤,但根据我的实际调试,并未发现这一步,这还可以从param3和var1之间只有4字节的间隙这点看出来);第三步,将栈顶(ESP)减去一个数,为本地变量分配内存空间,上例中是减去12字节(ESP=ESP-3*4,每个int变量占用4个字节);接着就初始化本地变量的内存空间。由于“__stdcall”调用由被调函数调整堆栈,所以在函数返回前要恢复堆栈,先回收本地变量占用的内存(ESP=ESP+3*4),然后取出返回地址,填入EIP寄存器,回收先前压入参数占用的内存(ESP=ESP+3*4),继续执行调用者的代码。参见下列汇编代码: 

;--------------func 函数的汇编代码------------------- 

:00401000 83EC0C sub esp, 0000000C //创建本地变量的内存空间 
:00401003 8B442410 mov eax, dword ptr [esp+10] 
:00401007 8B4C2414 mov ecx, dword ptr [esp+14] 
:0040100B 8B542418 mov edx, dword ptr [esp+18] 
:0040100F 89442400 mov dword ptr [esp], eax 
:00401013 8D442410 lea eax, dword ptr [esp+10] 
:00401017 894C2404 mov dword ptr [esp+04], ecx 

……………………(省略若干代码) 

:00401075 83C43C add esp, 0000003C ;恢复堆栈,回收本地变量的内存空间 
:00401078 C3 ret 000C ;函数返回,恢复参数占用的内存空间 
;如果是“__cdecl”的话,这里是“ret”,堆栈将由调用者恢复 

;-------------------函数结束------------------------- 


;--------------主程序调用func函数的代码-------------- 

:00401080 6A03 push 00000003 //压入参数param3 
:00401082 6A02 push 00000002 //压入参数param2 
:00401084 6A01 push 00000001 //压入参数param1 
:00401086 E875FFFFFF call 00401000 //调用func函数 
;如果是“__cdecl”的话,将在这里恢复堆栈,“add esp, 0000000C” 

聪明的读者看到这里,差不多就明白缓冲溢出的原理了。先来看下面的代码: 

#include <stdio.h> 
#include <string.h> 

void __stdcall func() 

char lpBuff[8]="\0"; 
strcat(lpBuff,"AAAAAAAAAAA"); 
return; 

int main() 

func(); 
return 0; 

编译后执行一下回怎么样?哈,“"0x00414141"指令引用的"0x00000000"内存。该内存不能为"read"。”,“非法操作”喽!"41"就是"A"的16进制的ASCII码了,那明显就是strcat这句出的问题了。"lpBuff"的大小只有8字节,算进结尾的\0,那strcat最多只能写入7个"A",但程序实际写入了11个"A"外加1个\0。再来看看上面那幅图,多出来的4个字节正好覆盖了RET的所在的内存空间,导致函数返回到一个错误的内存地址,执行了错误的指令。如果能精心构造这个字符串,使它分成三部分,前一部份仅仅是填充的无意义数据以达到溢出的目的,接着是一个覆盖RET的数据,紧接着是一段shellcode,那只要着个RET地址能指向这段shellcode的第一个指令,那函数返回时就能执行shellcode了。但是软件的不同版本和不同的运行环境都可能影响这段shellcode在内存中的位置,那么要构造这个RET是十分困难的。一般都在RET和shellcode之间填充大量的NOP指令,使得exploit有更强的通用性。 


├———————┤<—低端内存区域 
│ …… │ 
├———————┤<—由exploit填入数据的开始 
│ │ 
│ buffer │<—填入无用的数据 
│ │ 
├———————┤ 
│ RET │<—指向shellcode,或NOP指令的范围 
├———————┤ 
│ NOP │ 
│ …… │<—填入的NOP指令,是RET可指向的范围 
│ NOP │ 
├———————┤ 
│ │ 
│ shellcode │ 
│ │ 
├———————┤<—由exploit填入数据的结束 
│ …… │ 
├———————┤<—高端内存区域 


windows下的动态数据除了可存放在栈中,还可以存放在堆中。了解C++的朋友都知道,C++可以使用new关键字来动态分配内存。来看下面的C++代码: 

#include <stdio.h> 
#include <iostream.h> 
#include <windows.h> 

void func() 

char *buffer=new char[128]; 
char bufflocal[128]; 
static char buffstatic[128]; 
printf("0x%08x\n",buffer); //打印堆中变量的内存地址 
printf("0x%08x\n",bufflocal); //打印本地变量的内存地址 
printf("0x%08x\n",buffstatic); //打印静态变量的内存地址 

void main() 

func(); 
return; 

程序执行结果为: 

0x004107d0 
0x0012ff04 
0x004068c0 

可以发现用new关键字分配的内存即不在栈中,也不在静态数据区。VC编译器是通过windows下的“堆(heap)”来实现new关键字的内存动态分配。在讲“堆”之前,先来了解一下和“堆”有关的几个API函数: 

HeapAlloc 在堆中申请内存空间 
HeapCreate 创建一个新的堆对象 
HeapDestroy 销毁一个堆对象 
HeapFree 释放申请的内存 
HeapWalk 枚举堆对象的所有内存块 
GetProcessHeap 取得进程的默认堆对象 
GetProcessHeaps 取得进程所有的堆对象 
LocalAlloc 
GlobalAlloc 

当进程初始化时,系统会自动为进程创建一个默认堆,这个堆默认所占内存的大小为1M。堆对象由系统进行管理,它在内存中以链式结构存在。通过下面的代码可以通过堆动态申请内存空间: 

HANDLE hHeap=GetProcessHeap(); 
char *buff=HeapAlloc(hHeap,0,8); 

其中hHeap是堆对象的句柄,buff是指向申请的内存空间的地址。那这个hHeap究竟是什么呢?它的值有什么意义吗?看看下面这段代码吧: 

#pragma comment(linker,"/entry:main") //定义程序的入口 
#include <windows.h> 

_CRTIMP int (__cdecl *printf)(const char *, ...); //定义STL函数printf 
/*--------------------------------------------------------------------------- 
写到这里,我们顺便来复习一下前面所讲的知识: 
(*注)printf函数是C语言的标准函数库中函数,VC的标准函数库由msvcrt.dll模块实现。 
由函数定义可见,printf的参数个数是可变的,函数内部无法预先知道调用者压入的参数个数,函数只能通过分析第一个参数字符串的格式来获得压入参数的信息,由于这里参数的个数是动态的,所以必须由调用者来平衡堆栈,这里便使用了__cdecl调用规则。BTW,Windows系统的API函数基本上是__stdcall调用形式,只有一个API例外,那就是wsprintf,它使用__cdecl调用规则,同printf函数一样,这是由于它的参数个数是可变的缘故。 
---------------------------------------------------------------------------*/ 
void main() 

HANDLE hHeap=GetProcessHeap(); 
char *buff=HeapAlloc(hHeap,0,0x10); 
char *buff2=HeapAlloc(hHeap,0,0x10); 
HMODULE hMsvcrt=LoadLibrary("msvcrt.dll"); 
printf=(void *)GetProcAddress(hMsvcrt,"printf"); 
printf("0x%08x\n",hHeap); 
printf("0x%08x\n",buff); 
printf("0x%08x\n\n",buff2); 

执行结果为: 

0x00130000 
0x00133100 
0x00133118 

hHeap的值怎么和那个buff的值那么接近呢?其实hHeap这个句柄就是指向HEAP首部的地址。在进程的用户区存着一个叫PEB(进程环境块)的结构,这个结构中存放着一些有关进程的重要信息,其中在PEB首地址偏移0x18处存放的ProcessHeap就是进程默认堆的地址,而偏移0x90处存放了指向进程所有堆的地址列表的指针。windows有很多API都使用进程的默认堆来存放动态数据,如windows 2000下的所有ANSI版本的函数都是在默认堆中申请内存来转换ANSI字符串到Unicode字符串的。对一个堆的访问是顺序进行的,同一时刻只能有一个线程访问堆中的数据,当多个线程同时有访问要求时,只能排队等待,这样便造成程序执行效率下降。 

最后来说说内存中的数据对齐。所位数据对齐,是指数据所在的内存地址必须是该数据长度的整数倍,DWORD数据的内存起始地址能被4除尽,WORD数据的内存起始地址能被2除尽,x86 CPU能直接访问对齐的数据,当他试图访问一个未对齐的数据时,会在内部进行一系列的调整,这些调整对于程序来说是透明的,但是会降低运行速度,所以编译器在编译程序时会尽量保证数据对齐。同样一段代码,我们来看看用VC、Dev-C++和lcc三个不同编译器编译出来的程序的执行结果: 

#include <stdio.h> 

int main() 

int a; 
char b; 
int c; 
printf("0x%08x\n",&a); 
printf("0x%08x\n",&b); 
printf("0x%08x\n",&c); 
return 0; 

这是用VC编译后的执行结果: 
0x0012ff7c 
0x0012ff7b 
0x0012ff80 
变量在内存中的顺序:b(1字节)-a(4字节)-c(4字节)。 

这是用Dev-C++编译后的执行结果: 
0x0022ff7c 
0x0022ff7b 
0x0022ff74 
变量在内存中的顺序:c(4字节)-中间相隔3字节-b(占1字节)-a(4字节)。 

这是用lcc编译后的执行结果: 
0x0012ff6c 
0x0012ff6b 
0x0012ff64 
变量在内存中的顺序:同上。 

三个编译器都做到了数据对齐,但是后两个编译器显然没VC“聪明”,让一个char占了4字节,浪费内存哦。 


基础知识: 
堆栈是一种简单的数据结构,是一种只允许在其一端进行插入或删除的线性表。允许插入或删除操作的一端称为栈顶,另一端称为栈底,对堆栈的插入和删除操作被称为入栈和出栈。有一组CPU指令可以实现对进程的内存实现堆栈访问。其中,POP指令实现出栈操作,PUSH指令实现入栈操作。CPU的ESP寄存器存放当前线程的栈顶指针,EBP寄存器中保存当前线程的栈底指针。CPU的EIP寄存器存放下一个CPU指令存放的内存地址,当CPU执行完当前的指令后,从EIP寄存器中读取下一条指令的内存地址,然后继续执行。 


参考:《Windows下的HEAP溢出及其利用》by: isno 
《windows核心编程》by: Jeffrey Richter 



摘要: 讨论常见的堆性能问题以及如何防范它们。(共 9 页)

前言
您是否是动态分配的 C/C++ 对象忠实且幸运的用户?您是否在模块间的往返通信中频繁地使用了“自动化”?您的程序是否因堆分配而运行起来很慢?不仅仅您遇到这样的问题。几乎所有项目迟早都会遇到堆问题。大家都想说,“我的代码真正好,只是堆太慢”。那只是部分正确。更深入理解堆及其用法、以及会发生什么问题,是很有用的。

什么是堆?
(如果您已经知道什么是堆,可以跳到“什么是常见的堆性能问题?”部分)

在程序中,使用堆来动态分配和释放对象。在下列情况下,调用堆操作: 

事先不知道程序所需对象的数量和大小。


对象太大而不适合堆栈分配程序。
堆使用了在运行时分配给代码和堆栈的内存之外的部分内存。下图给出了堆分配程序的不同层。

GlobalAlloc/GlobalFree:Microsoft Win32 堆调用,这些调用直接与每个进程的默认堆进行对话。

LocalAlloc/LocalFree:Win32 堆调用(为了与 Microsoft Windows NT 兼容),这些调用直接与每个进程的默认堆进行对话。

COM 的 IMalloc 分配程序(或 CoTaskMemAlloc / CoTaskMemFree):函数使用每个进程的默认堆。自动化程序使用“组件对象模型 (COM)”的分配程序,而申请的程序使用每个进程堆。

C/C++ 运行时 (CRT) 分配程序:提供了 malloc() 和 free() 以及 new 和 delete 操作符。如 Microsoft Visual Basic 和 Java 等语言也提供了新的操作符并使用垃圾收集来代替堆。CRT 创建自己的私有堆,驻留在 Win32 堆的顶部。

Windows NT 中,Win32 堆是 Windows NT 运行时分配程序周围的薄层。所有 API 转发它们的请求给 NTDLL。

Windows NT 运行时分配程序提供 Windows NT 内的核心堆分配程序。它由具有 128 个大小从 8 到 1,024 字节的空闲列表的前端分配程序组成。后端分配程序使用虚拟内存来保留和提交页。

在图表的底部是“虚拟内存分配程序”,操作系统使用它来保留和提交页。所有分配程序使用虚拟内存进行数据的存取。

分配和释放块不就那么简单吗?为何花费这么长时间?

堆实现的注意事项
传统上,操作系统和运行时库是与堆的实现共存的。在一个进程的开始,操作系统创建一个默认堆,叫做“进程堆”。如果没有其他堆可使用,则块的分配使用“进程堆”。语言运行时也能在进程内创建单独的堆。(例如,C 运行时创建它自己的堆。)除这些专用的堆外,应用程序或许多已载入的动态链接库 (DLL) 之一可以创建和使用单独的堆。Win32 提供一整套 API 来创建和使用私有堆。有关堆函数(英文)的详尽指导,请参见 MSDN。

当应用程序或 DLL 创建私有堆时,这些堆存在于进程空间,并且在进程内是可访问的。从给定堆分配的数据将在同一个堆上释放。(不能从一个堆分配而在另一个堆释放。)

在所有虚拟内存系统中,堆驻留在操作系统的“虚拟内存管理器”的顶部。语言运行时堆也驻留在虚拟内存顶部。某些情况下,这些堆是操作系统堆中的层,而语言运行时堆则通过大块的分配来执行自己的内存管理。不使用操作系统堆,而使用虚拟内存函数更利于堆的分配和块的使用。

典型的堆实现由前、后端分配程序组成。前端分配程序维持固定大小块的空闲列表。对于一次分配调用,堆尝试从前端列表找到一个自由块。如果失败,堆被迫从后端(保留和提交虚拟内存)分配一个大块来满足请求。通用的实现有每块分配的开销,这将耗费执行周期,也减少了可使用的存储空间。

Knowledge Base 文章 Q10758,“用 calloc() 和 malloc() 管理内存” (搜索文章编号), 包含了有关这些主题的更多背景知识。另外,有关堆实现和设计的详细讨论也可在下列著作中找到:“Dynamic Storage Allocation: A Survey and Critical Review”,作者 Paul R. Wilson、Mark S. Johnstone、Michael Neely 和 David Boles;“International Workshop on Memory Management”, 作者 Kinross, Scotland, UK, 1995 年 9 月(http://www.cs.utexas.edu/users/oops/papers.html)(英文)。

Windows NT 的实现(Windows NT 版本 4.0 和更新版本) 使用了 127 个大小从 8 到 1,024 字节的 8 字节对齐块空闲列表和一个“大块”列表。“大块”列表(空闲列表[0]) 保存大于 1,024 字节的块。空闲列表容纳了用双向链表链接在一起的对象。默认情况下,“进程堆”执行收集操作。(收集是将相邻空闲块合并成一个大块的操作。)收集耗费了额外的周期,但减少了堆块的内部碎片。

单一全局锁保护堆,防止多线程式的使用。(请参见“Server Performance and Scalability Killers”中的第一个注意事项, George Reilly 所著,在 “MSDN Online Web Workshop”上(站点:http://msdn.microsoft.com/workshop/server/iis/tencom.asp(英文)。)单一全局锁本质上是用来保护堆数据结构,防止跨多线程的随机存取。若堆操作太频繁,单一全局锁会对性能有不利的影响。

什么是常见的堆性能问题?
以下是您使用堆时会遇到的最常见问题: 

分配操作造成的速度减慢。光分配就耗费很长时间。最可能导致运行速度减慢原因是空闲列表没有块,所以运行时分配程序代码会耗费周期寻找较大的空闲块,或从后端分配程序分配新块。


释放操作造成的速度减慢。释放操作耗费较多周期,主要是启用了收集操作。收集期间,每个释放操作“查找”它的相邻块,取出它们并构造成较大块,然后再把此较大块插入空闲列表。在查找期间,内存可能会随机碰到,从而导致高速缓存不能命中,性能降低。


堆竞争造成的速度减慢。当两个或多个线程同时访问数据,而且一个线程继续进行之前必须等待另一个线程完成时就发生竞争。竞争总是导致麻烦;这也是目前多处理器系统遇到的最大问题。当大量使用内存块的应用程序或 DLL 以多线程方式运行(或运行于多处理器系统上)时将导致速度减慢。单一锁定的使用—常用的解决方案—意味着使用堆的所有操作是序列化的。当等待锁定时序列化会引起线程切换上下文。可以想象交叉路口闪烁的红灯处走走停停导致的速度减慢。 
竞争通常会导致线程和进程的上下文切换。上下文切换的开销是很大的,但开销更大的是数据从处理器高速缓存中丢失,以及后来线程复活时的数据重建。

堆破坏造成的速度减慢。造成堆破坏的原因是应用程序对堆块的不正确使用。通常情形包括释放已释放的堆块或使用已释放的堆块,以及块的越界重写等明显问题。(破坏不在本文讨论范围之内。有关内存重写和泄漏等其他细节,请参见 Microsoft Visual C++(R) 调试文档 。)


频繁的分配和重分配造成的速度减慢。这是使用脚本语言时非常普遍的现象。如字符串被反复分配,随重分配增长和释放。不要这样做,如果可能,尽量分配大字符串和使用缓冲区。另一种方法就是尽量少用连接操作。
竞争是在分配和释放操作中导致速度减慢的问题。理想情况下,希望使用没有竞争和快速分配/释放的堆。可惜,现在还没有这样的通用堆,也许将来会有。

在所有的服务器系统中(如 IIS、MSProxy、DatabaseStacks、网络服务器、 Exchange 和其他), 堆锁定实在是个大瓶颈。处理器数越多,竞争就越会恶化。

尽量减少堆的使用
现在您明白使用堆时存在的问题了,难道您不想拥有能解决这些问题的超级魔棒吗?我可希望有。但没有魔法能使堆运行加快—因此不要期望在产品出货之前的最后一星期能够大为改观。如果提前规划堆策略,情况将会大大好转。调整使用堆的方法,减少对堆的操作是提高性能的良方。

如何减少使用堆操作?通过利用数据结构内的位置可减少堆操作的次数。请考虑下列实例:

struct ObjectA {
   // objectA 的数据 
}

struct ObjectB {
   // objectB 的数据 
}

// 同时使用 objectA 和 objectB

//
// 使用指针 
//
struct ObjectB {
   struct ObjectA * pObjA;
   // objectB 的数据 
}

//
// 使用嵌入
//
struct ObjectB {
   struct ObjectA pObjA;
   // objectB 的数据 
}

//
// 集合 – 在另一对象内使用 objectA 和 objectB
//

struct ObjectX {
   struct ObjectA  objA;
   struct ObjectB  objB;
}

避免使用指针关联两个数据结构。如果使用指针关联两个数据结构,前面实例中的对象 A 和 B 将被分别分配和释放。这会增加额外开销—我们要避免这种做法。


把带指针的子对象嵌入父对象。当对象中有指针时,则意味着对象中有动态元素(百分之八十)和没有引用的新位置。嵌入增加了位置从而减少了进一步分配/释放的需求。这将提高应用程序的性能。


合并小对象形成大对象(聚合)。聚合减少分配和释放的块的数量。如果有几个开发者,各自开发设计的不同部分,则最终会有许多小对象需要合并。集成的挑战就是要找到正确的聚合边界。


内联缓冲区能够满足百分之八十的需要(aka 80-20 规则)。个别情况下,需要内存缓冲区来保存字符串/二进制数据,但事先不知道总字节数。估计并内联一个大小能满足百分之八十需要的缓冲区。对剩余的百分之二十,可以分配一个新的缓冲区和指向这个缓冲区的指针。这样,就减少分配和释放调用并增加数据的位置空间,从根本上提高代码的性能。


在块中分配对象(块化)。块化是以组的方式一次分配多个对象的方法。如果对列表的项连续跟踪,例如对一个 {名称,值} 对的列表,有两种选择:选择一是为每一个“名称-值”对分配一个节点;选择二是分配一个能容纳(如五个)“名称-值”对的结构。例如,一般情况下,如果存储四对,就可减少节点的数量,如果需要额外的空间数量,则使用附加的链表指针。 
块化是友好的处理器高速缓存,特别是对于 L1-高速缓存,因为它提供了增加的位置 —不用说对于块分配,很多数据块会在同一个虚拟页中。

正确使用 _amblksiz。C 运行时 (CRT) 有它的自定义前端分配程序,该分配程序从后端(Win32 堆)分配大小为 _amblksiz 的块。将 _amblksiz 设置为较高的值能潜在地减少对后端的调用次数。这只对广泛使用 CRT 的程序适用。
使用上述技术将获得的好处会因对象类型、大小及工作量而有所不同。但总能在性能和可升缩性方面有所收获。另一方面,代码会有点特殊,但如果经过深思熟虑,代码还是很容易管理的。

其他提高性能的技术
下面是一些提高速度的技术: 

使用 Windows NT5 堆 
由于几个同事的努力和辛勤工作,1998 年初 Microsoft Windows(R) 2000 中有了几个重大改进:

改进了堆代码内的锁定。堆代码对每堆一个锁。全局锁保护堆数据结构,防止多线程式的使用。但不幸的是,在高通信量的情况下,堆仍受困于全局锁,导致高竞争和低性能。Windows 2000 中,锁内代码的临界区将竞争的可能性减到最小,从而提高了可伸缩性。


使用 “Lookaside”列表。堆数据结构对块的所有空闲项使用了大小在 8 到 1,024 字节(以 8-字节递增)的快速高速缓存。快速高速缓存最初保护在全局锁内。现在,使用 lookaside 列表来访问这些快速高速缓存空闲列表。这些列表不要求锁定,而是使用 64 位的互锁操作,因此提高了性能。


内部数据结构算法也得到改进。
这些改进避免了对分配高速缓存的需求,但不排除其他的优化。使用 Windows NT5 堆评估您的代码;它对小于 1,024 字节 (1 KB) 的块(来自前端分配程序的块)是最佳的。GlobalAlloc() 和 LocalAlloc() 建立在同一堆上,是存取每个进程堆的通用机制。如果希望获得高的局部性能,则使用 Heap(R) API 来存取每个进程堆,或为分配操作创建自己的堆。如果需要对大块操作,也可以直接使用 VirtualAlloc() / VirtualFree() 操作。

上述改进已在 Windows 2000 beta 2 和 Windows NT 4.0 SP4 中使用。改进后,堆锁的竞争率显著降低。这使所有 Win32 堆的直接用户受益。CRT 堆建立于 Win32 堆的顶部,但它使用自己的小块堆,因而不能从 Windows NT 改进中受益。(Visual C++ 版本 6.0 也有改进的堆分配程序。)

使用分配高速缓存 
分配高速缓存允许高速缓存分配的块,以便将来重用。这能够减少对进程堆(或全局堆)的分配/释放调用的次数,也允许最大限度的重用曾经分配的块。另外,分配高速缓存允许收集统计信息,以便较好地理解对象在较高层次上的使用。

典型地,自定义堆分配程序在进程堆的顶部实现。自定义堆分配程序与系统堆的行为很相似。主要的差别是它在进程堆的顶部为分配的对象提供高速缓存。高速缓存设计成一套固定大小(如 32 字节、64 字节、128 字节等)。这一个很好的策略,但这种自定义堆分配程序丢失与分配和释放的对象相关的“语义信息”。 

与自定义堆分配程序相反,“分配高速缓存”作为每类分配高速缓存来实现。除能够提供自定义堆分配程序的所有好处之外,它们还能够保留大量语义信息。每个分配高速缓存处理程序与一个目标二进制对象关联。它能够使用一套参数进行初始化,这些参数表示并发级别、对象大小和保持在空闲列表中的元素的数量等。分配高速缓存处理程序对象维持自己的私有空闲实体池(不超过指定的阀值)并使用私有保护锁。合在一起,分配高速缓存和私有锁减少了与主系统堆的通信量,因而提供了增加的并发、最大限度的重用和较高的可伸缩性。

需要使用清理程序来定期检查所有分配高速缓存处理程序的活动情况并回收未用的资源。如果发现没有活动,将释放分配对象的池,从而提高性能。

可以审核每个分配/释放活动。第一级信息包括对象、分配和释放调用的总数。通过查看它们的统计信息可以得出各个对象之间的语义关系。利用以上介绍的许多技术之一,这种关系可以用来减少内存分配。

分配高速缓存也起到了调试助手的作用,帮助您跟踪没有完全清除的对象数量。通过查看动态堆栈返回踪迹和除没有清除的对象之外的签名,甚至能够找到确切的失败的调用者。

MP 堆 
MP 堆是对多处理器友好的分布式分配的程序包,在 Win32 SDK(Windows NT 4.0 和更新版本)中可以得到。最初由 JVert 实现,此处堆抽象建立在 Win32 堆程序包的顶部。MP 堆创建多个 Win32 堆,并试图将分配调用分布到不同堆,以减少在所有单一锁上的竞争。

本程序包是好的步骤 —一种改进的 MP-友好的自定义堆分配程序。但是,它不提供语义信息和缺乏统计功能。通常将 MP 堆作为 SDK 库来使用。如果使用这个 SDK 创建可重用组件,您将大大受益。但是,如果在每个 DLL 中建立这个 SDK 库,将增加工作设置。

重新思考算法和数据结构 
要在多处理器机器上伸缩,则算法、实现、数据结构和硬件必须动态伸缩。请看最经常分配和释放的数据结构。试问,“我能用不同的数据结构完成此工作吗?”例如,如果在应用程序初始化时加载了只读项的列表,这个列表不必是线性链接的列表。如果是动态分配的数组就非常好。动态分配的数组将减少内存中的堆块和碎片,从而增强性能。

减少需要的小对象的数量减少堆分配程序的负载。例如,我们在服务器的关键处理路径上使用五个不同的对象,每个对象单独分配和释放。一起高速缓存这些对象,把堆调用从五个减少到一个,显著减少了堆的负载,特别当每秒钟处理 1,000 个以上的请求时。

如果大量使用“Automation”结构,请考虑从主线代码中删除“Automation BSTR”,或至少避免重复的 BSTR 操作。(BSTR 连接导致过多的重分配和分配/释放操作。)

摘要
对所有平台往往都存在堆实现,因此有巨大的开销。每个单独代码都有特定的要求,但设计能采用本文讨论的基本理论来减少堆之间的相互作用。 

评价您的代码中堆的使用。


改进您的代码,以使用较少的堆调用:分析关键路径和固定数据结构。


在实现自定义的包装程序之前使用量化堆调用成本的方法。


如果对性能不满意,请要求 OS 组改进堆。更多这类请求意味着对改进堆的更多关注。


要求 C 运行时组针对 OS 所提供的堆制作小巧的分配包装程序。随着 OS 堆的改进,C 运行时堆调用的成本将减小。


操作系统(Windows NT 家族)正在不断改进堆。请随时关注和利用这些改进。
Murali Krishnan 是 Internet Information Server (IIS) 组的首席软件设计工程师。从 1.0 版本开始他就设计 IIS,并成功发行了 1.0 版本到 4.0 版本。Murali 组织并领导 IIS 性能组三年 (1995-1998), 从一开始就影响 IIS 性能。他拥有威斯康星州 Madison 大学的 M.S.和印度 Anna 大学的 B.S.。工作之外,他喜欢阅读、打排球和家庭烹饪。



http://community.csdn.net/Expert/FAQ/FAQ_Index.asp?id=172835
我在学习对象的生存方式的时候见到一种是在堆栈(stack)之中,如下  
CObject  object;  
还有一种是在堆(heap)中  如下  
CObject*  pobject=new  CObject();  
 
请问  
(1)这两种方式有什么区别?  
(2)堆栈与堆有什么区别??  
 
 
---------------------------------------------------------------  
 
1)  about  stack,  system  will  allocate  memory  to  the  instance  of  object  automatically,  and  to  the
 heap,  you  must  allocate  memory  to  the  instance  of  object  with  new  or  malloc  manually.  
2)  when  function  ends,  system  will  automatically  free  the  memory  area  of  stack,  but  to  the 
heap,  you  must  free  the  memory  area  manually  with  free  or  delete,  else  it  will  result  in  memory
leak.  
3)栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。  
4)堆上分配的内存可以有我们自己决定,使用非常灵活。  
---------------------------------------------------------------  
 
 
堆和栈的比较  
 
     从堆和栈的功能和作用来通俗的比较,堆主要用来存放对象的,栈主要是用来执行程序的.而这种不同又主要是由于堆和栈的特点决定的:  
 
     在编程中,例如C/C++中,所有的方法调用都是通过栈来进行的,所有的局部变量,形式参数都是从栈中分配内存空间的。实际上也不是什么分配,只是从栈顶向上用就行,就好像工厂中的传送带(conveyor  belt)一样,Stack  Pointer会自动指引你到放东西的位置,你所要做的只是把东西放下来就行.退出函数的时候,修改栈指针就可以把栈中的内容销毁.这样的模式速度最快,当然要用来运行程序了.需要注意的是,在分配的时候,比如为一个即将要调用的程序模块分配数据区时,应事先知道这个数据区的大小,也就说是虽然分配是在程序运行时进行的,但是分配的大小多少是确定的,不变的,而这个"大小多少"是在编译时确定的,不是在运行时.  
 
     堆是应用程序在运行的时候请求操作系统分配给自己内存,由于从操作系统管理的内存分配,所以在分配和销毁时都要占用时间,因此用堆的效率非常低.但是堆的优点在于,编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间,因此,用堆保存数据时会得到更大的灵活性。事实上,面向对象的多态性,堆内存分配是必不可少的,因为多态变量所需的存储空间只有在运行时创建了对象之后才能确定.在C++中,要求创建一个对象时,只需用new命令编制相关的代码即可。执行这些代码时,会在堆里自动进行数据的保存.当然,为达到这种灵活性,必然会付出一定的代价:在堆里分配存储空间时会花掉更长的时间!这也正是导致效率低的原因,  
 
我想你现在该明白了吧。:)   

posted @ 2006-05-09 18:23 Martin 阅读(681) | 评论 (0)编辑 收藏
仅列出标题  下一页