在C语言中遍历多个数组

3

我有一个头文件,里面有很多数组。每个数组对(即x0,y0)的长度相同,但不同的数组对长度不同。

numPaths[] = 4;
pathLengths[] = {12, 11, 13, 10};
int x0[] ={4.0, 4.0, ...};
int x1[] ={224.0, 224.0, ...};
int x2[] ={446.0, 446.0, 446.0, ...};
int x3[] ={598.0, 598.0, ...};
int y0[] ={11.0, 11.0, 11.0, 15.0, ...};
int y1[] ={2.0, 2.0, 2.0, 6.0, 17.0, ...};
int y2[] ={1.0, 1.0, 1.0, 5.0, ...};
int y3[] ={4.0, 4.0, 4.0, 5.0, 10.0, ...};

我希望能够遍历这个数组列表并访问数据点信息。由于路径长度不同,我不知道如何做到这一点。我考虑使用类似于伪代码的方式:

i=0;
n=0;
if i< numPaths:
    if n < pathLengths[i]:
        x_tot = x'[i]'[n]
        y_tot = x'[i]'[n]
    n++
    i++

这里的'[i]'用引号括起来是因为这些数组的名称是字符串。


为什么不使用二维数组?另外,为什么您 int 数组中的所有元素都是 double 类型? - FDinoff
“double”和“int”的使用是错误的 - 我会更正。我可以使用2D数组,但我的pathLengths不同。我认为2D数组的所有行必须具有相同的大小。 - user2253513
5个回答

5
C语言不是一种反射性的语言。编译器会忽略变量名字,例如 x0x1,因此代码无法执行“查找名称为x1的数组”的操作,这实际上就是你的代码所尝试做的事情。在编译期间使用C的预处理器进行此操作可能会导致代码难以维护。因此,最好的方法是像下面这样:
int *xs[] = {x0, x1, x2, x3};

for(int c < numPaths)
{
    int *thisX = xs[c];

    ... read from thisX[0], thisX[1], etc...
}

所以你做的是告诉编译器把x0、x1、x2等的地址放进一个数组里。这样你就可以根据索引查找数组的地址,然后从那里访问数组。


2

好的,正如@Tommy指出的那样,你使用的语法在C语言中是不可能的,因为它不是一种反射性语言。我将尝试帮助您以C语言应该使用的方式进行设置,并可以以动态方式使用;

您真正想做的是设置一个结构体(我称其为点,但您可以称其为任何内容),如下所示:

struct point { double x; double y; };

现在,您可以轻松地建立多个由此结构体构建的数组:

struct point array0[] = { { 4.0, 11.0 }, { 4.0 , 11.0 }, ... };
struct point array1[] = { { 224.0, 2.0 }, { 224.0, 2.0 }, { }, ... };
/* etc... */

是的,你可以将它们放在多行上,使语法比我的更美观 :-)

这将让你以这种方式获取任何给定数组的长度:

size_t length_array0 = sizeof(array0) / sizeof(struct point);

然而,对于您的情况来说这仍然没有用,因为您无法以编程方式访问正确的数组;要正确地完成这项任务需要进行相当多的设置。在软件静态执行此操作非常麻烦,最好设置一组可以处理点、映射和映射数组并从文件中读取映射的函数:

正确的方法是构建一堆处理每个组件并构建所有内容的函数和结构。

下面是一个可能的方案:

我们需要一些基本库:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>

让我们在地图上设置一个基本点和一组点集合

struct point { double x, y; };

struct map { 
    size_t count;           /* number of points in this map */
    struct point *points;   /* array of points in the map */
}; 

现在让我们创建一些函数来分配和释放它们。
struct map * map_init(size_t count)
{
        struct map *m = (struct map *) malloc(sizeof (struct map));
        if ( !m )
                return NULL;
        m->count = count;
        m->point = (struct point *) malloc (count * sizeof(struct point));
        if ( !m->point ) {
                free(m);
                m = NULL;
        }
        return m;
}


void map_free(struct map *m)
{
        if ( m ) {
                if ( m->points )
                        free (m->points);
                free(m);
        }
}

还有一些函数可以在分配的地图中的特定索引处添加新点:

struct point *map_set_point(struct map *m, int index, struct point *p)
{
    if ( !m )
        return NULL;

    if ( index < m->count ) {
        m->points[index].x = p->x;
        m->points[index].y = p->y;
        return &m->points[index];
    }

    return NULL;
}

struct point *map_set_point_xy(struct map *m, int index, double x, double y)
{
    struct point p;
    p.x = x;
    p.y = y;
    return map_set_point(m, index, &p);
}

为了避免在代码中频繁进行索引范围检查,获取索引位置的值是个好主意。因此我们需要编写一个包装器实现该功能。

struct point *map_get_point(struct map *m, int index)
{

    if ( !m || index < 0 || index >= m->count ) {
        return NULL;
    }

    return &m->points[index];
}

为了完整起见,我们也会对地图中的点数进行相同的操作:
size_t map_get_count(struct map *m)
{
    if ( m ) 
        return m->count;

    return ((size_t)-1);
}

希望能够不用每次都设置一个for循环,而是通过一个回调函数迭代地调用我们稍后定义的函数,以便能够顺利地遍历地图中所有点。

typedef int (*point_callback_func_t)(struct point *p);

int for_each_point_call(struct map *m, point_callback_func_t f, int *error)
{
    int k;
    int rv; /* return value from call back function */
    for (k = 0; k < m->count; k++) {
        if ((rv = f(&m->points[k])) != 0 ) {
            if (error != NULL) 
                *error =rv;
            break;
        }
    }
    return (k - m->count); /* 
                * if we go through all of the points this will be zero 
                * otherwise it will be a negative number  indicating how far 
                * we were able to go in the loop
                */
}

现在我们已经有了一张地图,让我们创建一组它们。
struct mapset {
    size_t count;       /* number of maps in this set */
    struct map **maps;  /* a NULL terminated array of maps in this set */
};

但是我们如何初始化这样一个复杂的东西呢?硬编码并不是一个好主意,如果我们能够从文本文件中读取这些信息,那就更好了。因此,让我们创建一个代码库来从文件中加载地图:

/* 
 * The read_maps_from_file  reads a text file of the format:

 MAP
 <M>
 <x1>, <y1>
 <x2>, <y2>
  ...
 <xM>, <yM>

 MAP
 <N>
 <x1>, <y1>
 <x2>, <y2>
  ...
 <xN>, <yN>


 and so on and can return a struct mapset filled with the map 
 coordinates

 A file with 3 maps would be:

--CUT-HERE--
 MAP
 3
 5.5, 2.2
 -3.5, 5.9
 2.0, -125.0

 MAP
 5
 2.2, -89.0
 551, 223.5
 7.5, -8.9
 7.8, 6.9
 4.3, -9.9

 MAP
 1
 2.5, 0.3

--CUT-HERE-- 

*/

如果load_mapset函数出现错误,我们需要能够释放地图集;您应该先阅读该函数;但是我们首先要声明它,以便从load_mapset函数中调用。

void free_mapset(struct mapset *set) {
    int k;
    if ( set ) {
        if ( set->maps ) {
            for(k = 0; k < set->count; k++) {
                map_free(set->maps[k]);
            }
        }
        free(set);
    }
}

现在让我们来看一下如何从文件中加载地图:

struct mapset *load_mapset(const char *filename)
{
    FILE * fp;
    struct mapset *set;
    size_t mapcnt = 0; /* number of maps in this set */

    char buf[1024]; /* line buffer */

    struct map *tmpmap;
    size_t tmpcnt;

    struct map **tmp_maps_arr;

    double x, y;

    int k;

    set = (struct mapset*) malloc(sizeof(struct mapset)); 

    if ( !set )
        goto build_error;

    set->count = 0;
    set->maps = NULL;

    fp = fopen("somefile.txt", "r");

    while(!feof(fp)) {

        fgets(buf, sizeof(buf), fp); /* read a line */

        if ( strcmp ( buf, "MAP") != 0 )  /* look for keyword 'MAP' */
            continue; /* nope, let's reloop */

        /* found 'MAP' read the next line to get the count */

        fgets(buf, sizeof(buf), fp); 

        if (feof(fp)) 
            goto build_error;

        sscanf(buf, "%lu", &tmpcnt); /* number of points in this map */

        tmpmap = map_init(tmpcnt);   /*  make a tmpmap of tmpcnt points */

        if ( !tmpmap )
            goto build_error; 

        for ( k = 0; k < tmpcnt; k++ ) {
            fgets(buf, sizeof(buf), fp);
            if (feof(fp)) 
                goto build_error;

            sscanf(buf, "%lf , %lf", &x, &y);
            map_set_point_xy(tmpmap, k, x, y); /* add x, y to index k */
        }

        /* try to increase the size of maps array */
        tmp_maps_arr= (struct map **) realloc(set->maps, sizeof(struct map *) * (1+set->count));
        if ( !tmp_maps_arr ) 
            goto build_error;

        set->maps  = tmp_maps_arr;
        set->maps[set->count] = tmpmap; /* save the pointer to the map */
        set->count++;
        tmpmap = NULL;
    }

    return set;

build_error:
    free_mapset(set);
    if (tmpmap) 
        map_free(tmpmap);

    return NULL;
}

这是我们处理点、地图和地图集的库代码的结尾;;

你可以从http://pastebin.com/i2m624yX获取完整的代码副本。

代码应该可以直接编译并且可以正常工作(虽然我没有仔细检查,如果有问题,请告知我,我们看看会发生什么)

关于load_setmap函数中使用GOTO的说明

这个函数使用goto语句来处理无法建立地图的情况,无论因为什么原因。在任何纯洁的结构型程序员抨击我使用goto之前,我希望你们检查代码并验证它确实是使用提供的方法进行错误处理的最清晰方式。

更多说明

这段代码涉及很多内容;但这是一段可用的通用方法。还有更有趣的方法,比如使用链接列表等,但我想让你采取一个结构化方法。

值得注意的一点是iterate_over_map函数,它接受函数指针作为参数,并可以遍历所有点并回调自己的函数。如果你不明白这里发生了什么,请给我留言,我会详细解释。但请研究这个,它是在C中设置易于遵循的事情的合理方法,一旦你通过每个函数,大多数函数和它们的使用结构都是不言自明的。

另一个注意点:我还没有创建原型和单独的头文件,因为我希望你看到逻辑的建立过程。

代码应该可以很容易地从上到下阅读并形成逻辑。我将结构分散在代码中以将它们靠近处理它们的函数;通常,你会将这些放在自己的文件中,并有相应的头文件用于包含其他地方,但这会减弱我希望传达的逻辑建立。这可以在其他地方学到。

如果你感到困惑,请给我留言。


也许我错过了什么,但对于Sareena所追求的用途来说,这似乎过于复杂了。 - Timothy Jones
@TimothyJones 我展示这个的想法有两个方面:1)从范围的角度来看,她并没有指出她是否仅限于4个地图集,也没有指出她的地图中有多少点;这种方法将解决大多数一般情况。2)从基本原则出发,在它们自己的集合中创建这些函数只是良好的编程习惯;每个函数都不是特别长或难以理解;load_mapset包括空格在内共75行,其余每个函数不到10行。这只花了大约15分钟的时间,并教给初学者良好的方法。 - Ahmed Masud

0
你可以使用一个数组的数组:
numPaths = 4;
pathLengths[] = {12, 11, 13, 10};
int x0[] ={4.0, 4.0, ...}; 
int x1[] ={224.0, 224.0, ...};
. . .
int *xs[] = {x0, x1, x2, x3};
int *ys[] = {y0, y1, y2, y3};

int i, j;
for (i = 0; i < numPaths; ++i){
    for (j = 0; j< pathLenghts[i] ++j){
        x_tot = xs[i][j];
        y_tot = ys[i][j];
    }
}

0
除了单独的数组x0、x1等,还有一个结构体数组,用于保存每个数组的大小和地址:
int x0[] ={4.0, 4.0, ...};
int x1[] ={224.0, 224.0, ...};
int x2[] ={446.0, 446.0, 446.0, ...};
int x3[] ={598.0, 598.0, ...};

#define ARRAY_SIZE(n) (sizeof(n) / sizeof(*n))

struct {
    int* array;
    size_t length;
} x[] = {
    {x0, ARRAY_SIZE(x0)},
    {x1, ARRAY_SIZE(x1)},
    {x2, ARRAY_SIZE(x2)},
    {x3, ARRAY_SIZE(x3)}
};

然后您可以遍历 x 数组并访问每个单独的数组以及它的大小。


0
numPaths[] = 4;
pathLengths[] = {12, 11, 13, 10};
/* int x0[] ={4.0, 4.0, ...}; ... */
int **pairs = {x0, y0, x1, y1, x2, y2, x3, y3};
for (int i = 0; i < 4; i++)
    process_pair(&x_tot, &y_tot, pairs[i*2], pairs[i*2+1], pathLengths[i]);

void process_pair(int *x_tot, int *y_tot, int *x, int *y, int n) {
  for (int i = 0; i < n; i++) {
    x_tot[0] += x[i];
    y_tot[0] += y[i];
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接