我可以通过指针访问受限内存 space
Can I access restricted memory space by pointers
所以当我学习指针时,我有了一个疯狂的想法:
如果我打印一个指针,它会给出内存中的一个地址,如果是这样,实际读取计算机内存的哪一部分?例如
#include <iostream>
using namespace std;
main()
{
int a=1;
int* ptr=&a;
int i=0;
while(1)
{
cout<<(ptr+i)<<"\t"<<*(ptr+i)<<"\n";
i++;
}
}
当我 运行 这个程序(考虑到没有错误)时,它给了我来自 &a
++ 的地址。所以它循环遍历内存中的地址,并显示存储在那里的信息。所以我的问题是,正在读取内存的哪一部分?我是否有可能访问每个内存位置(存储在每个内存中的值)?我可以通过这样的方式删除(这听起来很愚蠢)我电脑上的文件吗
ptr=nullptr;
while(1)
{
*(ptr+i)=0;
i++;
}
我不知道我的语法是否正确,虽然你明白了。我不敢运行在我的电脑上...
If I print a pointer, it gives an address in the memory, and if so, what part of the computer's memory is actually read?
解释可执行文件如何工作的最简单方法是,首先将可执行文件从 ROM(HHD and/or SSD)加载到 RAM,然后 CPU 执行。当你引用一个 valid 变量的指针时,它显示的是该变量在 RAM 中的内存地址。
When I run this program (considering no errors) it gives me addresses from &a ++. So it loops through addresses in memory, and displays the information stored there. So my question is, which part of the memory is being read?
它显示可执行文件可能不拥有的内存位置。你不应该访问你不允许的内存!通常它会触发一个运行时错误声明:Access Violation Reading Location 0x...
.
And is it possible for me to access every single memory location (the value that is stored in each)?
不,你不能也不应该。那是因为 OS 不允许您读取不属于您的内存,这是一种安全威胁。如果要访问一块内存,先分配它。
Can I delete (this sounds stupid as hell) files on my computer by sth like this
不是,文件是存在ROM中的,而你在那个指针中指向的是RAM中的。
I don't dare run this on my computer...
是的,你不应该也不能这样做,因为 OS 可能会阻止你这样做(某些操作系统可能不会严格执行,即:嵌入式系统)。
一般意义上,动态分配的space通常放置在称为堆或空闲存储的程序段中。您必须使用 new
关键字让计算机从该堆 space.
分配内存
当你运行 out of heap space,例如当你一直动态分配新的指针但不删除未使用的指针时,可能会发生这样的错误std::bad_alloc
另一方面,如果您尝试访问非动态分配的内存。 Segmentation Fault
几乎肯定会发生。
一个非常简单的示例是当您尝试读取越界数组元素时。由于 C++ 没有绑定检查。您可以轻松地冒险进入危险区域并禁止非法内存访问。在您的情况下,尝试访问分配的内存旁边但未分配给您的内存会导致分段错误。当然,你肯定可以打印内存。但是访问内存和实际改变它的内容是完全不同的事情。
代码是程序编译后应该做什么的抽象描述。这种抽象描述的基础是标准中指定的 C++ 语言。标准中没有任何内容指定获取任意指针、递增它然后取消引用它的含义(除非所有这些都发生在数组内)。官方术语是:未定义的行为。如果您编译具有未定义行为的代码,则任何事情都可能发生。这有点像强迫某人将“foofoo moomoo”从英语翻译成另一种语言。
在存在未定义行为的情况下,我们可以使用我们对 C++ 定义明确的部分的了解,并尝试推断当您编译 运行 具有未定义行为的代码时会发生什么。然而,这是徒劳的,因为有时编译器可以检测到 UB 并采取相应的行动。如果你要求编译器编译你的代码,在生成的可执行文件中根本不需要任何指针增量(因为你的指针增量和后续的取消引用是未定义的)。
如果您仍然想知道生成的可执行文件实际做了什么,您需要研究编译器的输出,例如汇编。但是,当代码具有未定义的行为时,根据编译器、版本和设置的不同,结果会大不相同,请不要感到惊讶。
当运行一个C或C++程序时,地址可以分为以下几类:
已通过报告 const 限定对象地址的语言结构使 C 或 C++ 程序可用的对象。
那些已通过语言结构提供给 C 或 C++ 程序,但不是从 const 限定对象派生的对象。
空地址。
语言实现已被环境授予独占使用权,但尚未通过语言构造提供给程序的那些。
那些语言实现一无所知。
如果代码尝试对前三类以外的任何地址执行任何操作、尝试访问前两类以外的任何地址的存储或尝试在任何地址写入存储,将会发生什么,该标准没有强加任何要求那不属于第二类。
然而,设计用于低级编程任务的实现将定义超出标准规定的行为。最值得注意的是,此类实现将“以环境的 [记录] 时尚特征”处理类型 #5 的访问,无论何时以及环境记录它的行为都会记录其行为。
例如,如果一个是 运行 一个以 Commodore 64 为目标的 C 实现,例如,一个执行 *(char volatile*)0xD020 = 2;
,这将导致屏幕边框变成红色,因为Commodore 64 记录了写入该特定地址的效果。 C 编译器对诸如“屏幕”、“边框”或“红色”之类的概念一无所知,但它不需要知道这些东西。它只会执行对地址 0xD020 的写入,硬件会通过更改颜色控制锁存器中的值来响应,这些锁存器在光栅扫描位于边界区域时随时采样。此类代码仅在定义将值 2 存储到地址 0xD020 的效果的平台上才有意义,但用于低级编程的实现不应该期望知道,因此不应该关心此类构造何时会或将会没有用,因为程序员通常比编译器作者更了解目标环境。
顺便说一句,UB 可以擦除存储介质的想法可能与以下事实有关:在某些机器上,例如 Apple //c,或任何在插槽 6(通常位置)中带有软盘控制器的 Apple II 系列机器),当软盘驱动器为 运行(包括软盘驱动器访问的第一秒左右的任何时间)时访问(甚至读取)地址 0xC0EF 将导致驱动器开始覆盖当前磁道上的数据,直到下次访问 0xC0EE 时。尽管大多数读取在大多数平台上都没有副作用,但杂散读取可能造成灾难性后果的平台不仅仅是理论上的。
不,如果你能做到这一点,那么“限制”就没有用了。
过去 - 想想:MS-DOS 和 Windows 3.1 天 - 你可以做到这一点。而且它会覆盖属于其他程序的内存,并可能导致计算机崩溃。
现代操作系统使用一种称为虚拟内存的技术,操作系统控制您的地址!每当您访问指针 0x12345678 时,CPU 都会在 页 table 中查找有关 页 0x12345 的信息。
如果该页面属于您的程序,它会将页面地址更改为页面 table 所说的内容 - 所以现在是 0xabcde678 - 并从该 RAM 芯片获取数据。这允许操作系统在您的程序不注意的情况下移动页面。前一秒0x12345678是0xabcde678,后一秒是0x54321678,分不出来
如果它不是属于您的程序的页面,则页面table中不会有新地址。在这种情况下,CPU 引发 页面错误异常 - 它调用操作系统中处理页面错误的特定函数.这个函数会发现页面错误没有充分的理由(因为有一些很好的理由)。它结束你的程序并弹出一个消息框说“这个程序遇到了问题,需要关闭”。
我在Visual Studio Community 2019测试过你的程序,打印了很多内存位置,但是几秒后,抛出一个异常:read access violation。
程序中的所有变量都是在堆栈上创建的。循环的第一次迭代显示变量 'a'(值 1)的内容,它在堆栈中,但下一次迭代显示的内存内容也属于程序的堆栈。最后尝试读取出栈内存位置,抛出异常。
在 MS-DOS 时代,尝试读取程序堆栈之外的内存不会有任何问题,因为操作系统不保护内存位置。
当我尝试使用“*(ptr + i) = 0;”写入内存位置时抛出的不是读取它们,而是写访问冲突异常。在 MS-DOS 时代,这会导致覆盖程序堆栈之外的内存位置,这会影响其他 运行 程序,可能计算机会挂起,您将不得不重新启动它,它在使用指针时出错时很常见。
所以当我学习指针时,我有了一个疯狂的想法: 如果我打印一个指针,它会给出内存中的一个地址,如果是这样,实际读取计算机内存的哪一部分?例如
#include <iostream>
using namespace std;
main()
{
int a=1;
int* ptr=&a;
int i=0;
while(1)
{
cout<<(ptr+i)<<"\t"<<*(ptr+i)<<"\n";
i++;
}
}
当我 运行 这个程序(考虑到没有错误)时,它给了我来自 &a
++ 的地址。所以它循环遍历内存中的地址,并显示存储在那里的信息。所以我的问题是,正在读取内存的哪一部分?我是否有可能访问每个内存位置(存储在每个内存中的值)?我可以通过这样的方式删除(这听起来很愚蠢)我电脑上的文件吗
ptr=nullptr;
while(1)
{
*(ptr+i)=0;
i++;
}
我不知道我的语法是否正确,虽然你明白了。我不敢运行在我的电脑上...
If I print a pointer, it gives an address in the memory, and if so, what part of the computer's memory is actually read?
解释可执行文件如何工作的最简单方法是,首先将可执行文件从 ROM(HHD and/or SSD)加载到 RAM,然后 CPU 执行。当你引用一个 valid 变量的指针时,它显示的是该变量在 RAM 中的内存地址。
When I run this program (considering no errors) it gives me addresses from &a ++. So it loops through addresses in memory, and displays the information stored there. So my question is, which part of the memory is being read?
它显示可执行文件可能不拥有的内存位置。你不应该访问你不允许的内存!通常它会触发一个运行时错误声明:Access Violation Reading Location 0x...
.
And is it possible for me to access every single memory location (the value that is stored in each)?
不,你不能也不应该。那是因为 OS 不允许您读取不属于您的内存,这是一种安全威胁。如果要访问一块内存,先分配它。
Can I delete (this sounds stupid as hell) files on my computer by sth like this
不是,文件是存在ROM中的,而你在那个指针中指向的是RAM中的。
I don't dare run this on my computer...
是的,你不应该也不能这样做,因为 OS 可能会阻止你这样做(某些操作系统可能不会严格执行,即:嵌入式系统)。
一般意义上,动态分配的space通常放置在称为堆或空闲存储的程序段中。您必须使用 new
关键字让计算机从该堆 space.
当你运行 out of heap space,例如当你一直动态分配新的指针但不删除未使用的指针时,可能会发生这样的错误std::bad_alloc
另一方面,如果您尝试访问非动态分配的内存。 Segmentation Fault
几乎肯定会发生。
一个非常简单的示例是当您尝试读取越界数组元素时。由于 C++ 没有绑定检查。您可以轻松地冒险进入危险区域并禁止非法内存访问。在您的情况下,尝试访问分配的内存旁边但未分配给您的内存会导致分段错误。当然,你肯定可以打印内存。但是访问内存和实际改变它的内容是完全不同的事情。
代码是程序编译后应该做什么的抽象描述。这种抽象描述的基础是标准中指定的 C++ 语言。标准中没有任何内容指定获取任意指针、递增它然后取消引用它的含义(除非所有这些都发生在数组内)。官方术语是:未定义的行为。如果您编译具有未定义行为的代码,则任何事情都可能发生。这有点像强迫某人将“foofoo moomoo”从英语翻译成另一种语言。
在存在未定义行为的情况下,我们可以使用我们对 C++ 定义明确的部分的了解,并尝试推断当您编译 运行 具有未定义行为的代码时会发生什么。然而,这是徒劳的,因为有时编译器可以检测到 UB 并采取相应的行动。如果你要求编译器编译你的代码,在生成的可执行文件中根本不需要任何指针增量(因为你的指针增量和后续的取消引用是未定义的)。
如果您仍然想知道生成的可执行文件实际做了什么,您需要研究编译器的输出,例如汇编。但是,当代码具有未定义的行为时,根据编译器、版本和设置的不同,结果会大不相同,请不要感到惊讶。
当运行一个C或C++程序时,地址可以分为以下几类:
已通过报告 const 限定对象地址的语言结构使 C 或 C++ 程序可用的对象。
那些已通过语言结构提供给 C 或 C++ 程序,但不是从 const 限定对象派生的对象。
空地址。
语言实现已被环境授予独占使用权,但尚未通过语言构造提供给程序的那些。
那些语言实现一无所知。
如果代码尝试对前三类以外的任何地址执行任何操作、尝试访问前两类以外的任何地址的存储或尝试在任何地址写入存储,将会发生什么,该标准没有强加任何要求那不属于第二类。
然而,设计用于低级编程任务的实现将定义超出标准规定的行为。最值得注意的是,此类实现将“以环境的 [记录] 时尚特征”处理类型 #5 的访问,无论何时以及环境记录它的行为都会记录其行为。
例如,如果一个是 运行 一个以 Commodore 64 为目标的 C 实现,例如,一个执行 *(char volatile*)0xD020 = 2;
,这将导致屏幕边框变成红色,因为Commodore 64 记录了写入该特定地址的效果。 C 编译器对诸如“屏幕”、“边框”或“红色”之类的概念一无所知,但它不需要知道这些东西。它只会执行对地址 0xD020 的写入,硬件会通过更改颜色控制锁存器中的值来响应,这些锁存器在光栅扫描位于边界区域时随时采样。此类代码仅在定义将值 2 存储到地址 0xD020 的效果的平台上才有意义,但用于低级编程的实现不应该期望知道,因此不应该关心此类构造何时会或将会没有用,因为程序员通常比编译器作者更了解目标环境。
顺便说一句,UB 可以擦除存储介质的想法可能与以下事实有关:在某些机器上,例如 Apple //c,或任何在插槽 6(通常位置)中带有软盘控制器的 Apple II 系列机器),当软盘驱动器为 运行(包括软盘驱动器访问的第一秒左右的任何时间)时访问(甚至读取)地址 0xC0EF 将导致驱动器开始覆盖当前磁道上的数据,直到下次访问 0xC0EE 时。尽管大多数读取在大多数平台上都没有副作用,但杂散读取可能造成灾难性后果的平台不仅仅是理论上的。
不,如果你能做到这一点,那么“限制”就没有用了。
过去 - 想想:MS-DOS 和 Windows 3.1 天 - 你可以做到这一点。而且它会覆盖属于其他程序的内存,并可能导致计算机崩溃。
现代操作系统使用一种称为虚拟内存的技术,操作系统控制您的地址!每当您访问指针 0x12345678 时,CPU 都会在 页 table 中查找有关 页 0x12345 的信息。
如果该页面属于您的程序,它会将页面地址更改为页面 table 所说的内容 - 所以现在是 0xabcde678 - 并从该 RAM 芯片获取数据。这允许操作系统在您的程序不注意的情况下移动页面。前一秒0x12345678是0xabcde678,后一秒是0x54321678,分不出来
如果它不是属于您的程序的页面,则页面table中不会有新地址。在这种情况下,CPU 引发 页面错误异常 - 它调用操作系统中处理页面错误的特定函数.这个函数会发现页面错误没有充分的理由(因为有一些很好的理由)。它结束你的程序并弹出一个消息框说“这个程序遇到了问题,需要关闭”。
我在Visual Studio Community 2019测试过你的程序,打印了很多内存位置,但是几秒后,抛出一个异常:read access violation。 程序中的所有变量都是在堆栈上创建的。循环的第一次迭代显示变量 'a'(值 1)的内容,它在堆栈中,但下一次迭代显示的内存内容也属于程序的堆栈。最后尝试读取出栈内存位置,抛出异常。 在 MS-DOS 时代,尝试读取程序堆栈之外的内存不会有任何问题,因为操作系统不保护内存位置。
当我尝试使用“*(ptr + i) = 0;”写入内存位置时抛出的不是读取它们,而是写访问冲突异常。在 MS-DOS 时代,这会导致覆盖程序堆栈之外的内存位置,这会影响其他 运行 程序,可能计算机会挂起,您将不得不重新启动它,它在使用指针时出错时很常见。