为int *动态分配100个字节,然后尝试使用指针算法为其设置值有什么影响?

时间:2016-12-13 14:53:58

标签: c pointers malloc pointer-arithmetic

在我最近回来的一个测试中,它有一个问题,“假设这个代码编译,它会做什么?”

代码:

int *ptr

ptr = (int *) malloc( 25 * sizeof(int)); //100 bytes

*ptr = 'x';
*(ptr + 1) = 'x';
.... //go through all the values from 1 to 99 as well
*(ptr +99) = 'x';

我编写了代码并运行它,当用printf(%d, *x)打印时,结果为120,即x的ascii值。我知道int必须被设置为x,并且当打印为asc时打印ascii值,但是当涉及到malloc的实际效果时,我感到难过,以及所有{ {1}}确实如此。

4 个答案:

答案 0 :(得分:2)

在C中,数组和指针非常相似,为简单起见,在这种情况下,将它们视为相同是很方便的。所以,您可以将malloc视为动态分配25个整数的数组(例如,动态地说int ptr[25]),或者您可以将其视为阻塞内存中的25个连续整数地址并将它们标记为有效。这样,ptr == &ptr[0]。解除引用运算符*意味着“更改存储在此地址的值”,它基本上“撤消”&运营商。所以,*ptr == *(&ptr[0]) == ptr[0]。此命令只是将ptr [0]设置为等于'x',其ASCII值为120(并将打印为ASCII值,因为该数组的类型为'int'而不是'char'类型)。其余的任务也是这样做的。根据您的编译器和操作系统,ptr + 24以上的任何内容都可能会出现分段错误或无效写入,因为您只分配了25个整数,因此(ptr + 99)不应该是可写地址。如果您只分配了25个插槽,则无法编辑ptr[99]

答案 1 :(得分:1)

malloc()的实际效果,使语句*ptr = 'x';和子句访问实际上有效。

如果没有内存分配,尝试取消引用指针将调用undefined behavior

那就是说,

  • 在尝试取消引用返回的指针之前,必须检查malloc()是否成功。
  • 指针算术尊重数据类型。所以,像(ptr + 1)这样的表达式指向下一个整数的内存位置,而不是内存的下一个字节。因此,表达式n > 24的RHS的任何(ptr + <n>)都将调用UB。
  • 25 * sizeof(int) == 100 bytes的假设是非常具体的实现。如果sizeof(int)小于4个字节,您最终将在指针算术中访问超出范围的内存(即使您将指针别名为char*,也可以考虑)。

答案 2 :(得分:1)

  

当涉及malloc的实际效果

时,我感到难过

malloc调用为您的数组分配空间。当您最初声明ptr时,它未初始化为指向有效的内存位置:

     +---+
ptr: |   | ----> ???
     +---+

此时尝试读取或写入ptr将导致未定义的行为;您的代码可能会彻底崩溃,或者可能以某种方式损坏存储,或者出现以便在没有任何问题的情况下运行。

malloc调用从堆(a.k.a。,动态内存池)中分配空间,并将该空间的第一个元素的地址分配给ptr

     +---+
ptr: |   | ---+
     +---+    |
      ...     |
       +------+
       |
       V
     +---+
     |   | ptr[0]
     +---+
     |   | ptr[1]
     +---+
      ...

请注意,自{1989}标准以来,(int *)电话上的malloc投射是没有必要的,实际上被认为是不好的做法(根据C89,它可能掩盖了一个错误)。 IMO,写malloc电话的最佳方式是

T *p = malloc( N * sizeof *p );

其中T是任何类型,N是您要分配的T类型的元素数。由于表达式 *p的类型为T,因此sizeof *p等同于sizeof (T)

  

以及所有*(ptr + i)实际上做了什么。

*(ptr + i)相当于ptr[i],所以

*ptr = 'x';
*(ptr + 1) = 'x';

相当于写作

ptr[0] = 'x';
ptr[1] = 'x';

请注意

*(ptr +99) = 'x';

超出了您已分配的阵列的范围;你只留出足够的空间用于25个整数。同样,此操作(以及*(ptr + i) = 'x';大于24的任何操作i)将导致未定义的行为,并且您的代码可能会崩溃,损坏数据或其他方式。

指针算法将指向的类型考虑在内; ptr + 1生成下一个整数对象的地址,位于ptr之后的地址。因此,如果ptr0x8000sizeof (int)为4,则ptr + 1会产生0x8004不会 0x8001

答案 3 :(得分:0)

Oups,C指针算术基于定义 *(ptr + i) ptr[i]

这意味着当你为25个整数分配空间时,超过第24个元素的所有访问都将调用未定义的行为 - 你实际上试图访问一个你不知道它代表什么的内存。

但是,如果使用指向char(或unsigned char)的指针,则允许访问字节级别的任何对象。所以假设你的编译器sizeof(int)是4,这很好:

int *iptr;
char *cptr;
iptr = malloc( 25 * sizeof(int)); //100 bytes since we know that sizeof(int) is 4
cptr = (char *) iptr; // cast of pointer to any to pointer to char is valid
for(int i=0; i<25*sizeof(int); i++) cptr[i] = 'x'; // store chars 'x'
for(int i=0; i<25; i++) {
    printf(" %x", (unsigned int) iptr[i]);  // print the resulting ints in hexa
}
printf("\n");

假设您使用字符的ASCII表示(非常常见),您应该得到25个值,所有等于0x78787878,因为0x78是&#39; x&#39;的ASCII码。但是这部分是未指定的标准,只是实现定义。