首页 > 技术文章 > 对内存字节的理解

strive-sun 2019-08-30 17:49 原文

为了加强自己的记忆,我决定用文字记录下来。

这是源代码,主要是输出环境变量,具体可以参考CreateEnvironmentBlock函数

 1 #include <Windows.h>
 2 #include <UserEnv.h>
 3 #include <assert.h>
 4 #include <stdio.h>
 5 #pragma comment(lib, "userenv.lib")
 6 #pragma warning(disable : 4996)
 7 
 8 int main()
 9 {
10     HANDLE hToken = NULL;    
11     BOOL ok = OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken);
12     /*assert(ok);
13 
14     wchar_t* penv = NULL;
15     ok = CreateEnvironmentBlock((void**)&penv, hToken, TRUE);
16     assert(ok);
17 
18     while (*penv) {
19         printf("%ls\n", penv);
20         penv += wcslen(penv) + 1;
21     }*/
22     wchar_t* wnext = NULL;
23     LPVOID pEnvironment = NULL;
24     ok = CreateEnvironmentBlock(&pEnvironment,
25         hToken,
26         FALSE);
27     if (!ok)
28     {
29         // handle error
30     }
31 
32     wchar_t* wtemp = (wchar_t*)pEnvironment;
33 
34     do
35     {
36         wnext = wtemp + wcslen(wtemp) + 1;
37         int m = wcslen(wtemp);
38         if (*(wnext+1) == L'\0') // Crashes here
39         {
40             break;
41         }
42         else
43         {
44             wtemp = wnext;
45             printf("%ls\n", wtemp);
46         }
47     } while (1);
48 
49     return 0;
50 }

注释部分可以不用看,主要注意 if (*(wnext+1) == L'\0') 这行

下面两张图分别是控制台输出结果和内存字节

 

 

 

 

 

 从第二张图可以看到控制台打印到windir=C:\WINDOWS之后,下面就会打印空行,再然后就会进入break,这是为什么呢?

我们已知宽字节占两个字节,所以一次会跳过2个点,包括r. s. 等等,我们在执行wnext = wtemp + wcslen(wtemp)+1这行代码时,实际上是相当于wtemp地址往后移动 wcslen(wtemp)+1个地址位,而我们在往后移动 wcslen(wtemp)时,我们其实到达了C.:.\.W.I.N.D.O.W.S.的S.的位置,再+1就相当于到C.:.\.W.I.N.D.O.W.S...的位置,+1的原因是为了跳过\0的字符串结束位,到这边是将(..)这个空的赋给wnext,而在执行到下一行代码if (*(wnext+1) == L'\0'),地址相当于又往后面移动一个宽字节,也就是C.:.\.W.I.N.D.O.W.S.....,然后我们判断这之后的(\.),把它与L'\0'进行比较,发现它不为0,然后代码跳到else上面。

重点来了,wtemp = wnext;这行代码其实是把C.:.\.W.I.N.D.O.W.S...后面的(..)这个空的内存字节赋给wtemp,打印为空,并不是C.:.\.W.I.N.D.O.W.S.....,因为C.:.\.W.I.N.D.O.W.S.....这个只是后面用于比较的,然后到wnext = wtemp + wcslen(wtemp) + 1这行代码,之前wtemp为空,所以wcslen(wtemp) = 0, 再+1,往后面移一个地址,就到了C.:.\.W.I.N.D.O.W.S.....上的最后一个点的位置,接着执行到下一行代码if (*(wnext+1) == L'\0'),地址上再+1,相当于跳过了(\.),我们需要比较(\.)后面的值,也就是(..),可以看到是空,然后与L'\0'相同,到了break,跳出while循环,程序结束。

 

这段代码的正确输出要把if (*(wnext+1) == L'\0')中的+1去除,这样避免指针指向未知的内存区域。

可能写的有点乱,仅供参考吧,我也是第一次接触,很多术语都没怎么明白。

 

推荐阅读