【问题标题】:Dereferencing arrays that contain pointers in C在 C 中取消引用包含指针的数组
【发布时间】:2014-06-06 12:35:39
【问题描述】:

我编写了一个程序,其中涉及的两行整数会定期交换位置。因此,我所做的是分配两个单独的指向整数的指针,这些指针将对所需的行进行建模。然后我将这两个指针放入一个常量指针数组中,这样可以很容易地进行交换。

我遇到的问题完全是错误的。以此为例:

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

int main(void) {
    char* a = malloc(sizeof(char)*5);
    char* temp[] = {a};
    int i, h, k = 'a';

    for(i = 0; i < 5; i++)
        a[i] = k++;

    for(i = 0; i < 5; i++)
        printf("%c ", temp[0][i]);

    free(a);
    return 0;
}

带输出:a b c d e IdeOne

我不小心尝试通过二阶取消引用访问静态数组中包含的数组成员:“[][]”

我对这个事件没有多想,因为一切编译和运行都很完美,甚至在输出时给出了预期的结果。当我运行内存分析并发现我收到大量寻址错误和所谓的内存泄漏时,问题就出现了。

所以,为了开始调试过程,我想来这里询问为什么会这样以及它是否是有效的 C 语法。我的印象是:

temp[0]

将取消对char*a 的引用。然后需要进一步取消引用a 才能访问其成员。此外,我的印象是:

temp[row][col]

翻译成:

*(*(temp + row*col) + col)

在运行时。因此,temp[0][i] 的双重取消引用应该给出与temp 的偏移量,并且根本不访问a


更有趣的是:

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

int main(void) {
    char* a = malloc(sizeof(char)*5);
    char* b = malloc(sizeof(char)*5);
    char* temp[] = {a, b};
    int i, h, k = 'a', j = 'A'; 

    for(i = 0; i < 5; i++){
        a[i] = k++;
        b[i] = j++;
    }

    for(i = 0; i < 5; i++){
        for(h = 0; h < 5; h++)
            printf("%c ", temp[i][h]);
        puts(""); 
    }

    free(a);
    free(b);
    return 0;
}    

IdeOne 运行时不编译。但是,此代码确实c99 模式下使用 Microsoft Visual Studio Professional 2013 和 gcc 编译。

关于如何正确地做我想做的事情有什么想法或建议吗?也就是说,访问包含在静态数组 temp 中的成员 ab 而不使用非常丑陋的语法,例如:

*(temp[i] + sizeof(char)*h)

【问题讨论】:

  • 您的第一个程序完全没问题,并且您关于寻址的推理是正确的(row*col 计算除外)。第二个访问temp 的五个索引,而只有两个可用。
  • 数组不是“dereferenced”。
  • @alk,数组是指向内存的常量指针,因此在使用时会被取消引用。例如,某些int* foo[5] 数组实际上与指向内存块sizeof(int)*5 的常量整数数组相同。因此foo[2] 指向第三个整数,但也可以使用*(foo+2) 取消对同一成员的引用。也许您是正确的,因为标题中没有使用正确的行话,但每个人似乎都明白了这里的想法。
  • 我看不出有任何理由故意使用不正确的措辞。
  • int * foo[5] 是一个 指向 int 的指针数组。我的大小为5 * sizeof (int *)

标签: c arrays pointers memory malloc


【解决方案1】:

temp[row][col]等同于*(*(temp + row*col) + col),但实际上等同于*(*(temp + row) + col)

您的第二个示例代码不正确,因为 temp 是一个包含 2 个元素的数组,但您正在访问 temp[i]i 的范围从 05

【讨论】:

  • 我明白你的意思。我想我是在 char** 的上下文中思考的,其中内存“块”对应于行 .. 或类似的东西。我明白我的意思,但在这里没有相关性。
  • 假设我有一些:int temp[3][3],它不会在内存中构造为:1 2 3 4 5 6 7 8 9 吗?在与二维数组的行相对应的连续内存中,我们在哪里有符号“块”?即1 2 3, 4 5 6, 7 8 9。因此,如果我们想任意访问temp[2][2],我们必须这样做:*(*(temp+2*3)+2),对吗?我在原始帖子中包含的表达式中的第一个colnumber of columns,而不是col th8at 包含在temp[row][col] 中。您包含的表达式:*(*(temp + row) + col) 似乎仅对 char* 数组有效。
  • ajay 列出了 C99 标准的摘录,所以它必须是正确的 - 我只是不知道如何。二维数组是否使用指向行的常量指针分配?如果是这样,那将比我的想法更有意义,因此上述表达式将是有效的。
  • @sherrellbc 编译器知道temp 的元素是另一个数组,所以当它看到temp + row 时,它知道要提前多少大小才能找到下一个元素。
【解决方案2】:

当您认为 temp[0] 等于 a 并且 temp[1] 等于 b 时,您猜对了。但是等等,你为什么要超越这个,就像你试图取消引用 temp[2] 这样的东西?你没有正确初始化这个东西!

您的程序通过访问数组末尾的内存来导致undefined behaviour。编译器并不总是必须给你一个错误信息。检查this问题。

【讨论】:

    【解决方案3】:

    C99 标准 §6.5.2.1 ¶2 说 -

    下标运算符[]的定义是E1[E2]是 等同于(*((E1)+(E2)))

    因此,表达式temp[row][col] 被计算为

    temp[row][col] --> *((temp[row]) + col) --> *(*(temp + row) + col)
    |_______|  |
        |      |
       E1     E2
    

    而不是*(*(temp + row*col) + col)。请注意区别。因此,您的第一个程序很好。

    在第二个程序中,语句

    char* temp[] = {a, b};
    

    temp 定义并初始化为一个由2 指向字符的指针组成的数组。但是,在for 循环中,您以temp[i][h]i 的形式访问数组,范围从04。访问数组边界之外的元素是未定义的行为。因此,您的第二个程序是错误的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-03-25
      • 1970-01-01
      • 2017-05-10
      • 2019-05-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多