[英]C: Address on register or memory?
我对C中的关键字“寄存器”感到有些困惑。似乎告诉编译器应该将其存储在寄存器中,这意味着我的某些变量存储在寄存器中,有些存储在内存中? 如果是这样,是否有办法找出我的值是存储在寄存器中还是存储在存储器中?
例如:
int *x = (int*) 0x1234;
X现在似乎没有指向寄存器,因为此类地址用于存储。 但是我已经尝试了好几次来寻找一个不同的地址(也使用“注册”关键字)。 即使在互联网上,似乎也没有人关心。
所以我的主要问题是:指针中的地址指向寄存器时的外观如何?
编辑:在另一个问题中,我在主要问题末尾找不到答案。 我的问题不是关于关键字“注册”的,我刚才提到了它。
指针中的地址指向寄存器时的外观如何?
指针是内存的概念,寄存器没有地址。 每个处理器都有一个有限的固定数量的寄存器(可能为8-16个)。
正如其他人所提到的, register
不再是真正有用的,甚至有时被编译器忽略。
要了解真正的寄存器,请考虑以下示例:
int a = k / 53; // k is an int defined somewhere else...
int b = a * 9;
// a is not used after the above line
现在,我们还没有宣布a
与register
,但任何合理的编译器将仍然保持在寄存器中。
这样做的原因是要执行任何操作,操作数必须在某些寄存器中。 然后,运算结果将例如存储在第一个操作数的寄存器中。
为了从上面的示例中计算a
,编译器将编写代码以将k
(可能在内存中)加载到某个寄存器中(让我们将其称为寄存器A),然后将53加载到另一个寄存器中。 计算完成后,寄存器A将包含运算结果。 因为无论如何我们要在下一行将结果乘以9,所以我们可以保持原样,将9加载到另一个寄存器中并相乘。 将值存储到内存中,然后再将其加载回寄存器将浪费大量时间。
请注意,使用volatile
声明a
会阻止此类优化,并迫使编译器实际存储和加载a
。 (尽管volatile
在这里对于此示例完全没有任何意义,并且如果您不使用例如特殊的硬件接口,则几乎没有用。)
存储类说明符 register
是在C语言的第一个发行版上创建的,用于指示编译器使用处理器寄存器中的一个来保存具有允许更快访问的范围的变量。 那时,代码编写需要程序员特别注意,因为编译器并不聪明,无法检测到错误或纠正错误,从而导致错误代码。
无论如何,在成长的C标准中,有些技巧使一些细节难以理解。
存储类说明符register
就是其中之一。 因此,让我们开始查看规格,然后再对其进行评论。
根据ISO / IEC 9899:2011 ,C11标准:
§6.7.1存储类说明符
使用存储类说明符寄存器声明对象的标识符表明,对对象的访问应尽可能快。 这些建议有效的程度由实施定义。 (见注119)
和注释119:
注意
该实现可以将任何寄存器声明简单地视为自动声明。 但是,无论是否实际使用了可寻址存储,都无法显式(通过使用6.5.3.2中讨论的一元
&
运算符)或隐式地(通过存储类说明符寄存器声明的对象的任何部分的地址)进行计算。通过将数组名称转换为指针,如6.3.2.1中所述)。 因此,唯一可应用于用存储类说明符寄存器声明的数组的运算符是sizeof。
这意味着存储说明符register
是对编译器的建议 , 建议编译器使用处理器寄存器或任何其他方式以使变量访问尽可能快,但是编译器可以做出不同的决定。 可以将声明的变量视为标准auto
变量。
在最后一种情况下,从技术上来说可以获得存储地址,但是如注释中所述, 该地址被标准明确禁止,但在第6.5.3.2节“地址和间接运算符”的约束中也有强制执行:
一元
&
运算符的操作数应为函数指定符,[]
或一元*
运算符的结果,或者是指定不是位字段且未用register
存储类说明符声明的对象的左值。
当然,某些不兼容的编译器可能会让您应用运算符。
现在回到您的问题:
int *x = (int*) 0x1234;
X现在似乎没有指向寄存器,因为此类地址用于存储。 但是我已经尝试了好几次来寻找一个不同的地址(也使用“注册”关键字)。 即使在互联网上,似乎也没有人关心。
您的示例错误 ,您在声明一个指向int的指针并为其分配一个任意地址。 这与register
存储说明符无关。
声明register int *x = (int*) 0x1234;
然后尝试应用&
操作者x
在int **pp = &x;
(请注意,我们声明了一个指向int
的指针,这就是我们获取一个指向int
的指针的地址)。
在兼容的编译器上会出现错误。
所以我的主要问题是:指针中的地址指向寄存器时的外观如何?
答案很简单: 它与任何东西都不相似,因为它在标准C中不存在 。
在现代编译器中,您在C中声明的对象不一定具有其所在的单个位置。 为了优化您的程序,编译器有时会将其保存在寄存器中,有时将其保存在堆栈中,或者将其保存在特定的内存位置中。 通常,您不需要知道哪个。
当您获取对象的地址并使用指针时,编译器使您的程序像对象确实具有固定地址一样工作。 通常,这是通过至少在使用对象地址的时间内将对象放置在指定的内存位置中来完成的,但是允许编译器通过其他方式达到程序的最终结果。
如果用register
声明一个对象,那么按照C标准,您不应该使用它的地址。 但是,某些编译器可能允许这样做。
在大多数体系结构中,您不能指向CPU寄存器,因为它们不是内存映射的。 (8051是我可以想到的一种体系结构,其中核心寄存器是在内存中映射的)。
另一方面,如果您指的是外设寄存器 ; 它们是内存映射的,并且在地址空间中有地址,并且声明将与您所声明的一样—除非您确实需要volatile类型修饰符。
volatile int* x = (volatile int*)0x1234 ;
register
关键字与内存映射的外围设备寄存器无关。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.