断点调试
针对习题 1 的逐题解答(基于上述代码) a) 在 int andereZahl = 8; 这一行设断点,然后逐步执行。当某行 x 被高亮时,哪些行已执行?调试器如何处理条件判断?
答:
当第 x 行被高亮(即将执行)时,第 x 行之前的所有语句都已执行完毕。 例如,当高亮到 printf("Zahlenvergleich\n"); 时,eineZahl(声明)、andereZahl = 8、text1 = new...、text2[20] 声明都已完成。 调试器遇到 if 条件时: 会先计算整个条件表达式的布尔值(如 eineZahl == andereZahl)。 然后根据结果跳转到对应的分支。 在“单步跳过”(Step Over)模式下,不会进入未被选中的分支,而是直接执行被选中分支的第一条语句或跳过整个 if-else 结构。 b) 在 printf("Zahlenvergleich\n"); 处设断点。此时各变量的值是多少?text2 数组元素是什么?是否稳定?
答:
eineZahl:未初始化,其值是随机垃圾值(例如可能是 -858993460 或其他任意整数)。 andereZahl:已初始化为 8,值稳定。 text1:是一个有效的指针(指向堆上 20 字节内存),但指向的内容未初始化,是垃圾数据。 text2:是一个栈上数组,全部 20 个元素都是未初始化的垃圾字节(例如可能是 0xCC, 0xFD, 或其他随机值)。 稳定性:eineZahl 和 text2 的值在多次运行中不稳定,每次运行都可能不同。 解释:这是因为 C/C++ 不会自动初始化局部变量。它们的初始值就是内存中原来的内容(“脏数据”)。这是常见的 Bug 源头,调试器能直观暴露这个问题。 c) 声明两个 char 变量:'q' 和 '&'。调试器能提供什么信息?
答: 在调试器的 Variables(变量)视图 中,你可以看到:
字符形式:'q' 和 '&' 十进制 ASCII 码:113 和 38 十六进制 ASCII 码:0x71 和 0x26 这揭示了字符在计算机内部是以整数编码(ASCII/Unicode)存储的。
d) 在 else if (andereZahl > eineZahl) 行设断点,运行到此处,将 andereZahl 改为 15,再继续运行。会发生什么?
答:
程序运行到该断点时,原始值为:eineZahl = 9, andereZahl = 8。 此时条件 andereZahl > eineZahl(即 8 > 9)为 false,正常情况下会进入 else 分支。 但你在调试器中将 andereZahl 手动改为 15 后: 条件变为 15 > 9 → true 因此,程序会执行这个 else if 分支,输出: text 编辑 Der kleinere Wert ist eineZahl: 9 结论:调试器允许动态修改变量,从而改变程序的执行路径,用于测试不同场景。
#include <cstdio> // 或保留 stdio.h 也可以,但这是 C 风格
int main() {
int eineZahl; // 未初始化!
int andereZahl = 8; // 初始化为 8
char* text1 = new char[20]; // 动态分配,但未初始化内容
char text2[20]; // 栈上数组,未初始化!
printf("Zahlenvergleich\n"); // ← 断点 b) 设在这里
eineZahl = 9; // 在此之后才赋值
if (eineZahl == andereZahl) {
printf("Beide Werte sind gleich gross: %d\n", eineZahl);
}
else if (andereZahl > eineZahl) { // ← 断点 d) 设在这里
printf("Der kleinere Wert ist eineZahl: %d\n", eineZahl);
}
else {
printf("Der kleinere Wert ist andereZahl: %d\n", andereZahl); // 注意:原题这里打印错了,应为 andereZahl
}
delete[] text1;
return 0;
}
解释:这是因为 C/C++ 不会自动初始化局部变量。它们的初始值就是内存中原来的内容(“脏数据”)。这是常见的 Bug 源头,调试器能直观暴露这个问题。
枚举类型,结构体的应用
习题 2:宝可梦输出 (7 分) 六只宝可梦的类型、名称和等级定义如下:
火系 = {(小火龙, 等级 7), (六尾, 等级 7)} 水系 = {(杰尼龟, 等级 3), (可达鸭, 等级 2)} 草系 = {(妙蛙种子, 等级 6), (蔓藤怪, 等级 3)} 请创建一个名为 Praktikum_5/Aufgabe_2 的新项目,并将提供的文件 pokemon.cpp 添加进去。
a) 在模板中定义必要的数据类型来表示一只宝可梦。为此,请定义一个名为 PokeType 的枚举类型 (enum),用于表示三种宝可梦类型(火、水、草)。接着,定义一个名为 Pokemon 的结构体 (struct),它包含一个名称(最多 20 个字符)、一个类型 (PokeType) 和一个等级 (int)。(2 分)
b) 将上述六只宝可梦全部以 Pokemon 结构体的形式存储在一个名为 minipokedex 的数组中。然后编写一个辅助函数 void printpokemon(struct Pokemon poke),该函数以如下格式在控制台上输出单只宝可梦: 名称 (等级 N, 类型 PokeType) 在 main 函数中,使用此函数输出所有宝可梦。(1 分)
c) 使用一个辅助函数 void kampf(struct Pokemon pokeA, struct Pokemon pokeB) 来模拟两只宝可梦之间的对战。通过根据战斗力进行条件判断来确定对战的胜者(或平局)。战斗力的计算公式如下: 战斗力 = 等级 * 效果
效果规则如下:
火系攻击对草系的效果为 2 倍。 水系攻击对火系的效果为 3 倍。 草系攻击对水系的效果为 2 倍。 所有其他攻击(同类型或反向克制)的效果为 1 倍。 该函数应在控制台上始终输出包含两只参战宝可梦的对战结果。输出示例如下: 小火龙 (等级 7, 类型 火) 战胜了 妙蛙种子 (等级 6, 类型 草) 或者 对战以平局告终,因为 小火龙 (等级 7) 和 六尾 (等级 7) 的实力相同。
在 main 函数中读取两个索引,并模拟对应宝可梦之间的对战。(4 分)
/*
* Speichern und Ausgeben von Pokémon
*/
#include <iostream>
using namespace std;
//============================================
// a1) Hier bitte den Aufz?hlungstyp "PokeType" für verschiedene Pokémon-Typen definieren
enum PokeType {
Feuer,
Wasser,
Pflanze
};
// a2) Hier bitte die "Pokemon"-Struktur definieren, welche den Namen (nur bis zu 20 Zeichen lang) und Typen (PokeType) eines Pokémon beinhaltet
struct Pokemon {
char name[21]; // 20 Zeichen + '\0'
PokeType type;
int level;
};
//============================================
//--------------------------------------------
// b1) Hier bitte ein Feld namens "minipokedex" mit den genannten Pokemon definieren
struct Pokemon minipokedex[6] = {
{"Glumanda", Feuer, 7},
{"Vulpix", Feuer, 7},
{"Schiggy", Wasser, 3}, // Note: Corrected spelling from "Schiggym" to "Schiggy"
{"Enton", Wasser, 2},
{"Bisasam", Pflanze, 6},
{"Tangela", Pflanze, 3}
};
// Hilfsfunktion zur Typumwandlung für die Ausgabe
const char* getTypeString(PokeType t) {
switch (t) {
case Feuer: return "Feuer";
case Wasser: return "Wasser";
case Pflanze: return "Pflanze";
default: return "Unbekannt";
}
}
// b2) Hier bitte die Funktion printpokemon definieren
void printpokemon(struct Pokemon poke) {
cout << poke.name << " (Level " << poke.level << ", Typ " << getTypeString(poke.type) << ")";
}
//--------------------------------------------
//............................................
// c1) Kampfausgang berechnen
void kampf(struct Pokemon pokeA, struct Pokemon pokeB) {
// Effektivit?t von A gegen B berechnen
double effA = 1.0;
if (pokeA.type == Feuer && pokeB.type == Pflanze) {
effA = 2.0;
} else if (pokeA.type == Wasser && pokeB.type == Feuer) {
effA = 3.0;
} else if (pokeA.type == Pflanze && pokeB.type == Wasser) {
effA = 2.0;
}
// Effektivit?t von B gegen A berechnen
double effB = 1.0;
if (pokeB.type == Feuer && pokeA.type == Pflanze) {
effB = 2.0;
} else if (pokeB.type == Wasser && pokeA.type == Feuer) {
effB = 3.0;
} else if (pokeB.type == Pflanze && pokeA.type == Wasser) {
effB = 2.0;
}
double kraftA = pokeA.level * effA;
double kraftB = pokeB.level * effB;
if (kraftA > kraftB) {
printpokemon(pokeA);
cout << " gewinnt gegen ";
printpokemon(pokeB);
cout << endl;
} else if (kraftB > kraftA) {
printpokemon(pokeB);
cout << " gewinnt gegen ";
printpokemon(pokeA);
cout << endl;
} else {
cout << "Der Kampf endet unentschieden, weil ";
printpokemon(pokeA);
cout << " und ";
printpokemon(pokeB);
cout << " gleich stark sind." << endl;
}
}
//............................................
int main()
{
//--------------------------------------------
// b3) Bitte geben sie alle Pokémon im minipokedex aus
for (int i = 0; i < 6; i++) {
printpokemon(minipokedex[i]);
cout << endl;
}
//--------------------------------------------
//............................................
// c2) Pokemon w?hlen und Kampf simulieren
int idx1, idx2;
cin >> idx1 >> idx2;
kampf(minipokedex[idx1], minipokedex[idx2]);
//............................................
return 0;
}
—— 本文来自火龙信奥(义乌睿码科技):义乌青少年信息学奥赛与编程教育平台,专注 CSP-J/S、NOIP、GESP 竞赛培训,线上线下融合教学,助力编程升学。网址:hlcoding.com