【问题标题】:What is returned in this function?这个函数返回什么?
【发布时间】:2020-02-21 18:30:30
【问题描述】:

如果我理解正确,它会将两个(长)整数作为输入,创建一个数组,然后减去数组和一个整数,但我认为我不能减去数组和整数。

这个函数实际上返回了什么?

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;
}

【问题讨论】:

  • @mattiav27 如果 nl 不等于 0,该函数具有未定义的行为。:)
  • 这是一个模拟基索引不是 0 的数组的技巧。不幸的是,它不可移植。仅当您要使用的基数为 negative 时才定义明确(并且仅当它是绝对值小于数组大小的负数时)。特别是,使用这个技巧来模拟一个基于 1 的数组是不可移植的,这大概是通常的愿望。另请参阅C FAQ list 中的question 6.17
  • @VladfromMoscow 不!函数返回一个无效的指针。
  • 注释说明了一切:"vector with subscript range v[nl..nh]" 但不幸的是,在 C 中未定义数组边界之外的指针,即使稍后的索引(取消引用)将在数组的范围内。
  • 这也是相当邪恶的,因为即使它在哪里工作,它也会给你一个 malloc'd 指针,你不能 free,而不是不知道这个函数的内部结构。

标签: c


【解决方案1】:

在探索 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;

这不会从数组的值或任何东西中减去任何东西; 又是指针运算,从 指针值 retval 中减去 nl。 对于电话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 实际上并没有指向分配的数组

基于指针算术的工作方式,以及传统上由直接编译器为简单计算机架构实现的方式,您可以说服自己这段代码“应该”正常工作,并且您可以访问 @ 987654355@, a4[2], ... 直到 a4[10]。 如果你尝试访问a4[0],当然会出现可怕的问题,但没关系,你不应该那样做,因为a4 是一个从 1 开始的数组。

不幸的是,这最后一段代码保证可以工作。如果您计算一个指向数组“外部”的指针(您声明的实际数组,或者您通过调用malloc 获得的类似数组的内存块),则指针算术是定义的。 如果您尝试计算这样的指针,则行为是未定义的,即使您从未尝试访问越界指针指向的内存。 所以大多数知识渊博的 C 程序员会建议你不要编写像 ivector 这样的代码(或者如果你这样做,只为 nl &lt;= 0 调用它......但当然这几乎没有达到目的) .

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-21
    • 2018-01-28
    • 1970-01-01
    相关资源
    最近更新 更多