我有一个头文件,其中有许多数组。每个数组对(即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]'
是引号,因为这些数组的名称是字符串。
C不是一种反射语言。x0
被称为x0
, x1
被称为x1
的事实被编译器抛弃了。因此,一段代码没有办法执行"查找名称为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等的地址,并将它们放入数组中。这样您就可以根据索引查找数组的地址,然后从那里访问数组。
正如@Tommy指出的那样,你使用的语法在C语言中是不可能的,因为它不是一种自反性语言;我将试着帮助你设置它在C中应该如何完成,并且可以以动态的方式使用;
你真正想做的是建立一个结构(我称之为点,但你可以叫它任何东西),像这样:
struct point {双x;双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出错的情况下释放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);
}
}
让我们看看如何从文件中加载一个map:
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;
}
AND处理点、映射和映射集的库代码到此结束;;
您可以从http://pastebin.com/i2m624yX
获取此代码的完整副本。代码应该按原样编译并且应该正常工作(尽管我没有做太多检查,请让我知道它对您的工作有多好,如果不是,我们将看到会发生什么)
关于在load_setmap函数中使用goto的注意事项
此函数使用gotos来摆脱无法构建的情况不管出于什么原因,合适的地图;在任何清教徒的结构程序员之前我想让你检查一下代码并验证它确实是最干净的编程方式使用其中给出的方法进行错误处理
进一步指出这段代码中有很多内容;但这是有效的代码;应作为一种通用方法……还有更有趣的方法来解决链表等问题,但我希望你也有一个结构化的方法比其他任何东西;
一个有趣的地方是iterate_over_map函数,它接受一个函数指针作为参数,可以遍历所有的点并回调自己的函数;如果你不明白这里发生了什么,请在这里给我留言,我会详细说明;但是请学习这个,这是一种合理的方法,在C中建立东西,一旦你完成每个函数就很容易遵循;大多数函数的结构和用法都是不言自明的。
另一个注意:我还没有创建原型和单独的头文件,因为我想让您看到构建逻辑的流程
通过逻辑构建,从上到下阅读代码应该相当容易;我将结构分散到代码中,使它们接近处理它们的函数;通常情况下,你会把它们放在自己的文件中,并在其他地方有相应的包含头文件,但这将从我希望传达的逻辑积累中带走。这可以从别处学到。
如果你感到困惑,请注意。
可以使用数组的数组:
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];
}
}
除了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
数组并访问每个单独的数组及其大小。
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];
}
}