简体   繁体   English

C中的递增指针地址和值

[英]Increment pointer address and value in C

int main (void){
    int num1=2;
    int *pnum=NULL;
    pnum=&num1;
    *pnum++;
    printf("%d",*pnum);
    
}

why does this code print the address, not the value?为什么这段代码打印的是地址,而不是值? Doesn't * dereference the pnum? * 不取消引用 pnum 吗?

Check the precedence of operators: postfix unary operators bind tighter than prefix unary operators, so *pnum++ is equivalent to *(pnum++) , not to (*pnum)++ .检查运算符的优先级:后缀一元运算符比前缀一元运算符绑定更紧密,因此*pnum++等效于*(pnum++) ,而不是(*pnum)++

pnum++ increments the pointer pnum and returns the old value of pnum . pnum++增加指针pnum并返回pnum的旧值。 Incrementing a pointer makes it point to the next element of an array.递增指针使其指向数组的下一个元素。 Any variable can be treated as an array of one element, so pnum points to the element after the first in the one-element array located where num1 is, which I'll call num1_array[1] .任何变量都可以被视为一个元素的数组,因此pnum指向位于num1所在的单元素数组中第一个元素之后的元素,我将其称为num1_array[1] It is valid for a pointer to point to the end of an array, ie one position past the last element.指向数组末尾的指针是有效的,即一个 position 超过最后一个元素。 It is not valid to dereference that pointer: that would be an array overflow.取消引用该指针是无效的:那将是数组溢出。 But it is valid to calculate the pointer.但是计算指针是有效的。 Constructing an invalid pointer in C is undefined behavior , even if you don't dereference it;在 C 中构造一个无效的指针是未定义的行为,即使你不取消引用它; however this pointer is valid.但是这个指针是有效的。

*pnum++ dereferences the old value of pnum . *pnum++取消引用pnum的旧值。 Since that was a pointer to num1 , this expression is perfectly valid and its value is the value of num1 .因为那是一个指向num1的指针,所以这个表达式是完全有效的,它的值就是num1的值。 At this point, any halfway decent compiler would warn that the value is unused.此时,任何体面的编译器都会警告该值未使用。 If you didn't see this message, configure your compiler to print more warnings: unfortunately, many compilers default to accepting bad code rather than signal the badness.如果您没有看到此消息,请将您的编译器配置为打印更多警告:不幸的是,许多编译器默认接受错误代码而不是发出错误信号。 For example, with GCC or Clang:例如,对于 GCC 或 Clang:

$ gcc -Wall -Wextra -Werror a.c
a.c: In function ‘main’:
a.c:6:5: error: value computed is not used [-Werror=unused-value]
    6 |     *pnum++;
      |     ^~~~~~~
cc1: all warnings being treated as errors

The call to printf receives the argument *pnum .printf的调用接收参数*pnum We saw before that at this point, pnum points to the end of the one-element array num1_array[1] .我们之前看到,此时pnum指向单元素数组num1_array[1]的末尾。 This pointer is valid, but since it points to the end of an object, dereferencing has undefined behavior.此指针是有效的,但由于它指向 object 的末尾,因此取消引用具有未定义的行为。 In practice, this usually either crashes or prints some garbage value that happens to be in a particular memory location.在实践中,这通常会崩溃或打印一些恰好位于特定 memory 位置的垃圾值。 When you're debugging a program, there are tools that can help by making it more likely that an invalid pointer will cause a crash rather than silently using a garbage value.在调试程序时,有一些工具可以帮助您使无效指针更有可能导致崩溃,而不是默默地使用垃圾值。 For example, with GCC or Clang, you can use AddressSanitizer :例如,对于 GCC 或 Clang,您可以使用AddressSanitizer

$ export ASAN_OPTIONS=symbolize=1
$ gcc -Wall -Wextra -fsanitize=address a.c && ./a.out 
a.c: In function ‘main’:
a.c:6:5: warning: value computed is not used [-Wunused-value]
    6 |     *pnum++;
      |     ^~~~~~~
=================================================================
==2498121==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fff15ae3e74 at pc 0x55d593978366 bp 0x7fff15ae3e30 sp 0x7fff15ae3e20
READ of size 4 at 0x7fff15ae3e74 thread T0
    #0 0x55d593978365 in main (/tmp/stackoverflow/a.out+0x1365)
    #1 0x7f525a1380b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
    #2 0x55d59397818d in _start (/tmp/stackoverflow/a.out+0x118d)

Address 0x7fff15ae3e74 is located in stack of thread T0 at offset 36 in frame
    #0 0x55d593978258 in main (/tmp/stackoverflow/a.out+0x1258)

  This frame has 1 object(s):
    [32, 36) 'num1' (line 3) <== Memory access at offset 36 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow (/tmp/stackoverflow/a.out+0x1365) in main
Shadow bytes around the buggy address:
  0x100062b54770: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100062b54780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100062b54790: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100062b547a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100062b547b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x100062b547c0: 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1[04]f3
  0x100062b547d0: f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100062b547e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100062b547f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100062b54800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100062b54810: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==2498121==ABORTING

This trace tells you:此跟踪告诉您:

  • That there was a buffer overflow in a local variable ( stack-buffer-overflow ).局部变量( stack-buffer-overflow )中存在缓冲区溢出。
  • That the overflowing access was an attempt to read 4 bytes ( READ of size 4 ).溢出访问是试图读取 4 个字节( READ of size 4 )。
  • Some more information about the problematic address ( [32, 36) 'num1' ).有关有问题的地址的更多信息( [32, 36) 'num1' )。 You can see that the program tried to access memory just after num1 .您可以看到程序试图在num1之后访问 memory 。
  • The address of the problematic instruction ( #0 0x55d593978365 ).有问题的指令的地址( #0 0x55d593978365 )。 You can set a breakpoint there in a debugger to examine what the program might be doing.您可以在调试器中设置断点来检查程序可能在做什么。

On most platforms, given your program, num1 is a variable on the stack , and the end of num1 is the address of the previous variable on the stack.在大多数平台上,给定您的程序, num1堆栈上的变量,而num1的结尾是堆栈上前一个变量的地址。 This could be anything, depending on the details of how your compiler accesses memory.这可以是任何东西,具体取决于编译器如何访问 memory 的详细信息。 One of the many things this could be is pnum , if pnum and num1 happen to have the same size on your platform (this is typically the case on 32-bit platforms) and the compiler decides to put pnum just before num1 on the stack (this depends heavily on the compiler, the optimization level, and fine details of the program).这可能是pnum的许多事情之一,如果pnumnum1在您的平台上碰巧具有相同的大小(这通常是 32 位平台上的情况)并且编译器决定将pnum放在堆栈上的num1之前(这在很大程度上取决于编译器、优化级别和程序的细节)。 So it is plausible for your program to print the address of pnum : not because *pnum somehow didn't invoke the dereference operator, but because your program has made pnum point to itself.因此,您的程序打印pnum的地址是合理的:不是因为*pnum以某种方式没有调用取消引用运算符,而是因为您的程序已使pnum指向自身。

Postfix operators always have higher precedence than prefix operators in C.在 C 中,后缀运算符的优先级始终高于前缀运算符。 So *pnum++ is equivalent to *(pnum++) -- it increments the pointer, not the value pointed at.所以*pnum++等价于*(pnum++) - 它增加指针,而不是指向的值。

You need (*pnum)++ or ++*pnum if you want to increment the pointed at value.如果要增加指向的值,则需要(*pnum)++++*pnum

You need to put parenthesis around *pnum .您需要在*pnum周围加上括号。 Otherwise the address which the pointer points to is changed.否则指针指向的地址被改变。 pnum++ is pointer arithmetic and increments the pointer by the number equal to the size of the data type for which it is a pointer, in this case sizeof(int) . pnum++指针算术,并将指针增加等于它是指针的数据类型大小的数字,在这种情况下为sizeof(int) This means it doesn't point to the correct value anymore and the program therefore prints “garbage” as mentioned by Eugene Sh.这意味着它不再指向正确的值,因此程序会打印Eugene Sh提到的“垃圾” in the comments, because it dereferences the incremented pointer using *(pnum++) .在评论中,因为它使用*(pnum++)取消引用递增的指针。

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int num1 = 2;
    int *pnum = &num1;
    (*pnum)++; /* important */
    printf("%d\n", *pnum);

    return EXIT_SUCCESS;
}

When you write *pnum++;当你写*pnum++; There, you change the address pnum is pointing.在那里,您更改 pnum 指向的地址。 And incrementing it by one, we don't really know where it has started to point.将它加一,我们真的不知道它开始指向哪里。 As suggested above, it's referencing garbage right now.如上所述,它现在正在引用垃圾。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM