回到
顶部
意见
反馈
首页 > Mod工坊 > 骑马砍杀 > 修改教程 > 原版教程 > 《骑马与砍杀》MOD教程之字符串的使用

《骑马与砍杀》MOD教程之字符串的使用

2016-08-27 19:14:05| 来源: 佚名 |   已有[ ]人前来看过    | 已有[ ]人评论
字符串类型广泛存在于Module System的各个地方,比如对话,菜单内容,各种ID,变量,以及各种要求填字符串的地方。
  字符串的特征就是用它们都是用 " " 引起来的。
  
  
  字符串在Module System里主要是以2种方式存在的,一种是在特定的元组的特定元素里,一种是在用于输出的字符串里。
  
  1-1 在特定的元组的特定元素里的情况比较简单,就是一个萝卜一个坑,指定的位置填就可以了。比如下面的例子:
  
    ("start_game_options", 0, 
    "Welcome to Custom Commander - Restoration & Rebellion, a mod for Mount and Blade.^^Restoration: After a kingdom is destoryed, its lords will serve other kingdoms. If he get a town or castle, he will restorate his home country and become the king, the lords from that kingdom will come bace to him.^Rebellion: For every country, if the current king is not the original king, some lords will revolt against the king and become rebels. The first lord rebelled will become the leader of the rebels, and the official king late on. If the rebels defeat the government troops, the rebellion will be considered successful and their leader will become the new king of this country. When rebellion happens, if you join the civil war as the third party, you have a chance to be the king.^^Please select your game options. These options cannot be changed after the game has begun.",
    "none", [], 
    [
      ("choose_options", [],
        "Continue...",
        [
          (jump_to_menu,"mnu_choose_options_1")
        ]),
      ("go_back", [],
        "Go back",
        [(change_screen_quit)]),
    ]),
    
    这是截取的module_game_menus.py里最开头的一段。这里就有很多字符串。到底什么地方该填字符串,什么地方不该,module_game_menus.py的头部是有说明的。
    
########################################################################
#  (menu-id, menu-flags, menu_text, mesh-name, [], []),
#
#   Each game menu is a tuple that contains the following fields:
#
#  1) Game-menu id (string): used for referencing game-menus in other files.
#     The prefix menu_ is automatically added before each game-menu-id
#
#  2) Game-menu flags (int). See header_game_menus.py for a list of available flags.
#     You can also specify menu text color here, with the menu_text_color macro
#  3) Game-menu text (string).
#  4) mesh-name (string). Not currently used. Must be the string "none"
#  5) Operations block (list). A list of operations. See header_operations.py for reference.
#     The operations block is executed when the game menu is activated.
#  6) List of Menu options (List).
#     Each menu-option record is a tuple containing the following fields:
#   6.1) Menu-option-id (string) used for referencing game-menus in other files.
#        The prefix mno_ is automatically added before each menu-option.
#   6.2) Conditions block (list). This must be a valid operation block. See header_operations.py for reference.
#        The conditions are executed for each menu option to decide whether the option will be shown to the player or not.
#   6.3) Menu-option text (string).
#   6.4) Consequences block (list). This must be a valid operation block. See header_operations.py for reference.
#        The consequences are executed for the menu option that has been selected by the player.
#
#
# Note: The first Menu is the initial character creation menu.
########################################################################
    
    这段注释的大意是讲解菜单的元组是怎么构成的,包含那些地方以及各部分是什么类型。标有(string)的就是要求这个部分是字符串类型。
    其他文件的头部也都有类似的说明,这样你就知道这个文件的元组什么地方该填字符串了。
    
    
  1-2 在用于输出的字符串里,主要是用display_message来输出。
    下面就是display_message函数:
    (display_message,,[hex_colour_code]),
    这个元组的第2项要求是字符串,不过这和在特定的元组的特定元素里的情况完全不同,这里的字符串必须是有前缀的。
    前缀有两种,str开头的和@开头的。可以说是两种类型的输出字符串。
    
      str开头是标准的输出字符串,需要在module_strings.py里先定义,然后再被其他地方引用。比如下例:
      (display_message,"str_door_locked",0xFFFFAAAA),
      这里"str_door_locked"就是字符串的ID,并不是实际内容,实际内容在module_strings.py里:
      ("door_locked","The door is locked."),
      第1个元素就是字符串ID,在定义的时候不需要加str前缀。第2个元素就是实际内容,就是直接的字符串,也不需要加什么前缀。字符串ID和字符串内容不需要有任何字面上的联系。不过有联系的,引用的时候就能顾名思义了。
      
      @开头的是快捷的输出字符串,英文名字叫quick strings。它的使用非常自由,定义和引用是同时的,随时想用就随时用,快捷方便。
      比如上面的例子用快捷的输出字符串来写就是:
      (display_message,"@The door is locked.",0xFFFFAAAA),
      直接@然后再接字符串的内容,又定义了新字符串,又能直接看到字符串的内容,很方便快捷。
    
    当然了,str开头的和@开头的字符串不光是用display_message来输出,其实它还用于header_operations.py里其他的很多函数。
    在header_operations.py里,凡是含有string_id的函数都要用str开头的或@开头的字符串。比如:
      (display_log_message,,[hex_colour_code]),
      (tutorial_box,,),
      (create_text_overlay, , ),
      (faction_set_name, , ),
      (str_store_string,,),
      …………
下面要讲的是字符串使用的高级技巧:偏移和迭代。
  
  2-1 准确地说是字符串ID的偏移。
    看一下下面这个例子:
    ("describe_relation_to_s63",
      [(store_script_param_1, ":relation"),
        (store_add, ":normalized_relation", ":relation", 100),
        (val_add, ":normalized_relation", 5),
        (store_div, ":str_offset", ":normalized_relation", 10),
        (val_clamp, ":str_offset", 0, 20),
        (store_add, ":str_id", "str_relation_mnus_100",  ":str_offset"),
        (str_store_string, s63, ":str_id"),
    ]),
    
    这个script是用来把玩家与各领主的关系用字符串描述出来并存到s63里,巧妙地利用了字符串ID的偏移量。这里的局部变量":str_offset"就是偏移量。前面4行就是在算这个偏移量。(store_add, ":str_id", "str_relation_mnus_100",  ":str_offset"), 就是把基础字符串的ID加上字符串ID的偏移量存到":str_id"里。局部变量":str_id"就是目标字符串的ID了。
    比如玩家与某领主的关系是47,也就是这里的":relation"为47。 那么":str_offset"算出来就是15,而":str_id"就是"str_relation_mnus_100"加15,得到的就是"str_relation_plus_50",也就是s63会输出"Friendly"。这和关系的数值是完全符合的。
    
    下面是要用到的所有字符串:
      ("relation_mnus_100", "Vengeful"), # -100..-94
      ("relation_mnus_90",  "Vengeful"),  # -95..-84
      ("relation_mnus_80",  "Vengeful"),
      ("relation_mnus_70",  "Hateful"),
      ("relation_mnus_60",  "Hateful"),
      ("relation_mnus_50",  " Hostile"),
      ("relation_mnus_40",  "  Angry"),
      ("relation_mnus_30",  "    Resentful"),
      ("relation_mnus_20",  "      Grumbling"),
      ("relation_mnus_10",  "        Suspicious"),
      ("relation_plus_0",   "         Indifferent"),# -5...4
      ("relation_plus_10",  "          Cooperative"), # 5..14
      ("relation_plus_20",  "           Welcoming"),
      ("relation_plus_30",  "            Favorable"),
      ("relation_plus_40",  "             Supportive"),
      ("relation_plus_50",  "              Friendly"),
      ("relation_plus_60",  "               Gracious"),
      ("relation_plus_70",  "                 Fond"),
      ("relation_plus_80",  "                  Loyal"),
      ("relation_plus_90",  "                   Devoted"),
      
    如果不用字符串ID的偏移,那就很痛苦了,要不停地else_try且is_between地进行20遍。
    需要说明的是,要使用字符串ID的偏移,必须使用str开头的输出字符串。因为只有str开头的输出字符串才有明确的数字ID和有规律的顺序定义,可以用来相加,实现字符串ID的偏移,而@开头的输出字符串没有明确的数字ID,定义也太随意,不能实现字符串ID的偏移。所以使用str开头的输出字符串还是使用@开头的输出字符串不仅仅是一个单纯的习惯问题,而是要考虑实际的要求。
    
    再举一个稍微简单一些的例子:
  (
    "custom_battle_optional",0,
  "From here the time of day and the weather can be changed.",
  "none",
    [
      (store_add, ":rain_settings", "str_custom_battle_rain_0", "$g_rain_settings"),
      (str_store_string, s1, ":rain_settings"),
    ],
    [
      ("rain",[],"Rain/Snow ({s1})", 
        [
          (val_add, "$g_rain_settings", 1),
          (val_mod, "$g_rain_settings", 7),
        ]),
      ("next",[],"Next", 
        [
          (jump_to_menu, "mnu_custom_battle_config_finish"),
        ]),
    ]
  ),
  
  ("custom_battle_rain_0", "None"),
  ("custom_battle_rain_1", "Rain - Light"),
  ("custom_battle_rain_2", "Rain - Medium"),
  ("custom_battle_rain_3", "Rain - Heavy"),
  ("custom_battle_rain_4", "Snow - Light"),
  ("custom_battle_rain_5", "Snow - Medium"),
  ("custom_battle_rain_6", "Snow - Heavy"),
  
  这个是自定义战斗里关于天气设定的源代码,当然这是我改写后的,原本的源代码在下面:
  
  (
    "custom_battle_optional",0,
  "From here the time of day and the weather can be changed.",
  "none",
    [
      (try_begin),
        (eq, "$g_rain_settings", 0),
        (str_store_string, s1, "str_custom_battle_rain_0"),
      (else_try),
        (eq, "$g_rain_settings", 1),
        (str_store_string, s1, "str_custom_battle_rain_1"),
      (else_try),
        (eq, "$g_rain_settings", 2),
        (str_store_string, s1, "str_custom_battle_rain_2"),
      (else_try),
        (eq, "$g_rain_settings", 3),
        (str_store_string, s1, "str_custom_battle_rain_3"),
      (else_try),
        (eq, "$g_rain_settings", 4),
        (str_store_string, s1, "str_custom_battle_rain_4"),
      (else_try),
        (eq, "$g_rain_settings", 5),
        (str_store_string, s1, "str_custom_battle_rain_5"),
      (else_try),
        (eq, "$g_rain_settings", 6),
        (str_store_string, s1, "str_custom_battle_rain_6"),
      (try_end),
    ],
    [
      ("rain",[],"Rain/Snow ({s1})", 
        [
          (try_begin),
            (eq, "$g_rain_settings", 0),
            (assign, "$g_rain_settings", 1),
          (else_try),
            (eq, "$g_rain_settings", 1),
            (assign, "$g_rain_settings", 2),
          (else_try),
            (eq, "$g_rain_settings", 2),
            (assign, "$g_rain_settings", 3),
          (else_try),
            (eq, "$g_rain_settings", 3),
            (assign, "$g_rain_settings", 4),
          (else_try),
            (eq, "$g_rain_settings", 4),
            (assign, "$g_rain_settings", 5),
          (else_try),
            (eq, "$g_rain_settings", 5),
            (assign, "$g_rain_settings", 6),
          (else_try),
            (eq, "$g_rain_settings", 6),
            (assign, "$g_rain_settings", 0),
          (try_end),
        ]),
      ("next",[],"Next", 
        [
          (jump_to_menu, "mnu_custom_battle_config_finish"),
        ]),
    ]
  ),
  
  ("custom_battle_rain_0", "None"),
  ("custom_battle_rain_1", "Rain - Light"),
  ("custom_battle_rain_2", "Rain - Medium"),
  ("custom_battle_rain_3", "Rain - Heavy"),
  ("custom_battle_rain_4", "Snow - Light"),
  ("custom_battle_rain_5", "Snow - Medium"),
  ("custom_battle_rain_6", "Snow - Heavy"),
  
  这段源代码在两个地方的处理得都不够艺术,都是用的臃肿且繁琐的try_begin - else_try - try_end结构。
  第一个地方就是字符串的处理上,用符串ID的偏移的话,不管多少种下雨强度或下雪强度,2行就能搞定了。
  第二个地方是变量的循环变化上,用val_add(自加)结合val_mod(取余)也是2行就能搞定了。
  这段代码行数多但代码质量却很低,写得也很累。所以有时候单纯列举说写了几千几万行的代码一点意义都没有。实现同样的功能,写得精的话可能就十几行搞定,写得不精的话,可能代码行数就要多几倍,几十倍甚至几百倍。
  我写的Pentominoes(伤脑筋的十二块),总共就600多行代码,其中还包括了45个关卡的数据。如果用Module System里常规的方法写,别的不论,光45个关卡的数据就得花7200行代码。如果反编译Pentominoes的txt代码的话,到头来只能证明这种反编译行为是一种“聪明的白痴行为”,反编译45个关卡的txt数据最终得到的结果将会是7200行代码,而不是我写的那50多行代码。一点题外话。
  
  最后,字符串ID的偏移只是ID偏移的一种,任何有数字ID的各种ID都能这样偏移。连script都能偏移。下面就是script偏移的一个例子。
  该片断来自于我写的Pentominoes的源代码。
    (store_add, ":dest_script", "script_init_stages_data_1", "$g_stage_no"),
    (call_script, ":dest_script"),
  我写关卡数据都储存在一个一个独立的script里的。不同的关卡就得调用不同的script,跳关卡的时候,自然不可能用臃肿且繁琐的try_begin - else_try - try_end结构来根据关卡序号来调用相应的script,那样要判断45次。所以这里就用到了script-ID的偏移,用第1关的script的ID加上关卡序号(从0开始),得到":dest_script"(目标script),在call_script来调用":dest_script"(目标script)就行了。第1关的关卡序号是0,所以调用的就是"script_init_stages_data_1",第2关的关卡序号是1,自然调用的就是"script_init_stages_data_1"后面的一个script,就是"script_init_stages_data_2",依次类推。最终就用上面2行就搞定了。
  任何有数字ID的各种ID都能这样偏移才是我真正要讲的内容。只是因为这里主题已经定了,所以就侧重以字符串ID的偏移为例子来讲解。
  
  
  2-2 字符串的迭代:
  看下面的例子,这是从script_update_troop_notes里截取的一段,是关于领主的封地的。
  (assign, ":num_centers", 0),
  (str_store_string, s58, "@nowhere"),
  (try_for_range_backwards, ":cur_center", centers_begin, centers_end),
    (party_slot_eq, ":cur_center", slot_town_lord, ":troop_no"),
    (try_begin),
      (eq, ":num_centers", 0),
      (str_store_party_name_link, s58, ":cur_center"),
    (else_try),
      (eq, ":num_centers", 1),
      (str_store_party_name_link, s57, ":cur_center"),
      (str_store_string, s58, "@{s57} and {s58}"),
    (else_try),
      (str_store_party_name_link, s57, ":cur_center"),
      (str_store_string, s58, "@{s57}, {s58}"),
    (try_end),
    (val_add, ":num_centers", 1),
  (try_end),
  经过迭代,s58就包含了该领主所有的封地了,仅仅就用了两个字符串寄存器s57和s58就解决了。如果用常规的方法,一个封地一个封地地判断,并且每个封地都占用一个字符串寄存器,一来繁琐,二来字符串寄存器数量是有限的(65个),领主或者玩家的封地一多,字符串寄存器就完全不够用了。
  迭代原理比较难理解,也许你很难明白为什么频繁出现的s57和s58里寄存的字符串能不相互覆盖或者冲突,为什么能保证输出的各个字符串是相互区别开的。
  那就这样理解吧,s57和s58是两个存字符串的“容器”。s58相当于瓶子,s57相当于杯子,而":cur_center"相当于水。这里首先是把"@nowhere"放到s58里,如果领主没有封地,那么s58就只有"@nowhere"了。如果有1个封地":cur_center",那么就是直接把它放到s58,覆盖掉"@nowhere",继续判断,如果有第2个封地,那么就先把":cur_center"放到s57这个杯子里,然后“倒”到s58这个瓶子里,事实上是拿"@{s57} and {s58}"来覆盖s58,也就相当于把瓶子里的水和杯子里倒出来混合之后又倒回到瓶子里。如果有更多的封地也是按照这个原理,把新的s57和旧的s58连在一起覆盖掉旧的s58,形成新的s58。依次迭代,最终的s58里就含有所有的封地了。而且符合自然语言的语法规则,一个对象的时候就直接显示,两个对象就用and连接起来,多个对象的话,最后两个对象用and连接起来,其他的用逗号隔开。而s57里还含有最后一个封地的字符串,不过不要紧,s57再次使用的时候,s57里的内容会被新的内容覆盖,不会影响到新的内容。
  还是模拟一下迭代过程比较好,比如某个领主有5个封地:c1,c2,c3,c4,c5。注意了,下面的“=”不是等号,是赋值号。而且赋值是至右向左的,要从右往左来看。而且这个“=”也是方便说明才使用的,下面也都是伪代码。真正的代码里是用的str_store_party_name_link和str_store_string来完成字符串的赋值的。
  第1次满足条件的时候,s58 = c1。
  第2次满足条件的时候,先s57 = c2,此时"@{s57} and {s58}"里的内容就是"@c2 and c1"。然后s58 = "@{s57} and {s58}"就是 s58 = c2 and c1。
  第3次满足条件的时候,先s57 = c3,此时"@{s57}, {s58}"里的内容就是"@c3,c2 and c1"。然后s58 = "@{s57}, {s58}"就是 s58 = c3,c2 and c1。
  第4次满足条件的时候,先s57 = c4,此时"@{s57}, {s58}"里的内容就是"@c4,c3,c2 and c1"。然后s58 = "@{s57}, {s58}"就是 s58 = c4,c3,c2 and c1。
  第5次满足条件的时候,先s57 = c5,此时"@{s57}, {s58}"里的内容就是"@c5,c4,c3,c2 and c1"。然后s58 = "@{s57}, {s58}"就是 s58 = c5,c4,c3,c2 and c1。
  
  这样模拟一下,应该能看得更明白了,更多的封地也一样地迭代出来的。不光封地,其他的对象也能循环迭代,这只是一个例子而已。
  另外,根据迭代原理,先满足条件的会在后面,后满足条件的会在前面。所以这里用的try_for_range_backwards,倒过来从尾向头判断,以保证显示出来的是正着的。
  对比字符串ID的偏移,可以发现,这里都是使用的@开头的输出字符串。硬要用str开头的输出字符串也可以,不过那样做就太呆板,不够灵活快捷了。这也验证了输出字符串类型的选择不单纯是一个习惯问题。
  
  
  最后是一个综合的例子,偏移和迭代同时使用。
  (str_store_string, s0, "@ "),
  (try_for_range, ":cur_ek_slot", ek_item_0, num_equipment_kinds),
    (troop_get_inventory_slot, ":cur_item", "trp_player", ":cur_ek_slot"),
    (gt, ":cur_item", -1),
    (troop_get_inventory_slot_modifier, ":item_mod", "trp_player", ":cur_ek_slot"),
    (store_add, ":out_string", "str_imod_plain", ":item_mod"),
    (str_store_string, s2, ":out_string"),
    (str_store_item_name, s1, ":cur_item"),
    (str_store_string, s0, "@{s0}^{s2}{s1}"),
  (try_end),
  
  这样迭代之后,s0里面就包含了玩家身上10件装备以及他们的前缀,包括4件武器,4件盔甲,1匹马和1份食物(如果开启了食物栏)。如果对应的格子是空的,那么就跳过不显示,只罗列非空的格子里的物品以及他们的前缀。前缀的处理用的是字符串ID的偏移,而同时显示几件物品及前缀用的是字符串的迭代。
  如果不用字符串ID的偏移的话,代码就很臃肿且低效了。因为过多地try会使得效率降低,而且每件物品,都要把这些前缀从头到尾try一遍,效率就更低了。而这里是通过相加直接找到目标字符串,是无判断过程的。而不用迭代的话,最终输出的字符串里就需要花10个reg寄存器来判断每个格子是否是空的,要花20个字符串寄存器来分别纪录物品名字以及物品的前缀。如果是要纪录玩家物品栏里的所有物品及各自前缀的话,是必然要使用偏移和迭代。
  当然了,这只是我制造出来的一个例子,例子本身可能没有什么价值,价值都在例子折射出来的东西上。
  
  下面是要用到的字符串:
  ("imod_plain", " "), 
  ("imod_cracked", "Cracked "),
  ("imod_rusty", "Rusty "), 
  ("imod_bent", "Bent "), 
  ("imod_chipped", "Chipped "), 
  ("imod_battered", "Battered "), 
  ("imod_poor", "Poor "), 
  ("imod_crude", "Crude "), 
  ("imod_old", "Old "), 
  ("imod_cheap", "Cheap "), 
  ("imod_fine", "Fine "), 
  ("imod_well_made", "Well Made "), 
  ("imod_sharp", "Sharp "), 
  ("imod_balanced", "Balanced "), 
  ("imod_tempered", "Tempered "), 
  ("imod_deadly", "Deadly "), 
  ("imod_exquisite", "Exquisite "), 
  ("imod_masterwork", "Masterwork "), 
  ("imod_heavy", "Heavy "), 
  ("imod_strong", "Strong "), 
  ("imod_powerful", "Powerful "), 
  ("imod_tattered", "Tattered "), 
  ("imod_ragged", "Ragged "), 
  ("imod_rough", "Rough "), 
  ("imod_sturdy", "Sturdy "), 
  ("imod_thick", "Thick "), 
  ("imod_hardened", "Hardened "), 
  ("imod_reinforced", "Reinforced "), 
  ("imod_superb", "Superb "), 
  ("imod_lordly", "Lordly "), 
  ("imod_lame", "Lame "), 
  ("imod_swaybacked", "Swaybacked "), 
  ("imod_stubborn", "Stubborn "), 
  ("imod_timid", "Timid "), 
  ("imod_meek", "Meek "), 
  ("imod_spirited", "Spirited "), 
  ("imod_champion", "Champion "), 
  ("imod_fresh", "Fresh "), 
  ("imod_day_old", "Day Old "), 
  ("imod_two_day_old", "Two Day Old "), 
  ("imod_smelling", "Smelling "), 
  ("imod_rotten", "Rotten "), 
  ("imod_large_bag", "Large Bag "),

这一节会讲一下字符串里的变量问题。
  下面例子里的字符串都是字符常量。
  (display_message,"@The door is locked.",0xFFFFAAAA),
  如果要在字符串里安插变量,就得用寄存器了,而不能直接把数值变量或者字符串变量直接插在字符串里。数值变量必须赋值到一个reg类型的寄存器里,比如reg0,reg1等等。而字符串变量,比如兵种的名字,物品名字,必须赋值到字符串寄存器里,也就是s0,s1这样的寄存器。赋值的时候,不需要也不能加{}号。 而安插到字符串里就必须加{}号,如{reg0},{s0}等。
  
  下面是从Custom Commander截取的一小段代码。
  (try_begin),
    (eq, ":kinds_of_upgrade_troop", 0),
    (str_store_troop_name_by_count, s1, ":upgrade_troop", ":upgrade_size"),
    (assign, reg1, ":upgrade_size"),
    (str_store_string, s0, "@{reg1} {s1} can upgrade."),
  (else_try),
    (eq, ":kinds_of_upgrade_troop", 1),
    (str_store_troop_name_by_count, s1, ":upgrade_troop", ":upgrade_size"),
    (assign, reg1, ":upgrade_size"),
    (str_store_string, s0, "@{reg1} {s1} and {s0}"),
  (else_try),
    (str_store_troop_name_by_count, s1, ":upgrade_troop", ":upgrade_size"),
    (assign, reg1, ":upgrade_size"),
    (str_store_string, s0, "@{reg1} {s1}, {s0}"),
  (try_end),
  
  这两句就是赋值过程,一个是字符串的,一个是数值的。
    (str_store_troop_name_by_count, s1, ":upgrade_troop", ":upgrade_size"),
    (assign, reg1, ":upgrade_size"),
  字符串的这个操作叫赋值可能不大合适,但意思是差不多的。
  
  这几句就是具体的使用。
    (str_store_string, s0, "@{reg1} {s1} can upgrade."),
    (str_store_string, s0, "@{reg1} {s1} and {s0}"),
    (str_store_string, s0, "@{reg1} {s1}, {s0}"),
    
  你就不能像下面这样使用:
    (str_store_string, s0, "@":upgrade_size" ":upgrade_troop" can upgrade."),
    (str_store_string, s0, "@":upgrade_size" ":upgrade_troop" and {s0}"),
    (str_store_string, s0, "@":upgrade_size" ":upgrade_troop", {s0}"),
  全局变量(以$开头的)也不能直接插到字符串里。
    
  字符串里出现的变量只能是reg类型的寄存器和s类型的寄存器。 一个管数值,一个管字符串。同一个字符串里不要使用一样的寄存器,迭代除外。用迭代的前提是你要明白迭代原理并能熟练使用迭代,不然使用一样的寄存器,很容易就造成相互覆盖,显示出来的内容都是错误的。






  • |
  • |

热门排行榜