当前位置: 首页 > 知识库问答 >
问题:

在不知道索引的情况下迭代C中的多维数组

巩镜
2023-03-14

我在处理多维数组时遇到问题。我试图使用指针让“123”、“456”、“x123”和“x456”出现在函数内部的屏幕上:

void f(char ***array){
    while (**array != '\0'){
        while (*array != '\0'){
            printf("%s\n",*array);array++;
        }
    }
}

int main(){
    char* arr[50][50]={{"123","456"},{"X123","X456"}};
    f(arr);
    return 0;
}

编译时,我在f(arr)行收到来自不兼容指针类型的警告传递参数1“f” 。当运行代码时,我看到:

123
456
Segmentation fault

然后程序退出。

当我将代码更改为:

void f(char **array){
    while (*array != '\0'){
        printf("%s\n",*array);array++;
    }
}

int main(){
    char* arr[50]={"123","456"};
    f(arr);
    return 0;
}

这些数字可以很好地迭代,但为了更好地组织,我宁愿在某个时候将数据分组。为什么使用多维数组的第一组代码不能正确执行?


共有3个答案

严正初
2023-03-14

通常不应该交替使用指针和数组。众所周知,它们是相同的,但它们不是。对于多维数组尤其如此。

多维数组必须在内存中线性排列,因为地址是线性的。有几种方法可以做到这一点,但C使用行主排序。这意味着,随着地址的增加,最后一个索引的增长速度最快。

例如二维数组

int x[rows][cols];

这两种说法是等价的

x[row][col] = y;
*(x + (row * cols) + col) = y;

这意味着,为了访问函数中多维数组的元素,函数至少需要知道更高维度的大小才能有效访问。

void f(int rows, int cols, char* array[rows][cols]){
  for (int i = 0; i < rows; i++) {
    for (int j = 0; j < cols; j++) {
      printf("%s\n", array[i][j]);
    }
  }
}

int main(){
  char* arr[50][50]={{"123","456"},{"X123","X456"}};
  f(50, 50, arr);
  return 0;
}

但是,如果需要在不知道维度的情况下进行迭代,可以利用内存布局有效地迭代行和列(以及更高的维度)。不过,这增加了在数组中输入结束信号的必要性(例如NULL)。除了使代码更复杂之外,它还增加了内存开销,因为通常必须分配包含NULL的最后一行。

#include <stdio.h>

void f(char** array){

  while (*array)
    printf("%s\n", *(array++));
}

int main(){

  char* arr[][2] = { {"123","456"}, {"X123","X456"}, {NULL, NULL} };

  f((char**) arr);

  return 0;
}
纪翰
2023-03-14

很难判断您正在尝试做什么,但如果我理解正确,您似乎有一个不需要的额外数组维度。一旦您到达第5个字符串(导致您的分段错误),您就正在测试一个未初始化的值。要纠正它,您可以执行以下操作:

#include <stdio.h>

void f(char **array){
    while (*array != '\0'){
        // while (*array != '\0'){
        printf("%s\n",*array);array++;
        //}
    }
}

int main(){
    char *arr[50]={"123","456","X123","X456",NULL};
    f(arr);
    return 0;
}

输出

$ ./bin/func_f
123
456
X123
X456

注意:当数据耗尽时,显式的NULL被用作停止迭代的哨兵。有很多方法可以解决这个问题,这只是其中之一。

巫马泰
2023-03-14

首先,为什么是三星?你想要完成的是什么?显而易见的解决方案是创建一个二维字符数组,然后将字符串存储在数组中,每行一个。考虑以下示例

char arr[][ 6 ] = { "123", "456", "X123", "X456" };

注意,我们可以省略arr数组中的行数,但C要求我们指定列数。不幸的是,并不是所有的字符串都足够长来填充整个数组行,所以C用空字符填充它们。(请注意,阵列中存在一些浪费的空间)

      0     1     2     3     4     5
   +-----+-----+-----+-----+-----+-----+
 0 |  1  |  2  |  3  | '\0'| '\0'| '\0'|
   +-----+-----+-----+-----+-----+-----+
 1 |  4  |  5  |  6  | '\0'| '\0'| '\0'|
   +-----+-----+-----+-----+-----+-----+
 2 |  X  |  1  |  2  |  3  | '\0'| '\0'|
   +-----+-----+-----+-----+-----+-----+
 3 |  X  |  4  |  5  |  6  | '\0'| '\0'|
   +-----+-----+-----+-----+-----+-----+

如果代码中有三星是您想要的,那么您必须添加一个始终为空的额外元素。(顺便问一下,为什么不使用数组的长度?)

另一种方法是使用不规则阵列。C不提供“不规则数组类型”,但它确实为我们提供了模拟这种类型的工具。(只需创建一个数组,其元素是指向字符串的指针)考虑以下示例:

#define N   4

int main( int argc, const char * argv[] ) {

    char *str[] = { "123", "456", "X123", "X456" };

    for ( char **p = &str[ 0 ]; p < str + N; ++p ) {
        puts( *p );
    }

    return EXIT_SUCCESS;
}

要访问其中一个字符串,我们只需要在arr数组下标。

希望这有帮助。。

 类似资料:
  • 这就是我可以在没有索引信息的情况下得到Seires的第一个元素的任何方法。 例如,我们有一个系列 打印主题: 如果不使用索引145,如何在这里获得146的值? 非常感谢你们

  • 假设我们有一个冲突,但键值不同,因此根据定义,Hashmap将在该桶中创建一个链表,并将新的键值对添加为现有键值条目的下一个。 我的问题是在这种情况下我们如何迭代哈希图?默认迭代机制是否更改为实际检索所有冲突并存储在同一存储桶位置的键值对?

  • 我使用键值存储作为我的Go语言应用程序的后端,日期作为键(保持条目排序),json文档作为值。json的顶级命名空间()以及和存在于我存储的每个json文档中,但在其他方面存在一些差异(尤其是关于一些嵌套的json数据),所以当keyI从数据库中提取时,我真的不知道我在循环浏览的任何时间提取了什么。这是json数据的示例 当我从数据库中提取数据时,我要做的第一件事是将每个条目解组到<code>ma

  • 问题内容: 假设您具有以下数组: 您将如何将其转换为XML字符串,使其看起来像: 一种方法是通过类似如下的递归方法: 我正在寻找一种使用迭代的方法。 问题答案:

  • 本文向大家介绍MySQL在不知道列名情况下的注入详解,包括了MySQL在不知道列名情况下的注入详解的使用技巧和注意事项,需要的朋友参考一下 前言 最近感觉脑子空空,全在为了刷洞去挖洞,还是回归技术的本身让自己舒服些。好了,下面话不多说了,来一起看看详细的介绍吧 前提 以下情况适用于 MySQL < 5版本,或者在 MySQL >= 5 的版本[存在information_schema库],且已获取

  • 本文向大家介绍c++迭代器失效的情况汇总,包括了c++迭代器失效的情况汇总的使用技巧和注意事项,需要的朋友参考一下 一、序列式容器(数组式容器) 对于序列式容器(如vector,deque),序列式容器就是数组式容器,删除当前的iterator会使后面所有元素的iterator都失效。这是因为vetor,deque使用了连续分配的内存,删除一个元素导致后面所有的元素会向前移动一个位置。所以不能使用