我面临一个关于验证C中数组大小的问题。
We have one .h file, which contains the constant value like
A 1
B 2
C 3
...
...
END 10
最后一个元素是END。每当添加任何新常量时,应增加END的值。对于例如如果有些人添加了值为10的新常量调用F,那么他们必须将END的值从10增加到11.
现在,在.c文件中,我们有一个大小为END的数组。 .h文件中定义的每个常量都应该在此数组中有条目,即
abc[END] = {
1,
2,
1,
0,
...
...
}
现在,我们必须添加一个测试来检查,如果有人在.h文件中添加新条目并且他们没有在abc []数组中添加相应的条目,那么测试应该失败。之前我已经编写了以下代码来验证:
return (sizeof(tbl2schema)/sizeof(int) == END) ? TRUE : FALSE;
但我认为,这段代码并没有达到目的。希望,我已经解释了我的问题。
任何人都可以建议我这个,怎么做这个检查。我必须添加一个检查,如果有人在.h文件中添加条目,那么他们必须在abc []数组中添加条目。
答案 0 :(得分:4)
您的公式不起作用的原因是您已明确指定END
作为大小。从声明中删除它以使abc
数组的大小随常量数变化:
int abc[] = {
// ^^
1,
2,
1,
0,
...
...
};
如果有人在现在更新END
后忘记添加常量,则abc
的大小不会匹配。
使用static_assert
检查条件:
static_assert(sizeof(tbl2schema)/sizeof(int) == END, "abc size does not match.");
答案 1 :(得分:1)
这似乎是enum
的完美用法:
file.h
:
typedef enum { A, B, C, END } constants_list;
file.c
:
static int abc[END] = { 1, 2, 1 };
随着常量列表的增长,您的表abc
将自动增长。
但请注意,如果未在0
中明确设置,表格中的任何值都会初始化为file.c
。
答案 2 :(得分:1)
也许你想要:
return (sizeof(tbl2schema) / sizeof(int) > END) ? TRUE : FALSE;
相当于:
return sizeof(tbl2schema) / sizeof(int) > END;
答案 3 :(得分:1)
如果标头和源文件之间存在差异,下面是一个无法编译的C解决方案:
enum {
NAME_1,
NAME_2,
NAME_3,
END
};
#define STATIC_ASSERT_ARRAY_SIZE(array, expected_size) \
static int static_assert_array_size_array_##array##_too_small \
[sizeof(array)/sizeof(array[0]) - expected_size]; \
static int static_assert_array_size_array_##array##_too_large \
[expected_size - sizeof(array)/sizeof(array[0])]
#include "header.h"
int data[] = { 5, 8 };
STATIC_ASSERT_ARRAY_SIZE(data, END);
int main(void)
{
return 0;
}
#include "header.h"
int data[] = { 5, 8, 10 };
STATIC_ASSERT_ARRAY_SIZE(data, END);
int main(void)
{
return 0;
}
#include "header.h"
int data[] = { 5, 8, 10, 11 };
STATIC_ASSERT_ARRAY_SIZE(data, END);
int main(void)
{
return 0;
}
$ gcc main1.c
main.c:3: error: size of array static_assert_array_size_array_data_too_small is too large
$
$ gcc main2.c
$
$ gcc main3.c
main.c:3: error: size of array static_assert_array_size_array_data_too_large is too large
$
这个技巧利用了这样一个事实,即声明一个负数大小的数组是非法的(你不说!)。
答案 4 :(得分:0)
我假设你已经简化了一些示例,但看起来你只想拥有一大堆你希望能够通过 name 访问的值与#34;幻数" index - 因此#define
s。
如果是这样的话,为什么你不只是使用struct
,顺序字段的名称与数组索引的当前符号名称相同?
struct { int A, B, C, ... } abc = {
1,
2,
3,
...
};
这样,编译器将检查以确保您没有访问无效名称。您仍然可以使用指针迭代结构成员:
for (int *p = &abc.A; p <= &abc.Z; abc++) {
do_something_with(*abc);
}
答案 5 :(得分:0)
我想为程序中相互依赖但分散的数据问题推荐一个更通用的解决方案。
它使用单个预处理器宏文件,它以预处理器函数宏列表的形式包含单个位置的所有信息。该文件包含在定义数据结构或类型(如枚举)所需信息的任何位置。
在这个例子中,我使用纸张大小定义列表;当我写一个PCL6解析器时,这在我的现实生活中很有用。在PCL6中,每种纸张尺寸确实是一种数字标记。纸张尺寸具有相当多的关联属性,以及人类可读的名称。通常需要在名称和标记值之间双向映射并查找相关信息。这导致具有冗余信息的多个数据结构加上匹配的枚举定义。在添加新纸张类型时很容易错过其中一个更新(实际上有很多纸张)。
诀窍是将宏文件中的条目定义为适合于给定位置的语言构造。请注意该语言是如何精心设计的,以允许使用尾随逗号,例如:在枚举定义或初始化列表的末尾。这就是用例。
我们从包含宏的文件开始,这些宏包含与纸张大小相关的信息。实际上,当然还有更多,更多的属性。
//////////////////////////////////////////////////////
// papermacros.h
//////////////////////////////////////////////////////
// Has all information about paper sizes in one place.
// Note the function syntax. It's essential.
// token, name, height, width
PAPERSIZE_MACRO(5, A5, 200, 150)
PAPERSIZE_MACRO(4, A4, 300, 200)
PAPERSIZE_MACRO(3, A3, 400, 300)
然后是纸类和枚举。宏用于构建枚举类型 始终包含所有条目的纸币标记 宏文件。枚举元素名称是使用预处理程序连接运算符构造的,名称是使用stringize运算符构造的。 (我们不能立即在宏标题中包含字符串,因为我们也想使用该名称作为枚举标识符的基础 - 没有“unstringize”运算符。)
//////////////////////////////////////////////////////
// papers.h
//////////////////////////////////////////////////////
#include <string>
#include <map>
#include <sstream>
#undef PAPERSIZE_MACRO
#define PAPERSIZE_MACRO(token, name, height, width) \
e_##name = token,
enum PaperSizeE {
e_INVALID, // for default ctor
# undef PAPERSIZE_MACRO
# define PAPERSIZE_MACRO(token, name, height, width) \
e_##name = token,
// this included file expands to a series of enum definitions which
// make sure that each enum element is named
// like the paper name, with a prefix e_
# include "papermacros.h"
e_END // if you want. Note, however, that it has the (arbitrary)
// value of the last "real" enum plus 1.
#undef PAPERSIZE_MACRO
};
class PaperT
{
public:
PaperSizeE token;
int height;
int width;
std::string name;
PaperT(PaperSizeE t, std::string n, int h, int w)
:token(t), name(n), height(h), width(w)
{ }
// Funny, needed by map resp. std::pair
PaperT() : token(e_INVALID), name("invalid"), height(0), width(0)
{}
std::string ToString()
{
std::ostringstream stm;
stm << name << ", height: " << height << ", width: " << width;
return stm.str();
}
};
// Useful mappings. Paper information can now be
// efficiently looked up by token or by name.
extern std::map<PaperSizeE, PaperT> mapByToken;
extern std::map<std::string, PaperT> mapByName;
下一个文件包含上面声明的地图的定义。同样,初始化列表的元素是从(多次)包含的宏头构造的,具有相应的合适的宏定义。在这里,尾随逗号也会被忽略。
//////////////////////////////////////////////////////////////////
// paperdefs.cpp
//////////////////////////////////////////////////////////////////
#include "papers.h"
using namespace std;
std::map<PaperSizeE, PaperT> mapByToken
{
# define PAPERSIZE_MACRO(token, name, height, width) \
{e_##name, PaperT(e_##name, #name, height, width) },
// this expands to a series of
// { e_xx, PaperT(e_xx, "Ax", hhh, www) },
// which is used to initialize the entries a map enum -> paper.
# include "papermacros.h"
# undef PAPERSIZE_MACRO
};
std::map<string, PaperT> mapByName =
{
# define PAPERSIZE_MACRO(token, name, height, width) \
{#name, PaperT(e_##name, #name, height, width) },
// this expands to a series of
// { "Ax", PaperT(e_xx, "Ax", hhh, www) },
// which is used to initialize the entries a map name -> paper.
# include "papermacros.h"
# undef PAPERSIZE_MACRO
};
最后一个主要功能,以证明用法。
//////////////////////////////////////////////////////////////////
// main.cpp
// Demonstrate how to use the paper related data structures.
// Must be linked with paperdefs.o
//////////////////////////////////////////////////////////////////
#include "papers.h"
#include <iostream>
using namespace std;
int main()
{
{
PaperSizeE a4Token = e_A4;
cout << "The name of the paper with token " << a4Token
<< " is " << mapByToken[a4Token].name << endl;
}
{
string name = "A3";
cout << "The token val of the paper named " << name
<< " is " << mapByName[name].token << endl;
}
// iterate over all papers
for(auto &p: mapByName)
{
cout << "Paper by name " << p.first << ": "
<< p.second.ToString() << endl;
}
}
结果:
$ g++ -std=c++11 -o papers main.cpp paperdefs.cpp && ./papers
The name of the paper with token 4 is A4
The token val of the paper named A3 is 3
Paper by name A3: A3, height: 400, width: 300
Paper by name A4: A4, height: 300, width: 200
Paper by name A5: A5, height: 200, width: 150