Executive summary:
执行概要:
- How can I define an arbitrarily-sized 2D array in C?
- 如何在C中定义任意大小的2D数组?
- How can I determine the dimensions of that array at compile-time?
- 如何在编译时确定该数组的维数?
Full disclosure:
充分披露:
I'm writing code for an embedded controller. My application requires several lookup tables with different sizes which will all be used by one lookup function (a binary search). Here is what I have so far:
我在为嵌入式控制器编写代码。我的应用程序需要几个不同大小的查找表,这些表将由一个查找函数(二进制搜索)使用。这是我到目前为止所得到的:
typedef struct
{
unsigned char count; // number of rows in the table
unsigned char width; // number of bytes in each row
const unsigned char * data; // pointer to table data[count][width]
}
LookupTable;
// returns the index of a value from within a table
unsigned char Lookup(unsigned long value, const LookupTable * table);
This part is working. What I would like to do now is define these tables in my source without having to manually enter the count
and width
constants. Here is what I am doing now:
这部分是有效的。我现在要做的是在源代码中定义这些表,而不必手动输入计数和宽度常量。下面是我现在正在做的:
#define T1_count 100
#define T1_width 3
const unsigned char table1_data[T1_count][T1_width] =
{
{ 0x12, 0x34, 0x56 },
{ 0x12, 0x38, 0x12 },
...
};
const LookupTable table1 = { T1_count, T1_width, table1_data };
Here is what I would like to be able to do (pseudo-code, since this array definition will not actually compile):
下面是我想做的(伪代码,因为这个数组定义实际上不会编译):
const unsigned char table1_data[] =
{
{ 0x12, 0x34, 0x56 },
{ 0x12, 0x38, 0x12 },
...
};
const LookupTable table1 =
{
get_count_expr(table1_data),
get_width_expr(table1_data),
table1_data
};
Obviously, get_count_expr
and get_width_expr
would have to be constant expressions of some sort, based on the size of the table, and not actual function calls.
显然,get_count_expr和get_width_expr必须是某种类型的常量表达式,基于表的大小,而不是实际的函数调用。
To be clear, no part of this design is cast in stone. I'm just posting what I have so far, in the hopes that my intent is clear. Any ideas for improvement would be appreciated.
明确地说,这个设计的任何部分都不是用石头铸造的。我只是发布了我到目前为止的信息,希望我的意图是明确的。如有改进意见,我们将不胜感激。
The "why":
“为什么”:
These tables will be changed often, and it would make maintenance easier if entries could be added and removed, or the width of a table changed without having to manually adjust the constants each time. Having to keep track of the sizes manually can be error-prone and violates DRY. I'm looking for a better way.
这些表将经常更改,如果可以添加和删除条目,或者更改表的宽度,而不必每次手动调整常量,那么维护将更容易。必须手动跟踪大小很容易出错,并且违反了DRY。我在找更好的办法。
4 个解决方案
#1
3
Hmmm ... you can leave the leftmost size to the compiler:
嗯…可以将最左边的大小留给编译器:
#define T1_WIDTH 3
const unsigned char table1_data[][T1_WIDTH] =
{
{ 0x12, 0x34, 0x56 },
{ 0x12, 0x38, 0x12 },
/* ... */
};
T1_count = sizeof table1_data / sizeof *table1_data;
T1_width = sizeof *table1_data;
#2
2
Well, it's ugly as hell, but I think the only way to do it within the constraints you've listed is to include the data in a string, and than have initialization code parse the string and generate the table. Ideally you'd do that in a script rather than use C to do it, but if it has to be in C, it has to be in C..
好吧,这简直太丑了,但我认为在你列出的约束条件下做这件事的唯一方法是将数据包含在字符串中,而不是让初始化代码解析字符串并生成表格。理想情况下,您应该在脚本中执行该操作,而不是使用C来执行,但是如果它必须在C中,那么它必须在C中。
Note that in no way do I claim the following to be production code, but it's just a proof of concept...
请注意,我绝不声称以下内容是产品代码,但这只是概念的证明……
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define datatable "\
{ 0x12, 0x34, 0x56 },\
{ 0x14, 0x36, 0x10 },\
{ 0x13, 0x37, 0x11 },\
{ 0x12, 0x38, 0x12 }"
typedef struct
{
unsigned char count; // number of rows in the table
unsigned char width; // number of bytes in each row
unsigned char **data; // pointer to table data[count][width]
}
LookupTable;
int parsedatatable(char *data, LookupTable **table) {
char *p, *sp, save;
unsigned char *tabledata;
int count = 0, width = 0;
unsigned int tmp;
int i,j;
/* find count */
p = strstr(data,"{");
while (p) {
p++;
p = strstr(p, "{");
count++;
}
/* find width */
p = strstr(data, "{");
p++;
sp = strstr(p, "}");
if (sp != NULL) {
save = *sp;
*sp = '\0';
}
while (p) {
p = strstr(p, ",");
width++;
if (p != NULL) p++;
}
if (sp != NULL) {
*sp = save;
}
printf("Count = %d, width = %d\n",count, width);
tabledata = (unsigned char *)malloc(width*count*sizeof(unsigned char));
*table = (LookupTable *)malloc(sizeof(LookupTable));
(*table)->data = (unsigned char **)malloc(count*sizeof(unsigned char*));
for (i=0; i<count; i++) {
(*table)->data[i] = &(tabledata[i*width]);
}
(*table)->count = count;
(*table)->width = width;
p = data;
for (i=0; i<count; i++) {
p = strstr(p,"{");
if (!p) {
fprintf(stderr,"Fail (a) reading in data!: %s\n",data);
free((*table)->data);
free(tabledata);
free(*table);
return -1;
}
p++;
for (j=0; j<width; j++) {
printf("Scanning <%s>, ",p);
sscanf(p,"%x",&tmp);
printf("got %d\n",tmp);
(*table)->data[i][j] = tmp;
p = strstr(p,",");
if (!p && j<width-1) {
fprintf(stderr,"Fail (b) reading in data!: %d, %d, %s\n",i,j,data);
free((*table)->data);
free(tabledata);
free(*table);
return -1;
}
p++;
}
}
return 0;
}
void printtable(LookupTable *table) {
unsigned char i,j;
for (i=0; i<table->count; i++) {
printf("{");
for (j=0; j<table->width; j++) {
printf("%x ",table->data[i][j]);
}
printf("}\n");
}
return;
}
int main(int argc, char **argv) {
char *data;
LookupTable *table;
data = (char *)malloc(strlen(datatable)+1);
strcpy(data,datatable);
parsedatatable(data,&table);
printtable(table);
return 0;
}
#3
1
Well, but who fills these tables with data? I think that generated sources are better solution.
那么,谁会用数据填充这些表呢?我认为生成的源是更好的解决方案。
#4
0
Define table1_data
inside a header. You can auto-generate that header using a script. I do something similar to that for some of my projects. I have a CSV file with data and a Ruby or Python script that generates a header from it.
在头中定义table1_data。您可以使用脚本自动生成该标题。我在一些项目中做了类似的事情。我有一个带有数据的CSV文件,还有一个Ruby或Python脚本,可以从中生成一个头。
#1
3
Hmmm ... you can leave the leftmost size to the compiler:
嗯…可以将最左边的大小留给编译器:
#define T1_WIDTH 3
const unsigned char table1_data[][T1_WIDTH] =
{
{ 0x12, 0x34, 0x56 },
{ 0x12, 0x38, 0x12 },
/* ... */
};
T1_count = sizeof table1_data / sizeof *table1_data;
T1_width = sizeof *table1_data;
#2
2
Well, it's ugly as hell, but I think the only way to do it within the constraints you've listed is to include the data in a string, and than have initialization code parse the string and generate the table. Ideally you'd do that in a script rather than use C to do it, but if it has to be in C, it has to be in C..
好吧,这简直太丑了,但我认为在你列出的约束条件下做这件事的唯一方法是将数据包含在字符串中,而不是让初始化代码解析字符串并生成表格。理想情况下,您应该在脚本中执行该操作,而不是使用C来执行,但是如果它必须在C中,那么它必须在C中。
Note that in no way do I claim the following to be production code, but it's just a proof of concept...
请注意,我绝不声称以下内容是产品代码,但这只是概念的证明……
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define datatable "\
{ 0x12, 0x34, 0x56 },\
{ 0x14, 0x36, 0x10 },\
{ 0x13, 0x37, 0x11 },\
{ 0x12, 0x38, 0x12 }"
typedef struct
{
unsigned char count; // number of rows in the table
unsigned char width; // number of bytes in each row
unsigned char **data; // pointer to table data[count][width]
}
LookupTable;
int parsedatatable(char *data, LookupTable **table) {
char *p, *sp, save;
unsigned char *tabledata;
int count = 0, width = 0;
unsigned int tmp;
int i,j;
/* find count */
p = strstr(data,"{");
while (p) {
p++;
p = strstr(p, "{");
count++;
}
/* find width */
p = strstr(data, "{");
p++;
sp = strstr(p, "}");
if (sp != NULL) {
save = *sp;
*sp = '\0';
}
while (p) {
p = strstr(p, ",");
width++;
if (p != NULL) p++;
}
if (sp != NULL) {
*sp = save;
}
printf("Count = %d, width = %d\n",count, width);
tabledata = (unsigned char *)malloc(width*count*sizeof(unsigned char));
*table = (LookupTable *)malloc(sizeof(LookupTable));
(*table)->data = (unsigned char **)malloc(count*sizeof(unsigned char*));
for (i=0; i<count; i++) {
(*table)->data[i] = &(tabledata[i*width]);
}
(*table)->count = count;
(*table)->width = width;
p = data;
for (i=0; i<count; i++) {
p = strstr(p,"{");
if (!p) {
fprintf(stderr,"Fail (a) reading in data!: %s\n",data);
free((*table)->data);
free(tabledata);
free(*table);
return -1;
}
p++;
for (j=0; j<width; j++) {
printf("Scanning <%s>, ",p);
sscanf(p,"%x",&tmp);
printf("got %d\n",tmp);
(*table)->data[i][j] = tmp;
p = strstr(p,",");
if (!p && j<width-1) {
fprintf(stderr,"Fail (b) reading in data!: %d, %d, %s\n",i,j,data);
free((*table)->data);
free(tabledata);
free(*table);
return -1;
}
p++;
}
}
return 0;
}
void printtable(LookupTable *table) {
unsigned char i,j;
for (i=0; i<table->count; i++) {
printf("{");
for (j=0; j<table->width; j++) {
printf("%x ",table->data[i][j]);
}
printf("}\n");
}
return;
}
int main(int argc, char **argv) {
char *data;
LookupTable *table;
data = (char *)malloc(strlen(datatable)+1);
strcpy(data,datatable);
parsedatatable(data,&table);
printtable(table);
return 0;
}
#3
1
Well, but who fills these tables with data? I think that generated sources are better solution.
那么,谁会用数据填充这些表呢?我认为生成的源是更好的解决方案。
#4
0
Define table1_data
inside a header. You can auto-generate that header using a script. I do something similar to that for some of my projects. I have a CSV file with data and a Ruby or Python script that generates a header from it.
在头中定义table1_data。您可以使用脚本自动生成该标题。我在一些项目中做了类似的事情。我有一个带有数据的CSV文件,还有一个Ruby或Python脚本,可以从中生成一个头。