如何在C语言中获取指针的实际地址?

3
背景:
我正在为一项作业任务编写一个单级缓存模拟器,需要使用C语言,并且必须使用已有的代码。在我们讨论缓存时,我们被告知小缓存可以容纳大地址的方法是将大地址分成缓存中的位置和标识标签。也就是说,如果你有一个8个插槽的缓存,但想存储一个地址大于8的东西,你需要取右侧3位(因为2^3=8),并将数据放在该位置上;例如,如果你有地址22,二进制为10110,则取这3个最右边的位110(十进制为5),并将其放在缓存的第5个插槽中。你还需要在这个位置上存储标记,即剩余的位数10。

其中一个函数cache_load接受一个整数指针作为单一参数。所以实际上,我被给予了这个int* addr,它是一个实际的地址并指向某个值。为了将此值存储在缓存中,我需要拆分addr。然而,编译器不喜欢我直接使用指针。例如,我尝试通过以下方式获取位置:
npos=addr%num_slots

编译器会报错。我尝试将其转换为int类型,但这实际上给我了指针指向的值,而不是数值地址本身。任何帮助都将不胜感激,谢谢!

[编辑]

int load(int * addr) { 
  int value = (use_memory ? (*addr) : 0);
  intptr_t taddr=(intptr_t) addr;
  int npos=taddr % blocks;
  int ntag=taddr / blocks;
  printf("addr is %p, taddr is %p, npos is %d and ntag is %d\n",addr,taddr,npos,ntag);

当传入addr时,它的实际地址为58,并且它指向值88。我从printf中得到的输出是:
addr is 58, taddr is 58, npos is 0 and ntag is 11
所以似乎taddr得到了58(用%p打印时仍然显示88,用%d打印时显示88),但npos和ntag显示为0和11(好像数学运算是在88上进行的),而不是我想要的2和7。
代码的用法如下:
void load_words (int n, int words[]) {
  int i;
  for (i=0; i<n; i++) {
    load( (int *) (words[i] * 4));
    cache_print();
  }
}

2
将指针转换为整数不会给你它所指向的值。然而,通常应该转换为intptr_t。另外,请发布一些实际的代码。 - Matthew Flaschen
我现在放了一些代码,以及我得到的输出。我希望taddr与addr(58)是相同的,这样我就可以在其上使用模数和除法运算符。[编辑] 如果我尝试进行强制转换,则会出现“intptr_t未声明(此函数中首次使用)”。 - Joseph
1
blocks只是一个整数。我刚刚更新了原始帖子中的代码,并附上了新的输出结果。 - Joseph
2
@Airjoe 嘿。初学者的错误:%p 以十六进制(基数为16)而不是十进制(基数为10)打印输出,与 %d 不同。 - Earlz
1
那个pastebin链接是我的教授给我的“好”代码,没错。我有强烈的感觉我的教授可能不在状态,因为他通常是一位Java教授,而这学期正在教授低级计算机组织课程。我真的不明白他为什么不使用&或者为什么要乘以4。现在,我打算将其标记为已解决,直到我与他讨论。谢谢大家的帮助,抱歉耽误了时间。 - Joseph
显示剩余10条评论
5个回答

4

你应该使用类似于以下的内容:

int *pointer;
intptr_t address=(intptr_t) pointer;

这主要是对 @Andrew 帖子的更正。

所以你的函数应该变成:

(哎呀,我忘记这是作业了。我马上会回滚这个问题。不能直接给你答案 :) )


我按照描述更新了我的代码,但仍然得到以下输出: addr为58,taddr为88,npos为0,ntag为11。而我期望的是npos为2,ntag为7。 - Joseph
@Air 如果你访问http://www.easycalculation.com/hex-converter.php并输入`58`,它将变成88。实际上这些值是相同的,只是以不同的进制显示。 - Earlz
1
我非常确定这是将指针类型转换为整数类型的最标准方法。+1。 - dreamlax

4

C99标准规定指针类型和整数类型之间的转换是实现定义行为(6.3.2.3.5和6.3.2.3.6),除非使用在 <stdint.h> 中定义的 intptr_tuintptr_t,不过7.18.1.4说这两个类型并不是强制性的,实现无需提供它们。


0
int *pointer;
uint32_t address = (uint32_t) pointer;

当然,除非你有保证int*适合于uint32_t,否则你需要为你的平台选择正确的类型。


我该如何选择适合平台的正确类型?我收到了 "`uint32_t' undeclared (first use in this function)" 的错误提示,我认为这意味着它不是正确的类型。 - Joseph
1
@Airjoe,请确保你包含了<stdint.h> - dreamlax

0

十进制数88是0x58(十六进制数58)。当您在printf中使用%p格式说明符时,将打印出十六进制表示。因此,addr和taddr都是十进制数88,您的计算是正确的。


对的,Earlz在上面已经解释过了。所以58不是我想象中的那样,我的计算仍然是错误的。(我以为58是实际内存地址,我想知道为什么它是这么小的数字...)。我需要拆分实际的内存地址,但现在我似乎完全找不到了。 - Joseph
“%p”格式说明符的规范指出:“参数应为指向void的指针。指针的值将以实现定义的方式转换为一系列打印字符。”它不必以十六进制打印,也可以以八进制或十进制打印,但十六进制可能是最常见的表示形式。 - dreamlax
这里有些不对劲。参数addr从哪里来?你传递了什么给函数?我怀疑你正在做这个: int * value = 88; load(value);相反,你应该这样做: int value = 58; load(&value);这将把变量value的地址传递到你的函数中。 - Ray

0
这里有一件重要的事情尚未提及。当您使用%p指示符打印指针时,应对参数进行强制转换:
printf("addr is %p\n", (void *)addr);

%p 需要一个指向 void 的指针,printf 的格式说明符/参数不匹配是未定义的行为。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接