你是凝结的时间,流动的语言,
黑色的雾里,有隐约的光.
可是透过你的双眼,会看不清世界,
花朵的凋萎,在瞬间,
而花朵的绽放,在昨天。
有些事情,遇见了就不会忘记。 —— 2011 年 5 月 16 日
你是凝结的时间,流动的语言,
黑色的雾里,有隐约的光.
可是透过你的双眼,会看不清世界,
花朵的凋萎,在瞬间,
而花朵的绽放,在昨天。
有些事情,遇见了就不会忘记。 —— 2011 年 5 月 16 日
测试环境:Windwos 7 + GCC 4.5.2 Target: mingw32
测试代码:
1: code.c:
2:
3: int accum = 0;
4:
5: int sum(int x, int y)
6: {
7: int t = x +y;
8: accum += t;
9: return t;
10: }
11:
12: main.c:
13:
14: int main()
15: {
16: return sum(1, 3);
17: }
测试流程:
1. > gcc –O2 –S code.c
生成code.s 部分省略
1: _sum:
2: pushl %ebp
3: movl %esp, %ebp
4: movl 12(%ebp), %eax
5: addl 8(%ebp), %eax
6: addl %eax, _accum
7: leave
8: ret
可以注意到: sum函数首先保存ebp, 然后把当前esp 存入ebp, 因为esp寄存器存放当前栈顶指针,所以根据ebp的偏移获取参数x, y. (注意因为push ebp后,所以偏移为 12, 8, leave指令相当于 movl %ebp,%esp popl %ebp, 对应的还有ENTER指令)
2 >gcc –O2 –c code.c
>objdump –d code.o
得到输出
1: 00000000 <_sum>:
2: 0: 55 push %ebp
3: 1: 89 e5 mov %esp,%ebp
4: 3: 8b 45 0c mov 0xc(%ebp),%eax
5: 6: 03 45 08 add 0x8(%ebp),%eax
6: 9: 01 05 00 00 00 00 add %eax,0x0
7: f: c9 leave
8: 10: c3 ret
9: 11: 90 nop
10: 12: 90 nop
11: 13: 90 nop
注意这里 accum 的地方地址为全0,还没有最后确定。
3 >gcc –O2 –o prog code.o main.c
> objdump –d prog > tmp.txt
生成tmp.txt约1193行,查找到相关汇编码:
1: 004013c0 <_sum>:
2: 4013c0: 55 push %ebp
3: 4013c1: 89 e5 mov %esp,%ebp
4: 4013c3: 8b 45 0c mov 0xc(%ebp),%eax
5: 4013c6: 03 45 08 add 0x8(%ebp),%eax
6: 4013c9: 01 05 20 50 40 00 add %eax,0x405020
7: 4013cf: c9 leave
8: 4013d0: c3 ret
9: 4013d1: 90 nop
10: 4013d2: 90 nop
11: 4013d3: 90 nop
12:
13: 004013d4 <_main>:
14: 4013d4: 55 push %ebp
15: 4013d5: 89 e5 mov %esp,%ebp
16: 4013d7: 83 e4 f0 and $0xfffffff0,%esp
17: 4013da: 83 ec 10 sub $0x10,%esp
18: 4013dd: e8 3e 06 00 00 call 401a20 <___main>
19: 4013e2: c7 44 24 04 03 00 00 movl $0x3,0x4(%esp)
20: 4013e9: 00
21: 4013ea: c7 04 24 01 00 00 00 movl $0x1,(%esp)
22: 4013f1: e8 ca ff ff ff call 4013c0 <_sum>
23: 4013f6: c9 leave
24: 4013f7: c3 ret
25: ...
发现: 这时候 accsum处已经填入了真实地址,不过main函数内的行为有点奇怪,首先保存ebp和esp, 然后清空低4位的%esp, 加上16,调用___main, 而___main处理信息有些奇怪,判断 %405060地址(由accsum的地址知道那应该是全局变量区附近)处内容是否为空来决定是否调用
401a3d: eb 81 jmp 4019c0 <___do_global_ctors>
不过与我们现在的讨论无关,先行跳过,之后到Linux上去调试看看。
之后的送入参数,奇怪的是 sub 0x10 把栈增加了16个长度后只使用最上面的8个。
最后执行调用,值得注意的是,无论是被调用函数还是调用函数都不再负责保持堆栈平衡,只要使用esp,ebp反复恢复上一次的栈顶情况就可以了。
http://www.chinaunix.net 作者:dreamice 发表于:2009-08-02 18:05:41
一、预备知识—程序的内存分配
由C/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack): 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
3、全局区(static): 全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,程序结束后有系统释放 。
4、文字常量区: 常量字符串就是放在这里的, 程序结束后由系统释放。
5、程序代码区: 存放函数体的二进制代码。
Example:
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 申请方式
栈: 由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间
堆: 需要程序员自己申请,并指明大小,在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 小结:
堆和栈的区别可以用如下的比喻来看出: 使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。 使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。
还有就是函数调用时会在栈上有一系列的保留现场及传递参数的操作。栈的空间大小有限定,VC的缺省是2M。栈不够用的情况一般是程序中分配了大量数组和递归函数层次太深。有一点必须知道,当一个函数调用完返回后它会释放该函数中所有的栈空间。栈是由编译器自动管理的,不用你操心。堆是动态分配内存的,并且你可以分配使用很大的内存。但是用不好会产生内存泄漏。并且频繁地malloc和free会产生内存碎片(有点类似磁盘碎片),因为C分配动态内存时是寻找匹配的内存的。而用栈则不会产生碎片。在栈上存取数据比通过指针在堆上存取数据快些。一般大家说的堆栈和栈是一样的,就是栈(stack),而说堆时才是堆heap。栈是先入后出的,一般是由高地址向低地址生长。
晚上AE渲染的时候顺便看了下这篇文章。作者释戒悲写的挺好的 🙂
看的比较细,有点小困,先写两点。
1. 有求于佛,何谓解脱?
求佛并不会得到世俗上的解脱,般若希望人们直面人生。 问题还是要靠自己解决的。
菩萨虽然大慈大悲救苦救难,但是如果铲这种事您还是找God Father比较专业。解铃还需系铃人。自己的问题终归还要自己解决。有了大智慧到彼岸当然就离开了此案的种种痛苦烦恼。但并不是说你真的把一个烂摊子甩下,然后自己划着小船去通吃岛了。烂摊子还是原来的烂摊子,你该住几平米的房子还住几平米。只不过原来的问题已经不成其为问题了。按照更佛教一点的说法应该是本来就没有实际的问题,只是你自生分辨,认为有很多的问题。
2. 万物皆空?
总的来说,万物自性空,即最开始什么都不是,却又是最细小的存在。
因缘起而不断合和而成,这时候我们为他们加上了相,成为了我们所见的万物,都是缘起合和后在我们心理的投影。
“色即是空”的“空”也是指“缘起而无自性”。就是所谓的“自性空”。无自性可以用拳头来做比喻。世界上真的有一种实在的东西叫做拳头吗?其实没有。只不过是当我们把手指攥在一起的时候我们称它为拳头。这就是自性空。它不是说你伸出来的拳头本身是不存在的。而是说拳头只是一种“相”,是由因缘和合而成的。所以没有实在的“自性”。不存在一个绝对意义上的拳头,所以是“假名有”;但由于依五蕴和合而生,又是“自性空”。
同样的可以说海浪。我们如果说“空”就认为世界上不存在海浪,那就是执着于实空,是“恶趣空”,是不正确的观点。如果认为有一个实有自性的海浪存在,那就是执着于实有,也是不正确的观点。离开实有和实空两边的偏见,认识到浪是由水的形态幻化而出,既非实有也非实无而是“性空而假名有”才是中道实相。
同理,刚才我们说的那个瓷杯子,你把它当成“杯具”还是当成“洗具”也都是你的自生分辨而已了。
你可能经常看到罗马数字,即使你没有意识到他们。
你可能曾经在老电影或者电视中看到他们(“版权所有 MCMXLVI” 而不是 “版权所有1946”),或者在某图书馆或某大学的贡献墙上看到他们(“成立于 MDCCCLXXXVIII”而不是“成立于1888”)。
你也可能在某些文献的大纲或者目录上看到他们。这是一个表示数字的系统,他能够真正回溯到远古的罗马帝国(因此而得名)。
在罗马数字中,利用7个不同字母进行重复或者组合来表达各式各样的数字。
下面是关于构造罗马数字的一些通用的规则的介绍:
这段时间研究了WCF和C#的ADO.NET, SQL操作,LINQ查询虽然方便,但是对于刚开始学习,特别是没有好的指导老师的人来说,还是不推荐大范围使用。所以这里还是使用常规的数据库操作方法。
1. 连接数据库,其实作用并不大,好处在于自动生成ConnectString。
右键点击 Data Connections,选择Add Connection, 开始添加数据库,注意\SQLEXPRESS,另外如果要远程链接,现在数据库中注册有权限的帐号,然后登陆。
2. 连接ADO.NET,作用在于会自动生成对于的数据结构类,方便我们的操作,也就不用每个表都要创建类了。
右键点击Project, 添加Data页的ADO.NET,同样也是配置数据库的连接。
3. 在我们第一次连接的数据库Server属性中有一个Connection String, Copy之。
public int SQL_connect()
{
SQLConnect = new SqlConnection();
SQLConnect.ConnectionString = "Data Source=WIN-01L9BROHARG\\SQLEXPRESS;Initial Catalog=TutorNet;Persist Security Info=True;User ID=ctq;Password=ctqmumu";
SQLConnect.Open();
if (SQLConnect.State != System.Data.ConnectionState.Open)
{
//TODO output error
return ErrorReturn;
}
return 0;
}
有返回值的数据库操作
public List SQL_select_user_By_ID(string UID)
{
if (SQLConnect == null) SQL_connect();
List listUser = new List();
try
{
SQLCommand = new SqlCommand();
SQLCommand.Connection = SQLConnect;
SQLCommand.CommandText = "SELECT * FROM UserTable WHERE UID = '" + UID + "';";
SQLCommand.CommandType = CommandType.Text;
SqlDataReader reader = SQLCommand.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
listUser.Add(new UserTable
{
UID = (int)reader[0],
Password = (string)reader[1],
Name = (string)reader[2],
PhoneNumber = (reader[3] is System.DBNull) ? null : (string)reader[3],
Email = (string)reader[4],
ST = (reader[5] is System.DBNull) ? 0 : (int)reader[5],
Point = (reader[6] is System.DBNull) ? 0 : (int)reader[6],
PopIndex = (reader[7] is System.DBNull) ? 0 : (int)reader[7],
Login = (int)reader[8]
});
}
}
else
{
//MessageBox.Show("No rows found.");
}
reader.Close();
}
catch (Exception e)
{
return null; // 因为在WCF部署后无法正确处理,所以return null
}
return listUser;
}
无返回类型的操作,返回为bool型,即插入,更新和删除操作
public bool SQL_UPDATE_UserTable_Login(string UID, int val)
{
Int32 recordsAffected = 0;
try
{
string queryString = "UPDATE UserTable SET Login=" + val + " WHERE UID=" + UID + " ;";
SqlCommand command = new SqlCommand(queryString, SQLConnect);
recordsAffected = command.ExecuteNonQuery();
}
catch (Exception e)
{
return false;
}
return true;
}
刚刚在调试WCF传输Byte[]格式图片时,遇到了WCF默认限制传输不能超过16K的问题。
最开始显示的提示是:
Bad request , Code: 400
想了一会,觉得是IIS7的配置问题,于是查看了一下默认配置,打开IIS的“失败请求跟踪规则”
配置跟踪400错误,之后再运行,显示错误:
“The formatter threw an exception while trying to deserialize the message: Error in deserializing body of request message for operation ‘XXX’. The maximum array length quota (16384) has been exceeded while reading XML data. This quota may be increased by changing the MaxArrayLength property on the XmlDictionaryReaderQuotas object used when creating the XML reader. Line 1, position 39987.”
现在看到错误了,可以去Google一下,查看到具体信息,需要调整:
这篇文章写的很详细:How to Increase the MaxArrayLength in a WCF Component
注意的情况有:1. 分别更新客服端和服务端的.config文件 2. 推荐使用工具里的“WCF服务配置编辑器”
最后成功的情况:
昨晚过错了帮小葛的签到,愧疚。
因此晚上和他们聊了一会,体会到很多,这也是我要自己作出抉择的地方吧。
感性和理性,如果一直这样交错占据主动,以后会出大问题的。
需要跟多时间好好想想,不要伤害了身边的人。
查看C++书的时候,碰巧看到一个函数声明
bool Sales_item::same_isbn (const Sales_item * const this, const Sales_item &rhs) const
意思是:当一个函数被声明为常量成员函数(const member function),
这时隐藏的this指针被标记成 const Sales_item * const this, 是的,这里有两个const。
下面的这篇文章教导人们如何快速的阅读 C Declarations。
给出三个实例。 断言为 *a = b 是合法的。
const int * a = &b; // 假
int const * a = &b; // 假
int * const a = &b; // 真
ZZ from http://blogold.chinaunix.net/u1/36290/showart_443799.html
Even experienced C programmers have difficulty reading declarations that go beyond simple arrays and pointers. For example, is the following an array of pointers or a pointer to an array?
int *a[10];
What the heck does the following mean?
int (*(*vtable)[])();
Naturally, it’s a pointer to an array of pointers to functions returning integers. 😉
This short article tells you how to read any C declaration correctly using a very simple technique. I am 99% certain that I read this in a book in the late 1980s, but I can’t remember where. I doubt that I discovered this on my own (even though I’ve always been delighted by computer language structure and esoterica). I do remember, however, building a simple program that would translate any declaration into English.
The rule goes like this:
"Start at the variable name (or innermost construct if no identifier is present. Look right without jumping over a right parenthesis; say what you see. Look left again without jumping over a parenthesis; say what you see. Jump out a level of parentheses if any. Look right; say what you see. Look left; say what you see. Continue in this manner until you say the variable type or return type."
这条规律是这样的:
从变量名(或者是一个最深处的结构)开始,先看他右边一个命名,不要越过右括号(即遇到括号放弃),说出你看到的,然后再看他的左边一个命名,同样不越过当前层左括号,说出你看到的,一次循环后,如果这次遇到了阻挡,那么跳出这对括号;依次反复,直到读完所有的变量。
我或许可以模糊的告诉你大意,但是我认为更好的办法就是阅读原文(直接看英语更利于你理解)。起码该自己阅读下面的话:
The degenerate case is:
int i;
Starting at i, you look right and find nothing. You look left and find the type int, which you say. Done.
Ok, now a more complicated one:
int *a[3];
Start at a. Look right, say array of size 3. Look left and say pointer. Look right and see nothing. Look left and say int. All together you say a is an array of size 3 pointers to int.
Adding parentheses is when it gets weird:
int (*a)[3];
The parentheses change the order just like in an expression. When you look right after a, you see the right parenthesis, which you cannot jump over until you look left. Hence, you would say a is a pointer to an array of 3 ints.
The C "forward" declaration:
extern int foo();
just says that foo is a function returning int. This follows the same pattern for reading declarators as you saw in previous section. Start at foo and look right. You see () so say function. You look left and see int. Say int.
Now, try this one:
extern int *foo();
Yep, you say foo is a function returning a pointer to int.
Now for the big leap. Just like we can make a pointer to an int or whatever, let’s make a pointer to a function. In this case, we can drop the extern as it’s no longer a function forward reference, but a data variable declaration. Here is the basic pattern for function pointer:
int (*foo)();
You start at foo and see nothing to the right. So, to the left, you say pointer. Then to the right outside you see function. Then left you see int. So you say foo is a pointer to a function returning int.
Here is an array of pointers to functions returning int, which we’ll need for vtables below:
int (*Object_vtable[])();
You need one last, incredibly bizarre declaration, for the lab:
int (*(*vtable)[])();
This is the pointer to the vtable you will need in each "object" you define.
This pointer to a vtable is set to the address of a vtable; for example, &Truck_vtable.
The following examples summarize the cases needed for building virtual tables ala C++ to implement polymorphism (like the original cfront C++ to C translator).
int *ptr_to_int; int *func_returning_ptr_to_int(); int (*ptr_to_func_returning_int)(); int (*array_of_ptr_to_func_returning_int[])(); int (*(*ptr_to_an_array_of_ptr_to_func_returning_int)[])();