Feb 29, 2008

基于Struts-Spring-Hibernate的Java应用开发

http://book.csdn.net/bookfiles/111/1001113461.shtml

Spring原理简介

在用ssh(Struts+Spring+Hibernate)实现的mvc模式中Spring是作为连接Struts和Hibernate的控制层。 与Spring框架相关的概念有以下: 轻量级:轻量级是针对重量级容器(EJB)来说的,Spring的核心包不到1M大小,而使用Spring的核心包所需的资源也很小,所以可以在小型设备中使用。 非侵入性:所有的框架都是提供大量的功能公用户去使用,从而简化开发时间和成本,但由于大量的使用了框架的API,使应用程序和框架发生了大量的依赖性,无法从框架中独立出来,更加无法使程序组件在其他程序中使用,这样的框架叫做入侵式的框架,而Spring目标是一个非入侵式的服务框架。 容器:容器就是一个帮助你把原来自行编写程序去管理对象关系的工作转移给容器来做。Spring提供了容器功能,容器可以管理对象的生命周期、对象与对象之间的关系、你可以通过编写XML来设置对象关系和初始值,这样容器在启动之后,所有的对象都直接可以使用,不用编写任何编码来产生对象。 IOC/DI:Spring最核心的概念就是IOC(反转控制),而他的另一个名字就是DI(依赖注入);使用Spring,你不必在程序中维护对象的依赖关系,只要在xml中设定即可,Spring容器会自己根据相关的配置去产生他们之间的关系,所有的关系都是都是在容器运行的时候注入的,而他们本身是没有关系的。打个比方:比如张三和李四,之前是没有任何关系的两个对象,但当他俩进入班级这个容器中后,班级这个容器就将他俩赋予了同学的关系。这样的做法就是用容器去赋予对象之间的关系,而不是对象本身之间来创建关系。这样做的好处显然实现了松偶合。 AOP(Aspect Oriented Programming面向切面/方面编程):Spring最被人重视的另一个方面就是对AOP的支持,AOP是Spring支持的一个子容器。在一个服务流程中插入与业务逻辑无关的系统服务逻辑(如:Logging登录、Security安全等),而把这些独立出来设计成一个对象,这样的对象称为Aspect。打个比方:做一次环球旅行,在旅行途中要经过若干国家的海关关口办理出入境手续,这样的一个一个的关口就是整个旅行流程中的一个一个的Aspect。 demo:(仅仅解释了什么是依赖注入DI或者叫反转控制IOC) 1、首先看一个原来的操作 //User.java package org.myspring; public class User { private String username; private int age; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } } //Test.java package org.myspring; public class Test { public static void main(String[] args) { User user=new User(); user.setUsername("zhangsan"); System.out.println(user.getUsername()); } } 以上是原始的做法,产生的问题是:如果想要把zhangsan改为lisi则需要在Test类中修改代码,这样是一种紧耦合,改动一个类就牵扯到另外一个类。 松耦合的情况是User.java和Test.java这两个类都不需要改动,就能实现输出不同username属性的效果,这就需要加入Spring的IOC/DI机制。方法如下: 2、MyEclipse->Add Spring Capabilities...->仅加入核心包即可,这样就生成了applicationContext.xml配置文件 3、修改applicationContext.xml:在xml文件的编辑页中 右键->Spring->New Bean,在弹出窗口中进行如下设置:

zhangsan 25 4、新的测试类 //Test.java package org.myspring; import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; public class Test { public static void main(String[] args) { ApplicationContext c FileSystemXmlApplicationContext("src/org/myspring/applicationContext.xml"); User user=(User)context.getBean("user"); System.out.println("name:"+user.getUsername()+"; age:"+user.getAge()); } } 注意:在上面的代码中context.getBean()返回的是一个Object对象,需要进行相应的类对象的转换。在代码中没有出现用new来实例化对象的语句,实现了Test类跟User类的松耦合。对象的实例化都在xml配置文件中实现了。
该文章由作者使用maikr blog备份工具于 2007-08-17

Feb 13, 2008

Private constructor



Private constructor
Topic:

Private constructors prevent a class from being explicitly instantiated by callers.

There are some common cases where a private constructor can be useful :

* classes containing only static utility methods
* classes containing only constants
* type safe enumerations
* singletons

These examples fall into two categories.
Object construction is entirely forbidden
No objects can be constructed, either by the caller or by the native class. This is only suitable for classes that offer only static members to the caller.

In these cases, the lack of an accessbile constructor says to the caller : "There are no use cases for this class where you need to build an object. You can only use static items. I am preventing you from even trying to build an object of this class." See class for constants for an illustration.

If the programmer does not provide a constructor for a class, then the system will always provide a default, public no-argument constructor. To disable this default constructor, simply add a private no-argument constructor to the class. This private constructor may be empty. Somewhat paradoxically, creation of objects by the caller is in effect disabled by the addition of this private no-argument constructor.
Object construction is private only
Objects can be constructed, but only internally. For some reason, a class needs to prevent the caller from creating objects.

In the case of a singleton, the policy is that only one object of that class is supposed to exist. Creation of multiple objects of that class is forbidden.

In the case of JDK 1.4 type-safe enumerations, more than one object can be created, but again, there is a limitation. Only a specific set of objects is permitted, one for each element of the enumeration. (For JDK 1.5 enums, the creation of such objects is done implicitly by the Java language, not explicitly by the application programmer.)

See Also :
Type-Safe Enumerations
Class for constants
Factory methods
Singleton
Would you use this technique?
Yes No Undecided
Add your comment to this Topic :

Comment:

© 2008 Hirondelle Systems | Source Code | Contact | License | Quotes | RSS
Individual code snippets can be used under this license - Last updated on Dec 20, 2007.
Over 78,000 unique IPs (and 164,000 sessions) last month - Built with WEB4J.
- In Memoriam : Bill Dirani -

Java Garbage Collection Interview Questions

Java Garbage Collection Interview Questions
Explain garbage collection?

Or

How you can force the garbage collection?

Or

What is the purpose of garbage collection in Java, and when is it used?

Or

What is Garbage Collection and how to call it explicitly?

Or

Explain Garbage collection mechanism in Java?

Garbage collection is one of the most important features of Java. The purpose of garbage collection is to identify and discard objects that are no longer needed by a program so that their resources can be reclaimed and reused. A Java object is subject to garbage collection when it becomes unreachable to the program in which it is used. Garbage collection is also called automatic memory management as JVM automatically removes the unused variables/objects (value is null) from the memory. Every class inherits finalize() method from java.lang.Object, the finalize() method is called by garbage collector when it determines no more references to the object exists. In Java, it is good idea to explicitly assign null into a variable when no more in use. In Java on calling System.gc() and Runtime.gc(), JVM tries to recycle the unused objects, but there is no guarantee when all the objects will garbage collected. Garbage collection is an automatic process and can't be forced. There is no guarantee that Garbage collection will start immediately upon request of System.gc().

What kind of thread is the Garbage collector thread?

It is a daemon thread.

Can an object’s finalize() method be invoked while it is reachable?

An object’s finalize() method cannot be invoked by the garbage collector while the object is still reachable. However, an object’s finalize() method may be invoked by other objects.

Does garbage collection guarantee that a program will not run out of memory?

Garbage collection does not guarantee that a program will not run out of memory. It is possible for programs to use up memory resources faster than they are garbage collected. It is also possible for programs to create objects that are not subject to garbage collection.

What is the purpose of finalization?

The purpose of finalization is to give an unreachable object the opportunity to perform any cleanup, before the object gets garbage collected. For example, closing an opened database Connection.

If an object is garbage collected, can it become reachable again?

Once an object is garbage collected, It can no longer become reachable again.

Feb 11, 2008

auto_ptr

// 示例 2: 使用一个 auto_ptr
//
void g()
{
T* pt1 = new T;
// 现在,我们有了一个分配好的对象

// 将所有权传给了一个auto_ptr对象
auto_ptr pt2( pt1 );

// 使用auto_ptr就像我们以前使用简单指针一样
*pt2 = 12; // 就像 "*pt1 = 12;"
pt2->SomeFunc(); // 就像 "pt1->SomeFunc();"

// 用get()来获得指针的值
assert( pt1 == pt2.get() );

// 用release()来撤销所有权
T* pt3 = pt2.release();

// 自己删除这个对象,因为现在
// 没有任何auto_ptr拥有这个对象
delete pt3;

} // pt2不再拥有任何指针,所以不要
// 试图删除它...ok,不要重复删除

最后,我们可以使用auto_ptr的reset()函数来重置auto_ptr使之拥有另一个对象。如果这个auto_ptr已经拥有了一个对象,那么,它会先删除已经拥有的对象,因此调用reset()就如同销毁这个auto_ptr,然后新建一个并拥有一个新对象:

// 示例 3: 使用reset()
//
void h()
{
auto_ptr pt( new T(1) );

pt.reset( new T(2) );
// 删除由"new T(1)"分配出来的第一个T

} // 最后,pt出了作用域,
// 第二个T也被删除了

Feb 10, 2008

对齐

【答疑】字长对齐带来的效率提升



经常看到有人问起对齐有什么作用之类问题
因为今天和同事谈到了ARM平台下数据总线宽度及对齐方式对程序效率的影响问题
在定义结构数据类型时,为了提高系统效率,要注意字长对齐原则。
正好有点感触给大家谈谈 本人水平有限的很有什么问题请朋友指正:
本文主要给大家解释下所谓的对齐到底是什么?怎么对齐?为什么会对齐或者说对齐带来什么样的效率差异?

1.
先看下面的例子:
#include
#pragma pack(4)
struct A
{
char a;
int b;
};
#pragma pack()

#pragma pack(1)
struct B
{
char a;
int b;
};
#pragma pack()

int main()
{

A a;
cout<< sizeof(a);

//define b;
cout<}

默认的vc我记得是4字节对齐ADS下是一字节对齐
因为是c/c++社区大家对PC比较熟悉 我就谈PC下的对齐
PC下设计放的太长时间的有错误就别客气直接说

大家可以看到在ms的vc下按4字节对齐和1字节对齐的结果是截然不同的分别为8和5
为什么会有这样的结果呢?这就是x86上字节对齐的作用。为了加快程序执行的速度,
一些体系结构以对齐的方式设计,通常以字长作为对齐边界。对于一些结构体变量,
整个结构要对齐在内部成员变量最大的对齐边界,如A,整个结构以4为对齐边界,所以sizeof(a)为8,而不是5。
如果是原始我们概念下的的A中的成员将会一个挨一个存储 应该只有char+int只有5个字节
这个差异就是由于对齐导致的
显然我们可以看到 A的对齐要比B浪费3个字节的存储空间
那为什么还要采取对齐呢?
那是因为体系结构的对齐和不对齐,是在时间和空间上的一个权衡。
字节对齐节省了时间。应该是设计者考虑用空间换取时间。
为什么说对齐会提高效率呢节省时间?我想大家要理解的重点之重点就在这里了
在我们常用的PC下总线宽度是32位
1.如果是总线宽度对齐的话
那么所有读写操作都是获取一个<=32位数据可以一次保证在数据总线传输完毕
没有任何的额外消耗
|1|2|3|4|5|6|7|8|
从1开始这里是a的起始位置,5起始为b的位置 访问的时候
如果访问a一次在总线传输8位其他24位无效的
访问b时则一次在总线上传输32完成
读写均是一次完整
插叙一下 读操作先要将读地址放到地址总线上然后下个时钟周期再从外部
存储器接口上读回数据通过数据总线返回需要两个周期
而写操作一次将地址及数据写入相应总线就完成了
读操作要比写操作慢一半

2.我们看访问数据时如果不对齐地址的情况
|1|2|3|4|5|6|7|8|
此时a的地址没变还在1而因为是不对齐则b的位置就在2处
这时访问就带来效率上问题 访问a时没问题还是读会一个字节
但是2处地址因为不是总线宽度对齐一般的CPU在此地址操作将产生error
如sparc,MIPS。它们在硬件的设计上就强制性的要求对齐。在不对齐的地址上肯定发生错误
但是x86是支持非对齐访问的
它通过多次访问来拼接得到的结果,具体做法就是从1地址处先读回后三字节234 暂存起来
然后再由5地址处读回一个字节5 与234进行拼接组成一个完整的int也就是b返回
大家看看如此的操作带来的消耗多了不止三倍很明显在字长对齐时效率要高许多
淡然这种效率仅仅是访问多字节带来的 如果还是进行的byte操作那效率差不了多少


目前的开发普遍比较重视性能,所以对齐的问题,有2种不同的处理方法:
1) 有一种使用空间换时间做法是显式的插入reserved成员:
struct A{
char a;
char reserved1[3];//使用空间换时间
int b;

}a;
2) 随便怎么写,一切交给编译器自动对齐。
还有一种将逻辑相关的数据放在一起定义

代码中关于对齐的隐患,很多是隐式的。比如在强制类型转换的时候。下面举个例子:
unsigned int i = 0x12345678;
unsigned char *p=NULL;
unsigned short *p1=NULL;

p=&i;
*p=0x00;
p1=(unsigned short *)(p+1);
*p1=0x0000;
最后两句代码,从奇数边界去访问unsignedshort型变量,显然不符合对齐的规定。
在x86上,类似的操作只会影响效率,但是在MIPS或者sparc上,可能就是一个error

Array vs. List

Memory allocation
Most often, arrays are static, with their size defined upon creation. Additionally, the memory allocated for arrays is contiguous. Therefore, they are typically used when the maximum number of elements is known at design time. The drawback to this approach is that large arrays require large amounts of memory, which may go unused, especially those designed for a maximum number of elements that will often not approach their capacity. And on some platforms, such as certain handheld devices that use older operating systems, memory constraints could limit the size of the arrays you can use.

On the other hand, linked lists are usually dynamic. They can grow and shrink as needed at runtime. Due to this trait, linked lists are more appealing when the number of elements is unknown. Also, the linked list memory is allocated on an element-by-element basis and thus is rarely contiguous. The downside to being able to deal with uncertainty is that adding and deleting elements to linked lists requires more overhead than merely assigning values to preallocated array elements. But the only limits on how much memory may be allocated to a linked list are imposed by the size of the memory heap used by the application.

Accessing elements
The elements within arrays are accessed by their indices. Thus, data access is easy and fast if you know which element to retrieve. If you don’t know the index of the element needed, but the elements are sorted based on some key value, you can perform highly efficient search algorithms to locate specific elements. These algorithms allow only a minimal number of comparisons to locate a unique element. There are also several established and efficient algorithms for sorting and merging arrays. However, arrays are inefficient when the ordering of their elements is likely to change. Maintaining a sorted array upon element deletion or insertion could require the transfer of every element in the array.

Linked lists are usually traversed element by element until a match is found. Because the memory for linked lists is not guaranteed to be contiguous, this list traversal is the only method for searching the list (without involving the use of other data structures as indices). The upside of noncontiguous memory is that reordering the list simply involves manipulating the links. Insertion or deletion of an element requires only a couple of pointer modifications. The transfer of the actual data isn’t required at all.

Breaking the rules
Using language-specific constructs may allow for the best of both worlds. With C, pointers to variables or objects can be used as arrays of the corresponding type if they are pointed to the first element in an allocated array. This allows a pointer to be used as an array, but when resizing is necessary, the realloc() function allocates a new block of memory and transfers all existing elements to the new location. This technique allows for dynamic resizing of an array while maintaining contiguous memory and element indexing.

With Java, the provided linked-list class offers an indexed linked list that supports all of the standard list methods (top, next, previous, etc.) as well as indexed operation. The indexOf(), get(), and set() methods allow array-like access to the elements of the list. Additionally, Java provides an ArrayList class that represents a resizable-array implementation of the list class. Both of these classes support methods for returning true arrays from their list representations.

Programming languages continue to become more advanced, and there is less distinction between the various types of data implementations as their structures expand to include the strengths and correct the deficiencies found in the standard models. However, it will always be important to remember where these structures originated and how they are still used within the newer classes. Although these newer implementations hide the details from the programmer, the computational overhead and resources required do not change.

Making the decision
If your data is best represented using a multidimensional structure, or the number of elements is known in advance and will remain consistent, an array is best. If your data is easily represented in one dimension, and the number of elements is unknown or is expected to change often throughout the operation of your program, a linked list is more efficient.

If your data will be searched and accessed often but will change infrequently, the array offers the least overhead for your expected operations. If you expect to be regularly adding or subtracting elements, especially if you need to maintain a sorted order, the versatility of the linked list will be of greater benefit.

In the end, your data and your operational needs will decide which structure is best for your application.

Stack Frames



Class 28: Stack Frames (1)

Back to Type Checking (3): Conclusion. On to Stack Frames (2).

Held: Wednesday, 7 April 2004

Overview:

* About the back end
* Where do variables and values go?
* Stacks and stack frames
* Function and procedure calls
* Non-local variables

Moving to the Back End

* We've covered most of the front-end details: lexing, parsing, and basic semantic analysis.
* Now it's time to move on to the back end of the parser. That is, the generation of code (perhaps intermediate code, perhaps assembly code) from the annotated parse tree.
* We'll look at the issue in steps.
o We'll consider some general issues (run-time environment, assembly code) this week.
o We'll start looking at particular translations starting next week.

Storing Variables in Memory

* A first consideration is how to handle the storage of variables and parameters in memory.
* As you know, in most modern languages it is possible to call procedures recursively and create new instantiations of the local variables for those procedures.
* In addition, when a function exits you no longer need access to its local variables.
* However, in languages that support the dynamic allocation of memory (e.g., most object-oriented languages), there are also some values that live beyond the function that created them.
* Typically, values that are only active during the lifetime of a procedure are allocated on a stack and values that are independent of procedure lifetime are allocated on a heap.
o Most languages assume one stack and one heap.
* In modern architectures, some variables should be stored in registers to improve performance.

The Stack

* At the machine level, the stack is simply an area of memory that is allocated and deallocated in a stack-like manner.
* Typically, the stack starts at the high end of memory and the heap starts at the low end of memory.
o This design makes it possible to delay the decision of how much memory to use for heap and how much for stack until run time (i.e., you can grow either heap or stack until the two cross).
o This design suggests that stacks grown downward and shrink upwards, like bungie cords.
* A designated register called the stack pointer keeps track of the end of the stack.

The Heap

* The heap is an even more amorphous area of memory. Parts of the area are allocated by explicit allocate calls (e.g., new) although the determination of which area to use is up to the system rather than the program.
* In many languages (including Pascal) programmers must manage the memory they allocate, freeing it when no more memory is available.
o The system still must do some behind-the-scences work in keeping track of which memory the programmer has designated as available and free.
* In some more modern languages, the system is in charge of keeping track of which memory is in use and freeing unused memory "automatically". This technique is commonly referred to as gargage collection.

Stack Frames

* Since a function will often require space for many variables (parameters, local variables, temporaries, etc.) it is more convenient to allocate all of that space at once.
o This means that we should predetermine the maximum amount of space a function will use.
o As long as we've determined that space, we might as well lay out the data in that space.
* The organization of local data for the invocation of a function is typically called a stack frame.
* A frame pointer indicates the beginning of the frame.
o Why have both frame pointer and stack pointer? At times, you need to keep track of other frames.
* What goes in a frame (or in accompanying registers)?
o Any local variables
o Parameters (subject to the caveats below)
o The return address (what statement to branch to when the method exits)
o Any temporaries
o Saved registers
o Space for the return value
o Other things ...

Function Calls

* How do we call a function?
* The caller places some of the formal parameters on the stack (often, in its own stack frame).
* The caller places some of the formal parameters in registers.
o If those registers are currently in use, the caller must store their current values on the stack.
* The caller places a return address and static link on the stack (often, in the next stack frame).
* The caller branches to the beginning of the called function.
* The called function allocates a new stack frame, updating the stack pointer.
* The called function executes.
* The called function stores its result in a register (or on the stack, in a more primitive implementation).
* The called function deallocates its stack frame.
* The called function branches back to the return address.
* The caller makes use of the result value.
* The caller restores any registers necessary.

Accessing Non-Local Variables

* In nested languages, like Pascal, it is possible to refer to a variable from a non-local scope. How do you get access to that variable?
* One possibility is to have every frame include a pointer to the frame of the enclosing scope (not necessarily the caller). This means that you have to trace backward an appropriate amount, but that amount can be computed at compile time. Such a pointer is typically called a static link.
* Another possibility is to use a global display which maps each scope to the appropriate stack frame.
* A third possibility is to pass all of the variables to the function and restore them afterwards. This can be particularly difficult to implement.

函数调用过程

一.环境:

x86/WinXP/VC 6.0

二.用例:
int swap(int a, int b)
{
int v;
v = a;
a = b;
b = v;
return v;
}

void main(void)
{
int a = 7;
int b = 10;
int c = 0;
c = swap(a,b);
return;
}

三.分析:
1: int swap(int a, int b)
2: {
00401020 push ebp
00401021 mov ebp,esp
00401023 sub esp,44h
00401026 push ebx
00401027 push esi
00401028 push edi
00401029 lea edi,[ebp-44h]
0040102C mov ecx,11h
00401031 mov eax,0CCCCCCCCh
00401036 rep stos dword ptr [edi]
3: int v;
4: v = a;
00401038 mov eax,dword ptr [ebp+8]
0040103B mov dword ptr [ebp-4],eax
5: a = b;
0040103E mov ecx,dword ptr [ebp+0Ch]
00401041 mov dword ptr [ebp+8],ecx
6: b = v;
00401044 mov edx,dword ptr [ebp-4]
00401047 mov dword ptr [ebp+0Ch],edx
7: return v;
0040104A mov eax,dword ptr [ebp-4]
8: }
0040104D pop edi
0040104E pop esi
0040104F pop ebx
00401050 mov esp,ebp
00401052 pop ebp
00401053 ret

swap函数内部参数处理:
1.将基址指针EBP压栈;
2.将堆栈指针ESP拷贝一份到EBP中;
3.将ESP值减0x44(为了将这68个字节填充为0xCC);
4.将EBX/ESI/EDI压栈;
5.将EBP-0x44的地址偏移量存到目标地址指针EDI;
6.将计数器ECX置为0x11(即17个整数);
7.将EDI所开始的大小为17的内存空间填充为0xCCCCCCCC;

8.取出堆栈中被压的a的值(EBP+8所存的值)放到EAX中;
9.将EAX中的值(参数a的值)赋给到v;
10.取出堆栈中被压的b的值(EBP+0CH所存的值)放到ECX中;
11.将ECX中的值(参数b的值)赋给到a;
12.取出堆栈中被压的b的值(EBP-4所存的值)放到EDX中;
13.将EDX中的值(变量v的值)赋给到a;

14.将返回值(v的值)拷贝到EAX中;

15.恢复EDI/ESI/EBX;
16.恢复ESP为EBP(即进入swap时的ESP);
17.恢复EBP为调用swap前的值;
18.swap函数返回;


10: void main(void)
11: {
00401070 push ebp
00401071 mov ebp,esp
00401073 sub esp,4Ch
00401076 push ebx
00401077 push esi
00401078 push edi
00401079 lea edi,[ebp-4Ch]
0040107C mov ecx,13h
00401081 mov eax,0CCCCCCCCh
00401086 rep stos dword ptr [edi]
12: int a = 7;
00401088 mov dword ptr [ebp-4],7
13: int b = 10;
0040108F mov dword ptr [ebp-8],0Ah
14: int c = 0;
00401096 mov dword ptr [ebp-0Ch],0
15: c = swap(a,b);
0040109D mov eax,dword ptr [ebp-8]
004010A0 push eax
004010A1 mov ecx,dword ptr [ebp-4]
004010A4 push ecx
004010A5 call @ILT+5(_swap) (0040100a)
004010AA add esp,8
004010AD mov dword ptr [ebp-0Ch],eax
16: return;
17: }
004010B0 pop edi
004010B1 pop esi
004010B2 pop ebx
004010B3 add esp,4Ch
004010B6 cmp ebp,esp
004010B8 call __chkesp (004010e0)
004010BD mov esp,ebp
004010BF pop ebp
004010C0 ret


15: c = swap(a,b);
0040109D mov eax,dword ptr [ebp-8]
004010A0 push eax
004010A1 mov ecx,dword ptr [ebp-4]
004010A4 push ecx
004010A5 call @ILT+5(_swap) (0040100a)
004010AA add esp,8
004010AD mov dword ptr [ebp-0Ch],eax
分析函数swap调用过程:
1.将参数值b拷到EAX;
2.将EAX值压栈;
3.将参数值a拷到ECX;
4.将ECX值压栈;
5.CALL swap函数
6.将堆栈指针加8,丢弃堆栈中a,b的值;
7.将EAX中的返回值拷贝到变量c;

调用swap时的堆栈情况:

0012FEC8 | EDI |<-- ESP (执行到 v = a语句时堆栈指针)
0012FECC | ESI |
0012FED0 | EBX |
0012FED4 | 0xCCCCCCCC |
| . |
| . |
| . |
0012FF14 | 0xCCCCCCCC |
0012FF18 | EBP |<-- EBP (执行到 v = a语句时堆栈指针)
0012FF1C | 0x004010AA | 执行完调用swap后的返回地址
0012FF20 | ECX | a的值
0012FF24 | EAX | b的值


四.总结:

1.实参压栈过程是从右至左,然后压入函数调用后的返回地址;
2.被调用的函数从堆栈中取实参值;
3.如果采用编译器进行优化,那么被调用的函数内部是从寄存器还是从堆栈中取实参值?具体情况根据CPU架构和参数个数有关(可以继续分析哦!);
4.如果函数参数个数超过较多(如果超过5个参数),函数参数如何传递呢?
5.函数的返回值一般会存在一个寄存器,如EAX.

根据成员变量的地址推算出结构体变量的地址

根据成员变量的地址推算出结构体变量的地址2006-12-26 14:47我们在书写C程序的时候,有时候需要根据结构体成员变量的地址,得到结构体的地址,特别是我们想用C来实现C++的继承特性的时候。
我们对问题的分析如下:

输入:一个结构体定义type,这个结构体中某个成员变量的名字member以及它的地址ptr
输出:包含此成员变量的结构体的地址
为了便于分析,我们给出一个实例来说明
struct father_t {
int a;
char *b;
double c;
}f;
char *ptr = &(f.b);
//而不是 ptr = f.b; 这里ptr是b的地址,而不是它指向的地址。
根据C语言对struct类型的存储特性,我们可以画这么一个图示:

通过分析图示,我们可以看出,我们只需要把当前知道的成员变量的地址ptr,减去它在结构体当中相对偏移4就的到了结构体的地址(ptr-4)。
在linux当中对此有一个很好的宏可以使用,叫做 container_of, 放在 linux/kernel.h当中。它的定义如下所示:
/**
* container_of - cast a member of a structure out to the containing structure
*
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({
const typeof( ((type *)0)->member ) *__mptr = (ptr);
(type *)( (char *)__mptr - offsetof(type,member) );})

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
对上面的定义,分析如下:

(type *)0->member为设计一个type类型的结构体,起始地址为0,编译器将结构体的起始的地址加上此结构体成员变量的偏移得到此结构体成员变 量的偏移地址,由于结构体起始地址为0,所以此结构体成员变量的偏移地址就等于其成员变量在结构体内的距离结构体开始部分的偏移量。即:&((type *)0->member)就是取出其成员变量的偏移地址。而其等于其在结构体内的偏移量:即为:(size_t)(& ((type *)0)->member)经过size_t的强制类型转换后,其数值为结构体内的偏移量。该偏移量这里由offsetof()求出。
typeof(((type *)0)->member)为取出member成员的变量类型。用其定义__mptr指针。ptr为指向该成员变量的指针。__mptr为member数据类型的常量指针,其指向ptr所指向的变量处。
(char*)__mptr转换为字节型指针。(char*)__mptr - offsetof(type,member))用来求出结构体起始地址(为char *型指针),然后(type*)(char*)__mptr - offsetof(type,member))在(type *)作用下进行将字节型的结构体起始指针转换为type *型的结构体起始指针。
这就是从结构体某成员变量指针来求出该结构体的首指针。指针类型从结构体某成员变量类型转换为该结构体类型。

function pointer

// is a pointer to a function which returns an int and takes a float and two char
void PassPtr(int (*pt2Func)(float, char, char))
{
int result = (*pt2Func)(12, 'a', 'b'); // call using function pointer
cout << result << endl;
}

// execute example code - 'DoIt' is a suitable function like defined above in 2.1-4
void Pass_A_Function_Pointer()
{
cout << endl << "Executing 'Pass_A_Function_Pointer'" << endl;
PassPtr(&DoIt);
}

Bit Fields

Bit Fields allow the packing of data in a structure. This is especially useful when memory or data storage is at a premium. Typical examples:

* Packing several objects into a machine word. e.g. 1 bit flags can be compacted -- Symbol tables in compilers.
* Reading external file formats -- non-standard file formats could be read in. E.g. 9 bit integers.

C lets us do this in a structure definition by putting :bit length after the variable. i.e.


struct packed_struct {
unsigned int f1:1;
unsigned int f2:1;
unsigned int f3:1;
unsigned int f4:1;
unsigned int type:4;
unsigned int funny_int:9;
} pack;


Here the packed_struct contains 6 members: Four 1 bit flags f1..f3, a 4 bit type and a 9 bit funny_int.

C automatically packs the above bit fields as compactly as possible, provided that the maximum length of the field is less than or equal to the integer word length of the computer. If this is not the case then some compilers may allow memory overlap for the fields whilst other would store the next field in the next word (see comments on bit fiels portability below).

Access members as usual via:

pack.type = 7;

NOTE:

* Only n lower bits will be assigned to an n bit number. So type cannot take values larger than 15 (4 bits long).
* Bit fields are always converted to integer type for computation.
* You are allowed to mix ``normal'' types with bit fields.
* The unsigned definition is important - ensures that no bits are used as a $\pm$ flag.

End-Of-File (EOF) when reading from cin

End-Of-File (EOF) when reading from cin

When a program is reading from a disk file, the system "knows" when it gets to the end. This condition is called End-Of-File (EOF). All systems also provide some way of indicating an EOF when reading from the keyboard. This varies from system to system.

Dev-C++
Type: Enter Control-z Enter
MS Visual C++
Type: Enter Control-z Enter Enter
Reportedly there is a Microsoft patch that can be applied so that only one Enter is required after the Control-z. I wouldn't bother.
Other systems
Some may use other characters: control-D then Enter, or control-D followed by a control-Z, or ... .

You can just provide bad data to make cin fail in many cases. A student once claimed that typing "EOF" was the way to indicate and end-of-file from the console. Yes, it stops reading (because of an error) if you're reading numbers, but not when reading characters or strings!
Resetting after EOF

Altho it doesn't make sense to read after an EOF on a file, it is reasonable to read again from the console after an EOF has been entered. The clear function allows this.

while (cin >> x) {
... // loop reading until EOF (or bad input)
}

cin.clear(); // allows more reading
cin >> n;
...

Find the number of set bits in a given integer

Q: Find the number of set bits in a given integer

Sol: Parallel Counting: MIT HAKMEM Count

HAKMEM (Hacks Memo) is a legendary collection of neat mathematical and programming hacks contributed mostly by people at MIT and some elsewhere. This source is from the MIT AI LABS and this brilliant piece of code orginally in assembly was probably conceived in the late 70’s.

int BitCount(unsigned int u)

{
unsigned int uCount;


uCount = u
- ((u >> 1) & 033333333333)
- ((u >> 2) & 011111111111);
return
((uCount + (uCount >> 3))
& 030707070707) % 63;



}

Lets take a look at the theory behind this idea.

Take a 32bit number n; n = a31 * 231 + a30 * 230 +.....+ ak * 2k +....+ a1 * 2 + a0;

Here a0 through a31 are the values of bits (0 or 1) in a 32 bit number. Since the problem at hand is to count the number of set bits in the number, simply summing up these co-efficients would yeild the solution. (a0 + a1 +..+ a31 ).

How do we do this programmatically?

Take the original number n and store in the count variable. count=n;

Shift the orignal number 1 bit to the right and subtract from the orignal. count = n - (n >>1);

Now Shift the original number 2 bits to the right and subtract from count; count = n - (n>>1) - (n>>2);

Keep doing this until you reach the end. count = n - (n>>1) - (n>>2) - ... -( n>>31);

Let analyze and see what count holds now. n = a31 * 231 + a30 * 230 +.....+ ak * 2k +....+ a1 * 2 + a0; n >> 1 = a31 * 230 + a30 * 229 +.....+ ak * 2k-1 +....+ a1; n >> 2 = a31 * 229 + a30 * 228 +.....+ ak* 2k-2 +....+ a2

; .. n >> k = a31 * 2(31-k) + a30 * 2(30-k) +…..+ ak * 2k;

.. n>>31 = a31;

You can quickly see that: (Hint: 2k - 2k-1 = 2k-1; ) count = n - (n>>1) - (n>>2) - ... -( n>>31) =a31+ a30 +..+a0; which is what we are looking for;

int BitCount(unsigned int u)
{
unsigned int uCount=u;
do
{
u=u>>1;
uCount -= u;

}
while(u);
}

This certainaly is an interesting way to solve this problem. But how do you make this brilliant? Run this in constant time with constant memory!!.

int BitCount(unsigned int u)

{
unsigned int uCount;


uCount = u
- ((u >> 1) & 033333333333)
- ((u >> 2) & 011111111111);
return
((uCount + (uCount >> 3))
& 030707070707) % 63;



}

For those of you who are still wondering whats going? Basically use the same idea, but instead of looping over the entire number, sum up the number in blocks of 3 (octal) and count them in parallel.

After this statement uCount = n - ((n >> 1) & 033333333333) - ((n >> 2) & 011111111111); uCount has the sum of bits in each octal block spread out through itself.

So if you can a block of 3 bits

u = a222 + a12+ a0; u>>1 = a2*2 + a1; u>>2 = a2;

u - (u>>1) - (u>>2) is a2+a1+a0 which is the sum of bits in each block of 3 bits.

The nexe step is to grab all these and sum them up:

((uCount + (uCount >> 3)) will re-arrange them in blocks of 6 bits and sum them up in such a way the every other block of 3 has the sum of number of set bits in the original number plus the preceding block of 3. The only expection here is the first block of 3. The idea is not to spill over the bits to adjacent blocks while summing them up. How is that made possible. Well, the maximum number of set bits in a block of 3 is 3, in a block of 6 is 6. and 3 bits can represent upto 7. This way you make sure you dont spill the bits over. To mask out the junk while doing a uCount>>3. Do and AND with 030707070707. THe only expection is the first block as I just mentioned.

What does ((uCount + (uCount >> 3)) & 030707070707) hold now? Its 2^0 * (2^6 - 1) * sum0 + 2^1 * (2^6 - 1) * sum1 + 2^2 * (2^6 - 1) * sum2 + 2^3 * (2^6 - 1) * sum3 + 2^4 * (2^6 - 1) * sum4 + 2^5 * (2^3 - 1) * sum5 where sum0 is the sum of number of set bits in every block of 6 bits starting from the ‘low’ position. What we need is sum0 + sum1 + sum2 + sum3 + sum4 + sum5; 2^6-1

Feb 9, 2008

堆和栈的区别

堆和栈的区别
solost 于 2004年 10月09日发表
一、预备知识—程序的内存分配
一个由c/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器(Compiler)自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
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堆和栈中的存储内容
栈:在函数调用时,(1) 第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,(2) 然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,(3) 然后是函数中的局部变量。注意: 静态变量是不入栈的。
当本次函数调用结束后,(1) 局部变量先出栈,(2) 然后是参数,(3) 最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。

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

Feb 5, 2008

Java and C++ 区别

AVA和C++都是面向对象语言。也就是说,它都能够实现面向对象思想(封装,继乘,多态)。而由于c++为了照顾大量的C语言使用者,

而兼容了C,使得自身仅仅成为了带类的C语言,多多少少影响了其面向对象的彻底性!JAVA则是完全的面向对象语言,它句法更清晰,规模更小,更易学。它是在对多种程序设计语言进行了深入细致研究的基础上,据弃了其他语言的不足之处,从根本上解决了c++的固有缺陷。

Java和c++的相似之处多于不同之处,但两种语言问几处主要的不同使得Java更容易学习,并且编程环境更为简单。

我在这里不能完全列出不同之处,仅列出比较显著的区别:

1.指针

JAVA 语言让编程者无法找到指针来直接访问内存无指针,并且增添了自动的内存管理功能,从而有效地防止了c/c++语言中指针操作失误,如野指针所造成的系统崩溃。但也不是说JAVA没有指针,虚拟机内部还是使用了指针,只是外人不得使用而已。这有利于Java程序的安全。

2.多重继承

c+ +支持多重继承,这是c++的一个特征,它允许多父类派生一个类。尽管多重继承功能很强,但使用复杂,而且会引起许多麻烦,编译程序实现它也很不容易。 Java不支持多重继承,但允许一个类继承多个接口(extends+implement),实现了c++多重继承的功能,又避免了c++中的多重继承实现方式带来的诸多不便。

3.数据类型及类

Java是完全面向对象的语言,所有函数和变量部必须是类的一部分。除了基本数据类型之外,其余的都作为类对象,包括数组。对象将数据和方法结合起来,把它们封装在类中,这样每个对象都可实现自己的特点和行为。而c++允许将函数和变量定义为全局的。此外,Java中取消了c/c++中的结构和联合,消除了不必要的麻烦。

4.自动内存管理

Java程序中所有的对象都是用new操作符建立在内存堆栈上,这个操作符类似于c++的new操作符。下面的语句由一个建立了一个类Read的对象,然后调用该对象的work方法:

Read r=new Read(); r.work();



语句Read r=new Read();在堆栈结构上建立了一个Read的实例。Java自动进行无用内存回收操作,不需要程序员进行删除。而c十十中必须由程序贝释放内存资源,增加了程序设计者的负扔。Java中当一个对象不被再用到时,无用内存回收器将给它加上标签以示删除。JAVA里无用内存回收程序是以线程方式在后台运行的,利用空闲时间工作。

5.操作符重载

Java不支持操作符重载。操作符重载被认为是c十十的突出特征,在Java中虽然类大体上可以实现这样的功能,但操作符重载的方便性仍然丢失了不少。Java语言不支持操作符重载是为了保持Java语言尽可能简单。

6.预处理功能

Java不支持预处理功能。c/c十十在编译过程中都有一个预编泽阶段,即众所周知的预处理器。预处理器为开发人员提供了方便,但增加丁编译的复杂性。JAVA虚拟机没有预处理器,但它提供的引入语句(import)与c十十预处理器的功能类似。

7. Java不支持缺省函数参数,而c十十支持

在c中,代码组织在函数中,函数可以访问程序的全局变量。c十十增加了类,提供了类算法,该算法是与类相连的函数,c十十类方法与Java类方法十分相似,然而,由于c十十仍然支持c,所以不能阻止c十十开发人员使用函数,结果函数和方法混合使用使得程序比较混乱。

Java没有函数,作为一个比c十十更纯的面向对象的语言,Java强迫开发人员把所有例行程序包括在类中,事实上,用方法实现例行程序可激励开发人员更好地组织编码。

8 字符串

c和c十十不支持字符串变量,在c和c十十程序中使用Null终止符代表字符串的结束,在Java中字符串是用类对象(strinR和stringBuffer)来实现的,这些类对象是Java语言的核心,用类对象实现字符串有以下几个优点:

(1)在整个系统中建立字符串和访问字符串元素的方法是一致的;

(2)J3阳字符串类是作为Java语言的一部分定义的,而不是作为外加的延伸部分;

(3)Java字符串执行运行时检空,可帮助排除一些运行时发生的错误;

(4)可对字符串用“十”进行连接操作。

9“goto语句

“可怕”的goto语句是c和c++的“遗物”,它是该语言技术上的合法部分,引用goto语句引起了程序结构的混乱,不易理解,goto语句子要用于无条件转移子程序和多结构分支技术。鉴于以广理由,Java不提供goto语句,它虽然指定goto作为关键字,但不支持它的使用,使程序简洁易读。

l0.类型转换

在c和c十十中有时出现数据类型的隐含转换,这就涉及了自动强制类型转换问题。例如,在c十十中可将一浮点值赋予整型变量,并去掉其尾数。Java不支持c十十中的自动强制类型转换,如果需要,必须由程序显式进行强制类型转换。

11.异常

JAVA中的异常机制用于捕获例外事件,增强系统容错能力

try{//可能产生例外的代码 }catch(exceptionType name){ //处理 }



其中exceptionType表示异常类型。而C++则没有如此方便的机制。