3

这是我从 Microsoft Developer Network
http://msdn.microsoft.com/en-us/library/dd162487(v=VS.85).aspx复制的一些代码

LRESULT APIENTRY WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 
{

**PAINTSTRUCT ps;  
HDC hdc;**

switch (message) 
{ 
    case WM_PAINT: 
        hdc = BeginPaint(hwnd, &ps); 
        TextOut(hdc, 0, 0, "Hello, Windows!", 15);
        EndPaint(hwnd, &ps); 
        return 0L; 

    // Process other messages.   
} 
} 

我可能错了,但我认为每次编译器运行如下任何一个语句时:

int var1  
double var2  
char var3[]  
PAINTSTRUCT ps  
HDC hdc  

计算机将创建一个新变量。至少这是合乎逻辑的想法,因为当你想创建一个新变量时,这是你写的,对吧?

我也一直认为,如果你有这样的代码块:

for(int i = 0; i < 100; i++)
    int sum = i;

计算机将创建 100 个不同的变量,它们都具有相同的名称sum和一些包含在i

在上面的示例代码中,函数 WndProc 将在程序过程中被多次调用,但是函数创建的两个变量“ps”和“hdc”只会在函数执行的某些时候使用.

那么计算机会生成许多它永远不会使用的单独的、额外的 PAINTSTRUCT 和 HDC 变量吗?
像这样在 case WM_PAINT: 之后声明“ps”和“hdc”会不会更有效?

case WM_PAINT:
{ 
     **PAINTSTRUCT ps;  
     HDC hdc;**

     hdc = BeginPaint(hwnd, &ps); 
     TextOut(hdc, 0, 0, "Hello, Windows!", 15);
     EndPaint(hwnd, &ps); 
 }
 return 0L; 
4

5 回答 5

2

我可能错了,但我认为每次编译器运行如下任何一个语句时:

int var1  
double var2  
char var3[]  
PAINTSTRUCT ps  
HDC hdc  

计算机将创建一个新变量。

这些变量对应于运行时的存储空间。由于它们是本地人,他们可能存在于堆栈中,为这些本地人分配空间就像移动堆栈指针一样简单。这是非常快的。

我也一直认为,如果你有这样的代码块: for(int i = 0; i < 100; i++) int sum = i;

计算机将创建 100 个不同的变量,它们都具有相同的名称“sum”和一些包含在“i”中的值

这里可能会发生的情况是,堆栈上的相同位置将被循环的每次迭代重用。

像这样在 case WM_PAINT: 之后声明“ps”和“hdc”会不会更有效?

不,它们是本地人,它们很可能被分配在堆栈上,并且一旦输入方法,它们的空间就会被保留,而与您在代码中声明它们的位置无关。

最后,担心这里的性能充其量只是一个微优化。专注于代码的含义。让编译器尽可能有效地将其翻译成可执行代码。只有当您遇到不符合客户预期性能要求的性能问题时,您才应该开始担心。

于 2011-04-23T21:11:22.450 回答
1

您是正确的,计算机将声明很多局部变量,但幸运的是这是非常有效的。编译器通常会在函数第一次调用时将生成局部变量的代码作为单个汇编指令输出,因此创建这些变量的开销可以忽略不计。在您的循环示例中,编译器通常不会多次创建和销毁变量。相反,它创建一次,它们在循环的每次迭代中重用空间。换句话说,变量在每次迭代中逻辑上被创建和销毁,但在生成的代码中,它实际上在多次迭代中共享。

作为一般规则,不要担心程序的效率,除非您有特定的理由相信您的性能低于应有的水平。您的首要任务应该是编写干净、优雅的代码而不是第一次优化代码,因为如果不实际运行程序,很难知道您的优化是否会对程序的运行时产生有用的影响。通常,您会使用分析器来确定代码的哪些位置较慢,然后将优化工作集中在那里。

于 2011-04-23T21:09:14.137 回答
1

对于原始局部变量——指针、int、double、float 等——和原始数组,声明一个变量确实为它“分配”空间,但实际发生的只是编译器说“我要去调用堆栈上的这个点,堆栈指针下方的三个字,i。” 没有实际的计算发生——根本没有执行任何指令!因此,在循环内部或外部声明“总和”完全没有区别!

然而,对于本地声明的类实例,情况有所不同。声明对象意味着当进入块时将调用构造函数,而在退出时将调用析构函数,这确实会导致指令被执行——所以你需要小心。

于 2011-04-23T21:10:39.533 回答
1

计算机将创建一个新变量

这只是表面上的真实。从技术上讲,处理器实际上所做的是将堆栈指针移动到一定长度。如果您声明的变量没有任何构造函数,则此操作的开销几乎为零。

那么计算机会生成许多它永远不会使用的单独的、额外的 PAINTSTRUCT 和 HDC 变量吗?

不,这不是真的,一旦函数返回分配给这些变量的所有堆栈空间,就会被回收,不会浪费任何东西。

像这样在 case WM_PAINT: 之后声明“ps”和“hdc”会不会更有效?

不见得。编译器可能会在堆栈中看到这些变量的声明和分配的空间,无论您将它们的声明放在哪里。同样,只要变量没有构造函数,这就是正确的。如果他们这样做了,那么将它们放在案例中将保存对构造函数的调用。

于 2011-04-23T21:12:56.303 回答
0

只要结果相同,编译器就会对您的代码进行重大更改。特别是在发布(非调试)版本中。

这意味着如果您有未使用的代码、过早声明变量或有未在循环外使用的变量,编译器可能会删除或移动它们。

/* If you never use these, the compiler will probably throw them out */
int var1;
double var2;
char var3[];
PAINTSTRUCT ps;
HDC hdc;

/* Nothing here effects other code, so the code will probably be removed */
for(int i = 0; i < 100000; ++i)
{
    int j = i * 7;
}

除非你有非常强的性能要求,否则你能做的最好的事情就是让你的代码更具可读性。如果变量在较小的范围内使用,请将其放在那里,因为它更易于阅读,而不是因为它可能会使您的程序更快。

计算机非常擅长运行您所谈论的代码类型,而您只使用 CPU 和内存。当您处理相对较慢的设备(如网络)或从磁盘读取时,您实际上可以获得良好性能改进的地方。在这些情况下,单个字节的内存很重要。

CPU/内存优化最有意义的地方是更复杂的算法,或者当您处理大量数据时,例如图形或搜索算法。

这并不是说你不应该考虑优化——只是你应该选择你的战斗。在大多数程序中优化代码的可读性,并在您有更复杂的算法时优化性能,或者您正在处理低级图形或复杂的数据结构/算法,然后,只有当您测试过你的程序出来了一下,发现它运行缓慢。

于 2011-04-23T22:18:39.350 回答