C语言中的无符号指针

vh0rcniy  于 2023-01-20  发布在  其他
关注(0)|答案(4)|浏览(345)

我的一个朋友给了我这个问题,并要求我帮助它:在运行这段代码后,指针p中存储的地址是什么?

unsigned * p = (unsigned*)1000;
p += 10;

我刚刚在CodeBlocks上添加了printf("%u", p);,结果是1040。
(unsigned*)1000是什么,它是什么意思?printf("%u", p)是打印记忆地址的正确方法吗?还是需要使用另一种语法/另一种格式说明符?为什么答案是1040,而不是1010?
主要的问题是,给出的唯一的几行是:

unsigned * p = (unsigned*)1000;
p += 10;

基于这些,我做了一点google的研究,我认为正确的获取地址的方法是printf("%u", p);,即使这是正确的,这是正确的语法,我仍然不能理解这个添加背后的过程。

w3nuxt5m

w3nuxt5m1#

指针本身不像整数值那样是“有符号”或“无符号”的。
对于p的定义,您要做的是定义一个名为p的指针变量,它指向一个unsigned int值,并且您说它将指向地址1000(这对于大多数系统来说没有意义)。
对于p += 10,使p指向离其原始位置更远的10个unsigned int元素,假设公共sizeof(unsigned int) == 4,则在增量之后p将指向地址10401000 + (10 * sizeof(unsigned int)))。
(unsigned*)1000是一个普通的强制转换,它告诉编译器值1000是一个指向unsigned int的指针,如上所述,这在大多数系统上没有意义。

tsm1rwdh

tsm1rwdh2#

  • unsigned * p = (unsigned*)1000这是一个由实现定义的从整数到指针的转换。它是否工作以及如何工作取决于目标系统和编译器端口。您将得到地址1000 = 0x3E8。按照惯例,地址总是以十六进制书写。
  • p += 10;这可能会调用未定义的行为。严格地说p必须指向一个已分配的数组,否则您无法在其上执行指针运算。实际上,大多数编译器都为此类运算提供了定义良好的编译器扩展,否则编写低级C代码会变得很麻烦。

至于它做什么,它是 * 指针算术 *(在初级学习材料中查找该术语),基本上整个代码等价于unsigned* p = (unsigned*)(1000 + 10*sizeof(int));

  • printf(“%u”,p)是正确的打印方式吗

不,这是错误的和未定义的行为。请注意,unsigned等于unsigned int,但您并没有打印指针指向的内容,您打印的是指针地址本身。
你需要打印这样的指针地址:printf("%p", (void*)p)。始终使用%p,始终强制转换为void*。您应该获得0x410作为输出。

piztneat

piztneat3#

关于指针算术,已经有很好的答案了,所以我将集中讨论您问题的另一部分。

    • 第一个问题:**

printf(" % u",p)是否是打印记忆地址的正确方式

    • 答复:***否 *
    • 第二个问题:**

或者是否存在需要使用的另一语法/另一格式说明符?

    • 答案**:是的,正确的格式说明符是%p,并且指针必须强制转换为void *
    • 来自C11:**

p
参数应该是一个指向void的指针。指针的值以一种实现定义的方式转换为一个打印字符序列。

printf("%p", (void *) p);

由于%p需要一个void指针,因此向它传递指向任何其他类型的指针都会调用未定义的行为。

    • 来自C11:**

7.21.6.1 The fprintf function
如果转换规范无效,则行为未定义。如果任何参数不是对应转换规范的正确类型,则行为未定义。

mrfwxfqh

mrfwxfqh4#

unsigned * p = (unsigned*)1000;

创建类型为“指针指向unsigned int“的指针变量p,并将值10000x03E8)赋给它。由于不能直接将整型常量赋给指针类型,因此必须执行(unsigned *)强制转换。

p += 10;

这相当于p = p + 10。当你把一个整数i加到指针p上时,结果值是一个指针,指向p所指向的对象之后的被指向类型的第i个 * 对象 *。在大多数现代系统中,unsigned int是4字节宽。因此,p之后的下一个unsigned int对象将在地址10040x03EC)处开始,其后的对象将在10080x03F0)处开始,等等:

+---+
0x03E8: |   | <--- p
        +---+
0x03E9: |   |
        +---+
0x03EA: |   |
        +---+
0x03EB: |   |
        +---+
0x03EC: |   | <--- p + 1
        +---+
0x03ED: |   | 
        +---+
0x03EE: |   |
        +---+
0x03EF: |   |
        +---+
0x03F0: |   | <--- p + 2
        +---+
         ...
        +---+
0x0410  |   | <--- p + 10
        +---+
         ...

因此第10个unsigned int对象将开始于地址0x04101040)。
指针的正确格式说明符是%p,而不是%u;可以使用%u打印p指向的无符号值:

printf( " p = %p\n", (void *) p );
printf( "*p = %u\n", *p );

现在,在这个特定的例子中,pp+10都是 invalid 指针(它们在对象的生命周期中不指向对象),所以试图通过它们读写会导致未定义的行为。
下面是一个完整的程序,应该说明以上几点:

#include <stdio.h>

int main( void )
{
  unsigned int a[20];
  unsigned int *p = a; // sets p to point to the first element of a,
                       // rather than some random address
  
  for ( size_t i = 0; i < sizeof a / sizeof a[0]; i++ )
    a[i] = i;

  /**
   * Prints the *value* of p, which is a pointer.  The %p conversion
   * specifier expects a pointer to void (void *); this is probably
   * the only place in C where you need to *explicitly* cast
   * a pointer value to void *.
   */
  printf( " p = %p\n", (void *) p );  

  /**
   * Prints the value of what p *points to*, which is an unsigned
   * integer.  
   */
  printf( "*p = %u\n", *p );

  /**
   * Prints the value of p + 10, which is the address
   * of the 10'th object in the array.
   */
  printf( " (p + 10) = %p\n", (void *) (p + 10) );

  /**
   * Prints the value of the 10th object in the array.
   * The array subscript operation p[10] is *defined* 
   * as *(p + 10).  
   */
  printf( "*(p + 10) = %u\n", *(p + 10) );
  printf( " p[10]    = %u\n", p[10] );

  return 0;
}

相关问题