旷野的呼声

路漫漫其修远兮 吾将上下而求索

常用链接

统计

最新评论

2010年1月5日 #

难得上cppblog一次,分享一本书吧。《Pro Visual C++/CLI and The .NET3.5 Platform》英文清晰PDF版本

地址在这里

另外分享一小段没什么用处的测试代码:


#include <iostream>
#include 
<string>
#include 
<list>
#include 
<algorithm>

#include 
<vcclr.h>

using namespace System;

int main()
{
    std::list
<int> lstTest;

    std::copy( 
        std::istream_iterator
<int>(std::cin), 
        std::istream_iterator
<int>(), 
        std::back_insert_iterator
< std::list<int> >( lstTest ) 
        );

    System::Collections::Generic::List
<int>^ lstCLR = gcnew Collections::Generic::List<int>();

    
for( std::list<int>::const_iterator it = lstTest.begin();
        it 
!= lstTest.end();
        
++it )
    {
        lstCLR
->Add( *it );
    }

    printf_s( 
"-----------------------------------------\n" );

    lstCLR
->Sort();

    
for each( int i in lstCLR )
    {
        Console::WriteLine( i );
    }

    System::Collections::Generic::List
< String^ >^ lstString = gcnew Collections::Generic::List<String^>();

    
forint i =0; i < 10++i )
    {
        wchar_t sz[
12];
        swprintf_s( sz, L
"%d", i );

        lstString
->Add( gcnew String( sz ) );
    }

    std::list
<std::wstring> lstStdString;

    
for each( String^ Str in lstString )
    {
        pin_ptr
< const wchar_t > pStr = PtrToStringChars( Str );

        lstStdString.push_back( std::wstring( pStr ) );
    }

     std::copy( lstStdString.begin(), 
         lstStdString.end(), 
         std::ostream_iterator
< std::wstring, wchar_t >( std::wcout, L"\n" )
         );
     

    
return 0;
}

 这才是我经常活动的家

posted @ 2010-01-05 16:42 董波 阅读(421) | 评论 (0)编辑 收藏

2009年9月12日 #

柏林噪声 perlin noise

Perlin Noise  在wikipia上,可以查到定义,这里不多说了,http://freespace.virgin.net/hugo.elias/models/m_perlin.htm 上有完整的英文,http://www.azure.com.cn/article.asp?id=291 是带有翻译的英文, 看来这篇文章是原始作者的大作了。转帖过来

柏林噪声(Perlin Noise)(译)
原文链接:http://freespace.virgin.net/hugo.elias/models/m_perlin.htm
翻译:azure

Many people have used random number generators in their programs to create unpredictability, make the motion and behavior of objects appear more natural, or generate textures. Random number generators certainly have their uses, but at times their output can be too harsh to appear natural. This article will present a function which has a very wide range of uses, more than I can think of, but basically anywhere where you need something to look natural in origin. What's more it's output can easily be tailored to suit your needs.
很多人在他们的程序中使用随机数生成器去创造不可预测,使物体的行为和运动表现的更加自然,或者生成纹理。随机数生成器当然是有他们的用途的,但是它们似乎过于苛刻。这篇文章将会展示一个用途十分广泛的功能,甚至其用途比我想到的还要广泛,其结果可以轻易的适合你的需求。

If you look at many things in nature, you will notice that they are fractal. They have various levels of detail. A common example is the outline of a mountain range. It contains large variations in height (the mountains), medium variations (hills), small variations (boulders), tiny variations (stones) . . . you could go on. Look at almost anything: the distribution of patchy grass on a field, waves in the sea, the movements of an ant, the movement of branches of a tree, patterns in marble, winds. All these phenomena exhibit the same pattern of large and small variations. The Perlin Noise function recreates this by simply adding up noisy functions at a range of different scales.
如果你观察自然界中很多事物,你会注意到它们是分形的。它们有着很多层次细节。最平常的例子是山峰轮廓。它包含着高度上的很大变化(山峰),中等变化(丘陵),小的变化(砾石),微小变化(石头)...你可以继续想象。观察几乎所有事物:片状分布于田间草,海中的波浪,蚂蚁的运动方式,树枝的运动,大理石的花纹,风。所有这些现象表现出了同一种的大小的变化形式。柏林噪声函数通过直接添加一定范围内,不同比例的噪声函数来重现这种现象。

To create a Perlin noise function, you will need two things, a Noise Function, and an Interpolation Function.
为了创建一个柏林噪声函数,我们需要两个东西,一个噪声函数和一个插值函数。

Introduction To Noise Functions
噪声函数介绍

A noise function is essentially a seeded random number generator. It takes an integer as a parameter, and returns a random number based on that parameter. If you pass it the same parameter twice, it produces the same number twice. It is very important that it behaves in this way, otherwise the Perlin function will simply produce nonsense.
一个噪声函数基本上是一个种子随机发生器。它需要一个整数作为参数,然后返回根据这个参数返回一个随机数。如果你两次都传同一个参数进来,它就会产生两次相同的数。这条规律非常重要,否则柏林函数只是生成一堆垃圾。




Here is a graph showing an example noise function. A random value between 0 and1 is assigned to every
point on the X axis.
这里的一张图展现了噪声函数的一个例子。X轴上每个点被赋予一个0到1之间的随机数。


By smoothly interpolating between the values, we can define a continuous function that takes a non-integer as a parameter. I will discuss various ways of interpolating the values later in this article.
通过在值之间平滑的插值,我们定义了一个带有一个非整参数的连续函数。我们将会在后面的内容中讨论多种插值方式

Definitions
定义

Before I go any further, let me define what I mean by amplitude and frequency. If you have studied physics, you may well have come across the concept of amplitude and frequency applied to a sin wave.
当我们准备深入之前,让我定义下什么是振幅(amplitude)和频率(frequency)。如果你学过物理,你可能遇到过在正玄波中振幅(amlitude)和频率(frequency)的概念。


Sin Wave
The wavelength of a sin wave is the distance from one peak to another. The amplitude is the height of the wave. The frequency is defined to be 1/wavelength.
正玄波
正玄波的波长(wavelength)是两个波峰只间的距离。振幅是此波的高度。频率我们定义为 1/波长(wavelength)。


Noise Wave
In the graph of this example noise function, the red spots indicate the random values defined along the dimension of the function. In this case, the amplitude is the difference between the minimum and maximum values the function could have. The wavelength is the distance from one red spot to the next. Again frequency is defined to be 1/wavelength.
噪声波
图中这个噪声波的例子中,红点表示定义沿着在函数维上的随机值。在这种情况下,振幅是这个函数的最大值与最小值的差值。波长(wavelength)是两个红点之间的距离。同样的频率(frequency)定义为1/波长(wavelength).

Creating the Perlin Noise Function
创建柏林噪声函数

Now, if you take lots of such smooth functions, with various frequencies and amplitudes, you can add them all together to create a nice noisy function. This is the Perlin Noise Function.
现在,如果你使用很多平滑函数,分别拥有各种各样的频率和振幅,你可以把他们叠加在一起来创建一个漂亮的噪声函数。这个就是柏林噪声函数。

Take the following Noise Functions
使用以下的噪声函数



Add them together, and this is what you get.
将他们叠加起来,你将会得到:-)


You can see that this function has large, medium and small variations. You may even imagine that it looks a little like a mountain range. In fact many computer generated landscapes are made using this method. Of course they use 2D noise, which I shall get onto in a moment.
你能发现这个函数拥有大的,中的和小的变化。你甚至可以它已经有点像山的轮廓了。事实上很多电脑生成地形景观也是使用了这种方法,当然那使用的是2D的噪声,我们将过一下来研究这个。

You can, of course, do the same in 2 dimensions.
你当然同样的可以在二维下也这么做。

Some noise functions are created in 2D
一些2D的噪声函数




Adding all these functions together produces a noisy pattern.
把这些函数叠加起来产生的噪声样式。



Persistence
持续度

When you're adding together these noise functions, you may wonder exactly what amplitude and frequency to use for each one. The one dimensional example above used twice the frequency and half the amplitude for each successive noise function added. This is quite common. So common in fact, that many people don't even consider using anything else. However, you can create Perlin Noise functions with different characteristics by using other frequencies and amplitudes at each step. For example, to create smooth rolling hills, you could use Perlin noise function with large amplitudes for the low frequencies , and very small amplitudes for the higher frequencies. Or you could make a flat, but very rocky plane choosing low amplitudes for low frequencies.
当你把噪声函数叠加的时候,你可能想了解每次具体使用了什么振幅和频率。上面一维的例子对于每个连续叠加的噪声函数使用了两倍的频率和二分之一倍的振幅。这个太普通了,事实上太普通,以至于很多人甚至从来都没有考虑过使用其他什么。尽管如此,你可以通过在每步使用其他的频率和振幅来创建不同特征的柏林噪声函数。例如,为了创建一个平滑滚动的丘陵,你可以使用大的振幅和小的频率的柏林噪声函数,同时小的振幅和高的频率,你可以创建一个平地,另外要创建非常颠簸的平面,应该选择小的振幅和低的频率。

To make it simpler, and to avoid repeating the words Amplitude and Frequency all the time, a single number is used to specify the amplitude of each frequency. This value is known as Persistence. There is some ambiguity as to it's exact meaning. The term was originally coined by Mandelbrot, one of the people behind the discovery of fractals. He defined noise with a lot of high frequency as having a low persistence. My friend Matt also came up with the concept of persistence, but defined it the other way round. To be honest, I prefer Matt's definition. Sorry Mandelbrot. So our definition of persistence is this:
为了让这些更简单易懂,同时为了避免重复振幅和频率这两个词,我们用一个数来表示每个频率下的振幅,这个数就是持续度(Persistence)。这里的词和它的真实意义有些歧异。这个术语原本是Mandelbrot提出的,他是发现分形现象的人中的一个。他定义噪声拥有大量的高频率将体现出低的持续度。我的朋友Matt也想出了持续度的概念,但是是通过另外一种方式定义它的。诚然,我更喜欢Matt的定义方式。对不起了,Mandelbrot. 所以我们这样定义持续度(persistence):
frequency = 2i
amplitude = persistencei
Where i is the ith noise function being added. To illustrate the effect of persistence on the output of the Perlin Noise, take a look at the diagrams below. They show the component noise functions that are added, the effect of the persistence value, and the resultant Perlin noise function.
i 是表示第i个被叠加的噪声函数。为了展示柏林函数在输出上持续度的表现效果,请看下下面的图表。他们展示了叠加的每个组成部分,持续度的效果和最终的柏林函数。



Octaves
倍频

Each successive noise function you add is known as an octave. The reason for this is that each noise function is twice the frequency of the previous one. In music, octaves also have this property.
每个你所叠加的噪声函数就是一个倍频。因为每一个噪声函数是上一个的两倍频率。在音乐上,倍频也有着这项属性。

Exactly how many octaves you add together is entirely up to you. You may add as many or as few as you want. However, let me give you some suggestions. If you are using the perlin noise function to render an image to the screen, there will come a point when an octave has too high a frequency to be displayable. There simply may not be enough pixels on the screen to reproduce all the little details of a very high frequency noise function. Some implementations of Perlin Noise automatically add up as many noise functions they can until the limits of the screen (or other medium) are reached.
具体多少倍频你叠加在一起,这完全取决于你。你可以叠加很多也可以很少。尽管如此,还是让我给你一些建议吧。如果你正使用柏林噪声函数在屏幕上渲染图象的话,如果倍频频率太高将会使缩成一个点以至于不能显示,这就是因为你屏幕的分辨率不够。一些柏林噪声函数的实现会自动叠加噪声函数直到达到屏幕分辨率的极限。

It is also wise to stop adding noise functions when their amplitude becomes too small to reproduce. Exactly when that happens depends on the level of persistence, the overall amplitude of the Perlin function and the bit resolution of your screen (or whatever).
当振幅变的很小的时候,也应该明智的停止再叠加噪声函数。届时当发生依靠持续度的等级,柏林函数整体的振幅和屏幕的分辨率(翻译的烂)。

Making your noise functions
创造你的噪声函数

What do we look for in a noise function? Well, it's essentially a random number generator. However, unlike other random number generators you may have come across in your programs which give you a different random number every time you call them, these noise functions supply a random number calculated from one or more parameters. I.e. every time you pass the same number to the noise function, it will respond with the same number. But pass it a different number, and it will return a different number.
我们需要什么样的噪声函数?好,基本上就是一个随机数发生器。尽管如此,它不像你在程序中遇到的那中每次调用它都返回不同的随机数的随机函数,这些噪声函数生成一个随机数是通过一个或者多个参数计算而来。例如,每次你传入一个相同的数到噪声函数里,它将每次也返回相同的随机数。但是如果传入一个不同的数,那么它又将返回一个不同的数,

Well, I don't know a lot about random number generators, so I went looking for some, and here's one I found. It seems to be pretty good. It returns floating point numbers between -1.0 and1.0.
好,我对随机数生成器并不懂太多,所以我去找了一些,这里我找到了一个,好象很好用。它返回一个浮点数,范围是-1.0到1.0
function IntNoise(32-bit integer: x)      

x = (x<<13) ^ x;
return ( 1.0 - ( (x * (x * x * 15731 + 789221) + 1376312589) & 7fffffff) / 1073741824.0);
end IntNoise function
Now, you'll want several different random number generators, so I suggest making several copies of the above code, but use slightly different numbers. Those big scarey looking numbers are all prime numbers, so you could just use some other prime numbers of a similar size. So, to make it easy for you to find random numbers, I have written a little program to list prime numbers for you. You can give it a start number and an end number, and it will find all the primes between the two. Source code is also included, so you can easily include it into your own programs to produce a random prime number. Primes.zip
现在,你将要需要几个不同的随机数生成器,所以我建议把上面的代码复制几个拷贝,然后稍微修改下里面的参数。那些可怕的数字都是质数,所以你可以改成其他差不多大小的质数(让我想起了 hash key生成),为了让你轻松的找的随机数,我已经写了一个小程序来为你列出质数。你只用输入一个起始值和一个结束值,它找到所有在两值之间的质数。源代码也提供,所以你可以轻松的包含到你自己的程序中来生成随机的质数.Primes.zip

Interpolation
插值

Having created your noise function, you will need to smooth out the values it returns. Again, you can choose any method you like, but some look better than others. A standard interpolation function takes three inputs, a and b, the values to be interpolated between, and x which takes a value between 0 and1. The Interpolation function returns a value between a and b based on the value x. When x equals 0, it returns a, and when x is 1, it returns b. When x is between 0 and1, it returns some value between a and b.
当创建了你的噪声函数,你将需要平滑下他的返回值。再次,你可以选择任何你喜欢的方式,但是有一些效果更好。一个标准的插值函数需要三个输入,a 和 b, 需要在a和b之间进行插值,还有x,它取值范围是0到1。插值函数返回a到b之间取决与x的一个值。当x等于0,它返回a,当x等于1时,它返回b。当x 是0到1之间时,它将返回a到b之间的某值。

Linear Interpolation:
线性插值

Looks awful, like those cheap 'plasmas' that everyone uses to generate landscapes. It's a simple algorithm though, and I suppose would be excusable if you were trying to do perlin noise in realtime.
看起来很龌龊的,像那些每个人用来生成地形的廉价'plasmas'一样,它是一个简单的算法,如果你想实时的使用柏林噪声函数,这种插值方式是一个选择。

function Linear_Interpolate(a, b, x)
  return a*(1-x) + b*x
end of function
Cosine Interpolation:
余玄插值

This method gives a much smother curve than Linear Interpolation. It's clearly better and worth the effort if you can afford the very slight loss in speed.
这个方法线性插值生成了更平滑的曲线。它当然有着更好的效果,如果你愿意稍微损失点速度的话。

function Cosine_Interpolate(a, b, x)
  ft = x * 3.1415927
  f = (1 - cos(ft)) * .5

  return a*(1-f) + b*f
end of function
Cubic Interpolation:
立方插值:

This method gives very smooth results indeed, but you pay for it in speed. To be quite honest, I'm not sure if it would give noticeably better results than Cosine Interpolation, but here it is anyway if you want it. It's a little more complicated, so pay attention. Whereas before, the interpolation functions took three inputs, the cubic interpolation takes five. Instead of just a and b, you now need v0, v1, v2 and v3, along with x as before.
这个方法的确是生成了非常平滑的结果,但是你付出的代价就是速度。老实说,我不那么确定它能给你比余玄插值好很多的效果,但是如果你无论如何要使用它的话,它有一点点的复杂,所以这里请注意,之前,插值函数只需要三个参数,但是立方插值需要五个,取代了a和b,现在你需要v0,v1,v2,v3,x和以前一样也需要。


这些是:
v0 = a 前面一点
v1 = a 点
v2 = b 点
v3 = b 后面一点


function Cubic_Interpolate(v0, v1, v2, v3,x)
  P = (v3 - v2) - (v0 - v1)
  Q = (v0 - v1) - P
  R = v2 - v0
  S = v1

  return Px3 + Qx2 + Rx + S
end of function
Smoothed Noise
平滑的噪声

Aside from Interplolation, you can also smooth the output of the noise function to make it less random looking, and also less square in the 2D and 3D versions. Smoothing is done much as you would expect, and anyone who has written an image smoothing filter, or fire algorithm should already be familiar with the process.
Rather than simply taking the value of the noise function at a single coordinate, you can take the average of that value, and it's neighbouring values. If this is unclear, take a look at the pseudo code below.
除了插值,你也可以平滑噪声函数的输出来使它看起来不那么随机,和让2D和3D的版本少一点方块。平滑的结果和你所想的差不多,只要是写过平滑过滤或者火焰算法的人都应该相当熟悉此过程。相比在一个单独的坐标上取得噪声值,你可以取平均的噪声值,和它临近的值。如果你不清楚这个,可以看看下面的伪代码。

On the right, you can see a little diagram illustrating the difference between smoothed noise, and the same noise function without smoothing. You can see that the smooth noise is flatter, never reaching the extremes of unsmoothed noise, and the frequency appears to be roughly half. There is little point smoothing 1 dimensional noise, since these are really the only effects. Smoothing becomes more useful in 2 or three dimensions, where the effect is to reduce the squareness of the noise. Unfortunately it also reduces the contrast a little. The smoother you make it, obviously, the flatterthe noise will be.
在右面(这里看下面),你可以看见一个小的图展示了不同平滑函数的区别,和同样的一个噪声但未进行平滑处理。你可以看见平滑函数,从来都没有到底那个未平滑函数的极限值,并且频率显得只有大约一半。那里有小点平滑一维的噪声,只有这一个效果。平滑过程在二维和三维中,显得更有用处,那就是它减少了噪声大方块。不幸的是它也降低了一点对比度。你让它越平滑,这个噪声就会越平坦。



1-dimensional Smooth Noise
一维噪声函数
function Noise(x)
.
.
end function

function SmoothNoise_1D(x)

return Noise(x)/2 + Noise(x-1)/4 + Noise(x+1)/4

end function
2-dimensional Smooth Noise
二维噪声函数
function Noise(x, y)
.
.
end function

function SmoothNoise_2D(x>, y)

corners = ( Noise(x-1, y-1)+Noise(x+1, y-1)+Noise(x-1, y+1)+Noise(x+1, y+1) ) / 16
sides = ( Noise(x-1, y) +Noise(x+1, y) +Noise(x, y-1) +Noise(x, y+1) ) / 8
center = Noise(x, y) / 4

return corners + sides + center

end function
Putting it all together
把它们组合在一起

Now that you know all that, it's time to put together all you've learned and create a Perlin Noise function. Remember that it's just several Interpolated Noise functions added together. So Perlin Noise it just a function. You pass it one or more parameters, and it responds with a number. So, here's a simple 1 dimensional Perlin function.
既然你知道了全部这些,现在是时候把他们组合在一起了,你将学会并创建一个柏林函数。记住这知识几个插值的噪声函数叠加在一起。所以柏林函数只是一个函数。你传入一个或多个参数,然后它返回一个数给你。所以很简单,一维的柏林函数是这样。

The main part of the Perlin function is the loop. Each iteration of the loop adds another octave of twice the frequency. Each iteration calls a different noise function, denoted by Noisei. Now, you needn't actually write lots of noise functions, one for each octave, as the pseudo code seems to suggest. Since all the noise functions are essentially the same, except for the values of those three big prime numbers, you can keep the same code, but simply use a different set of prime numbers for each.
柏林函数重要的部分是那个循环。每次循环跌代叠加另一个两倍频率的倍频。每次跌代调用一个不同的噪声函数,称做Noisei。当然,你并不需要真的写为每个倍频很多噪声函数,伪代码中好象只是建议这么做。既然所有的噪声函数实际上都是相同的,除了那三个大质数不同除外,你可以使用同样的代码,只是每个代码改用不同的质数。

1-dimensional Perlin Noise Pseudo code
一维柏林噪声函数伪代码
function Noise1(integer x)
x = (x<<13) ^ x;
return ( 1.0 - ( (x * (x * x * 15731 + 789221) + 1376312589) & 7fffffff) / 1073741824.0);
end function

function SmoothedNoise_1(float x)
return Noise(x)/2 + Noise(x-1)/4 + Noise(x+1)/4
end function

function InterpolatedNoise_1(float x)

integer_X = int(x)
fractional_X = x - integer_X

v1 = SmoothedNoise1(integer_X)
v2 = SmoothedNoise1(integer_X + 1)

return Interpolate(v1 , v2 , fractional_X)

end function

function PerlinNoise_1D(float x)

total = 0
p = persistence
n = Number_Of_Octaves - 1

loop i from 0 to n

frequency = 2i
amplitude = pi

total = total + InterpolatedNoisei(x * frequency) * amplitude

end of i loop

return total

end function
Now it's easy to apply the same code to create a 2 or more dimensional Perlin Noise function:
现在可以轻松的使用同样的代码创建二维或者多维的柏林噪声函数了:

2-dimensional Perlin Noise Pseudocode
二维柏林噪声函数伪代码
function Noise1(integer x, integer y)
n = x + y * 57
n = (n<<13) ^ n;
return ( 1.0 - ( (n * (n * n * 15731 + 789221) + 1376312589) & 7fffffff) / 1073741824.0);
end function

function SmoothNoise_1(float x, float y)
corners = ( Noise(x-1, y-1)+Noise(x+1, y-1)+Noise(x-1, y+1)+Noise(x+1, y+1) ) / 16
sides = ( Noise(x-1, y) +Noise(x+1, y) +Noise(x, y-1) +Noise(x, y+1) ) / 8
center = Noise(x, y) / 4
return corners + sides + center
end function

function InterpolatedNoise_1(float x, float y)

integer_X = int(x)
fractional_X = x - integer_X

integer_Y = int(y)
fractional_Y = y - integer_Y

v1 = SmoothedNoise1(integer_X, integer_Y)
v2 = SmoothedNoise1(integer_X + 1, integer_Y)
v3 = SmoothedNoise1(integer_X, integer_Y + 1)
v4 = SmoothedNoise1(integer_X + 1, integer_Y + 1)

i1 = Interpolate(v1 , v2 , fractional_X)
i2 = Interpolate(v3 , v4 , fractional_X)

return Interpolate(i1 , i2 , fractional_Y)

end function

function PerlinNoise_2D(float x, float y)

total = 0
p = persistence
n = Number_Of_Octaves - 1

loop i from 0 to n

frequency = 2i
amplitude = pi

total = total + InterpolatedNoisei(x * frequency, y * frequency) * amplitude

end of i loop

return total

end function

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/anywn1314/archive/2007/10/15/1825765.aspx

posted @ 2009-09-12 21:15 董波 阅读(1192) | 评论 (0)编辑 收藏

2009年8月28日 #

有人问为什么游戏资源要打包。

相对而言,对比较小,而且多的文件打包是很有意义的,这也是为什么越来越多的游戏对声音资源不打包的原因。

在桌面上新建一个文本文件,写入abcd四个字母,右击它,属性,大小:4字节,占用空间:4.00KB。
这就是为什么要打包的原因之一。

另外你可以同时处理大的打包过的文件和一大堆几万甚至几十万个文件,速度会不同的。前者要快些。同时打包还可以对文件进行加密、压缩等等。

可见打包美术资源还是很有意义的,而且还有一个好处,看起来Cool!一些。

posted @ 2009-08-28 10:33 董波 阅读(226) | 评论 (0)编辑 收藏

2009年6月10日 #

【原创】游戏中的声音资源打包

     摘要: 游戏中的声音资源打包 董波 介绍 在以前的游戏当中,几乎所有的游戏都有将资源打包,无论是图形资源、声音文件等等。现在,越来越多的游戏的声音资源都公开了,通常背景音乐等采用ogg,音效采用wav;但是图形资源绝大部分游戏还是都是打包了的。有的有加密,有的没有。 在学习游戏编程的初期我就对这个东西很感兴趣,但是又没有人教,到网上找资料也少得可怜,所以就只好自己摸索了。记得去年也做过类似的工作,...  阅读全文

posted @ 2009-06-10 23:43 董波 阅读(1493) | 评论 (0)编辑 收藏

2009年5月25日 #

【转帖】Rational Purify 使用及分析实例

Rational Purify 使用及分析实例

developerWorks

级别: 初级

蔡 林, IBM 中国软件开发中心软件工程师

2006 年 2 月 23 日

本文介绍了 IBM Rational Purify的基本概念和在不同操作系统中使用Purify对C/C++源程序中存在的内存问题进行勘察和分析,并且提供了有关的实例以便读者在实际操作中作为参考。

简介

本文介绍了IBM Rational Purify的基本概念和在不同操作系统中使用Purify对C/C++源程序中存在的内存问题进行勘察和分析,并且提供了有关的实例以便读者在实际操作中作为参考。






1.内存问题的原因及分类

在C/C++程序中,有关内存使用的问题是最难发现和解决的。这些问题可能导致程序莫名其妙地停止、崩溃,或者不断消耗内存直至资源耗尽。由于C/C++语言本身的特质和历史原因,程序员使用内存需要注意的事项较多,而且语言本身也不提供类似Java的垃圾清理机制。编程人员使用一定的工具来查找和调试内存相关问题是十分必要的。

总的说来,与内存有关的问题可以分成两类:内存访问错误和内存使用错误。内存访问错误包括错误地读取内存和错误地写内存。错误地读取内存可能让你的模块返回意想不到的结果,从而导致后续的模块运行异常。错误地写内存可能导致系统崩溃。内存使用方面的错误主要是指申请的内存没有正确释放,从而使程序运行逐渐减慢,直至停止。这方面的错误由于表现比较慢很难被人工察觉。程序也许运行了很久才会耗净资源,发生问题。

1.1 内存解剖

一个典型的C++内存布局如下图所示:



自底向上,内存中依次存放着只读的程序代码和数据,全局变量和静态变量,堆中的动态申请变量和堆栈中的自动变量。自动变量就是在函数内声明的局部变量。当函数被调用时,它们被压入栈;当函数返回时,它们就要被弹出堆栈。堆栈的使用基本上由系统控制,用户一般不会直接对其进行控制,所以堆栈的使用还是相对安全的。动态内存是一柄双刃剑:它可以提供程序员更灵活的内存使用方法,而且有些算法没有动态内存会很难实现;但是动态内存往往是内存问题存在的沃土。

1.2 内存访问错误

相对用户使用的语言,动态内存的申请一般由malloc/new来完成,释放由free/delete完成。基本的原则可以总结为:一对一,不混用。也就是说一个malloc必须对应一且唯一的free;new对应一且唯一的delete; malloc不能和delete, new不能和free对应。另外在C++中要注意delete和delete[]的区别。delete用来释放单元变量,delete[]用来释放数组等集聚变量。有关这方面的详细信息可以参考[C++Adv]。

我们可以将内存访问错误大致分成以下几类:数组越界读或写、访问未初始化内存、访问已经释放的内存和重复释放内存或释放非法内存。

下面的代码集中显示了上述问题的典型例子:


1   #include <iostream>
                        2   using namespace std;
                        3   int main(){
                        4      char* str1="four";
                        5      char* str2=new char[4];	//not enough space
                        6      char* str3=str2;
                        7      cout<<str2<<endl;	//UMR
                        8      strcpy(str2,str1);	//ABW
                        9      cout<<str2<<endl;  //ABR
                        10     delete str2;
                        11     str2[0]+=2;	//FMR and FMW
                        12     delete str3;	//FFM
                        13   }
                        

由以上的程序,我们可以看到:在第5行分配内存时,忽略了字符串终止符"\0"所占空间导致了第8行的数组越界写(Array Bounds Write)和第9行的数组越界读(Array Bounds Read); 在第7行,打印尚未赋值的str2将产生访问未初始化内存错误(Uninitialized Memory Read);在第11行使用已经释放的变量将导致释放内存读和写错误(Freed Memory Read and Freed Memory Write);最后由于str3和str2所指的是同一片内存,第12行又一次释放了已经被释放的空间 (Free Freed Memory)。

这个包含许多错误的程序可以编译连接,而且可以在很多平台上运行。但是这些错误就像定时炸弹,会在特殊配置下触发,造成不可预见的错误。这就是内存错误难以发现的一个主要原因。

1.3 内存使用错误

内存使用错误主要是指内存泄漏,也就是指申请的动态内存没有被正确地释放,或者是没有指针可以访问这些内存。这些小的被人遗忘的内存块占据了一定的地址空间。当系统压力增大时,这些越来越多的小块将最终导致系统内存耗尽。内存使用错误比内存访问错误更加难以发现。这主要有两点原因:第一,内存使用错误是"慢性病",它的症状可能不会在少数、短时间的运行中体现;第二,内存使用错误是因为"不做为"(忘记释放内存)而不是"做错"造成的。这样由于忽略造成的错误在检查局部代码时很难发现,尤其是当系统相当复杂的时候。






2.Purify的原理及使用

IBM Rational PurifyPlus是一组程序运行时的分析软件。她包括了程序性能瓶颈分析软件Quantify, 程序覆盖面分析软件PureCoverage,和本文的主角:程序运行错误分析软件Purify。Purify可以发现程序运行时的内存访问,内存泄漏和其他难以发现的问题。

同时她也是市场上唯一支持多种平台的类似工具,并且可以和很多主流开发工具集成。Purify可以检查应用的每一个模块,甚至可以查出复杂的多线程或进程应用中的错误。另外她不仅可以检查C/C++,还可以对Java或.NET中的内存泄漏问题给出报告。

2.1 Purify的原理

程序运行时的分析可以采用多种方法。Purify使用了具有专利的目标代码插入技术(OCI:Object Code Insertion)。她在程序的目标代码中插入了特殊的指令用来检查内存的状态和使用情况。这样做的好处是不需要修改源代码,只需要重新编译就可以对程序进行分析。

对于所有程序中使用的动态内存,Purify将它们按照状态进行归类。这可以由下图来说明(来自[DEV205]):



参见本文中以上给出的代码,在程序第5行执行后,str2处于黄色状态。当在第7行进行读的时候,系统就会报告一个访问未初始化内存错误(Uninitialized Memory Read)。因为只有在绿色状态下,内存才可以被合法访问。

为了检查数据越界错误(ABR,ABW),Purify还在每个分配的内存前后插入了红色区域。这样一来,超过边界的访问指令必定落在非法区域,从而触发ABR或者ABW错误报告。这里需要指出一点。访问未初始化内存错误UMR在某些情况下其实是合法的操作,例如内存拷贝。所以在分析报告时可以把UMR放到最后,或者干脆从结果中滤除。

2.2 Purify的使用

这里简单介绍一下Purify在Windows和UNIX环境下的使用。

在Windows中,只要运行Purify,填入需要分析的程序及参数就可。Purify会自动插入检测代码并显示报告。报告的格式如下(来自[DEV205]):



蓝色的图标代表一些运行的信息,比如开始和结束等。黄色是Purify给出的警告。通常UMR会作为警告列出。红色则代表严重的错误。每一种相同的错误,尤其是在循环中的,会被集中在一起显示,并且标明发生的次数。由每个错误的详细信息,用户可以知道相应的内存地址和源代码的位置,并直接修改。另外用户还可以设置不同的滤过器,用来隐藏暂时不关心的消息。

在UNIX系统中,使用Purify需要重新编译程序。通常的做法是修改Makefile中的编译器变量。下面是用来编译本文中程序的Makefile:


        CC=purify gcc
                        all: pplusdemo
                        pplusdemo: pplusdemo.o
                        $(CC) -o pplusdemo pplusdemo.o -lstdc++
                        pplusdemo.o: pplusdemo.cpp
                        $(CC) -g -c -w pplusdemo.cpp
                        clean:
                        -rm pplusdemo pplusdemo.o
                        

首先运行Purify安装目录下的purifyplus_setup.sh来设置环境变量,然后运行make重新编译程序。需要指出的是,程序必须编译成调试版本。在gcc中,也就是必须使用"-g"选项。在重新编译的程序运行结束后,Purify会打印出一个分析报告。它的格式和含义与Windows平台大同小异。

下面是本文中的程序在Linux上Purify运行的结果:


    ****  Purify instrumented ./pplusdemo (pid 30669)  ****
                        UMR: Uninitialized memory read:
                        * This is occurring while in:
                        strlen         [rtlib.o]
                        std::basic_ostream< char,std::char_traits< char>> & std::operator
                        <<<std::char_traits< char>>(std::basic_ostream< char,std::char_traits<
                        char>> &, char const *) [libstdc++.so.5]
                        main           [pplusdemo.cpp:7]
                        __libc_start_main [libc.so.6]
                        _start         [crt1.o]
                        * Reading 1 byte from 0x80b45e0 in the heap.
                        * Address 0x80b45e0 is at the beginning of a malloc'd block of 4 bytes.
                        * This block was allocated from:
                        malloc         [rtlib.o]
                        operator new( unsigned) [libstdc++.so.5]
                        operator new []( unsigned) [libstdc++.so.5]
                        main           [pplusdemo.cpp:5]
                        __libc_start_main [libc.so.6]
                        _start         [crt1.o]
                        ****  Purify instrumented ./pplusdemo (pid 30669)  ****
                        ABW: Array bounds write:
                        * This is occurring while in:
                        strcpy         [rtlib.o]
                        main           [pplusdemo.cpp:8]
                        __libc_start_main [libc.so.6]
                        _start         [crt1.o]
                        * Writing 5 bytes to 0x80b45e0 in the heap (1 byte at 0x80b45e4 illegal).
                        * Address 0x80b45e0 is at the beginning of a malloc'd block of 4 bytes.
                        * This block was allocated from:
                        malloc         [rtlib.o]
                        operator new( unsigned) [libstdc++.so.5]
                        operator new []( unsigned) [libstdc++.so.5]
                        main           [pplusdemo.cpp:5]
                        __libc_start_main [libc.so.6]
                        _start         [crt1.o]
                        ****  Purify instrumented ./pplusdemo (pid 30669)  ****
                        ABR: Array bounds read:
                        * This is occurring while in:
                        strlen         [rtlib.o]
                        std::basic_ostream< char,std::char_traits< char>> & std::operator
                        <<<std::char_traits< char>>(std::basic_ostream< char,std::char_traits<
                        char>> &, char const *) [libstdc++.so.5]
                        main           [pplusdemo.cpp:9]
                        __libc_start_main [libc.so.6]
                        _start         [crt1.o]
                        * Reading 5 bytes from 0x80b45e0 in the heap (1 byte at 0x80b45e4 illegal).
                        * Address 0x80b45e0 is at the beginning of a malloc'd block of 4 bytes.
                        * This block was allocated from:
                        malloc         [rtlib.o]
                        operator new( unsigned) [libstdc++.so.5]
                        operator new []( unsigned) [libstdc++.so.5]
                        main           [pplusdemo.cpp:5]
                        __libc_start_main [libc.so.6]
                        _start         [crt1.o]
                        ****  Purify instrumented ./pplusdemo (pid 30669)  ****
                        FMM: Freeing mismatched memory:
                        * This is occurring while in:
                        operator delete( void *) [rtlib.o]
                        main           [pplusdemo.cpp:10]
                        __libc_start_main [libc.so.6]
                        _start         [crt1.o]
                        * Attempting to free block at 0x80b45e0 in the heap.
                        * Address 0x80b45e0 is at the beginning of a malloc'd block of 4 bytes.
                        * This block was allocated from:
                        malloc         [rtlib.o]
                        operator new( unsigned) [libstdc++.so.5]
                        operator new []( unsigned) [libstdc++.so.5]
                        main           [pplusdemo.cpp:5]
                        __libc_start_main [libc.so.6]
                        _start         [crt1.o]
                        * This block of memory was obtained using an allocation routine which is
                        not compatible with the routine by which it is being freed.
                        ****  Purify instrumented ./pplusdemo (pid 30669)  ****
                        FMR: Free memory read:
                        * This is occurring while in:
                        main           [pplusdemo.cpp:11]
                        __libc_start_main [libc.so.6]
                        _start         [crt1.o]
                        * Reading 1 byte from 0x80b45e0 in the heap.
                        * Address 0x80b45e0 is at the beginning of a freed block of 4 bytes.
                        * This block was allocated from:
                        malloc         [rtlib.o]
                        operator new( unsigned) [libstdc++.so.5]
                        operator new []( unsigned) [libstdc++.so.5]
                        main           [pplusdemo.cpp:5]
                        __libc_start_main [libc.so.6]
                        _start         [crt1.o]
                        * There have been 0 frees since this block was freed from:
                        free           [rtlib.o]
                        _ZdLpV         [libstdc++.so.5]
                        main           [pplusdemo.cpp:10]
                        __libc_start_main [libc.so.6]
                        _start         [crt1.o]
                        ****  Purify instrumented ./pplusdemo (pid 30669)  ****
                        FMW: Free memory write:
                        * This is occurring while in:
                        main           [pplusdemo.cpp:11]
                        __libc_start_main [libc.so.6]
                        _start         [crt1.o]
                        * Writing 1 byte to 0x80b45e0 in the heap.
                        * Address 0x80b45e0 is at the beginning of a freed block of 4 bytes.
                        * This block was allocated from:
                        malloc         [rtlib.o]
                        operator new( unsigned) [libstdc++.so.5]
                        operator new []( unsigned) [libstdc++.so.5]
                        main           [pplusdemo.cpp:5]
                        __libc_start_main [libc.so.6]
                        _start         [crt1.o]
                        * There have been 0 frees since this block was freed from:
                        free           [rtlib.o]
                        _ZdLpV         [libstdc++.so.5]
                        main           [pplusdemo.cpp:10]
                        __libc_start_main [libc.so.6]
                        _start         [crt1.o]
                        ****  Purify instrumented ./pplusdemo (pid 30669)  ****
                        FUM: Freeing unallocated memory:
                        * This is occurring while in:
                        free           [rtlib.o]
                        _ZdLpV         [libstdc++.so.5]
                        main           [pplusdemo.cpp:12]
                        __libc_start_main [libc.so.6]
                        _start         [crt1.o]
                        * Attempting to free block at 0x80b45e0 already freed.
                        * This block was allocated from:
                        malloc         [rtlib.o]
                        operator new( unsigned) [libstdc++.so.5]
                        operator new []( unsigned) [libstdc++.so.5]
                        main           [pplusdemo.cpp:5]
                        __libc_start_main [libc.so.6]
                        _start         [crt1.o]
                        * There have been 1 frees since this block was freed from:
                        free           [rtlib.o]
                        _ZdLpV         [libstdc++.so.5]
                        main           [pplusdemo.cpp:10]
                        __libc_start_main [libc.so.6]
                        _start         [crt1.o]
                        ****  Purify instrumented ./pplusdemo (pid 30669)  ****
                        Current file descriptors in use: 5
                        FIU: file descriptor 0: <stdin>
                        FIU: file descriptor 1: <stdout>
                        FIU: file descriptor 2: <stderr>
                        FIU: file descriptor 26: <reserved for Purify internal use>
                        FIU: file descriptor 27: <reserved for Purify internal use>
                        ****  Purify instrumented ./pplusdemo (pid 30669)  ****
                        Purify: Searching for all memory leaks...
                        Memory leaked: 0 bytes (0%); potentially leaked: 0 bytes (0%)
                        Purify Heap Analysis (combining suppressed and unsuppressed blocks)
                        Blocks        Bytes
                        Leaked          0            0
                        Potentially Leaked          0            0
                        In-Use          0            0
                        ----------------------------------------
                        Total Allocated          0            0
                        ****  Purify instrumented ./pplusdemo (pid 30669)  ****
                        * Program exited with status code 0.
                        * 7 access errors, 7 total occurrences.
                        * 0 bytes leaked.
                        * 0 bytes potentially leaked.
                        * Basic memory usage (including Purify overhead):
                        290012 code
                        152928 data/bss
                        6816 heap (peak use)
                        7800 stack
                        

我们对照程序可以发现Purify查出了程序中所有的错误。对于每个错误,她不但给出了源代码的位置还指出这些内存最初分配的源代码位置。这对于查找问题提供了很大帮助。对于程序12行的解释,Purify将其认为是不匹配的内存释放(FMM: Freeing mismatched memory),因为她认为这样的释放方式不符合严格的规定。

Purify在其报告和文档中使用了很多的缩写,在此一并列出,以便读者在使用时参考(来自[Purify]):



2.3 Purify的一些特性

这里简单介绍一下Purify提供的几个特性。有关这些特性的详细信息,请查阅文档[Purify]。

  • 观察点(Watchpoint):通过在程序或者调试器中调用Purify 提供的观察点函数,Purify可以报告有关被观察对象的读写或其他操作。
  • 与Rational其他产品的集成:在Puify的用户界面中可以方便地进入ClearCase和ClearQuest。Purify还可以和PureCoverage同时使用,对程序进行分析。
  • Purify的定制:无论是Purify报告中的消息,还是界面中的元素,都可以进行一定程度的定制。另外通过修改配置文件和调用Purify API,用户还可以自动记录运行日志,发送电子邮件等。
  • Purify提供的API:为了更好地把Purify融合到自动化测试的体系中,Purify提供了一系列的公开函数。用户完全可以通过脚本的方式自动运行,记录,和分析Purify。





3.总结

当使用C/C++进行开发时,采用良好的一致的编程规范是防止内存问题第一道也是最重要的措施。在此前提下,IBM Rational Purify作为一种运行时分析软件可以很好地帮助您发现忽略的内存问题,或成为软件自动测试中的一个重要组成部分。






4.参考资料

[C++Adv]
[DEV205] Essentials of Rational PurifyPlus
[Purify] IBM Rational PurifyPlus for Linux and UNIX Documentation



关于作者

 

蔡林,IBM 中国软件开发中心软件工程师,2004年获得美国Baylor University计算机系硕士学位,同年加入IBM 中国软件开发中心,从事Rational ClearQuest G11N的开发工作。

posted @ 2009-05-25 23:36 董波 阅读(689) | 评论 (0)编辑 收藏

2009年5月24日 #

【日记】最近干了点嘛?

     摘要:         现在已经是大学生涯的最后阶段了,回首过去的四年真是充满感慨。由于毕业设计早早完事以至于现在略显无聊,呵呵。但是一直都没闲着,最近在研究内存管理方面的东西,参考了不少的书,有IBM出的那本《C++应用程序性能与优化》、《深入解析Windows操作系统》、《Windows核心编程》等等。目的就是想对Windows的内...  阅读全文

posted @ 2009-05-24 19:03 董波 阅读(269) | 评论 (0)编辑 收藏

2009年5月23日 #

【转帖】Visual C++中的日历控件使用详解

文/刘涛  前言:

  控件在Visual C++编程学习中占据很重要的位置。笔者在国外英文网站上看到了这篇关于日历控件学习的文章,虽然内容看似简单,但读完后感觉到还是学到了一些东西。感觉到原著作者对一些不常用的日历控制的使用方法写的介绍的很详细,通俗易懂,于平淡之中显神奇,是对日历控件的"深度挖掘",对VC的初学者应该是很有帮助的,所以就将其翻译过来介绍给国内读者朋友。

  一、有关日历控件的介绍

  Win32 API提供了一个显示日期得当彩色日历控件,日期的显示模式是基于控制面板中的区域性设置或者说是依赖于用户的操作系统。具体的日历控件如下图一所示:

VisualC++中的日历控件使用详解
图一、日历控件显示效果

  这个常用的日历控件的标题条包含两个按钮和两个标签,左边的按钮准许用户单击选择前一个月份,左边的标签显示当前选择的月份,右边的标签显示当前日期中的年份。右边的按钮是让用户选择下一个月份。日历控件可以配置成显示多个月份,下图是一具体的实例:

VisualC++中的日历控件使用详解
图二、显示多个月份的日历控件

  如果要让日历控件显示多个月份,按钮的个数将通过前月或后月列表来增加或减少。例如,如果控件正在显示4月或5月,这时用户点击了左边的按钮,控件将显示3月或4月;如果用户点击了右边的按钮,控件将显示5月和6月。此外,选择当前年份中的任意一个月份,用户可以点击标题框中的月份名,这时候将显示一个月份列表供用户来选择。具体如图所示:

VisualC++中的日历控件使用详解
图三、显示月份列表

  如果要更改年份,用户可以点击年份标签,这时候将显示旋转按钮,用户可以通过点击旋转按钮的上下键来更改年份,也可以使用键盘上的上下箭头来更改年份。

VisualC++中的日历控件使用详解
图四、更改日历控件的年份


在标题条的下面,根据控制面板的格式显示着星期的简写,在英语地区,每个星期的第一天通常是星期天。开发人员可以改变控件中的第一天设置。

  控件上,当前选择的日期有一圆圈。用户可以点击欲选择的日期来在控件上选择一个日期。在大部分地区,日期以数字的形式显现在一个白色背景上(这个背景颜色乃至控件上的任何颜色可以通过程序来改变)。默认的情况下,控件显示一个椭圆围绕的当前日期。使用标题条按钮、月份和年份标签,用户可以更改日期。如果控件显示的不是当前日期,用户可以通过点击控件底部显示今天日期的标签来使控件显示今天的日期。(如果你是一个程序员,可以隐藏控件底部这个显示今日日期的标签)。

  二、创建日历控件

  我们可以在窗口、对话框架、工具条及其他任何容器窗口中创建日历控件。日历控件对应着CmonthCalCtrl类,所以要动态创建一个日历控件,需要声明一个CmonthCalCtrl变量或指向CmonthCalCtrl的指针,代码如下:

// Exercise1Dlg.h : header file
class CExercise1Dlg : public CDialog
{
 // Construction
 public:
  CExercise1Dlg(CWnd* pParent = NULL); // standard constructor
 private:
  CMonthCalCtrl *ctlCalendar;
};

  CmonthCalCtrl类象MFC中其他控件对应的类一样,提供了一个Create()方法用来在容器窗口中动态创建日历控件,代码如下:

CExercise1Dlg::CExercise1Dlg(CWnd* pParent /*=NULL*/)
: CDialog(CExercise1Dlg::IDD, pParent)
{
 //{{AFX_DATA_INIT(CExercise1Dlg)
 //}}AFX_DATA_INIT
 // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
 ctlCalendar = new CMonthCalCtrl;
}
/////////////////////////////////////////////////////////////////////////////
// CExercise1Dlg message handlers
BOOL CExercise1Dlg::OnInitDialog()
{
 CDialog::OnInitDialog();
 // Set the icon for this dialog. The framework does this automatically
 // when the application's main window is not a dialog
 SetIcon(m_hIcon, TRUE); // Set big icon
 SetIcon(m_hIcon, FALSE); // Set small icon
 // TODO: Add extra initialization here
 ctlCalendar->Create(WS_TABSTOP | WS_CHILD | WS_VISIBLE | WS_BORDER,CPoint(20, 20), this, 0x224);
 return TRUE; // return TRUE unless you set the focus to a control
}

三、日历控件属性操作

  在对话框或窗口中创建一个日历控件后,它仅显示当前的月份并仅仅只能显示一个月份,这是因为,按照默认的设计,控件的长、宽、高只能容纳一个月的日期,如果要显示两个月,就要增加控件的宽度(同样的道理,也可以通过增加高度来显示两个月份)。

  作为一个可视化对象,日历控件可以用不同的颜色来表现背景、星期日、标题条文本、标题条背景等。作为开发人员理所当然地可以通过程序来更换这些颜色,当然是要在不影响控件亲合力的情况下。改变日历控件的颜色,需要调用CMonthCalCtrl::SetColor() 方法,该方法的语法是:

COLORREF SetColor(int nRegion, COLORREF ref);


  默认情况下,控件的标题条显示蓝色背景,如果要改变它,需要向nRegion参数传递MCSC_TITLEBK值,向ref参数传递你所要显示的颜色。如果更改标题条上文本的颜色,需要向nRegion参数传递MCSC_TITLETEXT值。

VisualC++中的日历控件使用详解(3)VisualC++中的日历控件使用详解(3)
图五、更改控件的标题条颜色


  上文说过,在标题条的下方显示着星期日,在英语国家,一个星期的第一天是星期天,如果你想更改一个星期的第一天,可以调用函数SetFirstDayOfWeek(),它的语法是:

BOOL SetFirstDayOfWeek(int iDay, int* lpnOld = NULL);


  第一个参数必须是对应的下列整数值:

Value Weekday
0 Monday
1 Tuesday
2 Wednesday
3 Thursday
4 Friday
5 Saturday
6 Sunday


  如果想要获知日历控件的星期天中具体哪一天设置为第一天,可以调用函数:GetFirstDayOfWeek(),它的语法是:

int GetFirstDayOfWeek(BOOL* pbLocal = NULL) const;


  该函数返回一个整数值,它对应的含义与上个表格一致。

  星期日的名字使用的颜色与使用SetColor()函数传递MCSC_TITLETEXT时使用的颜色一致,在星期日的下面是一个水平分割线,默认情况下它该是黑色的,但这里它却与选择的日期一个颜色。在分割线下是日期列表,默认情况下背景是Windows默认的白色,如果要改变它,需要向nRegion参数传递MCSC_MONTHBK值,向ref参数传递你所要显示的颜色。

  表示日期的数字显示有两种颜色,当前选择的月份中的日期以黑色表示,如果要改变这种颜色,可以向需要向nRegion参数传递MCSC_TRAILINGTEXT值,向ref参数传递你所要显示的颜色。

VisualC++中的日历控件使用详解(3)VisualC++中的日历控件使用详解(3)
图六、更改日历控件的日期显示颜色

 

分割线下的日期列表以两种颜色显示,为了规定当前月份中日期的颜色,可以向需要向nRegion参数传递MCSC_TEXT值,向ref参数传递你所要显示的颜色。

  日历控件习惯于用两种形式来让用户了解当前的日期,一是在列表中以椭圆将当前日期圈起来,另一种方式是在底部以句子的形式显示。在创建控件时,将"today"属性设置为"NO"将不显示今天的日期。

VisualC++中的日历控件使用详解(4)
图七、不显示"今日"标签的日历控件


  如上所述,默认情况下控件显示今天日期,可以通过编程使用MCS_NOTODAY来隐藏这个标签,代码如下:

BOOL CExercise1Dlg::OnInitDialog()
{
 CDialog::OnInitDialog();

 // Set the icon for this dialog. The framework does this automatically
 // when the application's main window is not a dialog
 SetIcon(m_hIcon, TRUE); // Set big icon
 SetIcon(m_hIcon, FALSE); // Set small icon

 // TODO: Add extra initialization here

 ctlCalendar->Create(WS_TABSTOP | WS_CHILD |
   WS_VISIBLE | WS_BORDER | MCS_NOTODAY,
 CPoint(20, 20), this, 0x224);

 return TRUE; // return TRUE unless you set the focus to a control
}


  我们注意到, 当前日期还被一个椭圆圈了起来,如果要将它隐藏起来,应该使用MCS_NOTODAYCIRCLE类型,代码如下:

BOOL CExercise1Dlg::OnInitDialog()
{
 CDialog::OnInitDialog();
 // Set the icon for this dialog. The framework does this automatically
 // when the application's main window is not a dialog
 SetIcon(m_hIcon, TRUE); // Set big icon
 SetIcon(m_hIcon, FALSE); // Set small icon
 // TODO: Add extra initialization here
 ctlCalendar->Create(WS_TABSTOP | WS_CHILD |
   WS_VISIBLE | WS_BORDER |
   MCS_NOTODAYCIRCLE,
 CPoint(20, 20), this, 0x224);
 return TRUE; // return TRUE unless you set the focus to a control
}


  为了获取当前日历控件中选择的日期,可以使用方法:CMonthCalCtrl::GetCurSel(),该方法重载有3个版本,它们是:

BOOL GetCurSel(COleDateTime& refDateTime) const;
BOOL GetCurSel(CTime& refDateTime) const;
BOOL GetCurSel(LPSYSTEMTIME pDateTime) const;


  这里有一个例子:

void CExercise1Dlg::OnRetrieveBtn()
{
 // TODO: Add your control notification handler code here
 UpdateData();
 CTime tme = this->m_dtpCurrent.GetCurrentTime();
 this->m_Result.Format("%s", tme.Format("%A, %B %d, %Y"));
 UpdateData(FALSE);
}


为了控制用户是否可以选择两个以上的日期,在创建控件时可以相应地设置多项选择属性。例如,如果你想让用户在控件中选择一定范围的日期,可以将多项选择属性设置为真。为了动态设置多日期选择,应用MCS_MULTISELECT属性,代码如下:

BOOL CExercise1Dlg::OnInitDialog()
{
 CDialog::OnInitDialog();
 // Set the icon for this dialog. The framework does this automatically
 // when the application's main window is not a dialog
 SetIcon(m_hIcon, TRUE); // Set big icon
 SetIcon(m_hIcon, FALSE); // Set small icon

 // TODO: Add extra initialization here

 ctlCalendar->Create(WS_TABSTOP | WS_CHILD |
    WS_VISIBLE | WS_BORDER |
    MCS_NOTODAYCIRCLE | MCS_MULTISELECT,
 CPoint(20, 20), this, 0x224);

 return TRUE; // return TRUE unless you set the focus to a control
}


VisualC++中的日历控件使用详解(5)
图八、显示多选择日期的日历控件


  通过属性设置,用户可以在日历控件中选择多个日期,当然,也可以通过动态编程来选择多个日期,这时,可以调用CMonthCalCtrl::SetSelRange()方法,它有三个不同的版本,语法是:

BOOL SetSelRange(const COleDateTime& pMinRange, const COleDateTime& pMaxRange);
BOOL SetSelRange(const CTime& pMinRange, const CTime& pMaxRange);
BOOL SetSelRange(const LPSYSTEMTIME pMinRange, const LPSYSTEMTIME pMaxRange);


  如果想获取一个日历控件的可选择范围,可以调用CMonthCalCtrl::GetSelRange() 方法。

  为了控制用户可选择的日期范围,可以调用CMonthCalCtrl::SetRange()方法,它也有三中不同的形式,分别是:

BOOL SetRange(const COleDateTime* pMinRange, const COleDateTime* pMaxRange);
BOOL SetRange(const CTime* pMinRange, const CTime* pMaxRange);
BOOL SetRange(const LPSYSTEMTIME pMinRange, const LPSYSTEMTIME pMaxRange);


  第一个参数nMinRange是选择范围的开始日期,参数nMaxRange是可供选择的最大日期。

 

posted @ 2009-05-23 08:35 董波 阅读(363) | 评论 (0)编辑 收藏

2009年5月21日 #

【原创】C++字符串分词 -->C库、boost.tokenizer、stlsoft.string_tokeniser讨论

     摘要: C++字符串分词 董波 QQ:84638372 一 简介     字符串分词,即按照某一规则,将一个完整的字符串分割为更多的字段。在C库当中,strtok/wcstok提供了类似的功能,C++标准库兼容了C库。C++的stringstream有类似的功能,boost.string_algorithm也有提供类似的泛型算法。另外在boost当中专门提供了boos...  阅读全文

posted @ 2009-05-21 13:43 董波 阅读(7929) | 评论 (6)编辑 收藏

2009年5月18日 #

【原创】boost.shared_ptr的相等与等价

     摘要: boost.shared_ptr的相等与等价 关于相等和不等 template<class T, class U> inline bool operator==(shared_ptr<T> const & a, shared_ptr<U> const & b) {     return a.get() == ...  阅读全文

posted @ 2009-05-18 11:12 董波 阅读(1203) | 评论 (0)编辑 收藏

【原创】boost.compressed_pair源码剖析

     摘要: boost.compressed_pair源码剖析 意义 当compressed_pair的某一个模板参数为一个空类的时候将对其进行“空基类优化”,这样可以使得compressed_pair占用的空间比std::pair的更小。 参考如下代码: #include <iostream> using namespace std;   #inc...  阅读全文

posted @ 2009-05-18 11:09 董波 阅读(869) | 评论 (0)编辑 收藏

仅列出标题