P9754 [CSP-S 2023] 结构体 题解

发布时间 2023-10-22 13:59:38作者: 383494

前言

在考场上调了 2h+ 还没调出来,并且 T4 也没来得及做。希望看到这段文字的你能避免这样的悲剧。

正文

题目要求动态创建类型,于是我用结构体代表一个 struct(禁止套娃),要新建就 new 出来一个。(最后也没 delete

struct Obj{
    typnam tnam;
    ll len, align;
    std::map<std::string, std::pair<Obj*, ll>> subs;
    Obj():tnam(), len(0), align(0){}
    Obj(std::string nam, ll le=0, ll ali=0):tnam(nam), len(le), align(ali){}
};

由于结构体的名称和变量名称容易混淆,我用 typnam 标注了结构体的名称。

using typnam = std::string;

声明了结构体之后,题目要求根据名称找出一个结构体,我用 std::set 保存。这里也可以用 std::map

struct comp_obj_by_typnam{ bool operator()(Obj *a, Obj *b) const { return a->tnam < b->tnam; } };
std::set<Obj*, comp_obj_by_typnam> decl_objs = {&BByte, &SShort, &IInt, &LLong};
Obj *get_obj_by_tname(typnam &tnam){
    Obj tmp(tnam);
    return *decl_objs.find(&tmp);
}

对于已存在的变量,我用一个 map 保存名称,类型和起始位置。

其实这里也可以新建一个特殊的 struct,就像森林转树要加一个超级根一样。这样可以避免一些重复的代码。

std::map<std::string, std::pair<Obj*, ll>> exist_objs;
ll global_offset = 0;

关于如何计算对齐:

设第 \(i\) 个被定义的元素大小为 \(s_i\),对齐要求为 \(a_i\),起始地址为 \(b_i\);
\(b_1=0\),对于 \(2\le i\)\(b_i\) 为满足 \(b_{i−1}+s_{i−1} \le b_i\)\(a_i\) 整除 \(b_i\) 的最小值。

\(k=b_{i-1}+s_{i-1}\),若 \(k \equiv 0 \pmod {a_i}\),则 \(b_i\) 正好可以放入;

否则,\(b_i = k+a_i-(k \bmod {a_i})\)

ll get_start_addr(ll old_offset, Obj const *me){
    if(old_offset % me->align) old_offset += me->align - old_offset%me->align;
    return old_offset;
}

关于访问某个元素:

读入名称 s 后,可以用 s.substr(0, s.find(".")) 找到 struct 名。

附完整代码,用于 debug:

完整代码 ```cpp #include #define UP(i,s,e) for(auto i=s; i> subs; Obj():tnam(), len(0), align(0){} Obj(std::string nam, ll le=0, ll ali=0):tnam(nam), len(le), align(ali){} }; Obj BByte = {"byte", 1, 1}, SShort = {"short", 2, 2}, IInt = {"int", 4, 4}, LLong = {"long", 8, 8}; int in; struct comp_obj_by_typnam{ bool operator()(Obj *a, Obj *b) const { return a->tnam < b->tnam; } }; std::map> exist_objs; ll global_offset = 0; std::set decl_objs = {&BByte, &SShort, &IInt, &LLong}; Obj *get_obj_by_tname(typnam &tnam){ Obj tmp(tnam); return *decl_objs.find(&tmp); } ll get_start_addr(ll old_offset, Obj const *me){ if(old_offset % me->align) old_offset += me->align - old_offset%me->align; return old_offset; } void declare_struct(){ std::string is; int ik; cin >> is >> ik; Obj *now = new Obj; now->tnam = is; ll nowoff = 0; UP(i, 0, ik){ std::string it, in; cin >> it >> in; Obj *me = get_obj_by_tname(it); now->align = std::max(now->align, me->align); nowoff = get_start_addr(nowoff, me); now->subs.insert({in, {me, nowoff}}); nowoff += me->len; } now->len = get_start_addr(nowoff, now); decl_objs.insert(now); cout << now->len << ' ' << now->align << '\n'; } void create_obj(){ std::string it, in; cin >> it >> in; Obj *now = get_obj_by_tname(it); global_offset = get_start_addr(global_offset, now); exist_objs.insert({in, {now, global_offset}}); cout << global_offset << '\n'; global_offset += now->len; } void access_obj_recursive(ll sum_offset, std::string &is, Obj *now){ auto pl = is.find('.'); if(pl == std::string::npos){ cout << sum_offset+now->subs[is].second << '\n'; return; } auto &now_info = now->subs[is.substr(0, pl)]; now = now_info.first; is.erase(0, pl+1); access_obj_recursive(sum_offset + now_info.second, is, now); } void access_obj(){ std::string is; cin >> is; auto pl = is.find('.'); if(pl == std::string::npos){ cout << exist_objs[is].second << '\n'; return; } auto &now_info = exist_objs[is.substr(0, pl)]; Obj *now = now_info.first; is.erase(0, pl+1); access_obj_recursive(now_info.second, is, now); } void access_addr_recursive(ll addr, Obj *now, std::string &ans){ if(now == &BByte || now == &SShort || now == &IInt || now == &LLong){ cout << ans << '\n'; return; } for(auto &i:now->subs){ if(i.second.second <= addr && i.second.second + i.second.first->len > addr){ ans += '.'; ans += i.first; access_addr_recursive( addr-i.second.second, i.second.first, ans); return; } } cout << "ERR\n"; } void access_addr(){ ll addr; cin >> addr; std::string ans; for(auto &i:exist_objs){ if(i.second.second <= addr && i.second.second + i.second.first->len > addr){ ans = i.first; access_addr_recursive( addr-i.second.second, i.second.first, ans); return; } } cout << "ERR\n"; } void work(){ cin >> in; UP(i, 0, in){ int op; cin >> op; if(op == 1){ declare_struct(); } else if(op == 2){ create_obj(); } else if(op == 3){ access_obj(); } else { access_addr(); } } } } // {}}} int main(){m::work(); return 0; } ```