我正在阅读一些关于C语言中的内存地址和数据类型的论文,我一直在很好地掌握内容,但我偶然发现了一些数学,我似乎无法在互联网上找到它.
假设我创建了两个局部变量
void foo(void){
int i, j;
printf("int i is stored in %p\n", &i);
printf("int j is stored in %p\n", &j);
}
Run Code Online (Sandbox Code Playgroud)
我得到的输出是
int i is stored in 0x6ffc88
int j is stored in 0x6ffc84
Run Code Online (Sandbox Code Playgroud)
在我正在阅读的文本中,整数大小为4个字节(与我的计算机相同).所以,int i
应该分配4个字节.但是int i
和int j
内存地址之间的差异只有4位(0100
).
差异不是更大的东西,比如4个字节(0xffffffff
)吗?
请问我在哪里制造混乱?
我想声明一个指针,让它保存一个自定义地址,然后为其赋值:
void main()
{
char *ptr;
ptr = (char *)0x123123; //the assignment works perfectly with a cast
printf("%p\n", ptr); //and the pointer indeed holds the address it's supposed to
*ptr = 'a'; //but this breaks
puts("2");
}
Run Code Online (Sandbox Code Playgroud)
最初我认为原因是因为我试图取消引用未初始化的内存.但我确实怀疑是这种情况,因为这some_type *some_ptr = &some_variable;
完美无瑕,所以交易必须是我指定的地址.
后来我想,以同样的方式3
或'a'
或者"alpine"
是常数,(char *) 0x123123
必须是一个常量了.并且const
-s不能在C中编辑,但仍然不能,因为尝试更改const
值将无法编译.
第三个假设是这样的地址必须是不可用的,但这也没有意义,因为第4行总是工作,无论我给出的地址或类型pointer
.
基本上我想要实现的是使用指针,避免new
关键字来获得与其他语言相同的复制行为(=
如果我们谈论对象,运算符会将右操作数的 ref 分配给左操作数,类似于shared_ptr
应该提供)。如果我不使用数组运算符,我要展示的内容似乎有效[]
。为什么会发生这种情况,我该如何解决?
// Declaration way before
std::vector<int>* test;
test = &std::vector<int>();
(*test)[1] = 0;
Run Code Online (Sandbox Code Playgroud)
// Declaration way before
std::map<std::string, int>* test;
test = &std::map<std::string, int>();
(*test)["a"] = 0;
Run Code Online (Sandbox Code Playgroud)
错误发生在两者的最后一行。
每当我使用 C++ 时,我都可以一次“引用”多个字节。例如:
char* charptr = new char;
Run Code Online (Sandbox Code Playgroud)
此行在堆栈上分配一个新变量,即指针charptr
,该变量指向堆上的 1 个字节的数据。这是有道理的,因为我的计算机上的物理地址可以存储 1 个字节。
然而,混乱是从这样的一行代码开始的:
int* intptr = new int;
Run Code Online (Sandbox Code Playgroud)
该指针仅包含 8 字节整数的单个地址。
一个地址如何存储多个字节?
我尝试在 StackOverflow 上搜索,但找不到答案,我想知道这是如何工作的(我正在尝试制作一个小人计算机风格的模型,并希望为其实现一种高级语言)。
32位系统中的字大小为4字节.所以char a ='bcd'; 已验证.4个ASCII字符可以容纳32位整数.
char a='bcd';
//output is d for this line
printf("%c\n",a);
//output is 'z' when i use *(&a-1) and '?'(is -67 when manipulated)
printf("%c\n",*(&a-1));
Run Code Online (Sandbox Code Playgroud)
现在我在这里只是想知道会有什么&a-1 (this is not 'b')
意义?它的最后一个内存地址?还有一件事是字符被打包成的顺序int
.
------------------------------------------------------------------------------
| bits : _ _ _ _ _ _ _ 8 _ _ _ _ _ _ _ 16 _ _ _ _ _ _ _ 24 _ _ _ _ _ _ _ 32 |
| order : ? ? ? ? ? …
Run Code Online (Sandbox Code Playgroud) int *i = new int;
cout << &i << endl << i;
delete i;
i = 0;
Run Code Online (Sandbox Code Playgroud)
我得到这个输出:
0031FB2B
0057C200
为什么2个不同的地址 是不是&引用动态指针的地址,而i本身是指针的地址,它应该是相同的地址?
是否可以在GCC中做这样的事情?
void foo() {
if (something()) returnSomewhereElse;
else return;
}
void bar() {
foo();
return; // something failed, no point of continuing
somewhereElse:
// execution resumes here if something succeeds
// ...
}
Run Code Online (Sandbox Code Playgroud)
只是为了澄清意图 - 它是关于错误处理.这个例子是一个最小的例子,只是为了说明事情.我打算在更深层次的上下文中使用它,以便在发生错误时停止执行.我还假设状态没有改变,我可能错了,因为在两个返回点之间没有添加额外的局部变量,所以我希望编译器生成的代码在foo的返回时可以重用为此节省了使用longjmp
,设置和传递跳转缓冲区的开销.
这个例子"确实有意义",因为它的目的是展示我想要实现的目标,而不是为什么以及如何在实际代码中有意义.
为什么你的想法更简单,只需从foo()返回一个值并让bar()返回或执行somewhereElse:conditionally?
它并不简单,你所建议的不适用于实践,只是在一个简单的例子的背景下,但它更好,因为:
1 - 它不涉及额外返回值
2 - 它不涉及额外检查值
3 - 它不涉及额外的跳跃
我可能错误地认为目标应该在这一点上清楚,并且在所有澄清和解释之后.我们的想法是从深度调用链中提供"转义代码路径",而无需任何额外开销.通过重用编译器生成的代码来恢复前一个调用帧的状态,并简单地修改函数返回后执行恢复的指令.成功跳过"转义代码路径",发生的第一个错误进入它.
if (failure) return; // right into the escape code path
else {
doMagickHere(); // to skip the escape code path
return; // skip over the …
Run Code Online (Sandbox Code Playgroud)