繁体   English   中英

验证C中数组的大小

[英]Validating the size of array in C

我面临一个有关验证C中数组大小的问题。

We have one .h file, which contains the constant value like
A 1
B 2
C 3
...
...
END 10

最后一个元素是END。 每当添加任何新常数时,都应增加END的值。 例如,如果添加了一些新的常量调用F,其值为10,则必须将END的值从10增加到11。

现在,在.c文件中,我们有一个大小为END的数组。 .h文件中定义的每个常量都应在此数组中具有条目,即

abc[END] = {
1,
2,
1,
0,
...
...
}

现在,我们必须添加一个测试以检查是否有人在.h文件中添加了新条目,而他们没有在abc []数组中添加相应的条目,则测试应该失败。 之前,我编写了以下代码进行验证:

return (sizeof(tbl2schema)/sizeof(int) == END) ? TRUE : FALSE;

但我认为,此代码无法实现目的。 希望我已经解释了我的问题。

谁能建议我这个,如何进行检查。 我必须添加一个检查,如果有人在.h文件中添加条目,那么他们必须在abc []数组中添加条目。

公式不起作用的原因是您已明确指定END作为大小。 从声明中删除它,以使abc数组的大小随常数的数量而变化:

int abc[] = {
//     ^^
  1,
  2,
  1,
  0,
  ...
  ...
};

如果有人立即更新END后忘记添加常量,则abc的大小将不匹配。

使用static_assert检查条件:

static_assert(sizeof(tbl2schema)/sizeof(int) == END, "abc size does not match.");

看来enum的完美用法:

file.h

typedef enum { A, B, C, END } constants_list;

file.c

static int abc[END] = { 1, 2, 1 };

您的表abc将随着常量列表的增长而自动增长。 但是请注意,如果未在file.c明确设置, file.c表中的任何值都将初始化为0

也许你想要:

return (sizeof(tbl2schema) / sizeof(int) > END) ? TRUE : FALSE;

等效于:

return sizeof(tbl2schema) / sizeof(int) > END;

下面是一个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])]

来源#1

#include "header.h"
int data[] = { 5, 8 };
STATIC_ASSERT_ARRAY_SIZE(data, END);
int main(void)
{
    return 0;
}

来源#2

#include "header.h"
int data[] = { 5, 8, 10 };
STATIC_ASSERT_ARRAY_SIZE(data, END);
int main(void)
{
    return 0;
}

来源#3

#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
$

此技巧得益于以下事实:声明一个负大小的数组是非法的(您不必说!)。

我假设您已对示例进行了一些简化,但看起来您只是想通过名称而不是使用“幻数”索引来访问#define值,因此#define

如果是这样,为什么不只使用struct ,而将顺序字段命名为与数组索引的当前符号名相同的struct呢?

struct { int A, B, C, ... } abc = {
1,
2,
3,
...
};

这样,编译器将检查以确保您没有访问无效的名称。 您仍然可以使用指针遍历struct成员:

for (int *p = &abc.A; p <= &abc.Z; abc++) {
    do_something_with(*abc);
}

我想为程序中相互依赖但分散的数据问题推荐一个更通用的解决方案。

它使用一个预处理器宏文件,该文件以预处理器功能宏列表的形式在一个地方包含所有信息。 文件包含在定义信息结构或类型(例如枚举)所需的信息的任何位置。

在此示例中,我使用了纸张尺寸定义的列表。 当我编写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)

然后是纸类和枚举。 宏用于构建枚举类型的纸质令牌,该令牌始终包含宏文件中的所有条目。 枚举元素名称使用预处理程序串联运算符构造,名称使用字符串化运算符构造。 (我们不能立即在宏头中包含字符串,因为我们也想使用该名称作为枚举标识符的基础-没有“ 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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM