此函数返回什么?

时间:2019-10-25 11:45:22

标签: c

如果我正确地解释了这一点,它将两个(长)整数作为输入,创建一个数组,然后减去该数组和一个整数,但是我认为我不能减去数组和整数。

此函数实际上返回什么?

int *ivector (long nl, long nh)
/* allocate an int vector with subscript range v[nl..nh] */
{
  int *retval;

  retval = malloc(sizeof(int)*(nh-nl+1));

  return retval - nl;
}

1 个答案:

答案 0 :(得分:3)

在探讨此ivector()函数的行为之前,让我们回顾一下有关C中数组和指针的一些基本知识。

考虑代码

int a[10];
for(i = 0; i < 10; i++)
    a[i] = 100 + i;

这会在内存中产生一个数组,我们可以这样想:

    +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
 a: | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 |
    +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
       0     1     2     3     4     5     6     7     8     9

假设我们现在说

int *ip = a;

由于correspondence between arrays and pointers in C,这相当于说

int *ip = &a[0];

在任何情况下,我们都以指向a的第一个单元格的指针结尾,如下所示:

    +-----+
ip: |  *  |
    +--|--+
       |
       v
    +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
 a: | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 |
    +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+

现在,指针算术:将整数添加到指针时,可以“移动”指针,使其指向基础数组中的下一个元素。 确保您了解此代码打印数字102的所有不同方式:

int *ip2 = ip + 2;
printf("%d %d %d %d\n", *(ip + 2), ip[2], *ip2, ip2[0]);

(如果您了解所有四个表达式*(ip+2)ip[2]*ip2ip2[0]的计算结果是数字102 ,请阅读或询问。 这是“数组和指针之间的对应关系”的另一个方面,也是我们对ivector函数的理解的基础。)

指针减法也有效:调用

printf("%d %d\n", *(ip2 - 1), ip2[-1]);

以两种略有不同的方式打印101。

现在,让我们看一下ivector()函数。 它试图帮助我们模拟不一定从0开始的数组。 如果我们打电话

int a2 = ivector(0, 9);
for(i = 0; i <= 9; i++) a2[i] = 100 + i;

我们将得到一个几乎完全像以前一样的数组:

    +-----+
a2: |  *  |
    +--|--+
       |
       v
    +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
    | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 |
    +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+

唯一的区别是数组本身没有名称: 这是我们通过调用malloc获得的匿名存储区域。

现在假设我们打电话

int a3 = ivector(-5, 5);
for(i = -5; i <= 5; i++) a3[i] = 100 + i;

现在,我们得到一个11个元素的“数组”,我们可以将其看起来像这样:

    +-----+
a3: |  *-----------------------------+
    +-----+                          |
                                     v
    +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
    | 95  | 96  | 97  | 98  | 99  | 100 | 101 | 102 | 103 | 104 | 105 |
    +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
      -5    -4    -3    -2    -1     0     1     2     3     4     5

请注意,我们可以谈论a3[0]a3[3]a3[-2]等,就好像这是下限为-5的常规数组一样。 这样做的关键是在您询问的ivector末尾减去:

return retval - nl;

这不会从数组的值中减去任何东西,或者任何东西; 再次是指针运算,从指针值 nl中减去retval。 对于通话ivector(-5, 5),它转换为

return retval - -5;

当然等价于

return retval + 5;

所以我们在分配的区域中有5个元素的指针。

现在假设我们打电话

int *a4 = ivector(1, 10);
for(i = 1; i <= 10; i++) a4[i] = 100 + i;

这是所有故障所在。 目的是最终得到这样的图片:

    +-----+
a4: |  *  |
    +--|--+
       |
       v
          +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
          | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 |
          +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
             1     2     3     4     5     6     7     8     9    10

但是有一个非常明显的问题: a4实际上并不指向分配的数组

基于指针算法的工作方式,并且它们通常是由简单的编译器用于简单的计算机体系结构来实现的,因此,您可以说服自己,该代码“应该”可以正常工作,并且可以访问{ {1}},a4[1],...直至a4[2]。 当然,如果您尝试访问a4[10]会出现可怕的问题,但是没关系,您不应该这样做,因为a4[0]是一个基于1的数组。

不幸的是,最后的代码片段不能保证正常运行。如果您计算指向数组“外部”的指针(不是您声明的实际数组,或者是通过调用a4获得的类似数组的内存块),则不会定义指针算术)。 如果尝试计算这样的指针,则行为是不确定的,即使您从未尝试访问越界指针指向的内存,也不会定义行为。 因此,大多数知识渊博的C程序员都会建议您不要编写类似malloc的代码(或者,如果您愿意,则只为ivector来调用它……当然,无法达到目的。