逻辑推理 C 语言新手指针练习版作为刚接触 C 语言指针的新手我总想找机会练手指针用法 —— 哪怕代码冗余也没关系先把指针用起来这次以 “4 嫌疑人谋杀案3 真 1 假找凶手” 为例我刻意用指针写了初始版本再结合查资料学到的指针知识一步步优化既解了题又吃透了指针的核心用法分享一下新手练习指针的全过程。一、题目背景谋杀案逻辑推理题1.1题目描述某地发生谋杀案警察确定凶手是 4 个嫌疑犯A、B、C、D中的一人。4 人的供词如下A 说不是我。B 说是 C。C 说是 D。D 说C 在胡说。已知条件3 个人说了真话1 个人说的是假话。请编写程序找出凶手。1.2题目逻辑分析写代码前先理逻辑这是新手避免混乱的关键我先给嫌疑人做索引映射方便指针 / 数组操作A0、B1、C2、D3设凶手索引为killer_idx取值 0-3把供词转化为指针 / 数组能操作的逻辑表达式1 真0 假表格嫌疑人供词逻辑表达式A(0)不是我killer_idx ! 0B(1)是 Ckiller_idx 2C(2)是 Dkiller_idx 3D(3)C 在胡说killer_idx ! 3核心条件4 个表达式结果之和 3恰好 3 真 1 假。1.3练指针的初衷我写初始代码时刻意绕了点路 —— 不用简单的数组下标而是用指针*(pi)操作数组就是为了练习指针和数组的等价关系arr[i] *(arri)哪怕代码冗余先把指针用熟再说二、第一版代码为练指针刻意保留冗余初始版本我写得很 “臃肿”但核心目的是练指针所以保留了所有指针变量只通过查资料修正了致命的语法错误确保代码能跑。2.1查资料踩的第一个坑写sofe函数时我误把判断相等的写成了赋值if(arr[i]1)运行后程序完全不对。查 C 语言指针 条件判断的资料才发现是赋值才是判断这是新手用指针 / 数组最容易踩的语法坑2.2第一版代码刻意保留指针仅修正语法#define _CRT_SECURE_NO_WARNINGS #include stdio.h // 打印凶手用指针思路遍历数组练*(arri) void print(int arr[],int n) { // 练习指针用指针遍历数组替代arr[i] int *p arr; for(int i0;in;i) { if(*(pi) 1) // 刻意用*(pi)练指针偏移 { if(i0) printf(凶手是A\n); else if(i1) printf(凶手是B\n); else if(i2) printf(凶手是C\n); else if(i3) printf(凶手是D\n); } } } // 统计真话数量查资料修正赋值→判断 int sofe(int arr[],int n) { int count 0; int *p arr; // 练习指针用指针遍历统计 for(int i0;in;i) { if(*(pi) 1) // 修正把改成 { count; } } return count; } // 核心推理函数全程用指针操作arr1练*(pi) void Jud(int arr1[], int arr2[],int n) { int flag1 0; // 冗余变量先保留后续优化 int flag2 0; int* p2 arr2; // 冗余指针刻意保留先练指针声明/赋值 int killer_idx -1; // 新增存储凶手索引 // 遍历每个嫌疑人假设i是凶手 for(int i0;in;i) { int* p arr1; // 核心用指针操作arr1存储供词真假 // 练指针偏移*(p0)等价于arr1[0] *(p0) (i ! 0); // A不是我 *(p1) (i 2); // B是C *(p2) (i 3); // C是D *(p3) (i ! 3); // DC在胡说 flag2 sofe(p, n); // 指针传参p是arr1的地址 if (flag2 3) // 3真1假找到凶手 { flag1 1; killer_idx i; break; } } // 练指针重置数组用指针把arr1所有元素置0 int *p_reset arr1; for(int i0;in;i) { *(p_reseti) 0; } *(arr1 killer_idx) 1; // 指针标记凶手 print(arr1, n); } int main() { int arr1[4] { 1,1,1,1 }; // 存储供词真假指针操作的载体 int arr2[4] { 0,0,0,0 }; // 冗余数组练指针传参 int n sizeof(arr1) / sizeof(arr1[0]); Jud(arr1, arr2,n); // 指针传参数组名本质是首元素地址 return 0; }2.3第一版思考过程刻意设计全程用*(pi)替代arr[i]就是为了吃透 “指针偏移 数组下标” 这个核心知识点哪怕代码绕弯查资料修正最初写*(pi)1判断时程序统计结果全错查 C 语言指针条件判断的资料才发现把写成了查 “逻辑推理题解法”确认核心条件是 “3 真 1 假”把flag21改成flag23冗余保留p2、flag1、arr2这些没用的指针 / 数组我先保留 —— 新手练指针先 “能用”再 “精简”一步改太多容易忘知识点。运行结果凶手是C指针操作没出错核心逻辑也对第一步练指针的目标达成三、简化步骤 1查资料优化指针使用删冗余保核心第一版能跑但很多指针是 “无效练习”比如p2全程没操作。我查了 “C 语言指针优化技巧” 的资料知道了 “指针要为功能服务不是为了用而用”开始删冗余指针 / 变量保留核心指针用法。3.1简化思路结合指针知识点删冗余指针p2、flag1、p_resetp_reset可以合并到原有循环不用单独声明保留核心指针*(pi)操作数组、指针传参这些练手的核心用法查资料学到arr1[0] arr1[1] arr1[2] arr1[3] 0等价于指针循环置 0更简洁但我仍保留指针写法练手。3.2步骤 1 代码优化指针冗余#define _CRT_SECURE_NO_WARNINGS #include stdio.h // 保留指针遍历打印 void print(int arr[],int n) { int *p arr; for(int i0;in;i) { if(*(pi) 1) { if(i0) printf(凶手是A\n); else if(i1) printf(凶手是B\n); else if(i2) printf(凶手是C\n); else if(i3) printf(凶手是D\n); } } } // 保留指针统计逻辑 int sofe(int arr[],int n) { int count 0; int *p arr; for(int i0;in;i) { if(*(pi) 1) { count; } } return count; } // 删冗余指针p2、flag1保留核心指针p void Jud(int arr1[], int arr2[],int n) { int flag2 0; int killer_idx -1; for(int i0;in;i) { int* p arr1; // 核心指针操作保留 *(p0) (i ! 0); *(p1) (i 2); *(p2) (i 3); *(p3) (i ! 3); flag2 sofe(p, n); if (flag2 3) { killer_idx i; break; } } // 简化指针置0合并循环删p_reset int *p arr1; for(int i0;in;i) *(pi) 0; *(arr1 killer_idx) 1; print(arr1, n); } int main() { int arr1[4] { 1,1,1,1 }; int arr2[4] { 0,0,0,0 }; // 暂留下一步优化 int n sizeof(arr1) / sizeof(arr1[0]); Jud(arr1, arr2,n); return 0; }3.3步骤 1 思考过程指针练习进阶查资料懂了“冗余指针会增加代码复杂度”比如p2只声明没操作完全是无效练习删了不影响指针核心用法保留*(pi)这是我练指针的核心哪怕循环置 0 可以更简洁我还是用指针写法 —— 新手练知识点先熟练再简化指针传参验证sofe(p, n)中p是arr1的首地址传参后函数内操作的还是原数组这一步验证了 “指针传参可以修改原数组” 的知识点查资料确认的。四、简化步骤 2查资料理解 “指针 函数设计”精简参数保留指针我查 “C 语言函数参数设计” 的资料时看到“函数参数越少逻辑越清晰指针传参也要按需设计”。这一步我删无用的arr2重构print函数但仍保留指针核心用法。4.1简化思路指针 函数优化删arr2全程没用到函数参数里留着只是浪费指针传参也要 “按需传递”重构print不用数组直接传凶手索引但我加了一个 “指针版 print” 的备选练手保留Jud函数内的核心指针操作*(pi)赋值、指针传参给sofe。4.2步骤 2 代码指针 函数优化#define _CRT_SECURE_NO_WARNINGS #include stdio.h //简洁版print void print(int killer_idx) { // 练指针用字符数组指针打印替代多个if char suspects[] {A,B,C,D}; char *p suspects; // 指针指向字符数组首地址 printf(凶手是%c\n, *(p killer_idx)); // 指针偏移找凶手 } // 保留指针统计逻辑 int sofe(int arr[],int n) { int count 0; int *p arr; for(int i0;in;i) { if(*(pi) 1) { count; } } return count; } // 精简参数删arr2保留核心指针操作 void Jud(int arr1[],int n) { int flag2 0; int killer_idx -1; for(int i0;in;i) { int* p arr1; *(p0) (i ! 0); *(p1) (i 2); *(p2) (i 3); *(p3) (i ! 3); flag2 sofe(p, n); if (flag2 3) { killer_idx i; break; } } print(killer_idx); // 调用新的指针版print } int main() { int arr1[4] { 1,1,1,1 }; int n sizeof(arr1) / sizeof(arr1[0]); Jud(arr1, n); // 删arr2参数 return 0; }4.3步骤 2 思考过程指针 函数设计查资料学到字符数组也能靠指针偏移访问元素*(p killer_idx)比多个 if 判断更简洁还能练指针函数参数精简Jud函数原本传arr1、arr2、n现在只传arr1和n指针传参的 “目的性” 更强 —— 新手练指针要知道 “指针是工具不是负担”核心收获指针传参的本质是 “传地址”不管是 int 数组还是 char 数组指针偏移的逻辑是通用的这一步把数组指针的用法打通了。五、简化步骤 3指针进阶优化保留指针合并冗余函数最后一步我查 “C 语言指针最佳实践” 的资料知道了 “没必要为短逻辑单独写函数”于是合并sofe函数但全程保留指针核心用法毕竟练指针是核心目的。5.1简化思路指针终极优化不丢练手目标合并sofe函数统计真话的逻辑只有几行合并到Jud中减少指针传参的次数但保留指针统计的逻辑变量名语义化flag2改true_count查资料学到 “变量名要见名知意指针代码更需要清晰命名”保留所有核心指针操作*(pi)赋值、指针遍历、字符数组指针偏移。5.2步骤 3 代码指针版极简化#define _CRT_SECURE_NO_WARNINGS #include stdio.h // 指针版打印函数练手核心 void print(int killer_idx) { char suspects[] {A,B,C,D}; char *p suspects; // 字符数组指针练手 printf(凶手是%c\n, *(p killer_idx)); } // 核心推理函数保留所有指针练手逻辑 void findKiller() { int killer_idx -1; int arr1[4] {0}; // 指针操作的载体 int *p arr1; // 核心指针 // 遍历嫌疑人练指针赋值 for(int i0;i4;i) { // 指针偏移赋值练*(pi) *(p0) (i ! 0); // A的供词 *(p1) (i 2); // B的供词 *(p2) (i 3); // C的供词 *(p3) (i ! 3); // D的供词 // 指针统计真话数合并sofe函数保留指针逻辑 int true_count 0; int *p_count arr1; for(int j0;j4;j) { if(*(p_count j) 1) true_count; } if (true_count 3) { killer_idx i; break; } } print(killer_idx); } int main() { findKiller(); return 0; }5.3步骤 3 思考过程指针练习总结合并函数但不丢指针sofe函数合并后我仍用p_count指针统计没改用数组下标 —— 练指针的目标要守住查资料的关键收获arr[i]等价于*(arri)指针偏移和数组下标是同一逻辑的两种写法指针传参的本质是 “共享地址”所以函数内操作指针能修改原数组指针要 “按需使用”冗余指针只会增加理解成本最终代码既保留了所有练指针的核心用法数组指针、字符指针、指针偏移、指针统计又精简了冗余逻辑解题 练指针的目标都达成了。六、新手练指针的核心收获附避坑指南1. 指针核心知识点查资料 练手总结基础等价关系arr[i] *(arri) *(iarr) i[arr]后两种少用但验证了指针偏移的本质指针传参数组名作为参数传递时本质是传递首元素的地址函数内修改指针指向的内容会影响原数组字符数组指针char *p str中*(pi)可以访问字符串的第 i 个字符和 int 数组指针逻辑一致。2. 新手练指针的避坑点我踩过的坑语法坑把判断写成赋值指针条件判断尤其要注意逻辑坑为了用指针而加冗余指针比如p2忘记 “指针是工具不是目的”命名坑用flag1/flag2这种无意义命名指针代码本身抽象命名不清晰更难理解。3. 练指针的小技巧新手向先 “刻意用”哪怕代码绕弯先把指针的核心用法偏移、传参、遍历用熟再 “查资料优化”写完初始版本后查指针优化的资料区分 “有效指针” 和 “冗余指针”最后 “精简保留”删冗余但保留核心练手逻辑比如我最终版仍保留所有指针操作只合并了冗余函数。七、补充最优写法高效简洁版前面的所有版本核心目的是练指针所以刻意保留了指针操作哪怕有更简洁的写法。但如果单纯追求 “最优解”逻辑最简洁、代码最短、效率最高、可读性最好可以去掉所有冗余不用刻意用指针指针是工具无需为了用而用以下是这道题的最优写法7.1最优写法代码#define _CRT_SECURE_NO_WARNINGS #include stdio.h int main() { // 嫌疑人映射0A,1B,2C,3D char suspects[] {A,B,C,D}; // 遍历每个嫌疑人假设i是凶手 for(int i0;i4;i) { // 直接计算4人供词真假统计真话数核心逻辑一步到位 int true_count (i!0) (i2) (i3) (i!3); if(true_count 3) // 满足3真1假直接打印凶手 { printf(凶手是%c\n, suspects[i]); break; } } return 0; }7.2最优写法解析1.为什么最优效率最高无多余函数调用、无冗余指针 / 数组循环仅执行 4 次每次循环仅 1 行计算时间复杂度 O (1)固定 4 个嫌疑人无额外开销代码最简仅 15 行代码核心逻辑浓缩为 1 行真话数统计可读性最好去掉所有无关语法只保留 “遍历嫌疑人→计算真话数→判断输出” 的核心逻辑新手也能快速理解。2.与练手版的区别练手版核心是 “借解题练指针”刻意用指针操作哪怕冗余重点在知识点练习最优版核心是 “高效解题”按需选择语法不用多余工具重点在逻辑简洁。3.关键逻辑利用 C 语言 “逻辑表达式结果为 1真或 0假” 的特性直接把 4 个供词的逻辑表达式相加得到真话数无需额外数组 / 指针存储供词真假直接计算、直接判断一步到位。八、最终总结这次解题的核心目标不是 “写最简洁的代码”而是 “借解题练指针”—— 从刻意写冗余指针代码到查资料优化冗余、保留核心我不仅找出了凶手C更吃透了数组指针、字符指针、指针传参这些新手必学的知识点。对新手来说练指针不用追求 “一步到位写最优解”先 “能用指针实现功能”再 “查资料优化指针用法”最后 “精简并保留核心练手逻辑”这个过程比直接抄最优解更有收获。而最优写法则告诉我们C 语言中指针、数组都是工具解题的核心是 “逻辑清晰”按需选择工具不堆砌语法才是最优雅、最高效的编程方式。