[{"data":1,"prerenderedAt":10395},["ShallowReactive",2],{"series:index":3,"content:\u002Fugcourses\u002Fjunior-spring\u002Fcomputerorganization\u002Fl3-cmemory":972,"surround:\u002Fugcourses\u002Fjunior-spring\u002Fcomputerorganization\u002Fl3-cmemory":10392},[4,15,25,35,45,54,64,74,82,91,101,110,120,129,138,147,152,162,172,181,189,194,203,213,223,233,243,253,263,273,283,293,303,313,323,333,339,348,357,366,372,381,391,401,410,420,429,439,448,457,467,476,485,494,504,513,522,531,540,549,559,569,578,587,596,605,610,620,629,638,644,649,658,666,674,679,687,692,700,708,716,724,732,740,749,758,766,774,779,787,796,805,814,823,831,836,845,850,855,863,871,879,887,895,900,905,914,923,931,939,947,955,963],{"stem":5,"path":6,"title":7,"date":8,"collapsed":9,"readingTime":10},"posts\u002F2024\u002FXZZD-Aquire-Score","\u002F2024\u002F10\u002F23\u002FXZZD-Aquire-Score","XZZD_Aquire_Score","2024-10-23 15:08:30",false,{"text":11,"minutes":12,"time":13,"words":14},"3 min read",2.955,177300,591,{"stem":16,"path":17,"title":18,"date":19,"collapsed":9,"readingTime":20},"posts\u002F2024\u002Felec-design-contest-2024","\u002F2024\u002F07\u002F07\u002Felec-design-contest-2024","东四——隐秘的角落","2024-07-07 11:44:32",{"text":21,"minutes":22,"time":23,"words":24},"2 min read",1.74,104400,348,{"stem":26,"path":27,"title":28,"date":29,"collapsed":9,"readingTime":30},"posts\u002F2024\u002Fmechanics","\u002F2024\u002F07\u002F08\u002Fmechanics","about力学","2024-07-08 14:14:07",{"text":31,"minutes":32,"time":33,"words":34},"9 min read",8.54,512400,1708,{"stem":36,"path":37,"title":38,"date":39,"collapsed":9,"readingTime":40},"posts\u002F2025\u002F25GElecDesignContest","\u002F2025\u002F08\u002F26\u002F25GElecDesignContest","纪念最好的大二五组 - Soleil 的 电赛回忆录","2025-08-26 11:06:33",{"text":41,"minutes":42,"time":43,"words":44},"30 min read",29.99,1799400,5998,{"stem":46,"path":47,"title":48,"date":49,"collapsed":9,"readingTime":50},"posts\u002F2025\u002FEngineering-Innovation","\u002F2025\u002F01\u002F20\u002FEngineering-Innovation","工程实践与创新能力大赛","2025-01-20 22:20:59",{"text":21,"minutes":51,"time":52,"words":53},1.365,81900,273,{"stem":55,"path":56,"title":57,"date":58,"collapsed":9,"readingTime":59},"posts\u002F2025\u002FTOP","\u002F2025\u002F01\u002F20\u002FTOP","置顶","3025-01-20 21:53:36",{"text":60,"minutes":61,"time":62,"words":63},"1 min read",0.285,17100,57,{"stem":65,"path":66,"title":67,"date":68,"collapsed":9,"readingTime":69},"posts\u002FUGCourses\u002F1.freshman-spring\u002Fengineering-drawing-notes","\u002F2024\u002F06\u002F30\u002Fengineering-drawing-notes","工程图学期末复习","2024-06-30 21:02:09",{"text":70,"minutes":71,"time":72,"words":73},"15 min read",14.78,886800,2956,{"stem":75,"path":76,"title":77,"date":78,"collapsed":9,"readingTime":79},"posts\u002FUGCourses\u002F1.freshman-spring\u002Findex","\u002Fugcourses\u002Ffreshman-spring","大一春夏","2024-06-30 19:15:28",{"text":80,"minutes":81,"time":81,"words":81},"0 min read",0,{"stem":83,"path":84,"title":85,"date":86,"collapsed":9,"readingTime":87},"posts\u002FUGCourses\u002F2.sophomore-autumn\u002FComplex-Analysis","\u002F2025\u002F01\u002F21\u002FComplex-Analysis","复变函数与积分变换","2025-01-21 13:23:59",{"text":11,"minutes":88,"time":89,"words":90},2.855,171300,571,{"stem":92,"path":93,"title":94,"date":95,"collapsed":9,"readingTime":96},"posts\u002FUGCourses\u002F2.sophomore-autumn\u002FFundamentalsMarxism","\u002F2025\u002F06\u002F06\u002FFundamentalsMarxism","马克思主义基本原理","2025-06-06 00:22:45",{"text":97,"minutes":98,"time":99,"words":100},"6 min read",5.54,332400,1108,{"stem":102,"path":103,"title":104,"date":105,"collapsed":9,"readingTime":106},"posts\u002FUGCourses\u002F2.sophomore-autumn\u002FMathematical-Modeling","\u002F2024\u002F09\u002F22\u002FMathematical-Modeling","数学建模（课程）","2024-09-22 19:07:42",{"text":11,"minutes":107,"time":108,"words":109},2.475,148500,495,{"stem":111,"path":112,"title":113,"date":114,"collapsed":9,"readingTime":115},"posts\u002FUGCourses\u002F2.sophomore-autumn\u002FMicroelectronic-Circuits","\u002F2025\u002F01\u002F20\u002FMicroelectronic-Circuits","电基小子","2025-01-20 23:02:49",{"text":116,"minutes":117,"time":118,"words":119},"5 min read",4.225,253500,845,{"stem":121,"path":122,"title":123,"date":124,"collapsed":9,"readingTime":125},"posts\u002FUGCourses\u002F2.sophomore-autumn\u002FMumerical-Analysis","\u002F2024\u002F09\u002F19\u002FMumerical-Analysis","数值分析方法","2024-09-19 17:12:56",{"text":11,"minutes":126,"time":127,"words":128},2.1,126000,420,{"stem":130,"path":131,"title":132,"date":133,"collapsed":9,"readingTime":134},"posts\u002FUGCourses\u002F2.sophomore-autumn\u002FProbability-Theory","\u002F2025\u002F01\u002F21\u002FProbability-Theory","概率论与数理统计","2025-01-21 12:52:11",{"text":11,"minutes":135,"time":136,"words":137},2.205,132300,441,{"stem":139,"path":140,"title":141,"date":142,"collapsed":9,"readingTime":143},"posts\u002FUGCourses\u002F2.sophomore-autumn\u002Fengineering-training","\u002F2024\u002F09\u002F17\u002Fengineering-training","工程训练","2024-09-17 21:40:26",{"text":11,"minutes":144,"time":145,"words":146},2.385,143100,477,{"stem":148,"path":149,"title":150,"date":78,"collapsed":9,"readingTime":151},"posts\u002FUGCourses\u002F2.sophomore-autumn\u002Findex","\u002Fugcourses\u002Fsophomore-autumn","大二秋冬",{"text":80,"minutes":81,"time":81,"words":81},{"stem":153,"path":154,"title":155,"date":156,"collapsed":9,"readingTime":157},"posts\u002FUGCourses\u002F2.sophomore-autumn\u002Fsolid-state-physics","\u002F2024\u002F09\u002F11\u002Fsolid-state-physics","固体物理基础","2024-09-11 19:15:26",{"text":158,"minutes":159,"time":160,"words":161},"4 min read",3.21,192600,642,{"stem":163,"path":164,"title":165,"date":166,"collapsed":9,"readingTime":167},"posts\u002FUGCourses\u002F3.sophomore-spring\u002FAIFrontier","\u002Fugcourses\u002Fsophomore-spring\u002Faifrontier","人工智能前沿","2025-06-12 14:58:16",{"text":168,"minutes":169,"time":170,"words":171},"25 min read",24.02,1441200,4804,{"stem":173,"path":174,"title":175,"date":166,"collapsed":9,"readingTime":176},"posts\u002FUGCourses\u002F3.sophomore-spring\u002FDigitalSys","\u002Fugcourses\u002Fsophomore-spring\u002Fdigitalsys","数字系统设计",{"text":177,"minutes":178,"time":179,"words":180},"45 min read",44.87,2692200,8974,{"stem":182,"path":183,"title":184,"date":166,"collapsed":9,"readingTime":185},"posts\u002FUGCourses\u002F3.sophomore-spring\u002FMaoTheory","\u002F2025\u002F06\u002F12\u002FMaoTheory","毛概",{"text":70,"minutes":186,"time":187,"words":188},14.63,877800,2926,{"stem":190,"path":191,"title":192,"date":78,"collapsed":9,"readingTime":193},"posts\u002FUGCourses\u002F3.sophomore-spring\u002Findex","\u002Fugcourses\u002Fsophomore-spring","大二春夏",{"text":80,"minutes":81,"time":81,"words":81},{"stem":195,"path":196,"title":197,"date":198,"collapsed":9,"readingTime":199},"posts\u002FUGCourses\u002F4.junior-autumn\u002F1.microelectronic-devices\u002FLecture1","\u002Fugcourses\u002Fjunior-autumn\u002Fmicroelectronic-devices\u002Flecture1","Lecture 1：摩尔定律与 CMOS","2026-09-09 19:39:00",{"text":60,"minutes":200,"time":201,"words":202},0.535,32100,107,{"stem":204,"path":205,"title":206,"date":207,"collapsed":9,"readingTime":208},"posts\u002FUGCourses\u002F4.junior-autumn\u002F1.microelectronic-devices\u002FLecture10","\u002Fugcourses\u002Fjunior-autumn\u002Fmicroelectronic-devices\u002Flecture10","Lecture 10：从 MOS 电容到 MOSFET","2026-09-10 00:10:00",{"text":209,"minutes":210,"time":211,"words":212},"18 min read",17.615,1056900,3523,{"stem":214,"path":215,"title":216,"date":217,"collapsed":9,"readingTime":218},"posts\u002FUGCourses\u002F4.junior-autumn\u002F1.microelectronic-devices\u002FLecture11","\u002Fugcourses\u002Fjunior-autumn\u002Fmicroelectronic-devices\u002Flecture11","Lecture 11：亚阈值效应与 MOSFET 尺寸缩放","2026-09-10 00:20:00",{"text":219,"minutes":220,"time":221,"words":222},"23 min read",22.35,1341000,4470,{"stem":224,"path":225,"title":226,"date":227,"collapsed":9,"readingTime":228},"posts\u002FUGCourses\u002F4.junior-autumn\u002F1.microelectronic-devices\u002FLecture12","\u002Fugcourses\u002Fjunior-autumn\u002Fmicroelectronic-devices\u002Flecture12","Lecture 12：MOSFET 尺寸缩微","2026-09-10 00:30:00",{"text":229,"minutes":230,"time":231,"words":232},"35 min read",34.225,2053500,6845,{"stem":234,"path":235,"title":236,"date":237,"collapsed":9,"readingTime":238},"posts\u002FUGCourses\u002F4.junior-autumn\u002F1.microelectronic-devices\u002FLecture13","\u002Fugcourses\u002Fjunior-autumn\u002Fmicroelectronic-devices\u002Flecture13","Lecture 13：MOS 新范式","2026-09-10 00:40:00",{"text":239,"minutes":240,"time":241,"words":242},"24 min read",23.25,1395000,4650,{"stem":244,"path":245,"title":246,"date":247,"collapsed":9,"readingTime":248},"posts\u002FUGCourses\u002F4.junior-autumn\u002F1.microelectronic-devices\u002FLecture2","\u002Fugcourses\u002Fjunior-autumn\u002Fmicroelectronic-devices\u002Flecture2","Lecture 2：半导体基础","2026-09-09 19:39:31",{"text":249,"minutes":250,"time":251,"words":252},"7 min read",6.02,361200,1204,{"stem":254,"path":255,"title":256,"date":257,"collapsed":9,"readingTime":258},"posts\u002FUGCourses\u002F4.junior-autumn\u002F1.microelectronic-devices\u002FLecture3","\u002Fugcourses\u002Fjunior-autumn\u002Fmicroelectronic-devices\u002Flecture3","Lecture 3：费米能级、载流子散射与迁移率","2026-09-09 21:00:00",{"text":259,"minutes":260,"time":261,"words":262},"13 min read",12.565,753900,2513,{"stem":264,"path":265,"title":266,"date":267,"collapsed":9,"readingTime":268},"posts\u002FUGCourses\u002F4.junior-autumn\u002F1.microelectronic-devices\u002FLecture4","\u002Fugcourses\u002Fjunior-autumn\u002Fmicroelectronic-devices\u002Flecture4","Lecture 4：漂移电流、扩散电流与产生复合","2026-09-09 23:00:00",{"text":269,"minutes":270,"time":271,"words":272},"10 min read",9.22,553200,1844,{"stem":274,"path":275,"title":276,"date":277,"collapsed":9,"readingTime":278},"posts\u002FUGCourses\u002F4.junior-autumn\u002F1.microelectronic-devices\u002FLecture5","\u002Fugcourses\u002Fjunior-autumn\u002Fmicroelectronic-devices\u002Flecture5","Lecture 5：PN 结与耗尽区","2026-09-09 23:10:00",{"text":279,"minutes":280,"time":281,"words":282},"14 min read",13.515,810900,2703,{"stem":284,"path":285,"title":286,"date":287,"collapsed":9,"readingTime":288},"posts\u002FUGCourses\u002F4.junior-autumn\u002F1.microelectronic-devices\u002FLecture6","\u002Fugcourses\u002Fjunior-autumn\u002Fmicroelectronic-devices\u002Flecture6","Lecture 6：正向偏置 PN 结与理想二极管方程","2026-09-09 23:20:00",{"text":289,"minutes":290,"time":291,"words":292},"20 min read",19.095,1145700,3819,{"stem":294,"path":295,"title":296,"date":297,"collapsed":9,"readingTime":298},"posts\u002FUGCourses\u002F4.junior-autumn\u002F1.microelectronic-devices\u002FLecture7","\u002Fugcourses\u002Fjunior-autumn\u002Fmicroelectronic-devices\u002Flecture7","Lecture 7：金半接触","2026-09-09 23:40:00",{"text":299,"minutes":300,"time":301,"words":302},"16 min read",15.195,911700,3039,{"stem":304,"path":305,"title":306,"date":307,"collapsed":9,"readingTime":308},"posts\u002FUGCourses\u002F4.junior-autumn\u002F1.microelectronic-devices\u002FLecture8","\u002Fugcourses\u002Fjunior-autumn\u002Fmicroelectronic-devices\u002Flecture8","Lecture 8：PN 结在光电领域的应用","2026-09-09 23:50:00",{"text":309,"minutes":310,"time":311,"words":312},"54 min read",53.955,3237300,10791,{"stem":314,"path":315,"title":316,"date":317,"collapsed":9,"readingTime":318},"posts\u002FUGCourses\u002F4.junior-autumn\u002F1.microelectronic-devices\u002FLecture9","\u002Fugcourses\u002Fjunior-autumn\u002Fmicroelectronic-devices\u002Flecture9","Lecture 9：MOS 电容","2026-09-10 00:00:00",{"text":319,"minutes":320,"time":321,"words":322},"21 min read",20.355,1221300,4071,{"stem":324,"path":325,"title":326,"date":327,"collapsed":9,"readingTime":328},"posts\u002FUGCourses\u002F4.junior-autumn\u002F1.microelectronic-devices\u002FMidtermExam","\u002Fugcourses\u002Fjunior-autumn\u002Fmicroelectronic-devices\u002Fmidtermexam","期中复习题","2026-09-09 23:30:00",{"text":329,"minutes":330,"time":331,"words":332},"8 min read",7.595,455700,1519,{"stem":334,"path":335,"title":336,"date":337,"collapsed":9,"readingTime":338},"posts\u002FUGCourses\u002F4.junior-autumn\u002F1.microelectronic-devices\u002FMockExam","\u002Fugcourses\u002Fjunior-autumn\u002Fmicroelectronic-devices\u002Fmockexam","押题密卷","2026-09-10 00:50:00",{"text":21,"minutes":51,"time":52,"words":53},{"stem":340,"path":341,"title":342,"date":198,"collapsed":343,"readingTime":344},"posts\u002FUGCourses\u002F4.junior-autumn\u002F1.microelectronic-devices\u002Findex","\u002Fugcourses\u002Fjunior-autumn\u002Fmicroelectronic-devices","微电子器件",true,{"text":158,"minutes":345,"time":346,"words":347},3.055,183300,611,{"stem":349,"path":350,"title":351,"date":352,"collapsed":9,"readingTime":353},"posts\u002FUGCourses\u002F4.junior-autumn\u002F2.ICCAD\u002F1.HistoryMetaCharacters","\u002Fugcourses\u002Fjunior-autumn\u002Ficcad\u002Fhistorymetacharacters","History & Meta Characters","2026-09-10 20:25:44",{"text":269,"minutes":354,"time":355,"words":356},9.36,561600,1872,{"stem":358,"path":359,"title":360,"date":361,"collapsed":9,"readingTime":362},"posts\u002FUGCourses\u002F4.junior-autumn\u002F2.ICCAD\u002F10.PastExams\u002F1.ExamAutumn2223","\u002Fugcourses\u002Fjunior-autumn\u002Ficcad\u002Fpastexams\u002Fexamautumn2223","22-23 秋冬历年卷","2026-09-10 20:26:49",{"text":319,"minutes":363,"time":364,"words":365},20.295,1217700,4059,{"stem":367,"path":368,"title":369,"date":370,"collapsed":9,"readingTime":371},"posts\u002FUGCourses\u002F4.junior-autumn\u002F2.ICCAD\u002F10.PastExams\u002F2.Exam2021","\u002Fugcourses\u002Fjunior-autumn\u002Ficcad\u002Fpastexams\u002Fexam2021","20-21 历年卷","2026-09-10 20:26:50",{"text":289,"minutes":290,"time":291,"words":292},{"stem":373,"path":374,"title":375,"date":376,"collapsed":9,"readingTime":377},"posts\u002FUGCourses\u002F4.junior-autumn\u002F2.ICCAD\u002F10.PastExams\u002F3.ExamRecall1","\u002Fugcourses\u002Fjunior-autumn\u002Ficcad\u002Fpastexams\u002Fexamrecall1","不知名回忆卷 1","2026-09-10 20:26:51",{"text":116,"minutes":378,"time":379,"words":380},4.215,252900,843,{"stem":382,"path":383,"title":384,"date":385,"collapsed":9,"readingTime":386},"posts\u002FUGCourses\u002F4.junior-autumn\u002F2.ICCAD\u002F10.PastExams\u002F4.ExamRecall23","\u002Fugcourses\u002Fjunior-autumn\u002Ficcad\u002Fpastexams\u002Fexamrecall23","不知名回忆卷 2\u002F3","2026-09-10 20:26:52",{"text":387,"minutes":388,"time":389,"words":390},"12 min read",11.235,674100,2247,{"stem":392,"path":393,"title":394,"date":395,"collapsed":9,"readingTime":396},"posts\u002FUGCourses\u002F4.junior-autumn\u002F2.ICCAD\u002F11.Detailed\u002F1.Chapter1-Introduction","\u002Fugcourses\u002Fjunior-autumn\u002Ficcad\u002Fdetailed\u002Fchapter1-introduction","Chapter 1：Introduction","2026-09-10 20:27:26",{"text":397,"minutes":398,"time":399,"words":400},"19 min read",18.435,1106100,3687,{"stem":402,"path":403,"title":404,"date":405,"collapsed":9,"readingTime":406},"posts\u002FUGCourses\u002F4.junior-autumn\u002F2.ICCAD\u002F11.Detailed\u002F2.Chapter2-IntroductionToUnix","\u002Fugcourses\u002Fjunior-autumn\u002Ficcad\u002Fdetailed\u002Fchapter2-introductiontounix","Chapter 2：Introduction to Unix","2026-09-10 20:27:40",{"text":168,"minutes":407,"time":408,"words":409},24.895,1493700,4979,{"stem":411,"path":412,"title":413,"date":414,"collapsed":9,"readingTime":415},"posts\u002FUGCourses\u002F4.junior-autumn\u002F2.ICCAD\u002F11.Detailed\u002F3.Chapter3-ViAndShell","\u002Fugcourses\u002Fjunior-autumn\u002Ficcad\u002Fdetailed\u002Fchapter3-viandshell","Chapter 3：Vi and Shell","2026-09-10 20:27:41",{"text":416,"minutes":417,"time":418,"words":419},"32 min read",31.035,1862100,6207,{"stem":421,"path":422,"title":423,"date":424,"collapsed":9,"readingTime":425},"posts\u002FUGCourses\u002F4.junior-autumn\u002F2.ICCAD\u002F11.Detailed\u002F4.Chapter4-Network","\u002Fugcourses\u002Fjunior-autumn\u002Ficcad\u002Fdetailed\u002Fchapter4-network","Chapter 4：Network","2026-09-10 20:27:49",{"text":239,"minutes":426,"time":427,"words":428},23.975,1438500,4795,{"stem":430,"path":431,"title":432,"date":433,"collapsed":9,"readingTime":434},"posts\u002FUGCourses\u002F4.junior-autumn\u002F2.ICCAD\u002F11.Detailed\u002F5.Chapter5-Administration","\u002Fugcourses\u002Fjunior-autumn\u002Ficcad\u002Fdetailed\u002Fchapter5-administration","Chapter 5：Administration","2026-09-10 20:27:51",{"text":435,"minutes":436,"time":437,"words":438},"36 min read",35.41,2124600,7082,{"stem":440,"path":441,"title":442,"date":443,"collapsed":9,"readingTime":444},"posts\u002FUGCourses\u002F4.junior-autumn\u002F2.ICCAD\u002F11.Detailed\u002F6.Chapter6-Xwindow","\u002Fugcourses\u002Fjunior-autumn\u002Ficcad\u002Fdetailed\u002Fchapter6-xwindow","Chapter 6：Xwindow","2026-09-10 20:27:53",{"text":319,"minutes":445,"time":446,"words":447},20.235,1214100,4047,{"stem":449,"path":450,"title":451,"date":452,"collapsed":9,"readingTime":453},"posts\u002FUGCourses\u002F4.junior-autumn\u002F2.ICCAD\u002F11.Detailed\u002F7.Chapter7-SoftwareDevelopmentTools","\u002Fugcourses\u002Fjunior-autumn\u002Ficcad\u002Fdetailed\u002Fchapter7-softwaredevelopmenttools","Chapter 7：Software Development Tools","2026-09-10 20:27:54",{"text":209,"minutes":454,"time":455,"words":456},17.85,1071000,3570,{"stem":458,"path":459,"title":460,"date":461,"collapsed":9,"readingTime":462},"posts\u002FUGCourses\u002F4.junior-autumn\u002F2.ICCAD\u002F11.Detailed\u002F8.Chapter8-CProgrammingInUnix","\u002Fugcourses\u002Fjunior-autumn\u002Ficcad\u002Fdetailed\u002Fchapter8-cprogramminginunix","Chapter 8：C Programming in Unix","2026-09-10 20:27:57",{"text":463,"minutes":464,"time":465,"words":466},"17 min read",16.855,1011300,3371,{"stem":468,"path":469,"title":470,"date":471,"collapsed":9,"readingTime":472},"posts\u002FUGCourses\u002F4.junior-autumn\u002F2.ICCAD\u002F2.LinuxUnix","\u002Fugcourses\u002Fjunior-autumn\u002Ficcad\u002Flinuxunix","Linux & Unix","2026-09-10 20:25:46",{"text":41,"minutes":473,"time":474,"words":475},29.33,1759800,5866,{"stem":477,"path":478,"title":479,"date":480,"collapsed":9,"readingTime":481},"posts\u002FUGCourses\u002F4.junior-autumn\u002F2.ICCAD\u002F3.Vi","\u002Fugcourses\u002Fjunior-autumn\u002Ficcad\u002Fvi","Vi","2026-09-10 20:25:47",{"text":329,"minutes":482,"time":483,"words":484},7.95,477000,1590,{"stem":486,"path":487,"title":488,"date":489,"collapsed":9,"readingTime":490},"posts\u002FUGCourses\u002F4.junior-autumn\u002F2.ICCAD\u002F4.ViCopy","\u002Fugcourses\u002Fjunior-autumn\u002Ficcad\u002Fvicopy","Vi（详细版）","2026-09-10 20:25:49",{"text":279,"minutes":491,"time":492,"words":493},13.785,827100,2757,{"stem":495,"path":496,"title":497,"date":498,"collapsed":9,"readingTime":499},"posts\u002FUGCourses\u002F4.junior-autumn\u002F2.ICCAD\u002F5.Shells","\u002Fugcourses\u002Fjunior-autumn\u002Ficcad\u002Fshells","Shells","2026-09-10 20:25:51",{"text":500,"minutes":501,"time":502,"words":503},"22 min read",21.21,1272600,4242,{"stem":505,"path":506,"title":507,"date":508,"collapsed":9,"readingTime":509},"posts\u002FUGCourses\u002F4.junior-autumn\u002F2.ICCAD\u002F6.Network","\u002Fugcourses\u002Fjunior-autumn\u002Ficcad\u002Fnetwork","Network","2026-09-10 20:25:54",{"text":319,"minutes":510,"time":511,"words":512},20.595,1235700,4119,{"stem":514,"path":515,"title":516,"date":517,"collapsed":9,"readingTime":518},"posts\u002FUGCourses\u002F4.junior-autumn\u002F2.ICCAD\u002F7.SystemAdministration","\u002Fugcourses\u002Fjunior-autumn\u002Ficcad\u002Fsystemadministration","System Administration","2026-09-10 20:26:44",{"text":329,"minutes":519,"time":520,"words":521},7.12,427200,1424,{"stem":523,"path":524,"title":525,"date":526,"collapsed":9,"readingTime":527},"posts\u002FUGCourses\u002F4.junior-autumn\u002F2.ICCAD\u002F8.XWindow","\u002Fugcourses\u002Fjunior-autumn\u002Ficcad\u002Fxwindow","XWindow","2026-09-10 20:26:45",{"text":97,"minutes":528,"time":529,"words":530},5.105,306300,1021,{"stem":532,"path":533,"title":534,"date":535,"collapsed":9,"readingTime":536},"posts\u002FUGCourses\u002F4.junior-autumn\u002F2.ICCAD\u002F9.SystemDevelopmentTools","\u002Fugcourses\u002Fjunior-autumn\u002Ficcad\u002Fsystemdevelopmenttools","System Development Tools","2026-09-10 20:26:47",{"text":387,"minutes":537,"time":538,"words":539},11.35,681000,2270,{"stem":541,"path":542,"title":543,"date":544,"collapsed":343,"readingTime":545},"posts\u002FUGCourses\u002F4.junior-autumn\u002F2.ICCAD\u002Findex","\u002Fugcourses\u002Fjunior-autumn\u002Ficcad","面向 ICCAD 的软件基础技术","2026-09-10 12:37:00",{"text":60,"minutes":546,"time":547,"words":548},0.185,11100,37,{"stem":550,"path":551,"title":552,"date":553,"collapsed":9,"readingTime":554},"posts\u002FUGCourses\u002F4.junior-autumn\u002F3.HDL\u002FFeiQilai1","\u002Fugcourses\u002Fjunior-autumn\u002Fhdl\u002Ffeiqilai1","飞起来1.0","2026-09-10 12:51:49",{"text":555,"minutes":556,"time":557,"words":558},"105 min read",104.655,6279300,20931,{"stem":560,"path":561,"title":562,"date":563,"collapsed":9,"readingTime":564},"posts\u002FUGCourses\u002F4.junior-autumn\u002F3.HDL\u002FFeiQilai2","\u002Fugcourses\u002Fjunior-autumn\u002Fhdl\u002Ffeiqilai2","飞起来2.0","2026-09-10 12:52:06",{"text":565,"minutes":566,"time":567,"words":568},"29 min read",28.755,1725300,5751,{"stem":570,"path":571,"title":572,"date":573,"collapsed":9,"readingTime":574},"posts\u002FUGCourses\u002F4.junior-autumn\u002F3.HDL\u002FFeiQilai3","\u002Fugcourses\u002Fjunior-autumn\u002Fhdl\u002Ffeiqilai3","飞起来3.0","2026-09-10 12:52:12",{"text":116,"minutes":575,"time":576,"words":577},4.49,269400,898,{"stem":579,"path":580,"title":581,"date":582,"collapsed":9,"readingTime":583},"posts\u002FUGCourses\u002F4.junior-autumn\u002F3.HDL\u002FLecture13","\u002Fugcourses\u002Fjunior-autumn\u002Fhdl\u002Flecture13","Lecture 13：浮点数标准","2026-09-10 12:51:27",{"text":11,"minutes":584,"time":585,"words":586},2.575,154500,515,{"stem":588,"path":589,"title":590,"date":591,"collapsed":9,"readingTime":592},"posts\u002FUGCourses\u002F4.junior-autumn\u002F3.HDL\u002FLecture6","\u002Fugcourses\u002Fjunior-autumn\u002Fhdl\u002Flecture6","Lecture 6：Behavioral Modeling，非阻塞赋值","2026-09-10 12:51:12",{"text":97,"minutes":593,"time":594,"words":595},5.34,320400,1068,{"stem":597,"path":598,"title":599,"date":600,"collapsed":9,"readingTime":601},"posts\u002FUGCourses\u002F4.junior-autumn\u002F3.HDL\u002FPPTCorrespondence","\u002Fugcourses\u002Fjunior-autumn\u002Fhdl\u002Fpptcorrespondence","与PPT的对应","2026-09-10 12:52:18",{"text":269,"minutes":602,"time":603,"words":604},9.275,556500,1855,{"stem":606,"path":607,"title":608,"date":544,"collapsed":343,"readingTime":609},"posts\u002FUGCourses\u002F4.junior-autumn\u002F3.HDL\u002Findex","\u002Fugcourses\u002Fjunior-autumn\u002Fhdl","硬件描述语言原理与应用",{"text":60,"minutes":61,"time":62,"words":63},{"stem":611,"path":612,"title":613,"date":614,"collapsed":9,"readingTime":615},"posts\u002FUGCourses\u002F4.junior-autumn\u002F4.analogICDesign\u002F1.BeforeMidterm","\u002Fugcourses\u002Fjunior-autumn\u002Fanalogicdesign\u002Fbeforemidterm","期中前","2026-09-10 20:28:52",{"text":616,"minutes":617,"time":618,"words":619},"11 min read",10.835,650100,2167,{"stem":621,"path":622,"title":623,"date":624,"collapsed":9,"readingTime":625},"posts\u002FUGCourses\u002F4.junior-autumn\u002F4.analogICDesign\u002F2.MidtermExam2425","\u002Fugcourses\u002Fjunior-autumn\u002Fanalogicdesign\u002Fmidtermexam2425","24-25 期中","2026-09-10 20:29:00",{"text":21,"minutes":626,"time":627,"words":628},1.22,73200,244,{"stem":630,"path":631,"title":632,"date":633,"collapsed":9,"readingTime":634},"posts\u002FUGCourses\u002F4.junior-autumn\u002F4.analogICDesign\u002F3.FinalExamReview","\u002Fugcourses\u002Fjunior-autumn\u002Fanalogicdesign\u002Ffinalexamreview","期末考试整理","2026-09-10 20:29:09",{"text":387,"minutes":635,"time":636,"words":637},11.36,681600,2272,{"stem":639,"path":640,"title":641,"date":642,"collapsed":343,"readingTime":643},"posts\u002FUGCourses\u002F4.junior-autumn\u002F4.analogICDesign\u002Findex","\u002Fugcourses\u002Fjunior-autumn\u002Fanalogicdesign","模拟集成电路设计","2026-09-10 20:00:00",{"text":80,"minutes":81,"time":81,"words":81},{"stem":645,"path":646,"title":647,"date":78,"collapsed":9,"readingTime":648},"posts\u002FUGCourses\u002F4.junior-autumn\u002Findex","\u002Fugcourses\u002Fjunior-autumn","大三秋冬",{"text":80,"minutes":81,"time":81,"words":81},{"stem":650,"path":651,"title":652,"date":653,"collapsed":9,"readingTime":654},"posts\u002FUGCourses\u002F5.junior-spring\u002F1.digitalICDesign\u002F1.Chapter3-Device","\u002Fugcourses\u002Fjunior-spring\u002Fdigitalicdesign\u002Fchapter3-device","Chapter 3：器件 Device","2026-09-10 21:14:41",{"text":249,"minutes":655,"time":656,"words":657},6.055,363300,1211,{"stem":659,"path":660,"title":661,"date":653,"collapsed":9,"readingTime":662},"posts\u002FUGCourses\u002F5.junior-spring\u002F1.digitalICDesign\u002F10.Chapter12-Arithmetic","\u002Fugcourses\u002Fjunior-spring\u002Fdigitalicdesign\u002Fchapter12-arithmetic","Chapter 12：算术运算电路 Arithmetic",{"text":168,"minutes":663,"time":664,"words":665},24.245,1454700,4849,{"stem":667,"path":668,"title":669,"date":653,"collapsed":9,"readingTime":670},"posts\u002FUGCourses\u002F5.junior-spring\u002F1.digitalICDesign\u002F11.Chapter13-Memory","\u002Fugcourses\u002Fjunior-spring\u002Fdigitalicdesign\u002Fchapter13-memory","Chapter 13：存储器 Memory",{"text":229,"minutes":671,"time":672,"words":673},34.235,2054100,6847,{"stem":675,"path":676,"title":677,"date":653,"collapsed":9,"readingTime":678},"posts\u002FUGCourses\u002F5.junior-spring\u002F1.digitalICDesign\u002F12.Chapter14","\u002Fugcourses\u002Fjunior-spring\u002Fdigitalicdesign\u002Fchapter14","Chapter 14",{"text":80,"minutes":81,"time":81,"words":81},{"stem":680,"path":681,"title":682,"date":653,"collapsed":9,"readingTime":683},"posts\u002FUGCourses\u002F5.junior-spring\u002F1.digitalICDesign\u002F13.PastExams\u002F1.Midterm2022","\u002Fugcourses\u002Fjunior-spring\u002Fdigitalicdesign\u002Fpastexams\u002Fmidterm2022","2022 期中试题",{"text":60,"minutes":684,"time":685,"words":686},0.075,4500,15,{"stem":688,"path":689,"title":690,"date":653,"collapsed":9,"readingTime":691},"posts\u002FUGCourses\u002F5.junior-spring\u002F1.digitalICDesign\u002F13.PastExams\u002F2.Midterm2023","\u002Fugcourses\u002Fjunior-spring\u002Fdigitalicdesign\u002Fpastexams\u002Fmidterm2023","2023 期中试题",{"text":80,"minutes":81,"time":81,"words":81},{"stem":693,"path":694,"title":695,"date":653,"collapsed":9,"readingTime":696},"posts\u002FUGCourses\u002F5.junior-spring\u002F1.digitalICDesign\u002F13.PastExams\u002F3.FinalPolished","\u002Fugcourses\u002Fjunior-spring\u002Fdigitalicdesign\u002Fpastexams\u002Ffinalpolished","期末历年卷（AI 润色版）",{"text":21,"minutes":697,"time":698,"words":699},1.575,94500,315,{"stem":701,"path":702,"title":703,"date":653,"collapsed":9,"readingTime":704},"posts\u002FUGCourses\u002F5.junior-spring\u002F1.digitalICDesign\u002F14.ExamRecall2026","\u002Fugcourses\u002Fjunior-spring\u002Fdigitalicdesign\u002Fexamrecall2026","数字集成电路回忆 2026",{"text":60,"minutes":705,"time":706,"words":707},0.49,29400,98,{"stem":709,"path":710,"title":711,"date":653,"collapsed":9,"readingTime":712},"posts\u002FUGCourses\u002F5.junior-spring\u002F1.digitalICDesign\u002F2.Chapter4-Wire","\u002Fugcourses\u002Fjunior-spring\u002Fdigitalicdesign\u002Fchapter4-wire","Chapter 4：导线 Wire",{"text":397,"minutes":713,"time":714,"words":715},18.65,1119000,3730,{"stem":717,"path":718,"title":719,"date":653,"collapsed":9,"readingTime":720},"posts\u002FUGCourses\u002F5.junior-spring\u002F1.digitalICDesign\u002F3.Chapter5-Inverter","\u002Fugcourses\u002Fjunior-spring\u002Fdigitalicdesign\u002Fchapter5-inverter","Chapter 5：CMOS 反相器 Inverter",{"text":269,"minutes":721,"time":722,"words":723},9.385,563100,1877,{"stem":725,"path":726,"title":727,"date":653,"collapsed":9,"readingTime":728},"posts\u002FUGCourses\u002F5.junior-spring\u002F1.digitalICDesign\u002F4.Chapter6-LogicGate","\u002Fugcourses\u002Fjunior-spring\u002Fdigitalicdesign\u002Fchapter6-logicgate","Chapter 6：逻辑门 Logic Gate",{"text":31,"minutes":729,"time":730,"words":731},8.635,518100,1727,{"stem":733,"path":734,"title":735,"date":653,"collapsed":9,"readingTime":736},"posts\u002FUGCourses\u002F5.junior-spring\u002F1.digitalICDesign\u002F5.Chapter7-SequentialLogic","\u002Fugcourses\u002Fjunior-spring\u002Fdigitalicdesign\u002Fchapter7-sequentiallogic","Chapter 7：时序逻辑 Sequential Logic",{"text":463,"minutes":737,"time":738,"words":739},16.645,998700,3329,{"stem":741,"path":742,"title":743,"date":653,"collapsed":9,"readingTime":744},"posts\u002FUGCourses\u002F5.junior-spring\u002F1.digitalICDesign\u002F6.Chapter8-Speed","\u002Fugcourses\u002Fjunior-spring\u002Fdigitalicdesign\u002Fchapter8-speed","Chapter 8：延迟与速度 Speed",{"text":745,"minutes":746,"time":747,"words":748},"33 min read",32.885,1973100,6577,{"stem":750,"path":751,"title":752,"date":653,"collapsed":9,"readingTime":753},"posts\u002FUGCourses\u002F5.junior-spring\u002F1.digitalICDesign\u002F7.Chapter9-Power","\u002Fugcourses\u002Fjunior-spring\u002Fdigitalicdesign\u002Fchapter9-power","Chapter 9：功耗 Power",{"text":754,"minutes":755,"time":756,"words":757},"31 min read",31.005,1860300,6201,{"stem":759,"path":760,"title":761,"date":653,"collapsed":9,"readingTime":762},"posts\u002FUGCourses\u002F5.junior-spring\u002F1.digitalICDesign\u002F8.Chapter10-Interconnect","\u002Fugcourses\u002Fjunior-spring\u002Fdigitalicdesign\u002Fchapter10-interconnect","Chapter 10：互连 Interconnect",{"text":463,"minutes":763,"time":764,"words":765},16.32,979200,3264,{"stem":767,"path":768,"title":769,"date":653,"collapsed":9,"readingTime":770},"posts\u002FUGCourses\u002F5.junior-spring\u002F1.digitalICDesign\u002F9.Chapter11-Timing","\u002Fugcourses\u002Fjunior-spring\u002Fdigitalicdesign\u002Fchapter11-timing","Chapter 11：时序 Timing",{"text":289,"minutes":771,"time":772,"words":773},19.68,1180800,3936,{"stem":775,"path":776,"title":777,"date":653,"collapsed":343,"readingTime":778},"posts\u002FUGCourses\u002F5.junior-spring\u002F1.digitalICDesign\u002Findex","\u002Fugcourses\u002Fjunior-spring\u002Fdigitalicdesign","数字集成电路设计",{"text":80,"minutes":81,"time":81,"words":81},{"stem":780,"path":781,"title":782,"date":653,"collapsed":9,"readingTime":783},"posts\u002FUGCourses\u002F5.junior-spring\u002F2.computerOrganization\u002F1.L1-Intro","\u002Fugcourses\u002Fjunior-spring\u002Fcomputerorganization\u002Fl1-intro","L1：计算机发展史与体系结构导论",{"text":397,"minutes":784,"time":785,"words":786},18.695,1121700,3739,{"stem":788,"path":789,"title":790,"date":653,"collapsed":9,"readingTime":791},"posts\u002FUGCourses\u002F5.junior-spring\u002F2.computerOrganization\u002F10.L8-9-Memory","\u002Fugcourses\u002Fjunior-spring\u002Fcomputerorganization\u002Fl8-9-memory","L8-9：存储器层次结构",{"text":792,"minutes":793,"time":794,"words":795},"128 min read",127.1,7626000,25420,{"stem":797,"path":798,"title":799,"date":653,"collapsed":9,"readingTime":800},"posts\u002FUGCourses\u002F5.junior-spring\u002F2.computerOrganization\u002F11.L10-11-Parallelism","\u002Fugcourses\u002Fjunior-spring\u002Fcomputerorganization\u002Fl10-11-parallelism","L10-11：并行性",{"text":801,"minutes":802,"time":803,"words":804},"159 min read",158.86,9531600,31772,{"stem":806,"path":807,"title":808,"date":653,"collapsed":9,"readingTime":809},"posts\u002FUGCourses\u002F5.junior-spring\u002F2.computerOrganization\u002F12.L12-CacheCoherence","\u002Fugcourses\u002Fjunior-spring\u002Fcomputerorganization\u002Fl12-cachecoherence","L12：缓存一致性 Cache Coherence",{"text":810,"minutes":811,"time":812,"words":813},"76 min read",75.395,4523700,15079,{"stem":815,"path":816,"title":817,"date":653,"collapsed":9,"readingTime":818},"posts\u002FUGCourses\u002F5.junior-spring\u002F2.computerOrganization\u002F13.L13-14-ComplexPipeline","\u002Fugcourses\u002Fjunior-spring\u002Fcomputerorganization\u002Fl13-14-complexpipeline","L13-14：复杂流水线（衔接 L7）",{"text":819,"minutes":820,"time":821,"words":822},"44 min read",43.985,2639100,8797,{"stem":824,"path":825,"title":826,"date":653,"collapsed":9,"readingTime":827},"posts\u002FUGCourses\u002F5.junior-spring\u002F2.computerOrganization\u002F14.L15","\u002Fugcourses\u002Fjunior-spring\u002Fcomputerorganization\u002Fl15","L15：期末复习问答",{"text":269,"minutes":828,"time":829,"words":830},9.105,546300,1821,{"stem":832,"path":833,"title":834,"date":653,"collapsed":9,"readingTime":835},"posts\u002FUGCourses\u002F5.junior-spring\u002F2.computerOrganization\u002F2.L2-Binary","\u002Fugcourses\u002Fjunior-spring\u002Fcomputerorganization\u002Fl2-binary","L2：二进制数制",{"text":80,"minutes":81,"time":81,"words":81},{"stem":837,"path":838,"title":839,"date":653,"collapsed":9,"readingTime":840},"posts\u002FUGCourses\u002F5.junior-spring\u002F2.computerOrganization\u002F3.L3-CMemory","\u002Fugcourses\u002Fjunior-spring\u002Fcomputerorganization\u002Fl3-cmemory","L3：C 语言与内存模型",{"text":841,"minutes":842,"time":843,"words":844},"98 min read",97.32,5839200,19464,{"stem":846,"path":847,"title":848,"date":653,"collapsed":9,"readingTime":849},"posts\u002FUGCourses\u002F5.junior-spring\u002F2.computerOrganization\u002F4.L4a-wbx","\u002Fugcourses\u002Fjunior-spring\u002Fcomputerorganization\u002Fl4a-wbx","L4a：wbx",{"text":80,"minutes":81,"time":81,"words":81},{"stem":851,"path":852,"title":853,"date":653,"collapsed":9,"readingTime":854},"posts\u002FUGCourses\u002F5.junior-spring\u002F2.computerOrganization\u002F5.L4b-MachineCode","\u002Fugcourses\u002Fjunior-spring\u002Fcomputerorganization\u002Fl4b-machinecode","L4b：机器码 Machine Code",{"text":80,"minutes":81,"time":81,"words":81},{"stem":856,"path":857,"title":858,"date":653,"collapsed":9,"readingTime":859},"posts\u002FUGCourses\u002F5.junior-spring\u002F2.computerOrganization\u002F6.L5-DSD","\u002Fugcourses\u002Fjunior-spring\u002Fcomputerorganization\u002Fl5-dsd","L5：数字系统设计 Digital System Design",{"text":269,"minutes":860,"time":861,"words":862},9.955,597300,1991,{"stem":864,"path":865,"title":866,"date":653,"collapsed":9,"readingTime":867},"posts\u002FUGCourses\u002F5.junior-spring\u002F2.computerOrganization\u002F7.L6a-SingleCycle","\u002Fugcourses\u002Fjunior-spring\u002Fcomputerorganization\u002Fl6a-singlecycle","L6a：单周期处理器设计",{"text":219,"minutes":868,"time":869,"words":870},22.125,1327500,4425,{"stem":872,"path":873,"title":874,"date":653,"collapsed":9,"readingTime":875},"posts\u002FUGCourses\u002F5.junior-spring\u002F2.computerOrganization\u002F8.L6b-Microcode","\u002Fugcourses\u002Fjunior-spring\u002Fcomputerorganization\u002Fl6b-microcode","L6b：微码处理器架构",{"text":387,"minutes":876,"time":877,"words":878},11.86,711600,2372,{"stem":880,"path":881,"title":882,"date":653,"collapsed":9,"readingTime":883},"posts\u002FUGCourses\u002F5.junior-spring\u002F2.computerOrganization\u002F9.L7-Pipeline","\u002Fugcourses\u002Fjunior-spring\u002Fcomputerorganization\u002Fl7-pipeline","L7：流水线处理器",{"text":177,"minutes":884,"time":885,"words":886},44.505,2670300,8901,{"stem":888,"path":889,"title":890,"date":653,"collapsed":343,"readingTime":891},"posts\u002FUGCourses\u002F5.junior-spring\u002F2.computerOrganization\u002Findex","\u002Fugcourses\u002Fjunior-spring\u002Fcomputerorganization","计算机组成与系统结构",{"text":60,"minutes":892,"time":893,"words":894},0.12,7200,24,{"stem":896,"path":897,"title":898,"date":78,"collapsed":9,"readingTime":899},"posts\u002FUGCourses\u002F5.junior-spring\u002Findex","\u002Fugcourses\u002Fjunior-spring","大三春夏",{"text":80,"minutes":81,"time":81,"words":81},{"stem":901,"path":902,"title":903,"date":78,"collapsed":9,"readingTime":904},"posts\u002FUGCourses\u002Findex","\u002Fugcourses","本科课程",{"text":80,"minutes":81,"time":81,"words":81},{"stem":906,"path":907,"title":908,"date":909,"collapsed":9,"readingTime":910},"posts\u002Fubuntu\u002F1.config","\u002Fubuntu\u002Fconfig","ubuntu 系统配置","2025-08-26 19:38:06",{"text":158,"minutes":911,"time":912,"words":913},3.815,228900,763,{"stem":915,"path":916,"title":917,"date":918,"collapsed":9,"readingTime":919},"posts\u002Fubuntu\u002F2.cadence\u002F1.mmsim","\u002Fubuntu\u002Fcadence\u002Fmmsim","在 Ubuntu 24.04 上安装 Cadence MMSIM 15.1 的兼容性问题","2026-09-09 14:06:40",{"text":158,"minutes":920,"time":921,"words":922},3.5,210000,700,{"stem":924,"path":925,"title":926,"date":918,"collapsed":9,"readingTime":927},"posts\u002Fubuntu\u002F2.cadence\u002F2.innovus","\u002Fubuntu\u002Fcadence\u002Finnovus","Innovus安装",{"text":11,"minutes":928,"time":929,"words":930},2.08,124800,416,{"stem":932,"path":933,"title":934,"date":918,"collapsed":9,"readingTime":935},"posts\u002Fubuntu\u002F2.cadence\u002Findex","\u002Fubuntu\u002Fcadence","Ubuntu24.04安装Cadence 系列软件",{"text":269,"minutes":936,"time":937,"words":938},9.98,598800,1996,{"stem":940,"path":941,"title":942,"date":918,"collapsed":9,"readingTime":943},"posts\u002Fubuntu\u002F3.synopsys","\u002Fubuntu\u002Fsynopsys","Ubuntu 24.04 安装 Synopsys",{"text":269,"minutes":944,"time":945,"words":946},9.555,573300,1911,{"stem":948,"path":949,"title":950,"date":918,"collapsed":9,"readingTime":951},"posts\u002Fubuntu\u002F4.vivado","\u002Fubuntu\u002Fvivado","Ubuntu 24.04 安装 Vivado 2020.1",{"text":60,"minutes":952,"time":953,"words":954},0.62,37200,124,{"stem":956,"path":957,"title":958,"date":918,"collapsed":9,"readingTime":959},"posts\u002Fubuntu\u002F5.migrate","\u002Fubuntu\u002Fmigrate","硬盘迁移系统",{"text":60,"minutes":960,"time":961,"words":962},0.64,38400,128,{"stem":964,"path":965,"title":966,"date":967,"collapsed":9,"readingTime":968},"posts\u002Fubuntu\u002Findex","\u002Fubuntu","Ubuntu装进移动硬盘","2025-08-25 19:38:06",{"text":269,"minutes":969,"time":970,"words":971},9.26,555600,1852,{"id":973,"title":839,"body":974,"categories":10357,"collapsed":9,"date":653,"description":10358,"draft":9,"extension":10359,"hideInArchive":343,"hideInList":343,"image":10360,"meta":10361,"navigation":343,"path":838,"permalink":10360,"published":10360,"readingTime":10363,"recommend":10360,"references":10360,"seo":10364,"sitemap":10365,"stem":837,"tags":10387,"type":10390,"updated":653,"__hash__":10391},"content\u002Fposts\u002FUGCourses\u002F5.junior-spring\u002F2.computerOrganization\u002F3.L3-CMemory.md",{"type":975,"value":976,"toc":10124},"minimark",[977,987,991,1141,1144,1148,1153,1159,1162,1244,1250,1255,1258,1260,1264,1267,1270,1273,1275,1279,1317,1319,1323,1328,1383,1386,1396,1402,1411,1413,1417,1420,1426,1429,1435,1438,1486,1496,1498,1502,1505,1511,1519,1522,1527,1530,1532,1536,1542,1545,1548,1550,1554,1559,1562,1567,1569,1573,1598,1602,1610,1613,1619,1621,1624,1629,1635,1638,1644,1651,1654,1660,1665,1668,1670,1674,1677,1684,1691,1698,1703,1705,1709,1714,1717,1720,1723,1729,1734,1736,1740,1743,1756,1761,1763,1767,1771,1777,1783,1787,1793,1796,1799,1805,1808,1814,1817,1820,1831,1833,1837,1840,1879,1882,1888,1891,1894,1896,1900,1906,1912,1914,1918,1927,1933,1939,1980,1983,1985,1989,1995,1998,2013,2015,2019,2025,2028,2033,2035,2039,2042,2044,2050,2053,2055,2059,2064,2066,2070,2084,2086,2090,2093,2098,2101,2155,2158,2164,2171,2174,2177,2182,2184,2188,2191,2245,2251,2256,2259,2265,2270,2275,2277,2281,2284,2289,2292,2298,2301,2348,2351,2357,2377,2379,2383,2386,2392,2398,2400,2404,2409,2415,2422,2427,2430,2436,2444,2450,2458,2464,2479,2485,2487,2491,2494,2500,2503,2509,2512,2515,2521,2569,2572,2578,2584,2587,2593,2595,2599,2602,2608,2612,2618,2622,2628,2631,2637,2640,2646,2660,2666,2669,2675,2678,2684,2686,2692,2695,2698,2704,2717,2720,2726,2729,2734,2737,2743,2746,2751,2759,2761,2765,2768,2774,2777,2782,2786,2789,2792,2794,2798,2810,2813,2819,2822,2828,2831,2837,2840,2846,2849,2855,2867,2870,2876,2884,2886,2892,2895,2901,2910,2916,2918,2923,2926,2932,2935,2940,2952,2955,2961,2963,2967,2970,2976,2979,3040,3043,3048,3050,3054,3056,3062,3065,3071,3074,3077,3083,3086,3092,3094,3100,3103,3109,3111,3117,3120,3123,3128,3131,3137,3139,3145,3147,3153,3156,3162,3164,3170,3172,3178,3190,3196,3199,3205,3207,3213,3215,3221,3224,3229,3242,3247,3249,3253,3256,3262,3264,3268,3274,3287,3300,3302,3306,3311,3317,3328,3333,3339,3345,3350,3356,3358,3362,3365,3368,3374,3377,3380,3386,3393,3396,3402,3404,3408,3411,3414,3420,3423,3429,3432,3434,3438,3441,3448,3451,3457,3459,3465,3468,3474,3477,3512,3514,3522,3527,3530,3536,3539,3545,3558,3563,3566,3572,3577,3580,3586,3588,3592,3595,3601,3604,3610,3616,3619,3625,3638,3640,3644,3647,3649,3655,3657,3663,3669,3675,3681,3686,3692,3695,3701,3704,3706,3710,3713,3716,3721,3727,3730,3736,3784,3790,3792,3798,3804,3810,3891,3961,3967,3972,3974,3978,3981,3986,3992,3994,3998,4001,4007,4036,4043,4049,4052,4058,4061,4068,4071,4077,4098,4100,4104,4111,4113,4119,4122,4124,4130,4133,4139,4142,4148,4151,4154,4160,4163,4169,4171,4178,4181,4184,4190,4193,4199,4205,4211,4214,4220,4223,4229,4235,4249,4252,4258,4265,4267,4271,4274,4277,4283,4286,4292,4298,4300,4304,4307,4309,4315,4345,4348,4354,4357,4363,4369,4374,4376,4382,4385,4388,4394,4396,4402,4574,4576,4580,4583,4586,4592,4595,4601,4603,4607,4610,4616,4619,4625,4655,4658,4664,4666,4673,4676,4682,4685,4691,4694,4697,4703,4705,4711,4713,4719,4721,4727,4729,4735,4737,4743,4753,4759,4762,4768,4771,4777,4780,4785,4790,4796,4801,4809,4814,4816,4822,4824,4830,4832,4838,4844,4850,4853,4858,4868,4870,4874,4877,4899,4946,4948,4954,4957,4963,4965,4970,4973,4976,4982,4984,4988,4993,4996,5002,5004,5010,5013,5019,5021,5028,5031,5037,5040,5046,5049,5055,5058,5061,5067,5070,5076,5084,5087,5093,5127,5129,5137,5146,5152,5158,5161,5167,5176,5178,5184,5187,5193,5195,5199,5202,5210,5213,5219,5222,5228,5234,5237,5243,5250,5253,5259,5262,5268,5271,5274,5280,5282,5286,5289,5292,5298,5304,5306,5310,5317,5331,5333,5337,5342,5403,5406,5411,5413,5417,5420,5423,5431,5434,5440,5443,5446,5452,5455,5458,5465,5467,5471,5474,5484,5487,5493,5496,5502,5508,5510,5516,5522,5525,5527,5534,5536,5540,5551,5553,5559,5561,5567,5574,5593,5599,5601,5605,5608,5613,5619,5633,5639,5642,5645,5651,5656,5658,5662,5667,5670,5673,5676,5689,5692,5695,5697,5701,5703,5709,5711,5723,5726,5732,5735,5740,5746,5749,5751,5757,5762,5768,5780,5783,5789,5803,5813,5828,5830,5835,5837,5841,5844,5850,5853,5859,5869,5874,5880,5889,5895,5898,5901,5907,5909,5913,5916,5919,5927,5934,5937,6003,6006,6009,6015,6018,6020,6024,6027,6033,6036,6044,6046,6053,6056,6062,6089,6091,6097,6100,6106,6112,6117,6123,6125,6129,6131,6137,6140,6146,6152,6164,6170,6189,6195,6201,6207,6209,6216,6218,6224,6227,6235,6238,6241,6247,6250,6256,6259,6265,6268,6274,6277,6284,6286,6290,6293,6299,6302,6308,6313,6316,6322,6328,6330,6336,6339,6344,6346,6353,6356,6359,6408,6411,6414,6420,6422,6426,6429,6435,6441,6443,6447,6450,6455,6458,6464,6467,6469,6478,6486,6489,6495,6498,6501,6507,6523,6525,6531,6533,6537,6540,6551,6557,6559,6566,6569,6579,6585,6588,6591,6597,6603,6611,6613,6620,6623,6626,6632,6638,6644,6647,6652,6657,6659,6665,6668,6674,6677,6680,6686,6689,6692,6695,6697,6701,6704,6707,6713,6716,6722,6725,6731,6734,6737,6744,6746,6753,6759,6761,6767,6772,6775,6781,6784,6790,6796,6798,6802,6805,6811,6814,6820,6823,6830,6841,6843,6847,6862,6864,6868,6871,6901,6903,6907,6910,6916,6918,6924,6933,6935,6941,6960,6962,6968,6974,6980,6982,6988,6994,6997,7003,7018,7020,7024,7045,7052,7058,7061,7066,7069,7075,7081,7087,7095,7100,7103,7108,7111,7114,7120,7123,7129,7131,7135,7141,7144,7150,7153,7159,7165,7171,7176,7182,7185,7191,7198,7200,7207,7209,7215,7218,7224,7227,7233,7238,7244,7256,7259,7265,7277,7283,7289,7292,7298,7300,7304,7307,7310,7316,7319,7321,7325,7328,7334,7342,7345,7348,7354,7357,7360,7366,7378,7382,7385,7391,7394,7396,7402,7405,7415,7421,7424,7430,7433,7436,7442,7445,7447,7451,7454,7457,7463,7465,7469,7472,7475,7483,7486,7491,7496,7499,7507,7510,7516,7519,7522,7524,7528,7530,7536,7539,7545,7548,7554,7557,7563,7569,7572,7578,7581,7597,7599,7605,7608,7614,7617,7619,7623,7630,7635,7641,7656,7659,7665,7670,7673,7679,7682,7688,7691,7693,7697,7702,7716,7721,7737,7742,7767,7769,7773,7776,7779,7786,7789,7795,7798,7804,7807,7813,7816,7818,7822,7825,7830,7833,7839,7842,7848,7851,7857,7860,7872,7881,7891,7898,7906,7909,7917,7919,7923,7928,7930,7934,7939,7947,7952,7971,7973,7977,7982,8020,8025,8033,8038,8091,8093,8097,8104,8240,8250,8252,8256,8259,8265,8269,8275,8278,8288,8297,8299,8303,8309,8315,8321,8323,8326,8329,8335,8337,8341,8344,8350,8370,8378,8384,8390,8393,8396,8402,8404,8408,8414,8417,8432,8441,8443,8447,8450,8456,8459,8461,8465,8476,8489,8491,8495,8498,8504,8507,8509,8513,8519,8527,8532,8534,8538,8546,8548,8554,8556,8562,8568,8574,8576,8580,8586,8589,8592,8598,8601,8603,8606,8612,8614,8620,8623,8626,8632,8634,8641,8644,8650,8653,8655,8658,8664,8667,8670,8676,8679,8685,8687,8691,8697,8700,8703,8705,8711,8713,8719,8721,8731,8737,8743,8749,8752,8760,8767,8773,8776,8778,8786,8792,8803,8811,8814,8820,8822,8829,8832,8838,8841,8847,8850,8852,8856,8859,8865,8868,8874,8876,8880,8886,8889,8895,8897,8901,8904,8910,8917,8919,8923,8929,8937,8940,8946,8949,8955,8958,8964,8967,8973,8976,8979,8981,8987,8994,8996,9003,9006,9012,9015,9021,9024,9030,9036,9039,9043,9046,9052,9055,9061,9063,9068,9071,9077,9083,9085,9091,9095,9098,9104,9107,9113,9116,9121,9124,9130,9133,9139,9142,9144,9150,9152,9156,9159,9165,9168,9174,9177,9183,9185,9189,9195,9198,9204,9207,9213,9215,9222,9225,9231,9241,9243,9249,9255,9260,9264,9270,9273,9277,9283,9286,9288,9296,9302,9308,9313,9319,9331,9334,9340,9343,9349,9352,9358,9360,9367,9370,9376,9380,9386,9392,9402,9404,9409,9412,9418,9421,9423,9429,9431,9438,9444,9451,9454,9460,9463,9469,9472,9475,9481,9484,9486,9492,9494,9506,9509,9514,9523,9525,9531,9533,9536,9542,9545,9551,9554,9560,9562,9568,9570,9574,9577,9580,9582,9586,9588,9596,9602,9608,9611,9614,9616,9622,9624,9628,9631,9637,9643,9649,9652,9659,9662,9668,9671,9677,9679,9683,9686,9692,9698,9701,9703,9710,9713,9719,9725,9728,9734,9740,9743,9749,9752,9755,9760,9762,9766,9769,9775,9778,9781,9786,9792,9795,9801,9804,9806,9812,9814,9818,9821,9827,9830,9836,9842,9847,9850,9853,9855,9859,9862,9868,9871,9877,9880,9886,9889,9891,9895,9898,9904,9906,9912,9919,9922,9928,9930,9934,10113,10116,10122],[978,979,980,984],"blockquote",{},[981,982,983],"p",{},"这一章的整体难度不大，但很杂乱，有很多要记的小点，可以放在看其他章节累的时候看。",[981,985,986],{},"大部分例题的详细推导过程已经放在里面了，如果会做就可以不用看冗杂的推导过程了",[988,989,990],"h2",{"id":990},"章节一览",[992,993,994,1013],"table",{},[995,996,997],"thead",{},[998,999,1000,1004,1007,1010],"tr",{},[1001,1002,1003],"th",{},"学习块",[1001,1005,1006],{},"页码",[1001,1008,1009],{},"学什么",[1001,1011,1012],{},"学完要能做到",[1014,1015,1016,1036,1062,1088,1106,1127],"tbody",{},[998,1017,1018,1022,1025,1033],{},[1019,1020,1021],"td",{},"1. C 与编译地基",[1019,1023,1024],{},"p3–24",[1019,1026,1027,1028,1032],{},"编译\u002F解释、C 基本类型、",[1029,1030,1031],"code",{"code":1031},"sizeof","、函数、结构体、未初始化变量",[1019,1034,1035],{},"能说清 C 为什么快、为什么和机器相关；能解释未初始化变量为什么危险",[998,1037,1038,1041,1044,1059],{},[1019,1039,1040],{},"2. 指针入门",[1019,1042,1043],{},"p25–36",[1019,1045,1046,1047,1050,1051,1054,1055,1058],{},"地址和值、",[1029,1048,1049],{"code":1049},"&","、",[1029,1052,1053],{"code":1053},"*","、指针类型、",[1029,1056,1057],{"code":1057},"void *","、结构体指针、指针作函数参数",[1019,1060,1061],{},"看到一段指针代码，能手推变量最终值",[998,1063,1064,1067,1070,1081],{},[1019,1065,1066],{},"3. 数组与指针关系",[1019,1068,1069],{},"p37–59",[1019,1071,1072,1073,1076,1077,1080],{},"数组越界、",[1029,1074,1075],{"code":1075},"sizeof(array)"," vs ",[1029,1078,1079],{"code":1079},"sizeof(pointer)","、数组名\u002F指针对偶性、指针运算",[1019,1082,1083,1084,1087],{},"能解释 ",[1029,1085,1086],{"code":1086},"a[i] == *(a+i)","，知道数组传参会丢失长度",[998,1089,1090,1093,1096,1103],{},[1019,1091,1092],{},"4. C 内存布局",[1019,1094,1095],{},"p60–84",[1019,1097,1098,1099,1102],{},"code\u002Fstatic\u002Fstack\u002Fheap，",[1029,1100,1101],{"code":1101},"malloc\u002Ffree\u002Frealloc","，谁申请谁释放",[1019,1104,1105],{},"能画出一个程序中变量分别在栈、堆、静态区哪里",[998,1107,1108,1111,1114,1124],{},[1019,1109,1110],{},"5. 常见内存错误",[1019,1112,1113],{},"p85–96，p103–118",[1019,1115,1116,1117,1120,1121,1123],{},"越界、野指针、非法 ",[1029,1118,1119],{"code":1119},"free","、重复 ",[1029,1122,1119],{"code":1119},"、内存泄漏、返回栈地址",[1019,1125,1126],{},"能把错误分类：越界 \u002F 泄漏 \u002F 非法释放 \u002F use-after-free \u002F 返回局部地址",[998,1128,1129,1132,1135,1138],{},[1019,1130,1131],{},"6. 残酷现实与性能",[1019,1133,1134],{},"p97–102",[1019,1136,1137],{},"性能不只看复杂度，内存访问模式影响巨大，局部性",[1019,1139,1140],{},"能解释为什么同样复制二维数组，循环顺序不同性能差很多",[1142,1143],"hr",{},[988,1145,1147],{"id":1146},"一编译与解释","一：编译与解释",[1149,1150,1152],"h3",{"id":1151},"p4抽象表示-levels-of-representation","p4：抽象表示 Levels of Representation",[981,1154,1155],{},[1156,1157],"img",{"alt":1156,"src":1158},"\u002Fimg\u002FComputerOrganization\u002FL3-CMemory-01.png",[981,1160,1161],{},"课件从上到下给了几个层次：",[992,1163,1164,1177],{},[995,1165,1166],{},[998,1167,1168,1171,1174],{},[1001,1169,1170],{},"层次",[1001,1172,1173],{},"例子",[1001,1175,1176],{},"你该怎么理解",[1014,1178,1179,1193,1211,1222,1233],{},[998,1180,1181,1184,1190],{},[1019,1182,1183],{},"高级语言",[1019,1185,1186,1187],{},"C 代码：",[1029,1188,1189],{"code":1189},"temp = v[k];",[1019,1191,1192],{},"人比较容易读",[998,1194,1195,1198,1208],{},[1019,1196,1197],{},"汇编语言",[1019,1199,1200,1201,1204,1205],{},"RISC-V：",[1029,1202,1203],{"code":1203},"lw",", ",[1029,1206,1207],{"code":1207},"sw",[1019,1209,1210],{},"接近机器，但还能看懂",[998,1212,1213,1216,1219],{},[1019,1214,1215],{},"机器语言",[1019,1217,1218],{},"一串 0\u002F1",[1019,1220,1221],{},"CPU 真正能执行",[998,1223,1224,1227,1230],{},[1019,1225,1226],{},"硬件结构",[1019,1228,1229],{},"Register File, ALU",[1019,1231,1232],{},"机器码最终驱动硬件",[998,1234,1235,1238,1241],{},[1019,1236,1237],{},"逻辑电路",[1019,1239,1240],{},"门电路图",[1019,1242,1243],{},"最底层实现",[981,1245,1246],{},[1247,1248,1249],"strong",{},"任何东西都可以用数字表示，包括数据和指令",[978,1251,1252],{},[981,1253,1254],{},"C 代码为什么不能直接被 CPU 执行？",[981,1256,1257],{},"CPU 只能执行机器码，高级语言必须经过编译、汇编、链接等过程变成机器码。",[1142,1259],{},[1149,1261,1263],{"id":1262},"p5程序员必须关心硬件吗","p5：程序员必须关心硬件吗？",[981,1265,1266],{},"需考虑",[981,1268,1269],{},"性能、功耗、安全；",[981,1271,1272],{},"内存管理和线程管理。",[1142,1274],{},[1149,1276,1278],{"id":1277},"p6hello-worldc-和-java-对比","p6：Hello World：C 和 Java 对比",[992,1280,1281,1291],{},[995,1282,1283],{},[998,1284,1285,1288],{},[1001,1286,1287],{},"语言",[1001,1289,1290],{},"运行前发生什么",[1014,1292,1293,1301,1309],{},[998,1294,1295,1298],{},[1019,1296,1297],{},"C",[1019,1299,1300],{},"编译成机器码可执行文件",[998,1302,1303,1306],{},[1019,1304,1305],{},"Java",[1019,1307,1308],{},"编译成字节码，再由 JVM 运行",[998,1310,1311,1314],{},[1019,1312,1313],{},"Python",[1019,1315,1316],{},"通常由解释器运行源码或中间表示",[1142,1318],{},[1149,1320,1322],{"id":1321},"p7c-编译概述","p7：C 编译概述",[981,1324,1325],{},[1156,1326],{"alt":1156,"src":1327},"\u002Fimg\u002FComputerOrganization\u002FL3-CMemory-02.png",[992,1329,1330,1340],{},[995,1331,1332],{},[998,1333,1334,1337],{},[1001,1335,1336],{},"文件",[1001,1338,1339],{},"含义",[1014,1341,1342,1352,1362,1373],{},[998,1343,1344,1349],{},[1019,1345,1346],{},[1029,1347,1348],{"code":1348},".c",[1019,1350,1351],{},"C 源代码，文本",[998,1353,1354,1359],{},[1019,1355,1356],{},[1029,1357,1358],{"code":1358},".o",[1019,1360,1361],{},"object file，已经翻译成机器相关代码、但尚未完成地址解析和库连接的中间产物",[998,1363,1364,1370],{},[1019,1365,1366,1369],{},[1029,1367,1368],{"code":1368},"lib.o"," 或库文件",[1019,1371,1372],{},"别人提前编译好的机器码",[998,1374,1375,1380],{},[1019,1376,1377],{},[1029,1378,1379],{"code":1379},"a.out",[1019,1381,1382],{},"最终可执行文件",[981,1384,1385],{},"所以“编译”严格讲不止一步。常见流程是：",[1387,1388,1393],"pre",{"className":1389,"code":1391,"language":1392},[1390],"language-text","预处理 → 编译 → 汇编 → 链接 → 可执行文件\n","text",[1029,1394,1391],{"__ignoreMap":1395},"",[1387,1397,1400],{"className":1398,"code":1399,"language":1392},[1390],"main.c\n  │\n  ├─ 预处理：展开 #include、#define\n  │\n  ├─ 编译：C 代码 → 汇编代码\n  │\n  ├─ 汇编：汇编代码 → main.o\n  │\n  └─ 链接：main.o + 其他 .o + 库 → 可执行文件\n",[1029,1401,1399],{"__ignoreMap":1395},[981,1403,1404,1405,1407,1408,1410],{},"多个 ",[1029,1406,1348],{"code":1348}," 文件可以分别编译成 ",[1029,1409,1358],{"code":1358},"，最后由 linker 和库文件一起链接成可执行文件。",[1142,1412],{},[1149,1414,1416],{"id":1415},"p8编译-vs-解释","p8：编译 vs 解释",[981,1418,1419],{},"C：",[1387,1421,1424],{"className":1422,"code":1423,"language":1392},[1390],"源代码 → 编译器\u002F链接器 → 机器语言 → OS 加载 → 硬件直接执行\n",[1029,1425,1423],{"__ignoreMap":1395},[981,1427,1428],{},"Python：",[1387,1430,1433],{"className":1431,"code":1432,"language":1392},[1390],"解释器本身是由高级语言编写，翻译成机器码程序\n解释器读取 Python 源代码\n解释器边读边解释执行\n",[1029,1434,1432],{"__ignoreMap":1395},[981,1436,1437],{},"一个小细节很关键：\nPython 不是“CPU 直接看懂 Python”。CPU 执行的是 Python 解释器这个程序，解释器再去处理你的 Python 代码。",[992,1439,1440,1451],{},[995,1441,1442],{},[998,1443,1444,1447,1449],{},[1001,1445,1446],{},"项目",[1001,1448,1297],{},[1001,1450,1313],{},[1014,1452,1453,1464,1475],{},[998,1454,1455,1458,1461],{},[1019,1456,1457],{},"翻译时机",[1019,1459,1460],{},"运行前",[1019,1462,1463],{},"运行时",[998,1465,1466,1469,1472],{},[1019,1467,1468],{},"执行者",[1019,1470,1471],{},"硬件直接执行机器码",[1019,1473,1474],{},"解释器读代码并执行",[998,1476,1477,1480,1483],{},[1019,1478,1479],{},"性能",[1019,1481,1482],{},"通常更快",[1019,1484,1485],{},"通常有解释开销",[1487,1488,1490,1493],"alert",{"type":1489},"tip",[981,1491,1492],{},"C 由编译器和链接器翻译成机器语言，再由操作系统加载、硬件执行",[981,1494,1495],{},"Python 则由解释器读取源代码并“解释”它。",[1142,1497],{},[1149,1499,1501],{"id":1500},"p9java-字节码","p9：Java 字节码",[981,1503,1504],{},"Java 是中间路线。流程大概是：",[1387,1506,1509],{"className":1507,"code":1508,"language":1392},[1390],".java 源代码\n   ↓ javac\n.class 字节码\n   ↓ JVM\n解释执行 \u002F JIT 编译成本地机器码\n",[1029,1510,1508],{"__ignoreMap":1395},[981,1512,1513,1514,1518],{},"Java 字节码是",[1515,1516,1517],"mark",{},"平台无关","的中间表示，是一种抽象的汇编语言，不是某个真实 CPU 的机器码。实际运行中由 JVM 解释执行，或者用 JIT 把热点代码动态编译成本地机器码。",[981,1520,1521],{},"你可以把 Java 理解成：",[978,1523,1524],{},[981,1525,1526],{},"先编译成 JVM 能看懂的代码，再由 JVM 想办法让真实机器执行。",[981,1528,1529],{},"这也是 Java “一次编写，到处运行”的基础。（与c语言不同，同一套 C 代码在不同指令集架构下会编译出不同的机器码）",[1142,1531],{},[1149,1533,1535],{"id":1534},"p10编译的优点","p10：编译的优点",[981,1537,1538,1539],{},"核心：",[1247,1540,1541],{},"运行时性能好。",[981,1543,1544],{},"因为 C 代码已经提前变成了机器码，所以运行时 CPU 可以直接执行，不需要解释器一边读源代码一边处理。",[981,1546,1547],{},"计算机只运行机器代码；编译型语言预先形成可执行文件，运行时直接由硬件执行，而解释型语言运行时转换会有额外开销。",[1142,1549],{},[1149,1551,1553],{"id":1552},"p11编译优化例子乘以-12","p11：编译优化例子——乘以 12",[981,1555,1556],{},[1156,1557],{"alt":1156,"src":1558},"\u002Fimg\u002FComputerOrganization\u002FL3-CMemory-03.png",[981,1560,1561],{},"编译器会自动把某些算术改写成更适合机器执行的 shift\u002Fadd 组合。",[978,1563,1564],{},[981,1565,1566],{},"编译器不是逐字翻译，它会优化。",[1142,1568],{},[1149,1570,1572],{"id":1571},"p12-13无有符号除以-8","p12-13：无\u002F有符号除以 8",[992,1574,1576],{"style":1575},"width: 100%; border: 0; border-collapse: collapse; border-spacing: 0;",[1014,1577,1578],{},[998,1579,1581,1582,1581,1590],{"style":1580},"border: none;","\n  ",[1019,1583,1585,1581],{"style":1584},"width: 55.0%; text-align: center; border: none; padding: 0.3em; margin: 0;",[981,1586,1587],{},[1156,1588],{"alt":1156,"src":1589},"\u002Fimg\u002FComputerOrganization\u002FL3-CMemory-04.png",[1019,1591,1593,1581],{"style":1592},"width: 44.0%; text-align: center; border: none; padding: 0.3em; margin: 0;",[981,1594,1595],{},[1156,1596],{"alt":1156,"src":1597},"\u002Fimg\u002FComputerOrganization\u002FL3-CMemory-05.png",[1599,1600,1601],"h4",{"id":1601},"无符号",[1387,1603,1608],{"className":1604,"code":1606,"language":1607,"meta":1395},[1605],"language-c","unsigned long udiv8(unsigned long x) {\n    return x \u002F 8;\n}\n","c",[1029,1609,1606],{"__ignoreMap":1395},[981,1611,1612],{},"编译后可以变成逻辑右移：",[1387,1614,1617],{"className":1615,"code":1616,"language":1607,"meta":1395},[1605],"return x >> 3;\n",[1029,1618,1616],{"__ignoreMap":1395},[1142,1620],{},[1599,1622,1623],{"id":1623},"有符号",[981,1625,1626],{},[1247,1627,1628],{},"负数除法不能简单右移完事。",[1387,1630,1633],{"className":1631,"code":1632,"language":1607,"meta":1395},[1605],"long idiv8(long x) {\n    return x \u002F 8;\n}\n",[1029,1634,1632],{"__ignoreMap":1395},[981,1636,1637],{},"编译器会做类似逻辑：",[1387,1639,1642],{"className":1640,"code":1641,"language":1607,"meta":1395},[1605],"if (x \u003C 0)\n    x += 7;\nreturn x >> 3;\n",[1029,1643,1641],{"__ignoreMap":1395},[981,1645,1646,1647,1650],{},"为什么负数要先 ",[1029,1648,1649],{"code":1649},"+7","？\n因为 C 的整数除法通常是向 0 截断，而算术右移对负数更像向负无穷取整。为了让结果符合 C 语义，编译器要修正一下。",[981,1652,1653],{},"例子：",[1387,1655,1658],{"className":1656,"code":1657,"language":1392},[1390],"-9 \u002F 8 = -1   \u002F\u002F C 语言向 0 截断\n-9 >> 3 可能得到 -2\n",[1029,1659,1657],{"__ignoreMap":1395},[978,1661,1662],{},[981,1663,1664],{},"编译器优化算术时，必须保持 C 语言语义。无符号和有符号处理方式不同。",[981,1666,1667],{},"课件 p13 也明确区分了有符号除法使用算术移位，并在负数情况下先加偏移再右移。",[1142,1669],{},[1149,1671,1673],{"id":1672},"p14编译的缺点","p14：编译的缺点",[981,1675,1676],{},"编译不是全是优点。缺点主要两个：",[981,1678,1679,1680,1683],{},"第一，",[1247,1681,1682],{},"可执行文件依赖体系结构和操作系统","。\n比如 RISC-V、ARM、x86 不一样；Windows、Linux 也不一样。",[981,1685,1686,1687,1690],{},"第二，",[1247,1688,1689],{},"开发迭代可能慢","。\n你改一点代码，可能要重新编译再运行。课件 p14 提到，可执行文件是 architecture-specific 和 operating-system-specific；换新系统时通常要重新生成可执行文件，也就是所谓“移植”。",[981,1692,1693,1694,1697],{},"工具 ",[1029,1695,1696],{"code":1696},"make"," 的作用就是自动化“哪些文件改了，哪些需要重新编译”。",[1487,1699,1700],{"type":1489},[981,1701,1702],{},"C特点：运行快，但编译和移植成本更明显。",[1142,1704],{},[1149,1706,1708],{"id":1707},"p15c-预处理器-cpp","p15：C 预处理器 CPP",[981,1710,1711],{},[1156,1712],{"alt":1156,"src":1713},"\u002Fimg\u002FComputerOrganization\u002FL3-CMemory-06.png",[981,1715,1716],{},"C 源文件在真正编译前，会先经过预处理器 CPP。",[981,1718,1719],{},"CPP命令一般以“#”开头",[981,1721,1722],{},"常见预处理命令：",[1387,1724,1727],{"className":1725,"code":1726,"language":1607,"meta":1395},[1605],"#include \u003Cstdio.h>\n#include \"file.h\"\n#define M_PI (3.14159)\n#if\n#endif\n",[1029,1728,1726],{"__ignoreMap":1395},[978,1730,1731],{},[981,1732,1733],{},"预处理发生在编译之前，它处理的是文本层面的东西。",[1142,1735],{},[1149,1737,1739],{"id":1738},"p16c-语言中的变量","p16：C 语言中的变量",[981,1741,1742],{},"C 变量特点：",[1744,1745,1746,1750,1753],"ol",{},[1747,1748,1749],"li",{},"使用前要定义；",[1747,1751,1752],{},"类型不能随便变；",[1747,1754,1755],{},"和 Java 有点像，是静态类型语言。",[981,1757,1758],{},[1156,1759],{"alt":1156,"src":1760},"\u002Fimg\u002FComputerOrganization\u002FL3-CMemory-07.png",[1142,1762],{},[1149,1764,1766],{"id":1765},"p17常量和枚举","p17：常量和枚举",[1599,1768,1770],{"id":1769},"常量","常量：",[1387,1772,1775],{"className":1773,"code":1774,"language":1607,"meta":1395},[1605],"const float pi = 3.1415;\nconst unsigned long addr = 0xaf460;\n",[1029,1776,1774],{"__ignoreMap":1395},[981,1778,1779,1782],{},[1029,1780,1781],{"code":1781},"const"," 表示这个变量初始化后不能再改。",[1599,1784,1786],{"id":1785},"枚举","枚举：",[1387,1788,1791],{"className":1789,"code":1790,"language":1607,"meta":1395},[1605],"typedef enum { red, green, blue } Color;\nColor pants = green;\n",[1029,1792,1790],{"__ignoreMap":1395},[981,1794,1795],{},"枚举的作用是：给一组相关取值起名字。\n比如颜色、状态、错误码。",[981,1797,1798],{},"你可以这样理解：",[1387,1800,1803],{"className":1801,"code":1802,"language":1607,"meta":1395},[1605],"enum { red, green, blue };\n",[1029,1804,1802],{"__ignoreMap":1395},[981,1806,1807],{},"底层通常对应整数：",[1387,1809,1812],{"className":1810,"code":1811,"language":1392},[1390],"red = 0\ngreen = 1\nblue = 2\n",[1029,1813,1811],{"__ignoreMap":1395},[981,1815,1816],{},"但写名字比写 0、1、2 更清楚。",[981,1818,1819],{},"本页记忆点：",[978,1821,1822],{},[981,1823,1824,1826,1827,1830],{},[1029,1825,1781],{"code":1781}," 是“这个值别改”；",[1029,1828,1829],{"code":1829},"enum"," 是“给一组整数状态起名字”。",[1142,1832],{},[1149,1834,1836],{"id":1835},"p18整数pythonjavac","p18：整数：Python、Java、C",[981,1838,1839],{},"这一页讲不同语言里的整数大小。",[992,1841,1842,1854],{},[995,1843,1844],{},[998,1845,1846,1848],{},[1001,1847,1287],{},[1001,1849,1850,1853],{},[1029,1851,1852],{"code":1852},"int"," 大小特点",[1014,1855,1856,1863,1872],{},[998,1857,1858,1860],{},[1019,1859,1313],{},[1019,1861,1862],{},"普通整数至少够用，长整数可近似无限精度",[998,1864,1865,1867],{},[1019,1866,1305],{},[1019,1868,1869,1871],{},[1029,1870,1852],{"code":1852}," 固定 32 bits",[998,1873,1874,1876],{},[1019,1875,1297],{},[1019,1877,1878],{},"由机器和编译器决定，可能 16\u002F32\u002F64 bits",[981,1880,1881],{},"课件还给了 C 的保证关系：",[1387,1883,1886],{"className":1884,"code":1885,"language":1607,"meta":1395},[1605],"sizeof(long long) >= sizeof(long) >= sizeof(int) >= sizeof(short)\n",[1029,1887,1885],{"__ignoreMap":1395},[981,1889,1890],{},"int在不同指令集不同微架构的位数不一样",[981,1892,1893],{},"C （int）的工作效率最高但有可移植性问题。",[1142,1895],{},[1149,1897,1899],{"id":1898},"p19变量长度依赖机器","p19：变量长度依赖机器",[981,1901,1902,1903,1905],{},"给了某台机器上的 ",[1029,1904,1031],{"code":1031}," 输出：",[1387,1907,1910],{"className":1908,"code":1909,"language":1392},[1390],"char: 1\nshort: 2\nint: 4\nunsigned int: 4\nlong: 8\nlong long: 8\nfloat: 4\ndouble: 8\n",[1029,1911,1909],{"__ignoreMap":1395},[1142,1913],{},[1149,1915,1917],{"id":1916},"p20布尔-boolean","p20：布尔 Boolean",[1744,1919,1920],{},[1747,1921,1922,1923,1926],{},"老式 C 里没有内置 ",[1029,1924,1925],{"code":1925},"boolean"," 类型。课件给了一种写法：",[1387,1928,1931],{"className":1929,"code":1930,"language":1607,"meta":1395},[1605],"typedef int boolean;\nconst boolean false = 0;\nconst boolean true = 1;\n",[1029,1932,1930],{"__ignoreMap":1395},[1744,1934,1936],{"start":1935},2,[1747,1937,1938],{},"C 的真假规则：",[992,1940,1941,1951],{},[995,1942,1943],{},[998,1944,1945,1948],{},[1001,1946,1947],{},"值",[1001,1949,1950],{},"在 C 中的真假",[1014,1952,1953,1963,1972],{},[998,1954,1955,1960],{},[1019,1956,1957],{},[1029,1958,1959],{"code":1959},"0",[1019,1961,1962],{},"false",[998,1964,1965,1970],{},[1019,1966,1967],{},[1029,1968,1969],{"code":1969},"NULL",[1019,1971,1962],{},[998,1973,1974,1977],{},[1019,1975,1976],{},"非 0",[1019,1978,1979],{},"true",[981,1981,1982],{},"C 中 false 是 0 或 NULL，其他“不是假的东西都是真的”。",[1142,1984],{},[1149,1986,1988],{"id":1987},"p21c-语言中的函数","p21：C 语言中的函数",[1387,1990,1993],{"className":1991,"code":1992,"language":1607,"meta":1395},[1605],"int number_of_people() {\n    return 3;\n}\n\nvoid news() {\n    printf(\"no news\");\n}\n\nint sum(int x, int y) {\n    return x + y;\n}\n",[1029,1994,1992],{"__ignoreMap":1395},[981,1996,1997],{},"C 函数像 Java",[1999,2000,2001,2004,2010],"ul",{},[1747,2002,2003],{},"需要声明返回类型和参数类型",[1747,2005,2006,2007],{},"无返回值用 ",[1029,2008,2009],{"code":2009},"void",[1747,2011,2012],{},"函数使用前必须声明。",[1142,2014],{},[1149,2016,2018],{"id":2017},"p22未初始化变量","p22：未初始化变量",[1387,2020,2023],{"className":2021,"code":2022,"language":1607,"meta":1395},[1605],"void undefined_local() {\n    int x;  \u002F\u002F undefined\n    printf(\"x = %d\\n\", x);\n}\n",[1029,2024,2022],{"__ignoreMap":1395},[981,2026,2027],{},"未初始化的变量可能为任意值",[981,2029,2030],{},[1515,2031,2032],{},"所以申明变量之后一定要初始化",[1142,2034],{},[1149,2036,2038],{"id":2037},"p23结构体-struct","p23：结构体 Struct",[981,2040,2041],{},"Struct 是一组结构化变量",[981,2043,1653],{},[1387,2045,2048],{"className":2046,"code":2047,"language":1607,"meta":1395},[1605],"typedef struct {\n    int x, y;\n} Point;\n\nPoint p1;\np1.x = 0;\np1.y = 123;\n\nPoint p2 = {77, -8};\nprintf(\"p2 at (%d,%d)\\n\", p2.x, p2.y);\n",[1029,2049,2047],{"__ignoreMap":1395},[981,2051,2052],{},"它有点像 Java 类，但没有方法",[1142,2054],{},[1149,2056,2058],{"id":2057},"p24小结","p24：小结",[981,2060,2061],{},[1156,2062],{"alt":1156,"src":2063},"\u002Fimg\u002FComputerOrganization\u002FL3-CMemory-08.png",[1142,2065],{},[988,2067,2069],{"id":2068},"第二章指针和地址","第二章：指针和地址",[981,2071,2072,2073,2076,2077,1050,2079,1054,2081,2083],{},"这一段你要抓住一条主线：",[1247,2074,2075],{},"变量不只是“值”，变量还住在内存的某个“地址”；指针就是专门存地址的变量。"," 课件第 25–36 页围绕“指针和地址”展开，包括内存地址、",[1029,2078,1049],{"code":1049},[1029,2080,1053],{"code":1053},[1029,2082,1057],{"code":1057},"、结构体指针、函数参数传递和两道练习题。",[1142,2085],{},[1149,2087,2089],{"id":2088},"p26计算机组件","p26：计算机组件",[981,2091,2092],{},"这一页是一张计算机组成图。左边是处理器，右边是存储器，中间有地址、读写使能、写数据、读数据这些信号。",[981,2094,2095],{},[1156,2096],{"alt":1156,"src":2097},"\u002Fimg\u002FComputerOrganization\u002FL3-CMemory-09.png",[981,2099,2100],{},"处理器里主要有：",[992,2102,2103,2113],{},[995,2104,2105],{},[998,2106,2107,2110],{},[1001,2108,2109],{},"部件",[1001,2111,2112],{},"作用",[1014,2114,2115,2123,2131,2139,2147],{},[998,2116,2117,2120],{},[1019,2118,2119],{},"Controller 控制单元",[1019,2121,2122],{},"决定当前要执行什么操作",[998,2124,2125,2128],{},[1019,2126,2127],{},"Data Path 数据通路",[1019,2129,2130],{},"真正搬运和处理数据",[998,2132,2133,2136],{},[1019,2134,2135],{},"PC 程序计数器",[1019,2137,2138],{},"保存下一条指令的地址",[998,2140,2141,2144],{},[1019,2142,2143],{},"Registers 寄存器",[1019,2145,2146],{},"CPU 内部很快的小存储",[998,2148,2149,2152],{},[1019,2150,2151],{},"ALU",[1019,2153,2154],{},"做算术和逻辑运算",[981,2156,2157],{},"存储器里放两类东西：",[1387,2159,2162],{"className":2160,"code":2161,"language":1392},[1390],"Program 程序\nData 数据\n",[1029,2163,2161],{"__ignoreMap":1395},[981,2165,2166,2167,2170],{},"注意，这一页最重要的是 ",[1247,2168,2169],{},"Address 地址线","。",[981,2172,2173],{},"处理器要访问内存时，不是喊一句“我要变量 a”，而是给内存一个地址：",[981,2175,2176],{},"所以，后面 C 里的指针，其实就是把这种“地址”暴露给程序员。",[978,2178,2179],{},[981,2180,2181],{},"处理器通过地址访问内存；变量名是给人看的，机器真正用的是地址。",[1142,2183],{},[1149,2185,2187],{"id":2186},"p27计算机内存地址和值不要混","p27：计算机内存——地址和值不要混",[981,2189,2190],{},"这里一定要区分两件事：",[992,2192,2193,2203],{},[995,2194,2195],{},[998,2196,2197,2200],{},[1001,2198,2199],{},"概念",[1001,2201,2202],{},"对应什么",[1014,2204,2205,2218,2233],{},[998,2206,2207,2213],{},[1019,2208,2209,2212],{},[1029,2210,2211],{"code":2211},"a"," 的值",[1019,2214,2215],{},[1029,2216,2217],{"code":2217},"-85",[998,2219,2220,2225],{},[1019,2221,2222,2224],{},[1029,2223,2211],{"code":2211}," 的地址",[1019,2226,2227,2229,2230],{},[1029,2228,2211],{"code":2211}," 在内存中的位置，比如 ",[1029,2231,2232],{"code":2232},"0x7fff...",[998,2234,2235,2240],{},[1019,2236,2237],{},[1029,2238,2239],{"code":2239},"&a",[1019,2241,2242,2243,2224],{},"取出 ",[1029,2244,2211],{"code":2211},[1387,2246,2249],{"className":2247,"code":2248,"language":1607,"meta":1395},[1605],"printf(\"%d\", a);\n",[1029,2250,2248],{"__ignoreMap":1395},[981,2252,2253,2254,2170],{},"打印的是 ",[1247,2255,1947],{},[981,2257,2258],{},"而如果写：",[1387,2260,2263],{"className":2261,"code":2262,"language":1607,"meta":1395},[1605],"printf(\"%p\", &a);\n",[1029,2264,2262],{"__ignoreMap":1395},[981,2266,2253,2267,2170],{},[1247,2268,2269],{},"地址",[981,2271,2272],{},[1515,2273,2274],{},"不要混淆内存地址 address 和值 value。",[1142,2276],{},[1149,2278,2280],{"id":2279},"p28指针-pointers","p28：指针 Pointers",[981,2282,2283],{},"这页正式定义指针：",[978,2285,2286],{},[981,2287,2288],{},"指针是 C 语言中表示内存地址的东西。",[981,2290,2291],{},"课件给的代码大概是：",[1387,2293,2296],{"className":2294,"code":2295,"language":1607,"meta":1395},[1605],"int *x;      \u002F\u002F x 是一个指向 int 的指针\nint y = 9;   \u002F\u002F y 是一个 int\n\nx = &y;      \u002F\u002F 把 y 的地址赋给 x\nint z = *x;  \u002F\u002F 把 x 指向的值(9)赋给 z\n\n*x = -7;     \u002F\u002F 把 x 指向的东西（y）改成 -7\n",[1029,2297,2295],{"__ignoreMap":1395},[981,2299,2300],{},"这里有两个核心操作符。",[992,2302,2303,2316],{},[995,2304,2305],{},[998,2306,2307,2310,2313],{},[1001,2308,2309],{},"操作符",[1001,2311,2312],{},"名字",[1001,2314,2315],{},"意思",[1014,2317,2318,2333],{},[998,2319,2320,2324,2327],{},[1019,2321,2322],{},[1029,2323,1049],{"code":1049},[1019,2325,2326],{},"address operator 取地址",[1019,2328,2329,2332],{},[1029,2330,2331],{"code":2331},"&y"," 表示 y 的地址",[998,2334,2335,2339,2342],{},[1019,2336,2337],{},[1029,2338,1053],{"code":1053},[1019,2340,2341],{},"dereference 解引用",[1019,2343,2344,2347],{},[1029,2345,2346],{"code":2346},"*x"," 表示 x 指向的那个变量",[981,2349,2350],{},"最终结果：",[1387,2352,2355],{"className":2353,"code":2354,"language":1392},[1390],"x = &y      \u002F\u002F x 存着 y 的地址\ny = -7\nz = 9\n",[1029,2356,2354],{"__ignoreMap":1395},[981,2358,2359,2360,2363,2364,2367,2368,2370,2371,2373,2374,2170],{},"注意这个细节：",[1029,2361,2362],{"code":2362},"z"," 不会跟着变成 -7。\n因为 ",[1029,2365,2366],{"code":2366},"z = *x;"," 是把当时的值复制了一份给 ",[1029,2369,2362],{"code":2362},"，不是让 ",[1029,2372,2362],{"code":2362}," 也指向 ",[1029,2375,2376],{"code":2376},"y",[1142,2378],{},[1149,2380,2382],{"id":2381},"p29指针类型-pointer-type","p29：指针类型 Pointer Type",[981,2384,2385],{},"这一页说：指针也有类型。",[1387,2387,2390],{"className":2388,"code":2389,"language":1607,"meta":1395},[1605],"int *pi;      \u002F\u002F 指向 int 的指针\ndouble *pd;   \u002F\u002F 指向 double 的指针\nchar *pc;     \u002F\u002F 指向 char 的指针\n",[1029,2391,2389],{"__ignoreMap":1395},[981,2393,2394,2395],{},"原因是：",[1247,2396,2397],{},"编译器需要知道你通过这个地址一次要读多少字节，以及怎么解释这些字节。",[1142,2399],{},[1149,2401,2403],{"id":2402},"p30通用指针-void","p30：通用指针 void *",[981,2405,2406,2407,2170],{},"这一页讲 ",[1029,2408,1057],{"code":1057},[1387,2410,2413],{"className":2411,"code":2412,"language":1607,"meta":1395},[1605],"void *vp;\n",[1029,2414,2412],{"__ignoreMap":1395},[1744,2416,2417],{},[1747,2418,2419,2421],{},[1029,2420,1057],{"code":1057}," 是“通用指针”，意思是：",[978,2423,2424],{},[981,2425,2426],{},"它可以保存任意类型对象的地址，但它不知道这个地址上到底是什么类型。",[981,2428,2429],{},"比如它可以接收：",[1387,2431,2434],{"className":2432,"code":2433,"language":1607,"meta":1395},[1605],"int a = 3;\ndouble d = 1.5;\n\nvoid *vp;\n\nvp = &a;\nvp = &d;\n",[1029,2435,2433],{"__ignoreMap":1395},[1744,2437,2438],{"start":1935},[1747,2439,2440,2441,2443],{},"但是问题来了：\n因为 ",[1029,2442,1057],{"code":1057}," 不知道自己指向的对象大小和类型，所以你不能直接这样安全地用：",[1387,2445,2448],{"className":2446,"code":2447,"language":1607,"meta":1395},[1605],"*vp\n",[1029,2449,2447],{"__ignoreMap":1395},[1744,2451,2453],{"start":2452},3,[1747,2454,2455,2457],{},[1029,2456,1057],{"code":1057}," 的典型用途是通用函数，尤其是内存分配相关函数，比如：",[1387,2459,2462],{"className":2460,"code":2461,"language":1607,"meta":1395},[1605],"malloc\nfree\n",[1029,2463,2461],{"__ignoreMap":1395},[981,2465,2466,2467,2470,2471,1050,2473,2476,2477,2170],{},"因为 ",[1029,2468,2469],{"code":2469},"malloc"," 只是申请一块内存，它不知道你准备用这块内存放 ",[1029,2472,1852],{"code":1852},[1029,2474,2475],{"code":2475},"double"," 还是结构体，所以它返回的是 ",[1029,2478,1057],{"code":1057},[1387,2480,2483],{"className":2481,"code":2482,"language":1392},[1390],"int *    ：我指向 int\ndouble * ：我指向 double\nchar *   ：我指向 char\nvoid *   ：我只知道我是地址，但不知道指向什么\n",[1029,2484,2482],{"__ignoreMap":1395},[1142,2486],{},[1149,2488,2490],{"id":2489},"p31指向结构体的指针-pointer-to-struct","p31：指向结构体的指针 Pointer to struct",[981,2492,2493],{},"代码大概是：",[1387,2495,2498],{"className":2496,"code":2497,"language":1607,"meta":1395},[1605],"typedef struct {\n    int x, y;\n} Point;\n\nPoint pt = {0, 5};\n\nPoint *pt_ptr = &pt;\n",[1029,2499,2497],{"__ignoreMap":1395},[981,2501,2502],{},"这里：",[1387,2504,2507],{"className":2505,"code":2506,"language":1392},[1390],"pt 是一个 Point 结构体变量\npt_ptr 是一个指向 Point 的指针\npt_ptr = &pt 表示 pt_ptr 存放 pt 的地址\n",[1029,2508,2506],{"__ignoreMap":1395},[981,2510,2511],{},"如果要通过指针访问结构体成员，有两种写法。",[981,2513,2514],{},"第一种：",[1387,2516,2519],{"className":2517,"code":2518,"language":1607,"meta":1395},[1605],"(*pt_ptr).x = (*pt_ptr).y;\n",[1029,2520,2518],{"__ignoreMap":1395},[978,2522,2523,2526,2535,2541,2544,2550,2560,2563],{},[981,2524,2525],{},"为什么要加括号？",[981,2527,2466,2528,2531,2532,2534],{},[1029,2529,2530],{"code":2530},"."," 的优先级比 ",[1029,2533,1053],{"code":1053}," 高。\n如果你写：",[1387,2536,2539],{"className":2537,"code":2538,"language":1607,"meta":1395},[1605],"*pt_ptr.x\n",[1029,2540,2538],{"__ignoreMap":1395},[981,2542,2543],{},"编译器会先理解成：",[1387,2545,2548],{"className":2546,"code":2547,"language":1607,"meta":1395},[1605],"*(pt_ptr.x)\n",[1029,2549,2547],{"__ignoreMap":1395},[981,2551,2552,2553,2556,2557,2170],{},"但 ",[1029,2554,2555],{"code":2555},"pt_ptr"," 是指针，不是结构体变量，不能直接 ",[1029,2558,2559],{"code":2559},".x",[981,2561,2562],{},"所以必须写：",[1387,2564,2567],{"className":2565,"code":2566,"language":1607,"meta":1395},[1605],"(*pt_ptr).x\n",[1029,2568,2566],{"__ignoreMap":1395},[981,2570,2571],{},"第二种，也是更常用的写法：",[1387,2573,2576],{"className":2574,"code":2575,"language":1607,"meta":1395},[1605],"pt_ptr->x = pt_ptr->y;\n",[1029,2577,2575],{"__ignoreMap":1395},[981,2579,2580,2583],{},[1029,2581,2582],{"code":2582},"->"," 的意思就是：通过结构体指针访问成员",[981,2585,2586],{},"所以最终：",[1387,2588,2591],{"className":2589,"code":2590,"language":1392},[1390],"pt.x = 5\npt.y = 5\n",[1029,2592,2590],{"__ignoreMap":1395},[1142,2594],{},[1149,2596,2598],{"id":2597},"p32练习题-1手推指针代码","p32：练习题 1——手推指针代码",[981,2600,2601],{},"课件代码是：",[1387,2603,2606],{"className":2604,"code":2605,"language":1607,"meta":1395},[1605],"#include \u003Cstdio.h>\n\nint main(void) {\n    int a = 3, b = -7;\n    int *pa = &a, *pb = &b;\n\n    *pb = 5;\n\n    if (*pb > *pa)\n        a = *pa - b;\n\n    printf(\"a=%d b=%d\\n\", a, b);\n}\n",[1029,2607,2605],{"__ignoreMap":1395},[1599,2609,2611],{"id":2610},"答案","答案：",[1387,2613,2616],{"className":2614,"code":2615,"language":1392},[1390],"a = -2\nb = 5\n",[1029,2617,2615],{"__ignoreMap":1395},[1599,2619,2621],{"id":2620},"推导过程","推导过程：",[1387,2623,2626],{"className":2624,"code":2625,"language":1392},[1390],"a = 3\nb = -7\npa = &a\npb = &b\n",[1029,2627,2625],{"__ignoreMap":1395},[981,2629,2630],{},"也就是：",[1387,2632,2635],{"className":2633,"code":2634,"language":1392},[1390],"*pa == a == 3\n*pb == b == -7\n",[1029,2636,2634],{"__ignoreMap":1395},[981,2638,2639],{},"执行：",[1387,2641,2644],{"className":2642,"code":2643,"language":1607,"meta":1395},[1605],"*pb = 5;\n",[1029,2645,2643],{"__ignoreMap":1395},[981,2647,2648,2651,2652,2655,2656,2659],{},[1029,2649,2650],{"code":2650},"pb"," 指向 ",[1029,2653,2654],{"code":2654},"b","，所以 ",[1029,2657,2658],{"code":2658},"*pb = 5"," 等价于：",[1387,2661,2664],{"className":2662,"code":2663,"language":1607,"meta":1395},[1605],"b = 5;\n",[1029,2665,2663],{"__ignoreMap":1395},[981,2667,2668],{},"现在：",[1387,2670,2673],{"className":2671,"code":2672,"language":1392},[1390],"a = 3\nb = 5\n*pa = 3\n*pb = 5\n",[1029,2674,2672],{"__ignoreMap":1395},[981,2676,2677],{},"执行判断：",[1387,2679,2682],{"className":2680,"code":2681,"language":1607,"meta":1395},[1605],"if (*pb > *pa)\n",[1029,2683,2681],{"__ignoreMap":1395},[981,2685,2630],{},[1387,2687,2690],{"className":2688,"code":2689,"language":1607,"meta":1395},[1605],"if (5 > 3)\n",[1029,2691,2689],{"__ignoreMap":1395},[981,2693,2694],{},"成立。",[981,2696,2697],{},"于是执行：",[1387,2699,2702],{"className":2700,"code":2701,"language":1607,"meta":1395},[1605],"a = *pa - b;\n",[1029,2703,2701],{"__ignoreMap":1395},[981,2705,2706,2707,2710,2711,2713,2714,2716],{},"此时 ",[1029,2708,2709],{"code":2709},"*pa"," 是 ",[1029,2712,2211],{"code":2211}," 的当前值，也就是 3；",[1029,2715,2654],{"code":2654}," 是 5。",[981,2718,2719],{},"所以：",[1387,2721,2724],{"className":2722,"code":2723,"language":1392},[1390],"a = 3 - 5 = -2\n",[1029,2725,2723],{"__ignoreMap":1395},[981,2727,2728],{},"最终：",[1387,2730,2732],{"className":2731,"code":2615,"language":1392},[1390],[1029,2733,2615],{"__ignoreMap":1395},[981,2735,2736],{},"所以答案是课件表格里的：",[1387,2738,2741],{"className":2739,"code":2740,"language":1392},[1390],"YELLOW：a = -2, b = 5\n",[1029,2742,2740],{"__ignoreMap":1395},[981,2744,2745],{},"这题的核心坑是：",[1387,2747,2749],{"className":2748,"code":2643,"language":1607,"meta":1395},[1605],[1029,2750,2643],{"__ignoreMap":1395},[981,2752,2753,2754,2756,2757,2170],{},"它改的是 ",[1029,2755,2654],{"code":2654},"，不是 ",[1029,2758,2650],{"code":2650},[1142,2760],{},[1149,2762,2764],{"id":2763},"p33这个代码有什么问题","p33：这个代码有什么问题？",[981,2766,2767],{},"代码是：",[1387,2769,2772],{"className":2770,"code":2771,"language":1607,"meta":1395},[1605],"#include \u003Cstdio.h>\n\nint main(void) {\n    int a;\n    int *p;\n\n    printf(\"a = %d, p = %p, *p = %d\\n\", a, p, *p);\n\n    return 0;\n}\n",[1029,2773,2771],{"__ignoreMap":1395},[981,2775,2776],{},"这段代码问题很大，属于 C 语言经典“死亡三件套”。",[981,2778,1679,2779,2781],{},[1029,2780,2211],{"code":2211}," 没初始化：",[981,2783,1686,2784,2781],{},[1029,2785,981],{"code":981},[981,2787,2788],{},"第三，还解引用了这个垃圾指针*p：",[981,2790,2791],{},"输出垃圾",[1142,2793],{},[1149,2795,2797],{"id":2796},"p34指针作为函数实参","p34：指针作为函数实参",[981,2799,2800,2801,2806,2807,2170],{},"这一页讲 C 的参数传递机制：",[1515,2802,2803],{},[1247,2804,2805],{},"默认按值传递","。也就是说，函数收到的是实参的副本；在函数内部修改普通参数，不会影响外面的原变量。要想让函数修改外面的变量，就传指针。课件第 34 页明确强调 ",[1515,2808,2809],{},"C 函数参数默认是通过值传递，要通过引用传递则使用指针",[981,2811,2812],{},"代码：",[1387,2814,2817],{"className":2815,"code":2816,"language":1607,"meta":1395},[1605],"#include \u003Cstdio.h>\n\nvoid f(int x, int *p) {\n    x = 5;\n    *p = -9;\n}\n\nint main(void) {\n    int a = 1, b = -3;\n\n    f(a, &b);\n\n    printf(\"a=%d b=%d\\n\", a, b);\n}\n",[1029,2818,2816],{"__ignoreMap":1395},[1599,2820,2611],{"id":2821},"答案-1",[1387,2823,2826],{"className":2824,"code":2825,"language":1392},[1390],"a = 1\nb = -9\n",[1029,2827,2825],{"__ignoreMap":1395},[1599,2829,2621],{"id":2830},"推导过程-1",[1387,2832,2835],{"className":2833,"code":2834,"language":1392},[1390],"a = 1\nb = -3\n",[1029,2836,2834],{"__ignoreMap":1395},[981,2838,2839],{},"调用：",[1387,2841,2844],{"className":2842,"code":2843,"language":1607,"meta":1395},[1605],"f(a, &b);\n",[1029,2845,2843],{"__ignoreMap":1395},[981,2847,2848],{},"对应到函数参数：",[1387,2850,2853],{"className":2851,"code":2852,"language":1392},[1390],"x = a 的值 = 1\np = b 的地址 = &b\n",[1029,2854,2852],{"__ignoreMap":1395},[981,2856,2857,2858,2710,2861,2863,2864,2866],{},"注意：",[1029,2859,2860],{"code":2860},"x",[1029,2862,2211],{"code":2211}," 的副本，不是 ",[1029,2865,2211],{"code":2211}," 本人。",[981,2868,2869],{},"进入函数：",[1387,2871,2874],{"className":2872,"code":2873,"language":1607,"meta":1395},[1605],"x = 5;\n",[1029,2875,2873],{"__ignoreMap":1395},[981,2877,2878,2879,2881,2882,2170],{},"这只改了函数里的局部变量 ",[1029,2880,2860],{"code":2860},"，不会影响 ",[1029,2883,2211],{"code":2211},[981,2885,2719],{},[1387,2887,2890],{"className":2888,"code":2889,"language":1392},[1390],"a 仍然是 1\n",[1029,2891,2889],{"__ignoreMap":1395},[981,2893,2894],{},"然后：",[1387,2896,2899],{"className":2897,"code":2898,"language":1607,"meta":1395},[1605],"*p = -9;\n",[1029,2900,2898],{"__ignoreMap":1395},[981,2902,2903,2651,2905,2655,2907,2659],{},[1029,2904,981],{"code":981},[1029,2906,2654],{"code":2654},[1029,2908,2909],{"code":2909},"*p = -9",[1387,2911,2914],{"className":2912,"code":2913,"language":1607,"meta":1395},[1605],"b = -9;\n",[1029,2915,2913],{"__ignoreMap":1395},[981,2917,2728],{},[1387,2919,2921],{"className":2920,"code":2825,"language":1392},[1390],[1029,2922,2825],{"__ignoreMap":1395},[981,2924,2925],{},"这页的核心区别：",[1387,2927,2930],{"className":2928,"code":2929,"language":1607,"meta":1395},[1605],"f(a, b);\n",[1029,2931,2929],{"__ignoreMap":1395},[981,2933,2934],{},"只能把值传进去。",[1387,2936,2938],{"className":2937,"code":2843,"language":1607,"meta":1395},[1605],[1029,2939,2843],{"__ignoreMap":1395},[981,2941,2942,2943,2945,2946,2949,2950,2170],{},"把 ",[1029,2944,2654],{"code":2654}," 的地址传进去，函数就能通过 ",[1029,2947,2948],{"code":2948},"*p"," 修改 ",[1029,2951,2654],{"code":2654},[981,2953,2954],{},"口诀：",[1387,2956,2959],{"className":2957,"code":2958,"language":1392},[1390],"想让函数改外面的变量，就传它的地址。\n",[1029,2960,2958],{"__ignoreMap":1395},[1142,2962],{},[1149,2964,2966],{"id":2965},"p35java-中的参数传递","p35：Java 中的参数传递",[981,2968,2969],{},"这一页拿 Java 和 C 对比。",[1387,2971,2974],{"className":2972,"code":2973,"language":1392},[1390],"基本类型 int, char, double：按值传递\n对象 Objects：通过引用\n",[1029,2975,2973],{"__ignoreMap":1395},[981,2977,2978],{},"可以这样对比：",[992,2980,2981,2992],{},[995,2982,2983],{},[998,2984,2985,2988,2990],{},[1001,2986,2987],{},"对比项",[1001,2989,1297],{},[1001,2991,1305],{},[1014,2993,2994,3007,3018,3029],{},[998,2995,2996,2999,3004],{},[1019,2997,2998],{},"能否显式取地址",[1019,3000,3001,3002],{},"可以，",[1029,3003,2239],{"code":2239},[1019,3005,3006],{},"不可以",[998,3008,3009,3012,3016],{},[1019,3010,3011],{},"能否显式解引用",[1019,3013,3001,3014],{},[1029,3015,2948],{"code":2948},[1019,3017,3006],{},[998,3019,3020,3023,3026],{},[1019,3021,3022],{},"指针是否暴露给程序员",[1019,3024,3025],{},"暴露",[1019,3027,3028],{},"隐藏",[998,3030,3031,3034,3037],{},[1019,3032,3033],{},"对象位置能否由运行时移动",[1019,3035,3036],{},"C 一般不随便移动",[1019,3038,3039],{},"JVM 可以移动对象",[981,3041,3042],{},"这页的核心是：",[978,3044,3045],{},[981,3046,3047],{},"C 把地址暴露给你，所以你更自由，也更容易犯错。Java 把地址藏起来，所以更安全，但控制力少一些。",[1142,3049],{},[1149,3051,3053],{"id":3052},"p36练习题-2用指针实现交换","p36：练习题 2——用指针实现交换",[981,3055,2812],{},[1387,3057,3060],{"className":3058,"code":3059,"language":1607,"meta":1395},[1605],"#include \u003Cstdio.h>\n\nvoid foo(int *x, int *y) {\n    if (*x \u003C *y) {\n        int t = *x;\n        *x = *y;\n        *y = t;\n    }\n}\n\nint main(void) {\n    int a = 3, b = 1, c = 5;\n\n    foo(&a, &b);\n    foo(&b, &c);\n\n    printf(\"a=%d b=%d\\n\", a, b);\n}\n",[1029,3061,3059],{"__ignoreMap":1395},[1599,3063,2611],{"id":3064},"答案-2",[1387,3066,3069],{"className":3067,"code":3068,"language":1392},[1390],"YELLOW：a = 3, b = 5, c = 1\n",[1029,3070,3068],{"__ignoreMap":1395},[1599,3072,2620],{"id":3073},"推导过程-2",[981,3075,3076],{},"初始：",[1387,3078,3081],{"className":3079,"code":3080,"language":1392},[1390],"a = 3\nb = 1\nc = 5\n",[1029,3082,3080],{"__ignoreMap":1395},[981,3084,3085],{},"第一次调用：",[1387,3087,3090],{"className":3088,"code":3089,"language":1607,"meta":1395},[1605],"foo(&a, &b);\n",[1029,3091,3089],{"__ignoreMap":1395},[981,3093,2719],{},[1387,3095,3098],{"className":3096,"code":3097,"language":1392},[1390],"x = &a\ny = &b\n*x = a = 3\n*y = b = 1\n",[1029,3099,3097],{"__ignoreMap":1395},[981,3101,3102],{},"判断：",[1387,3104,3107],{"className":3105,"code":3106,"language":1607,"meta":1395},[1605],"if (*x \u003C *y)\n",[1029,3108,3106],{"__ignoreMap":1395},[981,3110,2630],{},[1387,3112,3115],{"className":3113,"code":3114,"language":1607,"meta":1395},[1605],"if (3 \u003C 1)\n",[1029,3116,3114],{"__ignoreMap":1395},[981,3118,3119],{},"不成立，所以不交换。",[981,3121,3122],{},"第一次后：",[1387,3124,3126],{"className":3125,"code":3080,"language":1392},[1390],[1029,3127,3080],{"__ignoreMap":1395},[981,3129,3130],{},"第二次调用：",[1387,3132,3135],{"className":3133,"code":3134,"language":1607,"meta":1395},[1605],"foo(&b, &c);\n",[1029,3136,3134],{"__ignoreMap":1395},[981,3138,2719],{},[1387,3140,3143],{"className":3141,"code":3142,"language":1392},[1390],"x = &b\ny = &c\n*x = b = 1\n*y = c = 5\n",[1029,3144,3142],{"__ignoreMap":1395},[981,3146,3102],{},[1387,3148,3151],{"className":3149,"code":3150,"language":1607,"meta":1395},[1605],"if (1 \u003C 5)\n",[1029,3152,3150],{"__ignoreMap":1395},[981,3154,3155],{},"成立，执行交换：",[1387,3157,3160],{"className":3158,"code":3159,"language":1607,"meta":1395},[1605],"int t = *x;\n",[1029,3161,3159],{"__ignoreMap":1395},[981,3163,2630],{},[1387,3165,3168],{"className":3166,"code":3167,"language":1392},[1390],"t = b = 1\n",[1029,3169,3167],{"__ignoreMap":1395},[981,3171,2894],{},[1387,3173,3176],{"className":3174,"code":3175,"language":1607,"meta":1395},[1605],"*x = *y;\n",[1029,3177,3175],{"__ignoreMap":1395},[981,3179,3180,2651,3182,3184,3185,2651,3187,3189],{},[1029,3181,2860],{"code":2860},[1029,3183,2654],{"code":2654},"，",[1029,3186,2376],{"code":2376},[1029,3188,1607],{"code":1607},"，所以：",[1387,3191,3194],{"className":3192,"code":3193,"language":1392},[1390],"b = c = 5\n",[1029,3195,3193],{"__ignoreMap":1395},[981,3197,3198],{},"最后：",[1387,3200,3203],{"className":3201,"code":3202,"language":1607,"meta":1395},[1605],"*y = t;\n",[1029,3204,3202],{"__ignoreMap":1395},[981,3206,2630],{},[1387,3208,3211],{"className":3209,"code":3210,"language":1392},[1390],"c = 1\n",[1029,3212,3210],{"__ignoreMap":1395},[981,3214,2728],{},[1387,3216,3219],{"className":3217,"code":3218,"language":1392},[1390],"a = 3\nb = 5\nc = 1\n",[1029,3220,3218],{"__ignoreMap":1395},[981,3222,3223],{},"所以答案是表格里的：",[1387,3225,3227],{"className":3226,"code":3068,"language":1392},[1390],[1029,3228,3068],{"__ignoreMap":1395},[981,3230,3231,3232,3235,3236,3238,3239,3241],{},"虽然 ",[1029,3233,3234],{"code":3234},"printf"," 里只打印了 ",[1029,3237,2211],{"code":2211}," 和 ",[1029,3240,2654],{"code":2654},"，但如果问三个变量最终值，就是：",[1387,3243,3245],{"className":3244,"code":3218,"language":1392},[1390],[1029,3246,3218],{"__ignoreMap":1395},[1142,3248],{},[1149,3250,3252],{"id":3251},"p2536-小结","p25–36 小结",[981,3254,3255],{},"最关键的心法是这句：",[1387,3257,3260],{"className":3258,"code":3259,"language":1392},[1390],"p 是地址，*p 是地址里的东西。\n",[1029,3261,3259],{"__ignoreMap":1395},[1142,3263],{},[988,3265,3267],{"id":3266},"第三章数组指针与字符串","第三章：数组、指针与字符串",[981,3269,3270,3273],{},[1247,3271,3272],{},"p37–59 是“数组和指针绑定在一起”的部分","，主线清楚：",[978,3275,3276],{},[981,3277,3278,3279,3282,3283,3286],{},"C 里的数组名很多时候会像指针一样用；数组不记录长度；指针运算按“元素大小”移动；字符串本质上是以 ",[1029,3280,3281],{"code":3281},"'\\0'"," 结尾的 ",[1029,3284,3285],{"code":3285},"char"," 数组。",[981,3288,3289,3290,3292,3293,3238,3296,3299],{},"课件这一段从 C 数组、越界、",[1029,3291,1031],{"code":1031},"、指针运算、数组\u002F指针对偶性，一直讲到字符串、",[1029,3294,3295],{"code":3295},"strlen",[1029,3297,3298],{"code":3298},"main"," 的命令行参数。",[1142,3301],{},[1149,3303,3305],{"id":3304},"p37c-数组-c-arrays","p37：C 数组 C Arrays",[1744,3307,3308],{},[1747,3309,3310],{},"基本声明",[1387,3312,3315],{"className":3313,"code":3314,"language":1607,"meta":1395},[1605],"int a[5];\n",[1029,3316,3314],{"__ignoreMap":1395},[981,3318,3319,3320,3323,3324,3327],{},"也就是说，",[1029,3321,3322],{"code":3322},"a[5]"," 只是分配空间，",[1247,3325,3326],{},"里面原来是什么值不确定","。如果是局部数组，里面可能是垃圾值。",[1744,3329,3330],{"start":1935},[1747,3331,3332],{},"初始化数组：",[1387,3334,3337],{"className":3335,"code":3336,"language":1607,"meta":1395},[1605],"int b[] = {3, 2, 1};\n",[1029,3338,3336],{"__ignoreMap":1395},[981,3340,3341,3342,3344],{},"这里编译器会根据初始化元素数量推断数组长度，所以 ",[1029,3343,2654],{"code":2654}," 长度是 3。",[1744,3346,3347],{"start":2452},[1747,3348,3349],{},"C 数组下标从 0 开始：",[1387,3351,3354],{"className":3352,"code":3353,"language":1392},[1390],"b[0] = 3\nb[1] = 2\nb[2] = 1\n",[1029,3355,3353],{"__ignoreMap":1395},[1142,3357],{},[1149,3359,3361],{"id":3360},"p38注意c-没有数组边界检查","p38：注意：C 没有数组边界检查",[981,3363,3364],{},"这一页非常重要。",[981,3366,3367],{},"课件例子类似：",[1387,3369,3372],{"className":3370,"code":3371,"language":1607,"meta":1395},[1605],"int a[] = {1, 2, 3};\n\nfor (int i = 0; i \u003C 4; i++)\n    printf(\"a[%d] = %d\\n\", i, a[i]);\n",[1029,3373,3371],{"__ignoreMap":1395},[981,3375,3376],{},"数组只有 3 个元素，但循环访问到了第四个",[981,3378,3379],{},"课件给出的输出是：",[1387,3381,3384],{"className":3382,"code":3383,"language":1392},[1390],"a[0] = 1\na[1] = 2\na[2] = 3\na[3] = -1870523725\n",[1029,3385,3383],{"__ignoreMap":1395},[981,3387,3388,3389,3392],{},"这个 ",[1029,3390,3391],{"code":3391},"a[3]"," 不是数组里的合法元素，而是数组后面某块内存里的“碰巧值”。课件也强调，结果可能更糟：不可预测行为、segmentation fault，而且 C 语言本身不知道数组长度。",[981,3394,3395],{},"C 不会帮你检查数组越界。",[981,3397,3398,3399],{},"Java\u002FPython 越界通常会报错；C 可能不报错，还继续跑，甚至把别的变量改坏。这个是 C 最阴险的地方之一：",[1247,3400,3401],{},"错了不一定立刻死，可能以后才炸。",[1142,3403],{},[1149,3405,3407],{"id":3406},"p39使用常量而不是字面量","p39：使用常量，而不是字面量",[981,3409,3410],{},"这页讲写代码习惯。",[981,3412,3413],{},"不好的写法：",[1387,3415,3418],{"className":3416,"code":3417,"language":1607,"meta":1395},[1605],"int i, ar[10];\n\nfor (i = 0; i \u003C 10; i++) {\n    ...\n}\n",[1029,3419,3417],{"__ignoreMap":1395},[981,3421,3422],{},"好的写法：",[1387,3424,3427],{"className":3425,"code":3426,"language":1607,"meta":1395},[1605],"const int ARRAY_SIZE = 10;\n\nint i, a[ARRAY_SIZE];\n\nfor (i = 0; i \u003C ARRAY_SIZE; i++) {\n    ...\n}\n",[1029,3428,3426],{"__ignoreMap":1395},[981,3430,3431],{},"这样数组长度只有一个来源：",[1142,3433],{},[1149,3435,3437],{"id":3436},"p40指向不同大小的对象","p40：指向不同大小的对象",[981,3439,3440],{},"这一页把 p29 的“指针类型”讲得更底层。",[981,3442,3443,3444,3447],{},"现代机器是 ",[1247,3445,3446],{},"byte-addressable，字节寻址"," 的。意思是：内存里每一个字节都有自己的地址。",[981,3449,3450],{},"但问题是，不同类型占用的字节数不同：",[1387,3452,3455],{"className":3453,"code":3454,"language":1392},[1390],"char   通常 1 字节\nshort  通常 2 字节\nint    通常 4 字节\ndouble 通常 8 字节\n",[1029,3456,3454],{"__ignoreMap":1395},[981,3458,2719],{},[1387,3460,3463],{"className":3461,"code":3462,"language":1607,"meta":1395},[1605],"char *z;\nshort *y;\nint *x;\n",[1029,3464,3462],{"__ignoreMap":1395},[981,3466,3467],{},"虽然它们本质上都存地址，但编译器通过类型知道：",[1387,3469,3472],{"className":3470,"code":3471,"language":1392},[1390],"*z 取 1 个字节\n*y 取 2 个字节\n*x 取 4 个字节\n",[1029,3473,3471],{"__ignoreMap":1395},[981,3475,3476],{},"这就是为什么指针类型重要。",[978,3478,3479,3486,3492,3509],{},[981,3480,3481,3482,3485],{},"比如地址 ",[1029,3483,3484],{"code":3484},"0x1000","：",[1387,3487,3490],{"className":3488,"code":3489,"language":1607,"meta":1395},[1605],"char *pc = (char *)0x1000;\nint  *pi = (int  *)0x1000;\n",[1029,3491,3489],{"__ignoreMap":1395},[981,3493,3494,3497,3498,3501,3502,3497,3505,3508],{},[1029,3495,3496],{"code":3496},"pc + 1"," 到 ",[1029,3499,3500],{"code":3500},"0x1001","，因为 char 一个元素 1 字节。\n",[1029,3503,3504],{"code":3504},"pi + 1",[1029,3506,3507],{"code":3507},"0x1004","，因为 int 一个元素 4 字节。",[981,3510,3511],{},"这一页为 p42、p43 的指针运算做铺垫。",[1142,3513],{},[1149,3515,3517,3518,3521],{"id":3516},"p41sizeof-操作符","p41：",[1029,3519,3520],{"code":3520},"sizeof()"," 操作符",[981,3523,3524,3526],{},[1029,3525,1031],{"code":1031}," 返回对象或类型占用的字节数。",[981,3528,3529],{},"如果代码里有：",[1387,3531,3534],{"className":3532,"code":3533,"language":1607,"meta":1395},[1605],"double d;\nint array[5];\n\nstruct {\n    short a;\n    char c;\n} s;\n",[1029,3535,3533],{"__ignoreMap":1395},[981,3537,3538],{},"课件例子输出：",[1387,3540,3543],{"className":3541,"code":3542,"language":1392},[1390],"double: 8\narray: 20\ns: 4\n",[1029,3544,3542],{"__ignoreMap":1395},[981,3546,3547,3550,3551,3554,3555,3557],{},[1029,3548,3549],{"code":3549},"sizeof(s)"," 是 4，不是 3，这里涉及结构体对齐。",[1029,3552,3553],{"code":3553},"short"," 2 字节，",[1029,3556,3285],{"code":3285}," 1 字节，但编译器可能为了对齐补 1 字节，所以整个结构体是 4 字节。",[1487,3559,3560],{"type":1489},[981,3561,3562],{},"struct中的总大小必须是其内部最大对齐要求成员的整数倍",[981,3564,3565],{},"C 里定义保证",[1387,3567,3570],{"className":3568,"code":3569,"language":1392},[1390],"sizeof(char) == 1\n",[1029,3571,3569],{"__ignoreMap":1395},[981,3573,3574,3575,2170],{},"其他类型大小取决于硬件和编译器，不要假设，要用 ",[1029,3576,1031],{"code":1031},[981,3578,3579],{},"你要记住两个常用公式：",[1387,3581,3584],{"className":3582,"code":3583,"language":1607,"meta":1395},[1605],"sizeof(array)              \u002F\u002F 整个数组的字节数，仅在数组还没退化成指针时有效\nsizeof(array) \u002F sizeof(array[0])  \u002F\u002F 数组元素个数\n",[1029,3585,3583],{"__ignoreMap":1395},[1142,3587],{},[1149,3589,3591],{"id":3590},"p42指针运算字符-char","p42：指针运算——字符 char",[981,3593,3594],{},"课件例子是字符数组：",[1387,3596,3599],{"className":3597,"code":3598,"language":1607,"meta":1395},[1605],"char c[] = {'a', 'b'};\nchar *pc = c;\n\npc++;\n",[1029,3600,3598],{"__ignoreMap":1395},[981,3602,3603],{},"课件输出类似：",[1387,3605,3608],{"className":3606,"code":3607,"language":1392},[1390],"*pc = b\nc  = 0x...3e\npc = 0x...3f\npc - c = 1\n",[1029,3609,3607],{"__ignoreMap":1395},[981,3611,3612,3613,3615],{},"注意地址差了 1，因为 ",[1029,3614,3285],{"code":3285}," 一个元素就是 1 字节。",[981,3617,3618],{},"这里有个很关键的点：",[1387,3620,3623],{"className":3621,"code":3622,"language":1607,"meta":1395},[1605],"pc - c\n",[1029,3624,3622],{"__ignoreMap":1395},[981,3626,3627,3630,3631,3635],{},[1515,3628,3629],{},"结果是 1，不是单纯地址数值相减的字节数概念，而是“相差几个 char 元素”。对于"," ",[1515,3632,3633],{},[1029,3634,3285],{"code":3285},[1515,3636,3637],{},"，刚好一个元素就是 1 字节，所以看起来一样。",[1142,3639],{},[1149,3641,3643],{"id":3642},"p43指针运算整型-int","p43：指针运算——整型 int",[981,3645,3646],{},"这一页和 p42 对比。",[981,3648,2493],{},[1387,3650,3653],{"className":3651,"code":3652,"language":1607,"meta":1395},[1605],"int i[] = {10, 20};\nint *pi = i;\n\npi++;\n",[1029,3654,3652],{"__ignoreMap":1395},[981,3656,3603],{},[1387,3658,3661],{"className":3659,"code":3660,"language":1392},[1390],"*pi = 20\ni  = 0x...40\npi = 0x...44\npi - i = 1\n",[1029,3662,3660],{"__ignoreMap":1395},[981,3664,3665,3666,3668],{},"注意地址差了 4，因为 ",[1029,3667,1852],{"code":1852}," 通常是 4 字节。\n但是：",[1387,3670,3673],{"className":3671,"code":3672,"language":1607,"meta":1395},[1605],"pi - i\n",[1029,3674,3672],{"__ignoreMap":1395},[981,3676,3677,3678,3680],{},"结果仍然是 1，因为它表示相差 1 个 ",[1029,3679,1852],{"code":1852}," 元素。",[981,3682,3683],{},[1515,3684,3685],{},"所以指针运算的规则是：",[1387,3687,3690],{"className":3688,"code":3689,"language":1392},[1390],"p + 1 不是地址数值 + 1\n而是地址数值 + sizeof(*p)\n",[1029,3691,3689],{"__ignoreMap":1395},[981,3693,3694],{},"比如：",[1387,3696,3699],{"className":3697,"code":3698,"language":1392},[1390],"char *p; p + 1   移动 1 字节\nint *p;  p + 1   移动 4 字节，假设 sizeof(int)=4\ndouble *p; p + 1 移动 8 字节，假设 sizeof(double)=8\n",[1029,3700,3698],{"__ignoreMap":1395},[981,3702,3703],{},"这页非常核心。",[1142,3705],{},[1149,3707,3709],{"id":3708},"p44数组名-指针对偶性","p44：数组名 \u002F 指针对偶性",[981,3711,3712],{},"这页是 C 里最经典的一页。",[981,3714,3715],{},"课件说：",[978,3717,3718],{},[981,3719,3720],{},"数组变量是指向第一个，也就是第 0 个元素的指针。",[981,3722,3723,3724,2170],{},"更准确地说：",[1247,3725,3726],{},"数组名在大多数表达式中会退化成指向首元素的指针",[981,3728,3729],{},"例如：",[1387,3731,3734],{"className":3732,"code":3733,"language":1607,"meta":1395},[1605],"int a[3] = {10, 20, 30};\n",[1029,3735,3733],{"__ignoreMap":1395},[981,3737,3738,3739,3783],{},"那么",[3740,3741,3744,3765],"span",{"className":3742},[3743],"katex",[3740,3745,3748],{"className":3746},[3747],"katex-mathml",[3749,3750,3752],"math",{"xmlns":3751},"http:\u002F\u002Fwww.w3.org\u002F1998\u002FMath\u002FMathML",[3753,3754,3755,3761],"semantics",{},[3756,3757,3758],"mrow",{},[3759,3760,2211],"mi",{},[3762,3763,2211],"annotation",{"encoding":3764},"application\u002Fx-tex",[3740,3766,3769],{"className":3767,"ariaHidden":1979},[3768],"katex-html",[3740,3770,3773,3778],{"className":3771},[3772],"base",[3740,3774],{"className":3775,"style":3777},[3776],"strut","height:0.4306em;",[3740,3779,2211],{"className":3780},[3781,3782],"mord","mathnormal","很多时候等价于：",[1387,3785,3788],{"className":3786,"code":3787,"language":1607,"meta":1395},[1605],"&a[0]\n",[1029,3789,3787],{"__ignoreMap":1395},[981,3791,2719],{},[1387,3793,3796],{"className":3794,"code":3795,"language":1607,"meta":1395},[1605],"a[0] == *a\na[2] == *(a + 2)\n",[1029,3797,3795],{"__ignoreMap":1395},[981,3799,3800,3801,3485],{},"但是数组名和真正的指针变量",[1515,3802,3803],{},"还有区别",[1387,3805,3808],{"className":3806,"code":3807,"language":1607,"meta":1395},[1605],"char astr[] = \"abc\";\nchar *pstr = astr;\n",[1029,3809,3807],{"__ignoreMap":1395},[981,3811,3812,3813,3890],{},"可以：",[3740,3814,3816,3845],{"className":3815},[3743],[3740,3817,3819],{"className":3818},[3747],[3749,3820,3821],{"xmlns":3751},[3753,3822,3823,3842],{},[3756,3824,3825,3827,3830,3833,3836,3840],{},[3759,3826,981],{},[3759,3828,3829],{},"s",[3759,3831,3832],{},"t",[3759,3834,3835],{},"r",[3837,3838,3839],"mo",{},"+",[3837,3841,3839],{},[3762,3843,3844],{"encoding":3764},"pstr++",[3740,3846,3848,3880],{"className":3847,"ariaHidden":1979},[3768],[3740,3849,3851,3855,3858,3861,3864,3868,3873,3877],{"className":3850},[3772],[3740,3852],{"className":3853,"style":3854},[3776],"height:0.8095em;vertical-align:-0.1944em;",[3740,3856,981],{"className":3857},[3781,3782],[3740,3859,3829],{"className":3860},[3781,3782],[3740,3862,3832],{"className":3863},[3781,3782],[3740,3865,3835],{"className":3866,"style":3867},[3781,3782],"margin-right:0.0278em;",[3740,3869],{"className":3870,"style":3872},[3871],"mspace","margin-right:0.2222em;",[3740,3874,3839],{"className":3875},[3876],"mbin",[3740,3878],{"className":3879,"style":3872},[3871],[3740,3881,3883,3887],{"className":3882},[3772],[3740,3884],{"className":3885,"style":3886},[3776],"height:0.6667em;vertical-align:-0.0833em;",[3740,3888,3839],{"className":3889},[3781],";",[981,3892,3893,3894,3890],{},"但不能：",[3740,3895,3897,3921],{"className":3896},[3743],[3740,3898,3900],{"className":3899},[3747],[3749,3901,3902],{"xmlns":3751},[3753,3903,3904,3918],{},[3756,3905,3906,3908,3910,3912,3914,3916],{},[3759,3907,2211],{},[3759,3909,3829],{},[3759,3911,3832],{},[3759,3913,3835],{},[3837,3915,3839],{},[3837,3917,3839],{},[3762,3919,3920],{"encoding":3764},"astr++",[3740,3922,3924,3952],{"className":3923,"ariaHidden":1979},[3768],[3740,3925,3927,3931,3934,3937,3940,3943,3946,3949],{"className":3926},[3772],[3740,3928],{"className":3929,"style":3930},[3776],"height:0.6984em;vertical-align:-0.0833em;",[3740,3932,2211],{"className":3933},[3781,3782],[3740,3935,3829],{"className":3936},[3781,3782],[3740,3938,3832],{"className":3939},[3781,3782],[3740,3941,3835],{"className":3942,"style":3867},[3781,3782],[3740,3944],{"className":3945,"style":3872},[3871],[3740,3947,3839],{"className":3948},[3876],[3740,3950],{"className":3951,"style":3872},[3871],[3740,3953,3955,3958],{"className":3954},[3772],[3740,3956],{"className":3957,"style":3886},[3776],[3740,3959,3839],{"className":3960},[3781],[981,3962,2466,3963,3966],{},[1029,3964,3965],{"code":3965},"astr"," 是数组名，不是一个可以被修改的指针变量。它像指针用，但不是普通指针变量。",[1487,3968,3969],{"type":1489},[981,3970,3971],{},"数组名像指针，但是不能随便改",[1142,3973],{},[1149,3975,3977],{"id":3976},"p45数组与指针示例","p45：数组与指针示例",[981,3979,3980],{},"这一页展示数组表示法和指针表示法可以混用。",[981,3982,3983],{},[1156,3984],{"alt":1156,"src":3985},"\u002Fimg\u002FComputerOrganization\u002FL3-CMemory-10.png",[981,3987,3988,3989],{},"但课件最后也提醒：",[1247,3990,3991],{},"混合指针和数组表示法会让人困惑，避免乱用。",[1142,3993],{},[1149,3995,3997],{"id":3996},"p46指针运算-pointer-arithmetic","p46：指针运算 Pointer Arithmetic",[981,3999,4000],{},"课件给出：",[1387,4002,4005],{"className":4003,"code":4004,"language":1607,"meta":1395},[1605],"int n = 3;\nint *p;\n\np += n;  \u002F\u002F adds n * sizeof(int) to p\np -= n;  \u002F\u002F subtracts n * sizeof(int) from p\n",[1029,4006,4004],{"__ignoreMap":1395},[978,4008,4009,4027,4030],{},[981,4010,4011,4012,3630,4016,3630,4019,3630,4023,4026],{},"这个例子隐含的关键前提是：",[1247,4013,4014],{},[1029,4015,981],{"code":981},[1247,4017,4018],{},"必须已经指向某个",[1247,4020,4021],{},[1029,4022,1852],{"code":1852},[1247,4024,4025],{},"数组中的元素","，而且运算结果仍不能越出该数组的合法范围。",[981,4028,4029],{},"应当类似这样：",[1387,4031,4034],{"className":4032,"code":4033,"language":1392},[1390],"int a[10];\nint *p = &a[2];\n\np += 3;   \u002F\u002F 现在指向 a[5]\np -= 3;   \u002F\u002F 回到 a[2]\n",[1029,4035,4033],{"__ignoreMap":1395},[981,4037,4038,4039,4042],{},"如果 ",[1029,4040,4041],{"code":4041},"sizeof(int) == 4","，那么：",[1387,4044,4047],{"className":4045,"code":4046,"language":1607,"meta":1395},[1605],"p += 3;\n",[1029,4048,4046],{"__ignoreMap":1395},[981,4050,4051],{},"实际地址增加：",[1387,4053,4056],{"className":4054,"code":4055,"language":1392},[1390],"3 * 4 = 12 字节\n",[1029,4057,4055],{"__ignoreMap":1395},[981,4059,4060],{},"重点不是“加 3 个字节”，而是“向后移动 3 个 int 元素”。",[981,4062,4063],{},[1515,4064,4065],{},[1247,4066,4067],{},"指针运算仅用于数组。",[981,4069,4070],{},"不要这样：",[1387,4072,4075],{"className":4073,"code":4074,"language":1607,"meta":1395},[1605],"char *p;\nchar a, b;\n\np = &a;\np += 1;\n",[1029,4076,4074],{"__ignoreMap":1395},[981,4078,4079,4080,4083,4084,4086,4087,3238,4089,4091,4092,4094,4095,4097],{},"你可能以为 ",[1029,4081,4082],{"code":4082},"p += 1"," 会指向 ",[1029,4085,2654],{"code":2654},"，但不一定。因为 ",[1029,4088,2211],{"code":2211},[1029,4090,2654],{"code":2654}," 不保证在内存里挨着，也不保证顺序。课件也明确说，",[1029,4093,4082],{"code":4082}," may point to ",[1029,4096,2654],{"code":2654},", or not。",[1142,4099],{},[1149,4101,4103],{"id":4102},"p47数组和指针数组传参会丢失大小","p47：数组和指针：数组传参会丢失大小",[981,4105,4106,4107,4110],{},"数组作为指针传给函数后，",[1515,4108,4109],{},"数组大小丢失","，所以要 explicitly pass size",[981,4112,3715],{},[1387,4114,4117],{"className":4115,"code":4116,"language":1607,"meta":1395},[1605],"a[i] ≡ *(a+i)\n",[1029,4118,4116],{"__ignoreMap":1395},[981,4120,4121],{},"但是，当数组作为函数参数传递时，会退化成指针。",[981,4123,3729],{},[1387,4125,4128],{"className":4126,"code":4127,"language":1607,"meta":1395},[1605],"int foo(int array[]) {\n    ...\n}\n",[1029,4129,4127],{"__ignoreMap":1395},[981,4131,4132],{},"实际上参数里的：",[1387,4134,4137],{"className":4135,"code":4136,"language":1607,"meta":1395},[1605],"int array[]\n",[1029,4138,4136],{"__ignoreMap":1395},[981,4140,4141],{},"差不多就是：",[1387,4143,4146],{"className":4144,"code":4145,"language":1607,"meta":1395},[1605],"int *array\n",[1029,4147,4145],{"__ignoreMap":1395},[981,4149,4150],{},"所以函数里面不知道数组原本有多长。",[981,4152,4153],{},"因此必须显式传入长度：",[1387,4155,4158],{"className":4156,"code":4157,"language":1607,"meta":1395},[1605],"int foo(int array[], unsigned int size) {\n    return array[size - 1];\n}\n\nint main(void) {\n    int a[10], b[5];\n\n    foo(a, 10);\n    foo(b, 5);\n}\n",[1029,4159,4157],{"__ignoreMap":1395},[981,4161,4162],{},"记住：",[1387,4164,4167],{"className":4165,"code":4166,"language":1392},[1390],"C 函数收到数组参数时，收到的是首元素地址，不是整个数组信息。\n",[1029,4168,4166],{"__ignoreMap":1395},[1142,4170],{},[1149,4172,4174,4175,4177],{"id":4173},"p48sizeofarray-在函数里为什么变了","p48：",[1029,4176,1075],{"code":1075}," 在函数里为什么变了？",[981,4179,4180],{},"这页是 p47 的实锤例子。",[981,4182,4183],{},"代码结构大概是：",[1387,4185,4188],{"className":4186,"code":4187,"language":1607,"meta":1395},[1605],"int foo(int array[], unsigned int size) {\n    printf(\"%d\\n\", sizeof(array));\n}\n\nint main(void) {\n    int a[10];\n\n    foo(a, 10);\n    printf(\"%d\\n\", sizeof(a));\n}\n",[1029,4189,4187],{"__ignoreMap":1395},[981,4191,4192],{},"假设：",[1387,4194,4197],{"className":4195,"code":4196,"language":1392},[1390],"sizeof(int) == 4\n",[1029,4198,4196],{"__ignoreMap":1395},[981,4200,4201,4202,4204],{},"在 ",[1029,4203,3298],{"code":3298}," 里：",[1387,4206,4209],{"className":4207,"code":4208,"language":1607,"meta":1395},[1605],"sizeof(a)\n",[1029,4210,4208],{"__ignoreMap":1395},[981,4212,4213],{},"是整个数组大小：",[1387,4215,4218],{"className":4216,"code":4217,"language":1392},[1390],"10 * 4 = 40\n",[1029,4219,4217],{"__ignoreMap":1395},[981,4221,4222],{},"所以输出 40。",[981,4224,4225,4226,4204],{},"但在 ",[1029,4227,4228],{"code":4228},"foo",[1387,4230,4233],{"className":4231,"code":4232,"language":1607,"meta":1395},[1605],"sizeof(array)\n",[1029,4234,4232],{"__ignoreMap":1395},[981,4236,4237,4238,4241,4242,4244,4245,4248],{},"这里的 ",[1029,4239,4240],{"code":4240},"array"," 已经不是原数组了，而是一个指针参数。\n在现代 64 位机器上，指针通常是 8 字节，所以输出 8。课件这页也给出 ",[1029,4243,1075],{"code":1075}," 打印 8，而 ",[1029,4246,4247],{"code":4247},"sizeof(a)"," 打印 40，并解释函数里的数组实际上是指针。",[981,4250,4251],{},"所以这个坑要背下来：",[1387,4253,4256],{"className":4254,"code":4255,"language":1607,"meta":1395},[1605],"void foo(int array[]) {\n    sizeof(array); \u002F\u002F 指针大小，不是数组大小\n}\n",[1029,4257,4255],{"__ignoreMap":1395},[981,4259,4260,4261,4264],{},"不要在函数里用 ",[1029,4262,4263],{"code":4263},"sizeof(array)\u002Fsizeof(array[0])"," 计算数组长度。会错。",[1142,4266],{},[1149,4268,4270],{"id":4269},"p49用下标遍历数组-vs-用指针遍历数组","p49：用下标遍历数组 vs 用指针遍历数组",[981,4272,4273],{},"课件说这两段代码效果相同。",[981,4275,4276],{},"第一种，下标写法：",[1387,4278,4281],{"className":4279,"code":4280,"language":1607,"meta":1395},[1605],"int i;\nint array[5];\n\nfor (i = 0; i \u003C 5; i++) {\n    array[i] = ...;\n}\n",[1029,4282,4280],{"__ignoreMap":1395},[981,4284,4285],{},"第二种，指针写法：",[1387,4287,4290],{"className":4288,"code":4289,"language":1607,"meta":1395},[1605],"int *p;\nint array[5];\n\nfor (p = array; p \u003C &array[5]; p++) {\n    *p = ...;\n}\n",[1029,4291,4289],{"__ignoreMap":1395},[1387,4293,4296],{"className":4294,"code":4295,"language":1392},[1390],"array[i] 视角：第 i 个元素\n*p 视角：当前指针指向的元素\n",[1029,4297,4295],{"__ignoreMap":1395},[1142,4299],{},[1149,4301,4303],{"id":4302},"p50数组末尾之后的指针","p50：数组末尾之后的指针",[981,4305,4306],{},"这一页讲一个有点反直觉但很常用的规则。",[981,4308,2812],{},[1387,4310,4313],{"className":4311,"code":4312,"language":1607,"meta":1395},[1605],"const int SZ = 10;\nint ar[SZ], *p, *q, sum = 0;\n\np = &ar[0];\nq = &ar[SZ];\n\nwhile (p != q) {\n    sum += *p++;\n}\n",[1029,4314,4312],{"__ignoreMap":1395},[978,4316,4317,4323,4326,4332],{},[1387,4318,4321],{"className":4319,"code":4320,"language":1607,"meta":1395},[1605],"sum += *p++;\n",[1029,4322,4320],{"__ignoreMap":1395},[981,4324,4325],{},"等价于：",[1387,4327,4330],{"className":4328,"code":4329,"language":1607,"meta":1395},[1605],"sum += *p;\np = p + 1;\n",[1029,4331,4329],{"__ignoreMap":1395},[981,4333,4334,4335,4338,4339,4341,4342,4344],{},"因为后缀 ",[1029,4336,4337],{"code":4337},"++"," 优先使用当前 ",[1029,4340,981],{"code":981},"，再让 ",[1029,4343,981],{"code":981}," 后移。",[981,4346,4347],{},"问题是：",[1387,4349,4352],{"className":4350,"code":4351,"language":1607,"meta":1395},[1605],"q = &ar[SZ];\n",[1029,4353,4351],{"__ignoreMap":1395},[981,4355,4356],{},"合法的吗？",[981,4358,4359,4362],{},[1029,4360,4361],{"code":4361},"ar[SZ]"," 看起来越界，因为最后一个合法元素是：",[1387,4364,4367],{"className":4365,"code":4366,"language":1607,"meta":1395},[1605],"ar[SZ - 1]\n",[1029,4368,4366],{"__ignoreMap":1395},[981,4370,4371],{},[1515,4372,4373],{},"但是 C 允许你形成“数组末尾后一个元素”的地址，也就是 one-past-the-end pointer。",[981,4375,2719],{},[1387,4377,4380],{"className":4378,"code":4379,"language":1607,"meta":1395},[1605],"&ar[SZ]\n",[1029,4381,4379],{"__ignoreMap":1395},[981,4383,4384],{},"可以作为边界指针来比较。课件也写明，C 定义数组结束后的一个元素必须是有效地址，不会导致错误。",[981,4386,4387],{},"但千万注意：",[1387,4389,4392],{"className":4390,"code":4391,"language":1607,"meta":1395},[1605],"q = &ar[SZ];  \u002F\u002F 可以\n*q           \u002F\u002F 不可以\n",[1029,4393,4391],{"__ignoreMap":1395},[981,4395,2630],{},[1387,4397,4400],{"className":4398,"code":4399,"language":1392},[1390],"数组末尾后一个位置可以拿来比较，但不能解引用。\n",[1029,4401,4399],{"__ignoreMap":1395},[1487,4403,4404,4500],{"type":1489},[981,4405,4406,4407,4465,4466,4499],{},"优先级\n",[3740,4408,4410,4431],{"className":4409},[3743],[3740,4411,4413],{"className":4412},[3747],[3749,4414,4415],{"xmlns":3751},[3753,4416,4417,4428],{},[3756,4418,4419,4422,4424,4426],{},[3837,4420,4421],{},"∗",[3759,4423,981],{},[3837,4425,3839],{},[3837,4427,3839],{},[3762,4429,4430],{"encoding":3764},"*p++",[3740,4432,4434,4456],{"className":4433,"ariaHidden":1979},[3768],[3740,4435,4437,4441,4444,4447,4450,4453],{"className":4436},[3772],[3740,4438],{"className":4439,"style":4440},[3776],"height:0.7778em;vertical-align:-0.1944em;",[3740,4442,4421],{"className":4443},[3781],[3740,4445,981],{"className":4446},[3781,3782],[3740,4448],{"className":4449,"style":3872},[3871],[3740,4451,3839],{"className":4452},[3876],[3740,4454],{"className":4455,"style":3872},[3871],[3740,4457,4459,4462],{"className":4458},[3772],[3740,4460],{"className":4461,"style":3886},[3776],[3740,4463,3839],{"className":4464},[3781],"： ",[3740,4467,4469,4484],{"className":4468},[3743],[3740,4470,4472],{"className":4471},[3747],[3749,4473,4474],{"xmlns":3751},[3753,4475,4476,4482],{},[3756,4477,4478,4480],{},[3837,4479,3839],{},[3837,4481,3839],{},[3762,4483,4337],{"encoding":3764},[3740,4485,4487],{"className":4486,"ariaHidden":1979},[3768],[3740,4488,4490,4493,4496],{"className":4489},[3772],[3740,4491],{"className":4492,"style":3886},[3776],[3740,4494,3839],{"className":4495},[3781],[3740,4497,3839],{"className":4498},[3781],"的优先级更高，所以是指针向后一个单位",[981,4501,4502,4573],{},[3740,4503,4505,4531],{"className":4504},[3743],[3740,4506,4508],{"className":4507},[3747],[3749,4509,4510],{"xmlns":3751},[3753,4511,4512,4528],{},[3756,4513,4514,4517,4519,4521,4524,4526],{},[3837,4515,4516],{"stretchy":1962},"(",[3837,4518,4421],{},[3759,4520,981],{},[3837,4522,4523],{"stretchy":1962},")",[3837,4525,3839],{},[3837,4527,3839],{},[3762,4529,4530],{"encoding":3764},"(*p)++",[3740,4532,4534,4564],{"className":4533,"ariaHidden":1979},[3768],[3740,4535,4537,4541,4545,4548,4551,4555,4558,4561],{"className":4536},[3772],[3740,4538],{"className":4539,"style":4540},[3776],"height:1em;vertical-align:-0.25em;",[3740,4542,4516],{"className":4543},[4544],"mopen",[3740,4546,4421],{"className":4547},[3781],[3740,4549,981],{"className":4550},[3781,3782],[3740,4552,4523],{"className":4553},[4554],"mclose",[3740,4556],{"className":4557,"style":3872},[3871],[3740,4559,3839],{"className":4560},[3876],[3740,4562],{"className":4563,"style":3872},[3871],[3740,4565,4567,4570],{"className":4566},[3772],[3740,4568],{"className":4569,"style":3886},[3776],[3740,4571,3839],{"className":4572},[3781]," ： 是先解引用，然后给p对应的值++",[1142,4575],{},[1149,4577,4579],{"id":4578},"p51有效的指针运算","p51：有效的指针运算",[981,4581,4582],{},"课件总结哪些指针运算合法。",[981,4584,4585],{},"合法的：",[1387,4587,4590],{"className":4588,"code":4589,"language":1392},[1390],"1. 指针 + 整数\n2. 指针 - 整数\n3. 两个指针相减，但必须指向同一个数组中的元素\n4. 比较指针：\u003C, \u003C=, ==, !=, >, >=\n5. 指针和 NULL 比较\n",[1029,4591,4589],{"__ignoreMap":1395},[981,4593,4594],{},"不合法的：",[1387,4596,4599],{"className":4597,"code":4598,"language":1392},[1390],"两个指针相加\n指针相乘\n整数 - 指针\n",[1029,4600,4598],{"__ignoreMap":1395},[1142,4602],{},[1149,4604,4606],{"id":4605},"p52指向指针的指针","p52：指向指针的指针",[981,4608,4609],{},"看一个简单例子：",[1387,4611,4614],{"className":4612,"code":4613,"language":1607,"meta":1395},[1605],"int x = 10;\nint *p = &x;\nint **pp = &p;\n",[1029,4615,4613],{"__ignoreMap":1395},[981,4617,4618],{},"关系是：",[1387,4620,4623],{"className":4621,"code":4622,"language":1392},[1390],"x   是 int，值是 10\np   是 int*，存的是 x 的地址\npp  是 int**，存的是 p 的地址\n",[1029,4624,4622],{"__ignoreMap":1395},[978,4626,4627,4629,4635,4638,4644,4647,4653],{},[981,4628,2719],{},[1387,4630,4633],{"className":4631,"code":4632,"language":1607,"meta":1395},[1605],"*p\n",[1029,4634,4632],{"__ignoreMap":1395},[981,4636,4637],{},"是 x。",[1387,4639,4642],{"className":4640,"code":4641,"language":1607,"meta":1395},[1605],"*pp\n",[1029,4643,4641],{"__ignoreMap":1395},[981,4645,4646],{},"是 p。",[1387,4648,4651],{"className":4649,"code":4650,"language":1607,"meta":1395},[1605],"**pp\n",[1029,4652,4650],{"__ignoreMap":1395},[981,4654,4637],{},[981,4656,4657],{},"可以画成：",[1387,4659,4662],{"className":4660,"code":4661,"language":1392},[1390],"pp ──> p ──> x ──> 10\n",[1029,4663,4661],{"__ignoreMap":1395},[1142,4665],{},[1149,4667,4669,4670],{"id":4668},"p53练习题int-pp","p53：练习题——",[1029,4671,4672],{"code":4672},"int **pp",[981,4674,4675],{},"课件代码：",[1387,4677,4680],{"className":4678,"code":4679,"language":1607,"meta":1395},[1605],"int x[] = {2, 4, 6, 8, 10};\n\nint *p = x;\nint **pp = &p;\n\n(*pp)++;\n(*(*pp))++;\n\nprintf(\"%d\\n\", *p);\n",[1029,4681,4679],{"__ignoreMap":1395},[1599,4683,2611],{"id":4684},"答案-3",[1387,4686,4689],{"className":4687,"code":4688,"language":1392},[1390],"5\n",[1029,4690,4688],{"__ignoreMap":1395},[1599,4692,2621],{"id":4693},"推导过程-3",[981,4695,4696],{},"初始数组：",[1387,4698,4701],{"className":4699,"code":4700,"language":1392},[1390],"x[0] = 2\nx[1] = 4\nx[2] = 6\nx[3] = 8\nx[4] = 10\n",[1029,4702,4700],{"__ignoreMap":1395},[981,4704,2639],{},[1387,4706,4709],{"className":4707,"code":4708,"language":1607,"meta":1395},[1605],"int *p = x;\n",[1029,4710,4708],{"__ignoreMap":1395},[981,4712,2630],{},[1387,4714,4717],{"className":4715,"code":4716,"language":1392},[1390],"p 指向 x[0]\n*p = 2\n",[1029,4718,4716],{"__ignoreMap":1395},[981,4720,2639],{},[1387,4722,4725],{"className":4723,"code":4724,"language":1607,"meta":1395},[1605],"int **pp = &p;\n",[1029,4726,4724],{"__ignoreMap":1395},[981,4728,2630],{},[1387,4730,4733],{"className":4731,"code":4732,"language":1392},[1390],"pp 指向 p\n*pp 就是 p\n**pp 就是 *p，也就是 x[0]\n",[1029,4734,4732],{"__ignoreMap":1395},[981,4736,2894],{},[1387,4738,4741],{"className":4739,"code":4740,"language":1607,"meta":1395},[1605],"(*pp)++;\n",[1029,4742,4740],{"__ignoreMap":1395},[981,4744,4745,4746,4749,4750,4752],{},"这里 ",[1029,4747,4748],{"code":4748},"*pp"," 就是 ",[1029,4751,981],{"code":981},"，所以这句等价于：",[1387,4754,4757],{"className":4755,"code":4756,"language":1607,"meta":1395},[1605],"p++;\n",[1029,4758,4756],{"__ignoreMap":1395},[981,4760,4761],{},"于是：",[1387,4763,4766],{"className":4764,"code":4765,"language":1392},[1390],"p 从指向 x[0] 变成指向 x[1]\n*p = 4\n",[1029,4767,4765],{"__ignoreMap":1395},[981,4769,4770],{},"接着：",[1387,4772,4775],{"className":4773,"code":4774,"language":1607,"meta":1395},[1605],"(*(*pp))++;\n",[1029,4776,4774],{"__ignoreMap":1395},[981,4778,4779],{},"里面：",[1387,4781,4783],{"className":4782,"code":4641,"language":1607,"meta":1395},[1605],[1029,4784,4641],{"__ignoreMap":1395},[981,4786,4787,4788,2170],{},"是 ",[1029,4789,981],{"code":981},[1387,4791,4794],{"className":4792,"code":4793,"language":1607,"meta":1395},[1605],"*(*pp)\n",[1029,4795,4793],{"__ignoreMap":1395},[981,4797,4798,4799,2170],{},"就是 ",[1029,4800,2948],{"code":2948},[981,4802,4803,4804,2651,4806,3189],{},"而此时 ",[1029,4805,981],{"code":981},[1029,4807,4808],{"code":4808},"x[1]",[1387,4810,4812],{"className":4811,"code":4774,"language":1607,"meta":1395},[1605],[1029,4813,4774],{"__ignoreMap":1395},[981,4815,4325],{},[1387,4817,4820],{"className":4818,"code":4819,"language":1607,"meta":1395},[1605],"x[1]++;\n",[1029,4821,4819],{"__ignoreMap":1395},[981,4823,4761],{},[1387,4825,4828],{"className":4826,"code":4827,"language":1392},[1390],"x[1] 从 4 变成 5\n",[1029,4829,4827],{"__ignoreMap":1395},[981,4831,3198],{},[1387,4833,4836],{"className":4834,"code":4835,"language":1607,"meta":1395},[1605],"printf(\"%d\\n\", *p);\n",[1029,4837,4835],{"__ignoreMap":1395},[981,4839,2706,4840,2651,4842,3189],{},[1029,4841,981],{"code":981},[1029,4843,4808],{"code":4808},[1387,4845,4848],{"className":4846,"code":4847,"language":1392},[1390],"*p = 5\n",[1029,4849,4847],{"__ignoreMap":1395},[981,4851,4852],{},"答案是：",[1387,4854,4856],{"className":4855,"code":4688,"language":1392},[1390],[1029,4857,4688],{"__ignoreMap":1395},[981,4859,4860,4861,4864,4865,4867],{},"这一题最容易错在 ",[1029,4862,4863],{"code":4863},"(*pp)++","：它不是改数组元素，而是让 ",[1029,4866,981],{"code":981}," 往后移动一个元素。",[1142,4869],{},[1149,4871,4873],{"id":4872},"p54c-字符串","p54：C 字符串",[981,4875,4876],{},"这一页进入字符串。",[1487,4878,4879,4882,4885,4888],{"type":1489},[981,4880,4881],{},"课件中可能有错的点\n课件中说：",[981,4883,4884],{},"C 字符串是以 NULL 结尾的字符数组。",[981,4886,4887],{},"但实际是：",[981,4889,4890,4891,3630,4894,4898],{},"C 字符串是以 ",[1247,4892,4893],{},"NUL 字符",[1247,4895,4896],{},[1029,4897,3281],{"code":3281}," 结尾的字符数组",[978,4900,4901,4914,4920,4940],{},[1999,4902,4903],{},[1747,4904,4905,3485,4907,4910,4911,4913],{},[1029,4906,3281],{"code":3281},[1247,4908,4909],{},"字符常量","，表示数值为 0 的字符，也叫 ",[1247,4912,4893],{},"。C 字符串靠它作为结束标记。",[1387,4915,4918],{"className":4916,"code":4917,"language":1392},[1390],"char s[] = \"abc\";   \u002F\u002F 实际是 {'a', 'b', 'c', '\\0'}\n",[1029,4919,4917],{"__ignoreMap":1395},[1999,4921,4922],{},[1747,4923,4924,3485,4926,4929,4930,1050,4932,4935,4936,4939],{},[1029,4925,1969],{"code":1969},[1247,4927,4928],{},"空指针常量","，表示“不指向任何有效对象”。通常定义为 ",[1029,4931,1959],{"code":1959},[1029,4933,4934],{"code":4934},"0L"," 或 ",[1029,4937,4938],{"code":4938},"(void *)0","，具体形式由实现决定。",[1387,4941,4944],{"className":4942,"code":4943,"language":1392},[1390],"char *p = NULL;\n",[1029,4945,4943],{"__ignoreMap":1395},[981,4947,3729],{},[1387,4949,4952],{"className":4950,"code":4951,"language":1607,"meta":1395},[1605],"char s[] = \"abc\";\n",[1029,4953,4951],{"__ignoreMap":1395},[981,4955,4956],{},"内存里不是只有 3 个字符，而是：",[1387,4958,4961],{"className":4959,"code":4960,"language":1392},[1390],"s[0] = 'a'\ns[1] = 'b'\ns[2] = 'c'\ns[3] = '\\0'\n",[1029,4962,4960],{"__ignoreMap":1395},[981,4964,2719],{},[1387,4966,4968],{"className":4967,"code":4951,"language":1607,"meta":1395},[1605],[1029,4969,4951],{"__ignoreMap":1395},[981,4971,4972],{},"数组长度其实是 4，不是 3。",[981,4974,4975],{},"这个点超级重要：",[1387,4977,4980],{"className":4978,"code":4979,"language":1392},[1390],"字符串长度 strlen(s) 是 3\n数组占用字符数是 4，因为还有 '\\0'\n",[1029,4981,4979],{"__ignoreMap":1395},[1142,4983],{},[1149,4985,4987],{"id":4986},"p55字符串例子","p55：字符串例子",[981,4989,4990],{},[1156,4991],{"alt":1156,"src":4992},"\u002Fimg\u002FComputerOrganization\u002FL3-CMemory-11.png",[981,4994,4995],{},"课件输出：",[1387,4997,5000],{"className":4998,"code":4999,"language":1392},[1390],"str = abc, length = 3\n",[1029,5001,4999],{"__ignoreMap":1395},[981,5003,2857],{},[1387,5005,5008],{"className":5006,"code":5007,"language":1392},[1390],"strlen 不包含 '\\0'\nsizeof(str) 包含 '\\0'\n",[1029,5009,5007],{"__ignoreMap":1395},[981,5011,5012],{},"那么通常：",[1387,5014,5017],{"className":5015,"code":5016,"language":1607,"meta":1395},[1605],"strlen(str) == 3\nsizeof(str) == 4\n",[1029,5018,5016],{"__ignoreMap":1395},[1142,5020],{},[1149,5022,5024,5025],{"id":5023},"p56简洁的-strlen","p56：简洁的 ",[1029,5026,5027],{"code":5027},"strlen()",[981,5029,5030],{},"课件给了一个短小但很 C 的实现：",[1387,5032,5035],{"className":5033,"code":5034,"language":1607,"meta":1395},[1605],"int strlen(char *s) {\n    char *p = s;\n\n    while (*p++)\n        ; \u002F* Null body of while *\u002F\n\n    return (p - s - 1);\n}\n",[1029,5036,5034],{"__ignoreMap":1395},[981,5038,5039],{},"关键是：",[1387,5041,5044],{"className":5042,"code":5043,"language":1607,"meta":1395},[1605],"while (*p++)\n",[1029,5045,5043],{"__ignoreMap":1395},[981,5047,5048],{},"意思是：",[1387,5050,5053],{"className":5051,"code":5052,"language":1392},[1390],"先取当前 p 指向的字符作为 while 条件\n然后 p 往后移动一格\n",[1029,5054,5052],{"__ignoreMap":1395},[1599,5056,5057],{"id":5057},"过程详解",[981,5059,5060],{},"假设字符串是：",[1387,5062,5065],{"className":5063,"code":5064,"language":1392},[1390],"a b c \\0\n",[1029,5066,5064],{"__ignoreMap":1395},[981,5068,5069],{},"过程：",[1387,5071,5074],{"className":5072,"code":5073,"language":1392},[1390],"p 指向 'a'，*p++ 是 'a'，非 0，继续\np 指向 'b'，*p++ 是 'b'，非 0，继续\np 指向 'c'，*p++ 是 'c'，非 0，继续\np 指向 '\\0'，*p++ 是 0，循环结束，但 p 已经又往后走了一格（这边涉及one-past-end）\n",[1029,5075,5073],{"__ignoreMap":1395},[981,5077,5078,5079,2651,5081,5083],{},"所以循环结束时，",[1029,5080,981],{"code":981},[1029,5082,3281],{"code":3281}," 后面的一个位置。",[981,5085,5086],{},"因此：",[1387,5088,5091],{"className":5089,"code":5090,"language":1607,"meta":1395},[1605],"return (p - s - 1);\n",[1029,5092,5090],{"__ignoreMap":1395},[981,5094,5095,5096,2170],{},"要减 1，得到真正字符串长度",[3740,5097,5099,5114],{"className":5098},[3743],[3740,5100,5102],{"className":5101},[3747],[3749,5103,5104],{"xmlns":3751},[3753,5105,5106,5112],{},[3756,5107,5108],{},[5109,5110,5111],"mn",{},"3",[3762,5113,5111],{"encoding":3764},[3740,5115,5117],{"className":5116,"ariaHidden":1979},[3768],[3740,5118,5120,5124],{"className":5119},[3772],[3740,5121],{"className":5122,"style":5123},[3776],"height:0.6444em;",[3740,5125,5111],{"className":5126},[3781],[1142,5128],{},[1149,5130,5132,5133,5136],{"id":5131},"p57main-的参数","p57：",[1029,5134,5135],{"code":5135},"main()"," 的参数",[981,5138,5139,5140,5142,5143,2170],{},"C 语言里，",[1029,5141,3298],{"code":3298}," 函数如何接收",[1247,5144,5145],{},"命令行参数",[1387,5147,5150],{"className":5148,"code":5149,"language":1607,"meta":1395},[1605],"int main(int argc, char *argv[])\n",[1029,5151,5149],{"__ignoreMap":1395},[1387,5153,5156],{"className":5154,"code":5155,"language":1392},[1390],"argc 是命令行上的字符串数\nargv 是一个指向数组的指针，数组中包含字符串形式的参数\n",[1029,5157,5155],{"__ignoreMap":1395},[981,5159,5160],{},"更具体一点：",[1387,5162,5165],{"className":5163,"code":5164,"language":1392},[1390],"argc = argument count，参数个数\nargv = argument vector，参数数组\n",[1029,5166,5164],{"__ignoreMap":1395},[981,5168,5169,5172,5173,2170],{},[1029,5170,5171],{"code":5171},"argv"," 是一个数组，里面每个元素都是一个 C 字符串，也就是 ",[1029,5174,5175],{"code":5175},"char *",[981,5177,2719],{},[1387,5179,5182],{"className":5180,"code":5181,"language":1607,"meta":1395},[1605],"char *argv[]\n",[1029,5183,5181],{"__ignoreMap":1395},[981,5185,5186],{},"可以理解成：",[1387,5188,5191],{"className":5189,"code":5190,"language":1392},[1390],"argv 是一个数组\n数组每个元素都是 char *\n每个 char * 指向一个字符串\n",[1029,5192,5190],{"__ignoreMap":1395},[1142,5194],{},[1149,5196,5198],{"id":5197},"p58命令行参数例子","p58：命令行参数例子",[981,5200,5201],{},"课件例子：",[1387,5203,5208],{"className":5204,"code":5206,"language":5207,"meta":1395},[5205],"language-bash","gcc -o ex Argc.c\n.\u002Fex -g a \"d e f\"\n","bash",[1029,5209,5206],{"__ignoreMap":1395},[981,5211,5212],{},"输出：",[1387,5214,5217],{"className":5215,"code":5216,"language":1392},[1390],"arg[0] = .\u002Fex\narg[1] = -g\narg[2] = a\narg[3] = d e f\n",[1029,5218,5216],{"__ignoreMap":1395},[981,5220,5221],{},"这里有几个点。",[981,5223,1679,5224,5227],{},[1029,5225,5226],{"code":5226},"argv[0]"," 通常是程序名或程序路径：",[1387,5229,5232],{"className":5230,"code":5231,"language":1392},[1390],"argv[0] = .\u002Fex\n",[1029,5233,5231],{"__ignoreMap":1395},[981,5235,5236],{},"第二，后面的每个命令行参数都是一个字符串：",[1387,5238,5241],{"className":5239,"code":5240,"language":1392},[1390],"argv[1] = \"-g\"\nargv[2] = \"a\"\nargv[3] = \"d e f\"\n",[1029,5242,5240],{"__ignoreMap":1395},[981,5244,5245,5246,5249],{},"第三，引号 ",[1029,5247,5248],{"code":5248},"\"d e f\""," 的作用是把带空格的内容作为一个整体参数，而不是拆成三个参数。",[981,5251,5252],{},"所以这个命令里：",[1387,5254,5257],{"className":5255,"code":5256,"language":1392},[1390],"argc = 4\n",[1029,5258,5256],{"__ignoreMap":1395},[981,5260,5261],{},"因为一共有：",[1387,5263,5266],{"className":5264,"code":5265,"language":1392},[1390],".\u002Fex\n-g\na\nd e f\n",[1029,5267,5265],{"__ignoreMap":1395},[981,5269,5270],{},"四个字符串。",[981,5272,5273],{},"可以用这样的程序打印：",[1387,5275,5278],{"className":5276,"code":5277,"language":1607,"meta":1395},[1605],"#include \u003Cstdio.h>\n\nint main(int argc, char *argv[]) {\n    for (int i = 0; i \u003C argc; i++) {\n        printf(\"arg[%d] = %s\\n\", i, argv[i]);\n    }\n    return 0;\n}\n",[1029,5279,5277],{"__ignoreMap":1395},[1142,5281],{},[1149,5283,5285],{"id":5284},"p59小结","p59：小结",[981,5287,5288],{},"这一页总结了 p25–59 的指针部分。",[981,5290,5291],{},"核心有五句话：",[1387,5293,5296],{"className":5294,"code":5295,"language":1392},[1390],"1. 指针是 C 语言里的机器内存地址\n2. 指针变量保存在内存中，指针值本质上是可被软件操作的数字\n3. C 中数组和指针关系非常密切\n4. 指针知道它指向对象的类型和大小，void * 除外\n5. 指针强大，但如果没有规划，会成为错误的主要来\n",[1029,5297,5295],{"__ignoreMap":1395},[1387,5299,5302],{"className":5300,"code":5301,"language":1392},[1390],"数组与指针\n├── 数组\n│   ├── 下标从 0 开始\n│   ├── 不检查越界\n│   └── 不自带长度\n├── sizeof\n│   ├── 在 main 里 sizeof(a) 是整个数组\n│   └── 在函数参数里 sizeof(array) 是指针大小\n├── 指针运算\n│   ├── p+1 移动 sizeof(*p) 字节\n│   ├── 只建议在数组内使用\n│   └── one-past-end 可比较，不可解引用\n├── 数组\u002F指针对偶\n│   ├── a[i] == *(a+i)\n│   ├── a 常退化成 &a[0]\n│   └── 数组名不能 a++\n└── 字符串\n    ├── char 数组\n    ├── 以 '\\0' 结尾\n    ├── strlen 不数 '\\0'\n    └── argv 是字符串数组\n",[1029,5303,5301],{"__ignoreMap":1395},[1142,5305],{},[988,5307,5309],{"id":5308},"第四章c-内存管理","第四章：C 内存管理",[978,5311,5312],{},[981,5313,5314],{},[1247,5315,5316],{},"C 程序里的数据放在哪里，决定了它什么时候存在、谁负责释放、能不能安全继续用。",[981,5318,5319,5320,5323,5324,5327,5328,5330],{},"这部分会讲四块内存：",[1247,5321,5322],{},"code、static data、stack、heap","。其中真正危险的是 ",[1247,5325,5326],{},"heap 堆","，也就是 ",[1029,5329,1101],{"code":1101}," 这一套。",[1142,5332],{},[1149,5334,5336],{"id":5335},"p61c-内存管理总图","p61：C 内存管理总图",[981,5338,5339],{},[1156,5340],{"alt":1156,"src":5341},"\u002Fimg\u002FComputerOrganization\u002FL3-CMemory-12.png",[992,5343,5344,5357],{},[995,5345,5346],{},[998,5347,5348,5351,5354],{},[1001,5349,5350],{},"区域",[1001,5352,5353],{},"放什么",[1001,5355,5356],{},"谁管理",[1014,5358,5359,5369,5379,5390],{},[998,5360,5361,5363,5366],{},[1019,5362,1029],{},[1019,5364,5365],{},"程序机器指令",[1019,5367,5368],{},"操作系统\u002F编译器",[998,5370,5371,5374,5377],{},[1019,5372,5373],{},"static data",[1019,5375,5376],{},"全局变量、静态变量",[1019,5378,5368],{},[998,5380,5381,5384,5387],{},[1019,5382,5383],{},"stack",[1019,5385,5386],{},"函数参数、局部变量",[1019,5388,5389],{},"自动管理",[998,5391,5392,5395,5400],{},[1019,5393,5394],{},"heap",[1019,5396,5397,5399],{},[1029,5398,2469],{"code":2469}," 出来的动态内存",[1019,5401,5402],{},"程序员手动管理",[981,5404,5405],{},"这一页要建立全局地图。后面所有 bug，本质都能归到一句话：",[978,5407,5408],{},[981,5409,5410],{},"你用了已经不属于你的内存，或者忘了释放你申请的内存。",[1142,5412],{},[1149,5414,5416],{"id":5415},"p62代码区-code","p62：代码区 Code",[981,5418,5419],{},"代码区放的是程序指令。",[981,5421,5422],{},"课件说 code 的特点是：",[1999,5424,5425,5428],{},[1747,5426,5427],{},"程序启动时加载",[1747,5429,5430],{},"不会改变",[981,5432,5433],{},"比如你写的：",[1387,5435,5438],{"className":5436,"code":5437,"language":1607,"meta":1395},[1605],"int main(void) {\n    printf(\"hello\\n\");\n}\n",[1029,5439,5437],{"__ignoreMap":1395},[981,5441,5442],{},"编译后变成机器指令，运行时这些指令会被加载到 code 区。",[981,5444,5445],{},"这块通常不是你手动操作的。你不会：",[1387,5447,5450],{"className":5448,"code":5449,"language":1607,"meta":1395},[1605],"free(main);\n",[1029,5451,5449],{"__ignoreMap":1395},[981,5453,5454],{},"代码区由系统负责，程序员基本不用直接管理。",[981,5456,5457],{},"一句话：",[978,5459,5460],{},[981,5461,5462],{},[1247,5463,5464],{},"code 区放“程序要做什么”，不是放普通运行时数据。",[1142,5466],{},[1149,5468,5470],{"id":5469},"p63静态数据区-static-data","p63：静态数据区 Static Data",[981,5472,5473],{},"静态数据区放的是程序整个运行期间都存在的数据。",[1999,5475,5476,5478,5481],{},[1747,5477,5427],{},[1747,5479,5480],{},"可以修改",[1747,5482,5483],{},"尺寸是固定的",[981,5485,5486],{},"典型例子：",[1387,5488,5491],{"className":5489,"code":5490,"language":1607,"meta":1395},[1605],"int global_x = 10;\n\nstatic int count = 0;\n",[1029,5492,5490],{"__ignoreMap":1395},[981,5494,5495],{},"这些变量在程序启动时就被安排好空间。课件说 static data 的特点是：",[1387,5497,5500],{"className":5498,"code":5499,"language":1392},[1390],"程序启动时加载\n可以修改\n尺寸固定\n",[1029,5501,5499],{"__ignoreMap":1395},[981,5503,5504,5505],{},"所以它和 code 区不同：",[1515,5506,5507],{},"code 通常不改，static data 可以改。",[981,5509,3694],{},[1387,5511,5514],{"className":5512,"code":5513,"language":1607,"meta":1395},[1605],"int g = 1;\n\nint main(void) {\n    g = 2;\n}\n",[1029,5515,5513],{"__ignoreMap":1395},[981,5517,5518,5521],{},[1029,5519,5520],{"code":5520},"g"," 在静态数据区，程序运行期间一直存在。",[981,5523,5524],{},"但它也不是动态的。你不能运行到一半说“全局变量数组突然变大十倍”，除非你用堆。",[981,5526,5457],{},[978,5528,5529],{},[981,5530,5531],{},[1247,5532,5533],{},"static data 生命周期长，可以修改值，但尺寸固定。",[1142,5535],{},[1149,5537,5539],{"id":5538},"p64栈-stack","p64：栈 Stack",[1999,5541,5542,5545,5548],{},[1747,5543,5544],{},"函数内部的局部变量和参数",[1747,5546,5547],{},"调用函数时分配",[1747,5549,5550],{},"栈通常向下增长",[981,5552,1653],{},[1387,5554,5557],{"className":5555,"code":5556,"language":1607,"meta":1395},[1605],"void f(int n) {\n    int x = 5;\n}\n",[1029,5558,5556],{"__ignoreMap":1395},[981,5560,2502],{},[1387,5562,5565],{"className":5563,"code":5564,"language":1392},[1390],"n 是函数参数\nx 是局部变量\n",[1029,5566,5564],{"__ignoreMap":1395},[981,5568,5569,5570,5573],{},"它们都在 ",[1029,5571,5572],{"code":5572},"f"," 的栈帧里。",[981,5575,5576,3630,5579,3630,5583,5586,3630,5590,2170],{},[1515,5577,5578],{},"调用",[1515,5580,5581],{},[1029,5582,5572],{"code":5572},[1515,5584,5585],{},"时，栈上开一块空间；",[1515,5587,5588],{},[1029,5589,5572],{"code":5572},[1515,5591,5592],{},"返回后，这块空间就失效",[981,5594,5595,5596],{},"这会直接导致 p68 的大坑：",[1247,5597,5598],{},"不能返回局部变量的地址。",[1142,5600],{},[1149,5602,5604],{"id":5603},"p65栈帧示意图","p65：栈帧示意图",[981,5606,5607],{},"这一页用代码展示栈帧怎么叠起来：",[981,5609,5610],{},[1156,5611],{"alt":1156,"src":5612},"\u002Fimg\u002FComputerOrganization\u002FL3-CMemory-13.png",[1387,5614,5617],{"className":5615,"code":5616,"language":1607,"meta":1395},[1605],"void a() {\n    int a_local = 0;\n    b(a_local);\n}\n\nint b(int arg) {\n    int b_local = 5;\n    return 2 * arg + b_local;\n}\n\nint main(void) {\n    a();\n    b(7);\n}\n",[1029,5618,5616],{"__ignoreMap":1395},[981,5620,5621,5622,5624,5625,3184,5627,5629,5630,5632],{},"当 ",[1029,5623,3298],{"code":3298}," 调用 ",[1029,5626,2211],{"code":2211},[1029,5628,2211],{"code":2211}," 又调用 ",[1029,5631,2654],{"code":2654}," 时，栈上会有类似：",[1387,5634,5637],{"className":5635,"code":5636,"language":1392},[1390],"Frame main\nFrame a\nFrame b\n",[1029,5638,5636],{"__ignoreMap":1395},[981,5640,5641],{},"栈指针 Stack Pointer 会随着函数调用和返回移动。",[981,5643,5644],{},"你可以把栈想成一摞盘子：",[1387,5646,5649],{"className":5647,"code":5648,"language":1392},[1390],"main 先放上去\na 再压上去\nb 再压上去\nb 返回，b 的盘子拿掉\na 返回，a 的盘子拿掉\n",[1029,5650,5648],{"__ignoreMap":1395},[981,5652,5653],{},[1515,5654,5655],{},"这就是 LIFO：last in, first out，后进先出。",[1142,5657],{},[1149,5659,5661],{"id":5660},"p66栈帧包含什么","p66：栈帧包含什么",[978,5663,5664],{},[981,5665,5666],{},"栈帧：是一次函数调用在调用栈中占用的一块记录区域",[981,5668,5669],{},"每次调用一个函数(function)，就会分配一个新的帧(frame)",[981,5671,5672],{},"当函数返回时，帧被释放",[981,5674,5675],{},"栈帧包含",[1999,5677,5678,5681,5684],{},[1747,5679,5680],{},"函数参数(function arguments)",[1747,5682,5683],{},"局部变量(local variables",[1747,5685,5686],{},[1515,5687,5688],{},"返回地址(return address，谁调用我的?)",[981,5690,5691],{},"栈使用连续的内存块– 栈指针指示当前堆栈级别",[981,5693,5694],{},"栈管理对C程序员来说是透明的，将在编写汇编语言时看到细节",[1142,5696],{},[1149,5698,5700],{"id":5699},"p67练习题递归时有多少份-x-和-y","p67：练习题——递归时有多少份 x 和 y？",[981,5702,2493],{},[1387,5704,5707],{"className":5705,"code":5706,"language":1607,"meta":1395},[1605],"#include \u003Cstdio.h>\n#include \u003Cstdlib.h>\n\nint x = 2;\n\nint foo(int n) {\n    int y;\n    if (n \u003C= 0) {\n        printf(\"End case!\\n\");\n        return 0;\n    } else {\n        y = n + foo(n - x);\n        return y;\n    }\n}\n\nint main(void) {\n    foo(10);\n}\n",[1029,5708,5706],{"__ignoreMap":1395},[981,5710,4347],{},[978,5712,5713],{},[981,5714,4201,5715,5717,5718,3238,5720,5722],{},[1029,5716,3234],{"code":3234}," 执行之后，但在返回 0 之前，内存中分配了多少个 ",[1029,5719,2860],{"code":2860},[1029,5721,2376],{"code":2376}," 的副本？",[1599,5724,2611],{"id":5725},"答案-4",[1387,5727,5730],{"className":5728,"code":5729,"language":1392},[1390],"#x = 1\n#y = 6\n",[1029,5731,5729],{"__ignoreMap":1395},[1599,5733,2621],{"id":5734},"推导过程-4",[981,5736,5737,5738,3485],{},"先看 ",[1029,5739,2860],{"code":2860},[1387,5741,5744],{"className":5742,"code":5743,"language":1607,"meta":1395},[1605],"int x = 2;\n",[1029,5745,5743],{"__ignoreMap":1395},[981,5747,5748],{},"它是全局变量，在静态数据区。全程序只有一份。",[981,5750,2719],{},[1387,5752,5755],{"className":5753,"code":5754,"language":1392},[1390],"#x = 1\n",[1029,5756,5754],{"__ignoreMap":1395},[981,5758,5759,5760,3485],{},"再看 ",[1029,5761,2376],{"code":2376},[1387,5763,5766],{"className":5764,"code":5765,"language":1607,"meta":1395},[1605],"int foo(int n) {\n    int y;\n    ...\n}\n",[1029,5767,5765],{"__ignoreMap":1395},[981,5769,5770,2710,5772,5774,5775,5777,5778,2170],{},[1029,5771,2376],{"code":2376},[1029,5773,4228],{"code":4228}," 的局部变量。每调用一次 ",[1029,5776,4228],{"code":4228},"，就有一个新的栈帧，也就有一份新的 ",[1029,5779,2376],{"code":2376},[981,5781,5782],{},"调用链是：",[1387,5784,5787],{"className":5785,"code":5786,"language":1392},[1390],"foo(10)\nfoo(8)\nfoo(6)\nfoo(4)\nfoo(2)\nfoo(0)\n",[1029,5788,5786],{"__ignoreMap":1395},[981,5790,5791,5792,5795,5796,5799,5800,2170],{},"到 ",[1029,5793,5794],{"code":5794},"foo(0)"," 时满足 ",[1029,5797,5798],{"code":5798},"n \u003C= 0","，执行 ",[1029,5801,5802],{"code":5802},"printf(\"End case!\\n\")",[981,5804,5805,5806,5809,5810,5812],{},"问题问的是 ",[1247,5807,5808],{},"printf 执行之后，但在 return 0 之前","。此时所有递归调用都还没返回，所以栈上还保留着 6 个 ",[1029,5811,4228],{"code":4228}," 栈帧。",[981,5814,5815,5816,5818,5819,5821,5822,5824,5825,5827],{},"每个 ",[1029,5817,4228],{"code":4228}," 栈帧里都有一个 ",[1029,5820,2376],{"code":2376},"，即使 ",[1029,5823,5794],{"code":5794}," 这一层的 ",[1029,5826,2376],{"code":2376}," 没真正赋值，它的局部变量空间也已经分配了。",[981,5829,2719],{},[1387,5831,5833],{"className":5832,"code":5729,"language":1392},[1390],[1029,5834,5729],{"__ignoreMap":1395},[1142,5836],{},[1149,5838,5840],{"id":5839},"p68返回局部变量地址大坑","p68：返回局部变量地址——大坑",[981,5842,5843],{},"这一页代码是：",[1387,5845,5848],{"className":5846,"code":5847,"language":1607,"meta":1395},[1605],"#include \u003Cstdio.h>\n#include \u003Cmath.h>\n\nint *f() {\n    int x = 5;\n    return &x;\n}\n\nint main(void) {\n    int *a = f();\n    double d = cos(1.57);\n    printf(\"a = %d\\n\", *a);\n}\n",[1029,5849,5847],{"__ignoreMap":1395},[981,5851,5852],{},"问题在这里：",[1387,5854,5857],{"className":5855,"code":5856,"language":1607,"meta":1395},[1605],"int x = 5;\nreturn &x;\n",[1029,5858,5856],{"__ignoreMap":1395},[981,5860,5861,2710,5863,5866,5867,5573],{},[1029,5862,2860],{"code":2860},[1029,5864,5865],{"code":5865},"f()"," 的局部变量，在 ",[1029,5868,5572],{"code":5572},[981,5870,5621,5871,5873],{},[1029,5872,5865],{"code":5865}," 返回后：",[1387,5875,5878],{"className":5876,"code":5877,"language":1392},[1390],"f 的栈帧被释放\nx 不再有效\n",[1029,5879,5877],{"__ignoreMap":1395},[981,5881,5882,5883,5885,5886,5888],{},"但是 ",[1029,5884,2211],{"code":2211}," 还保存着原来 ",[1029,5887,2860],{"code":2860}," 的地址。于是：",[1387,5890,5893],{"className":5891,"code":5892,"language":1607,"meta":1395},[1605],"*a\n",[1029,5894,5892],{"__ignoreMap":1395},[981,5896,5897],{},"就变成了访问一块已经失效的栈内存。",[981,5899,5900],{},"课件输出是：",[1387,5902,5905],{"className":5903,"code":5904,"language":1392},[1390],"a = -1085663214\n",[1029,5906,5904],{"__ignoreMap":1395},[1142,5908],{},[1149,5910,5912],{"id":5911},"p69堆-heap","p69：堆 Heap",[981,5914,5915],{},"这一页正式进入堆。",[981,5917,5918],{},"课件说堆是：",[1999,5920,5921,5924],{},[1747,5922,5923],{},"动态数据空间",[1747,5925,5926],{},"根据需要由程序分配和释放",[981,5928,5929,5930,5933],{},"这就是 ",[1029,5931,5932],{"code":5932},"malloc\u002Ffree"," 的地盘。",[981,5935,5936],{},"和栈相比：",[992,5938,5939,5952],{},[995,5940,5941],{},[998,5942,5943,5946,5949],{},[1001,5944,5945],{},"对比",[1001,5947,5948],{},"栈 stack",[1001,5950,5951],{},"堆 heap",[1014,5953,5954,5968,5981,5992],{},[998,5955,5956,5959,5962],{},[1019,5957,5958],{},"创建方式",[1019,5960,5961],{},"函数调用自动创建",[1019,5963,5964,5967],{},[1029,5965,5966],{"code":5966},"malloc\u002Fcalloc\u002Frealloc"," 手动申请",[998,5969,5970,5973,5976],{},[1019,5971,5972],{},"销毁方式",[1019,5974,5975],{},"函数返回自动销毁",[1019,5977,5978,5980],{},[1029,5979,1119],{"code":1119}," 手动释放",[998,5982,5983,5986,5989],{},[1019,5984,5985],{},"生命周期",[1019,5987,5988],{},"跟函数调用绑定",[1019,5990,5991],{},"由程序员决定",[998,5993,5994,5997,6000],{},[1019,5995,5996],{},"常见问题",[1019,5998,5999],{},"返回局部变量地址",[1019,6001,6002],{},"泄漏、重复释放、越界、use-after-free",[981,6004,6005],{},"为什么需要堆？",[981,6007,6008],{},"比如你运行时才知道数组大小：",[1387,6010,6013],{"className":6011,"code":6012,"language":1607,"meta":1395},[1605],"int n;\nscanf(\"%d\", &n);\nint *a = malloc(n * sizeof(int));\n",[1029,6014,6012],{"__ignoreMap":1395},[981,6016,6017],{},"这就是动态对象。",[1142,6019],{},[1149,6021,6023],{"id":6022},"p70管理堆的-c-函数","p70：管理堆的 C 函数",[981,6025,6026],{},"课件列了四个核心函数：",[1387,6028,6031],{"className":6029,"code":6030,"language":1392},[1390],"malloc()   分配一块未初始化的内存\ncalloc()   分配一块内存并初始化为 0\nfree()     释放先前分配的内存块\nrealloc()  更改先前分配块的大小\n",[1029,6032,6030],{"__ignoreMap":1395},[981,6034,6035],{},"还特别提醒：",[978,6037,6038],{},[981,6039,6040,6043],{},[1029,6041,6042],{"code":6042},"realloc()"," 之后，以前分配的内容可能会移动。",[1142,6045],{},[1149,6047,6049,6050],{"id":6048},"p71malloc","p71：",[1029,6051,6052],{"code":6052},"malloc()",[981,6054,6055],{},"原型：",[1387,6057,6060],{"className":6058,"code":6059,"language":1607,"meta":1395},[1605],"void *malloc(size_t n);\n",[1029,6061,6059],{"__ignoreMap":1395},[1999,6063,6064,6073,6079,6084],{},[1747,6065,6066,6068,6069,6072],{},[1029,6067,2469],{"code":2469}," 分配一块未初始化的内存，",[1029,6070,6071],{"code":6071},"n"," 是请求大小，单位是字节",[1747,6074,6075,6078],{},[1029,6076,6077],{"code":6077},"size_t"," 是足够大、可以计数字节数的无符号整数类型",[1747,6080,6081,6082],{},"返回 ",[1029,6083,1057],{"code":1057},[1747,6085,6086,6087,2170],{},"如果没有更多内存，返回 ",[1029,6088,1969],{"code":1969},[981,6090,1653],{},[1387,6092,6095],{"className":6093,"code":6094,"language":1607,"meta":1395},[1605],"#include \u003Cstdlib.h>\n\nint main(void) {\n    int *ip = (int *)malloc(50 * sizeof(int));\n\n    double *dp = malloc(1000 * sizeof(double));\n}\n",[1029,6096,6094],{"__ignoreMap":1395},[981,6098,6099],{},"解释：",[1387,6101,6104],{"className":6102,"code":6103,"language":1607,"meta":1395},[1605],"malloc(50 * sizeof(int))\n",[1029,6105,6103],{"__ignoreMap":1395},[981,6107,6108,6109,6111],{},"申请 50 个 ",[1029,6110,1852],{"code":1852}," 的空间。",[981,6113,4038,6114,6116],{},[1029,6115,4041],{"code":4041},"，就是：",[1387,6118,6121],{"className":6119,"code":6120,"language":1392},[1390],"50 * 4 = 200 字节\n",[1029,6122,6120],{"__ignoreMap":1395},[1142,6124],{},[1149,6126,6128],{"id":6127},"p72这段代码有什么问题","p72：这段代码有什么问题？",[981,6130,2767],{},[1387,6132,6135],{"className":6133,"code":6134,"language":1607,"meta":1395},[1605],"#include \u003Cstdio.h>\n#include \u003Cstdlib.h>\n\nint main(void) {\n    const int SZ = 10;\n    int *p = malloc(SZ * sizeof(int));\n    int *end = &p[SZ];\n\n    while (p \u003C end)\n        *p++ = 0;\n\n    free(p);\n}\n",[1029,6136,6134],{"__ignoreMap":1395},[981,6138,6139],{},"错误在最后：",[1387,6141,6144],{"className":6142,"code":6143,"language":1607,"meta":1395},[1605],"free(p);\n",[1029,6145,6143],{"__ignoreMap":1395},[1387,6147,6150],{"className":6148,"code":6149,"language":1607,"meta":1395},[1605],"while (p \u003C end)\n    *p++ = 0;\n",[1029,6151,6149],{"__ignoreMap":1395},[981,6153,4745,6154,6157,6158,6160,6161,6163],{},[1029,6155,6156],{"code":6156},"p++"," 会不断移动 ",[1029,6159,981],{"code":981},"。循环结束时，",[1029,6162,981],{"code":981}," 已经不再是原来的起始地址了，而是指向数组末尾后一个位置：",[1387,6165,6168],{"className":6166,"code":6167,"language":1392},[1390],"p == &原数组[SZ]\n",[1029,6169,6167],{"__ignoreMap":1395},[981,6171,6172,3630,6175,3630,6179,3630,6182,6187],{},[1515,6173,6174],{},"可是",[1515,6176,6177],{},[1029,6178,1119],{"code":1119},[1515,6180,6181],{},"要求你传入",[1515,6183,6184],{},[1247,6185,6186],{},"malloc 最初返回的地址",[1515,6188,2170],{},[981,6190,6191,6192,6194],{},"现在传的是被改过的 ",[1029,6193,981],{"code":981},"，所以报错：",[1387,6196,6199],{"className":6197,"code":6198,"language":1392},[1390],"pointer being freed was not allocated\n",[1029,6200,6198],{"__ignoreMap":1395},[1387,6202,6205],{"className":6203,"code":6204,"language":1392},[1390],"不要把 malloc 返回的“原始句柄”弄丢。\n",[1029,6206,6204],{"__ignoreMap":1395},[1142,6208],{},[1149,6210,6212,6213],{"id":6211},"p73free","p73：",[1029,6214,6215],{"code":6215},"free()",[981,6217,6055],{},[1387,6219,6222],{"className":6220,"code":6221,"language":1607,"meta":1395},[1605],"void free(void *p);\n",[1029,6223,6221],{"__ignoreMap":1395},[981,6225,6226],{},"课件强调两点：",[1999,6228,6229,6232],{},[1747,6230,6231],{},"释放 malloc() 分配的内存",[1747,6233,6234],{},"p 必须包含 malloc() 最初返回的地址",[981,6236,6237],{},"这就是 p72 错误的根源。",[981,6239,6240],{},"合法：",[1387,6242,6245],{"className":6243,"code":6244,"language":1607,"meta":1395},[1605],"int *p = malloc(10 * sizeof(int));\nfree(p);\n",[1029,6246,6244],{"__ignoreMap":1395},[981,6248,6249],{},"非法：",[1387,6251,6254],{"className":6252,"code":6253,"language":1607,"meta":1395},[1605],"int *p = malloc(10 * sizeof(int));\np++;\nfree(p);      \u002F\u002F 错\n",[1029,6255,6253],{"__ignoreMap":1395},[981,6257,6258],{},"也非法：",[1387,6260,6263],{"className":6261,"code":6262,"language":1607,"meta":1395},[1605],"int x;\nfree(&x);     \u002F\u002F 错，x 在栈上，不是 malloc 来的\n",[1029,6264,6262],{"__ignoreMap":1395},[981,6266,6267],{},"更非法：",[1387,6269,6272],{"className":6270,"code":6271,"language":1607,"meta":1395},[1605],"int a[5];\nfree(a);      \u002F\u002F 错，a 是栈上数组，不是 malloc 来的\n",[1029,6273,6271],{"__ignoreMap":1395},[981,6275,6276],{},"这页的金句：",[978,6278,6279],{},[981,6280,6281],{},[1247,6282,6283],{},"free 只认 malloc\u002Fcalloc\u002Frealloc 返回的那块堆内存的起始地址。",[1142,6285],{},[1149,6287,6289],{"id":6288},"p74fix修复-p72","p74：Fix——修复 p72",[981,6291,6292],{},"修复代码是：",[1387,6294,6297],{"className":6295,"code":6296,"language":1607,"meta":1395},[1605],"#include \u003Cstdio.h>\n#include \u003Cstdlib.h>\n\nint main(void) {\n    const int SZ = 10;\n    int *array = malloc(SZ * sizeof(int));\n\n    for (int *p = array; p \u003C &array[SZ]; )\n        *p++ = 0;\n\n    free(array);\n}\n",[1029,6298,6296],{"__ignoreMap":1395},[981,6300,6301],{},"关键变化是：",[1387,6303,6306],{"className":6304,"code":6305,"language":1607,"meta":1395},[1605],"int *array = malloc(...);\n",[1029,6307,6305],{"__ignoreMap":1395},[981,6309,6310,6312],{},[1029,6311,4240],{"code":4240}," 保存原始地址，不动它。",[981,6314,6315],{},"循环里另开一个指针：",[1387,6317,6320],{"className":6318,"code":6319,"language":1607,"meta":1395},[1605],"int *p = array;\n",[1029,6321,6319],{"__ignoreMap":1395},[981,6323,6324,6325,6327],{},"让 ",[1029,6326,981],{"code":981}," 去跑。",[981,6329,3198],{},[1387,6331,6334],{"className":6332,"code":6333,"language":1607,"meta":1395},[1605],"free(array);\n",[1029,6335,6333],{"__ignoreMap":1395},[981,6337,6338],{},"释放原始地址。",[981,6340,6341],{},[1247,6342,6343],{},"原始指针用来 free，游标指针用来移动。",[1142,6345],{},[1149,6347,6349,6350,6352],{"id":6348},"p75为什么调用-free","p75：为什么调用 ",[1029,6351,6215],{"code":6215},"？",[981,6354,6355],{},"手动回收不再使用的内存，避免内存耗尽",[981,6357,6358],{},"课件对比了两种方式：",[992,6360,6361,6376],{},[995,6362,6363],{},[998,6364,6365,6368,6370,6373],{},[1001,6366,6367],{},"方式",[1001,6369,1287],{},[1001,6371,6372],{},"优点",[1001,6374,6375],{},"缺点",[1014,6377,6378,6394],{},[998,6379,6380,6385,6388,6391],{},[1019,6381,6382,6384],{},[1029,6383,5932],{"code":5932}," 显式管理",[1019,6386,6387],{},"C\u002FC++",[1019,6389,6390],{},"控制力强",[1019,6392,6393],{},"容易泄漏、访问已释放内存",[998,6395,6396,6399,6402,6405],{},[1019,6397,6398],{},"垃圾收集 GC",[1019,6400,6401],{},"Java\u002FPython",[1019,6403,6404],{},"自动释放不用的对象",[1019,6406,6407],{},"性能开销、何时释放不可预测",[981,6409,6410],{},"课件也提到，手动管理需要计划：我如何知道内存不再被使用？如果忘记释放怎么办？缺点就是潜在 bug，包括内存泄漏和内存损坏。",[981,6412,6413],{},"你可以这么理解：",[1387,6415,6418],{"className":6416,"code":6417,"language":1392},[1390],"C：你负责倒垃圾，忘了就堆满屋子。\nJava\u002FPython：有人帮你倒垃圾，但他什么时候进屋不完全由你决定。\n",[1029,6419,6417],{"__ignoreMap":1395},[1142,6421],{},[1149,6423,6425],{"id":6424},"p76内存不足","p76：内存不足",[981,6427,6428],{},"这一页说：",[1387,6430,6433],{"className":6431,"code":6432,"language":1392},[1390],"空闲内存不足时，malloc() 返回 NULL\n",[1029,6434,6432],{"__ignoreMap":1395},[1387,6436,6439],{"className":6437,"code":6438,"language":1392},[1390],"malloc 可能失败，失败时返回 NULL。\n",[1029,6440,6438],{"__ignoreMap":1395},[1142,6442],{},[1149,6444,6446],{"id":6445},"p77示例动态分配树","p77：示例——动态分配树",[981,6448,6449],{},"这一页用二叉树说明为什么需要堆。",[981,6451,6452],{},[1156,6453],{"alt":1156,"src":6454},"\u002Fimg\u002FComputerOrganization\u002FL3-CMemory-14.png",[981,6456,6457],{},"动态数据结构，比如链表、树、图，经常用：",[1387,6459,6462],{"className":6460,"code":6461,"language":1607,"meta":1395},[1605],"malloc(sizeof(Node))\n",[1029,6463,6461],{"__ignoreMap":1395},[981,6465,6466],{},"来让节点活在堆上。",[1142,6468],{},[1149,6470,6472,6473,3238,6475,6477],{"id":6471},"p78malloc-和-free-是好朋友","p78：",[1029,6474,2469],{"code":2469},[1029,6476,1119],{"code":1119}," 是好朋友",[1999,6479,6480,6483],{},[1747,6481,6482],{},"如果在某个地方调用 malloc，则需要对结果调用 free，或者接受内存泄漏。",[1747,6484,6485],{},"没有 malloc，就没有 free。",[981,6487,6488],{},"示例：",[1387,6490,6493],{"className":6491,"code":6492,"language":1607,"meta":1395},[1605],"int *p = malloc(...);\n\n\u002F\u002F complicated code\n\nfree(p);\n",[1029,6494,6492],{"__ignoreMap":1395},[981,6496,6497],{},"这叫配对。",[981,6499,6500],{},"但下面这些不能 free：",[1387,6502,6505],{"className":6503,"code":6504,"language":1607,"meta":1395},[1605],"int x;\nint *p = &x;\n\u002F\u002F do not call free(p)\n\nint a[5];\n\u002F\u002F do not call free(a)\n",[1029,6506,6504],{"__ignoreMap":1395},[981,6508,2466,6509,3238,6511,6513,6514,6516,6517,6519,6520,6522],{},[1029,6510,2860],{"code":2860},[1029,6512,2211],{"code":2211}," 都不是堆内存，而是在栈上或其他自动\u002F静态区域。课件 p78 明确强调：没调用 ",[1029,6515,2469],{"code":2469}," 就不要调用 ",[1029,6518,1119],{"code":1119},"，比如不要 ",[1029,6521,1119],{"code":1119}," 栈变量地址或栈数组。",[981,6524,2954],{},[1387,6526,6529],{"className":6527,"code":6528,"language":1392},[1390],"malloc 来的，free 回去。\n不是 malloc 来的，别 free。\n",[1029,6530,6528],{"__ignoreMap":1395},[1142,6532],{},[1149,6534,6536],{"id":6535},"p79观察哪块内存最麻烦","p79：观察——哪块内存最麻烦？",[981,6538,6539],{},"课件总结：",[1999,6541,6542,6545,6548],{},[1747,6543,6544],{},"code 和 static storage 简单：不增长也不缩小，由 OS 负责",[1747,6546,6547],{},"stack 相对简单：栈帧 LIFO 创建和销毁，对程序员透明",[1747,6549,6550],{},"heap 是程序员的任务：可以随时分配\u002F释放，需要规划",[1387,6552,6555],{"className":6553,"code":6554,"language":1392},[1390],"code\u002Fstatic：系统管\nstack：自动管\nheap：你来管\n",[1029,6556,6554],{"__ignoreMap":1395},[1142,6558],{},[1149,6560,6562,6563,6565],{"id":6561},"p80mallocfree-是如何实现的","p80：",[1029,6564,5932],{"code":5932}," 是如何实现的？",[981,6567,6568],{},"这一页讲底层原理。",[981,6570,6571,6572,6574,6575,6578],{},"课件说：底层操作系统允许 ",[1029,6573,2469],{"code":2469}," 库请求堆中的大块内存，例如 Unix 的 ",[1029,6576,6577],{"code":6577},"sbrk()","；",[981,6580,6581,6582,6584],{},"C 标准 ",[1029,6583,2469],{"code":2469}," 库会在堆的未使用部分创建数据结构，用来跟踪空闲空间。写入未分配或已释放的内存，会破坏这些数据结构。",[981,6586,6587],{},"这句话很重要。",[981,6589,6590],{},"你写：",[1387,6592,6595],{"className":6593,"code":6594,"language":1607,"meta":1395},[1605],"int *p = malloc(4 * sizeof(int));\np[1000] = 1;\n",[1029,6596,6594],{"__ignoreMap":1395},[981,6598,6599,6600,6602],{},"不只是“写错一个数组元素”。你可能把 ",[1029,6601,2469],{"code":2469}," 内部维护的空闲链表、块大小信息等结构破坏了。",[981,6604,6605,6606,4935,6608,6610],{},"然后程序可能在很久之后才崩，比如下一次 ",[1029,6607,2469],{"code":2469},[1029,6609,1119],{"code":1119}," 时炸。C 的 bug 就爱搞这种悬疑片叙事。",[1142,6612],{},[1149,6614,6616,6617,6619],{"id":6615},"p81简单的-malloc-实现","p81：简单的 ",[1029,6618,6052],{"code":6052}," 实现",[981,6621,6622],{},"这一页讲简单分配器的问题。",[981,6624,6625],{},"简单想法：",[1387,6627,6630],{"className":6628,"code":6629,"language":1392},[1390],"malloc 库维护一个空闲块链表\n需要内存时，从空闲块里切一块出来\nfree 时，再放回空闲链表\n",[1029,6631,6629],{"__ignoreMap":1395},[981,6633,6634,6635,6637],{},"但多次 ",[1029,6636,5932],{"code":5932}," 后会出现：",[1387,6639,6642],{"className":6640,"code":6641,"language":1392},[1390],"内存碎片\n长链条的块\n",[1029,6643,6641],{"__ignoreMap":1395},[981,6645,6646],{},"课件说，很多小块空闲内存可能散落各处，没有大的连续空闲块；空闲块链表太长，查找会慢。",[981,6648,6649],{},[1156,6650],{"alt":1156,"src":6651},"\u002Fimg\u002FComputerOrganization\u002FL3-CMemory-15.png",[981,6653,5929,6654,2170],{},[1247,6655,6656],{},"fragmentation 内存碎片",[1142,6658],{},[1149,6660,6662,6663,6619],{"id":6661},"p82更好的-malloc-实现","p82：更好的 ",[1029,6664,2469],{"code":2469},[981,6666,6667],{},"这一页介绍更好的思路：",[1387,6669,6672],{"className":6670,"code":6671,"language":1392},[1390],"为不同大小的对象保留单独的内存池\n",[1029,6673,6671],{"__ignoreMap":1395},[981,6675,6676],{},"比如小对象一池，中等对象一池，大对象一池。这样分配和回收更快，也更容易管理。",[981,6678,6679],{},"课件举了 “Buddy allocator 伙伴分配器”。它会把请求大小向上取整到 2 的幂大小，比如：",[1387,6681,6684],{"className":6682,"code":6683,"language":1392},[1390],"34 KB -> 64 KB\n66 KB -> 128 KB\n",[1029,6685,6683],{"__ignoreMap":1395},[981,6687,6688],{},"这样做的好处是：找块、拆块、合并相邻空闲块会更简单。课件 p82 就说，伙伴分配器总是四舍五入到 2 次方大小的块，以简化查找正确大小和合并相邻块。",[981,6690,6691],{},"代价也很明显：可能浪费一些空间。",[981,6693,6694],{},"比如你要 34 KB，给你 64 KB，多出来的 30 KB 暂时用不上。",[1142,6696],{},[1149,6698,6700],{"id":6699},"p832-次幂-buddy-allocator","p83：2 次幂 Buddy Allocator",[981,6702,6703],{},"这一页是图示版。",[981,6705,6706],{},"程序请求：",[1387,6708,6711],{"className":6709,"code":6710,"language":1392},[1390],"A 请求 34k\nB 请求 66k\nC 请求 35k\nD 请求 67k\n",[1029,6712,6710],{"__ignoreMap":1395},[981,6714,6715],{},"如果按 2 的幂分配：",[1387,6717,6720],{"className":6718,"code":6719,"language":1392},[1390],"34k -> 64k\n66k -> 128k\n35k -> 64k\n67k -> 128k\n",[1029,6721,6719],{"__ignoreMap":1395},[981,6723,6724],{},"Buddy Allocator 的基本想法是：",[1387,6726,6729],{"className":6727,"code":6728,"language":1392},[1390],"大块可以拆成两个一样大的 buddy\n两个相邻空闲 buddy 可以合并回大块\n",[1029,6730,6728],{"__ignoreMap":1395},[981,6732,6733],{},"比如一个 128k 块可以拆成两个 64k；两个 64k 伙伴都空闲时，又可以合回 128k。",[981,6735,6736],{},"这页你不用深挖算法，只要理解：",[978,6738,6739],{},[981,6740,6741],{},[1515,6742,6743],{},"为了让 malloc\u002Ffree 更快、更可控，实际分配器会用各种数据结构管理空闲内存，不是随便拿一段就完事。",[1142,6745],{},[1149,6747,6749,6750],{"id":6748},"p84reallocp-size","p84：",[1029,6751,6752],{"code":6752},"realloc(p, size)",[981,6754,2406,6755,6758],{},[1029,6756,6757],{"code":6757},"realloc","，很关键。",[981,6760,3715],{},[1387,6762,6765],{"className":6763,"code":6764,"language":1607,"meta":1395},[1605],"realloc(p, size)\n",[1029,6766,6764],{"__ignoreMap":1395},[981,6768,6769],{},[1515,6770,6771],{},"作用是：把之前在 p 处分配的块调整为新的大小",[981,6773,6774],{},"特殊情况：",[1387,6776,6779],{"className":6777,"code":6778,"language":1392},[1390],"如果 p 为 NULL，realloc 行为类似 malloc\n如果 size 为 0，realloc 行为类似 free\n返回新内存块地址，而且很可能已经移动\n",[1029,6780,6778],{"__ignoreMap":1395},[981,6782,6783],{},"课件示例：",[1387,6785,6788],{"className":6786,"code":6787,"language":1607,"meta":1395},[1605],"int *ip;\n\nip = (int *) malloc(10 * sizeof(int));\n\u002F* always check for ip == NULL *\u002F\n\nip = (int *) realloc(ip, 20 * sizeof(int));\n\u002F* always check for ip == NULL *\u002F\n\u002F* contents of first 10 elements retained *\u002F\n\nrealloc(ip, 0); \u002F* identical to free(ip) *\u002F\n",[1029,6789,6787],{"__ignoreMap":1395},[981,6791,6792,6793,6795],{},"课件特别强调：",[1029,6794,6757],{"code":6757}," 返回的是新地址，内存块很可能已经移动。",[1142,6797],{},[1149,6799,6801],{"id":6800},"p6084-总结这段真正要掌握什么","p60–84 总结：这段真正要掌握什么",[981,6803,6804],{},"你现在要能把 C 内存分成四类：",[1387,6806,6809],{"className":6807,"code":6808,"language":1392},[1390],"code        程序指令，启动时加载，不变\nstatic data 全局\u002F静态数据，启动时加载，可修改，大小固定\nstack       局部变量\u002F参数，函数调用时创建，返回时失效\nheap        malloc\u002Fcalloc\u002Frealloc 创建，free 释放，程序员负责\n",[1029,6810,6808],{"__ignoreMap":1395},[981,6812,6813],{},"最重要的 8 条铁律：",[1387,6815,6818],{"className":6816,"code":6817,"language":1392},[1390],"1. 不要返回局部变量地址。\n2. malloc 后检查是否为 NULL。\n3. malloc 出来的内存未初始化。\n4. malloc 返回的原始地址要保存好。\n5. free 必须传 malloc\u002Fcalloc\u002Frealloc 返回的起始地址。\n6. 没 malloc 就别 free。\n7. malloc 和 free 要配对，否则泄漏。\n8. realloc 可能移动内存，旧指针可能失效。\n",[1029,6819,6817],{"__ignoreMap":1395},[981,6821,6822],{},"这一段的主线可以压成一句话：",[978,6824,6825],{},[981,6826,6827],{},[1247,6828,6829],{},"栈上的东西自动生死，堆上的东西你说了算；但你说了算，也意味着出了事你背锅。",[981,6831,6832,6833,1120,6835,6837,6838,6840],{},"下一段 p85 往后就是把这些坑集中爆破：未初始化、越界、非法 ",[1029,6834,1119],{"code":1119},[1029,6836,1119],{"code":1119},"、内存泄漏、",[1029,6839,6757],{"code":6757}," 后旧指针失效。",[1142,6842],{},[988,6844,6846],{"id":6845},"第五章常见内存问题与性能现实","第五章：常见内存问题与性能现实",[981,6848,6849,6850,6853,6854,6857,6858,6861],{},"好，继续 ",[1247,6851,6852],{},"p85–101","。这一段前半部分是 ",[1247,6855,6856],{},"C 内存错误集中爆破","，后半部分进入 ",[1247,6859,6860],{},"Great Reality 残酷现实","：整数\u002F浮点数不是数学里的数、内存错误很阴、性能不只看算法复杂度。",[1142,6863],{},[1149,6865,6867],{"id":6866},"p85常见的内存问题","p85：常见的内存问题",[981,6869,6870],{},"这一页是错误清单。课件列了几类 C 里最常见的内存问题",[1744,6872,6873,6876,6890,6893],{},[1747,6874,6875],{},"使用未初始化的值(Using uninitialized values)",[1747,6877,6878,6879],{},"使用不属于你的内存(Using memory that you don’t own)\n",[1999,6880,6881,6884,6887],{},[1747,6882,6883],{},"重新分配堆栈或堆变量(De-allocated stack or heap variable)",[1747,6885,6886],{},"对数组的越界引用(Out-of-bounds reference to array)",[1747,6888,6889],{},"使用NULL或垃圾数据作为指针(Using NULL or garbage data as a pointer)",[1747,6891,6892],{},"由于打乱malloc\u002Fcalloc返回的指针，导致不正确地使用free\u002Frealloc",[1747,6894,6895,6896],{},"内存泄漏(Memory leaks)\n",[1999,6897,6898],{},[1747,6899,6900],{},"你分配了一些东西，但后来忘记释放它",[1142,6902],{},[1149,6904,6906],{"id":6905},"p86常见内存问题use-after-free","p86：常见内存问题——use-after-free",[981,6908,6909],{},"这一页代码大概是：",[1387,6911,6914],{"className":6912,"code":6913,"language":1607,"meta":1395},[1605],"#include \u003Cstdlib.h>\n#include \u003Cstdio.h>\n\nint main(void) {\n    int *a = malloc(sizeof(int));\n    \u002F\u002F ...\n    free(a);        \u002F\u002F a no longer exists!\n\n    int *b = malloc(sizeof(int));\n    *b = 128;\n\n    *a = 55;        \u002F\u002F ERROR!\n\n    printf(\"a=%d, b=%d (==128!)\\n\", *a, *b);\n}\n",[1029,6915,6913],{"__ignoreMap":1395},[981,6917,5212],{},[1387,6919,6922],{"className":6920,"code":6921,"language":1392},[1390],"a=55, b=55 (==128!)\n",[1029,6923,6921],{"__ignoreMap":1395},[981,6925,6926,6927,6929,6930,6932],{},"这很离谱：明明给 ",[1029,6928,2654],{"code":2654}," 赋了 128，最后 ",[1029,6931,2654],{"code":2654}," 却变成 55。",[981,6934,2394],{},[1387,6936,6939],{"className":6937,"code":6938,"language":1607,"meta":1395},[1605],"free(a);\n",[1029,6940,6938],{"__ignoreMap":1395},[981,6942,6943,6946,3630,6950,3630,6953,3630,6957],{},[1515,6944,6945],{},"之后，",[1515,6947,6948],{},[1029,6949,2211],{"code":2211},[1515,6951,6952],{},"指向的那块堆内存已经被归还给堆管理器了。你不能再通过",[1515,6954,6955],{},[1029,6956,2211],{"code":2211},[1515,6958,6959],{},"使用它。",[981,6961,2894],{},[1387,6963,6966],{"className":6964,"code":6965,"language":1607,"meta":1395},[1605],"int *b = malloc(sizeof(int));\n",[1029,6967,6965],{"__ignoreMap":1395},[981,6969,6970,6971,6973],{},"堆管理器可能刚好把同一块内存又分配给 ",[1029,6972,2654],{"code":2654},"。于是：",[1387,6975,6978],{"className":6976,"code":6977,"language":1392},[1390],"a 和 b 可能指向同一块物理内存\n",[1029,6979,6977],{"__ignoreMap":1395},[981,6981,2719],{},[1387,6983,6986],{"className":6984,"code":6985,"language":1607,"meta":1395},[1605],"*a = 55;\n",[1029,6987,6985],{"__ignoreMap":1395},[981,6989,6990,6991,6993],{},"实际上把 ",[1029,6992,2654],{"code":2654}," 指向的值也改了。",[981,6995,6996],{},"这类错误叫：",[1387,6998,7001],{"className":6999,"code":7000,"language":1392},[1390],"use-after-free：释放后继续使用\n",[1029,7002,7000],{"__ignoreMap":1395},[981,7004,7005,7006,7011,7012,7014,7015,7017],{},"这页最可怕的点不是“程序崩了”，而是 ",[1515,7007,7008],{},[1247,7009,7010],{},"程序没崩，但结果悄悄错了","。课件也强调：赋值给 ",[1029,7013,2211],{"code":2211}," 可能破坏 ",[1029,7016,2654],{"code":2654},"，错误可能无法检测到。",[1142,7019],{},[1149,7021,7023],{"id":7022},"p87防御性编程-defensive-programming","p87：防御性编程 Defensive Programming",[992,7025,7026],{"style":1575},[1014,7027,7028],{},[998,7029,1581,7030,1581,7038],{"style":1580},[1019,7031,7033,1581],{"style":7032},"width: 49.0%; text-align: center; border: none; padding: 0.3em; margin: 0;",[981,7034,7035],{},[1156,7036],{"alt":1156,"src":7037},"\u002Fimg\u002FComputerOrganization\u002FL3-CMemory-16.png",[1019,7039,7040,1581],{"style":7032},[981,7041,7042],{},[1156,7043],{"alt":1156,"src":7044},"\u002Fimg\u002FComputerOrganization\u002FL3-CMemory-17.png",[981,7046,7047,7048,7051],{},"这一页是对 p86 的改进版本。代码里在 ",[1029,7049,7050],{"code":7050},"free(a)"," 后加了：",[1387,7053,7056],{"className":7054,"code":7055,"language":1607,"meta":1395},[1605],"a = NULL;\n",[1029,7057,7055],{"__ignoreMap":1395},[981,7059,7060],{},"然后后面又写：",[1387,7062,7064],{"className":7063,"code":6985,"language":1607,"meta":1395},[1605],[1029,7065,6985],{"__ignoreMap":1395},[981,7067,7068],{},"输出变成：",[1387,7070,7073],{"className":7071,"code":7072,"language":1392},[1390],"Segmentation fault: 11\n",[1029,7074,7072],{"__ignoreMap":1395},[981,7076,7077,7078,2170],{},"这看起来像程序更惨了，其实是 ",[1247,7079,7080],{},"更好了",[981,7082,7083,7084,7086],{},"p86 的错误是隐蔽的：",[1029,7085,2211],{"code":2211}," 指向已释放内存，写进去可能破坏别的数据，但程序不一定马上崩。",[981,7088,7089,7090,7092,7093,3485],{},"p87 把 ",[1029,7091,2211],{"code":2211}," 设成 ",[1029,7094,1969],{"code":1969},[1387,7096,7098],{"className":7097,"code":7055,"language":1607,"meta":1395},[1605],[1029,7099,7055],{"__ignoreMap":1395},[981,7101,7102],{},"之后再写：",[1387,7104,7106],{"className":7105,"code":6985,"language":1607,"meta":1395},[1605],[1029,7107,6985],{"__ignoreMap":1395},[981,7109,7110],{},"就是解引用空指针。这个错误通常会马上触发 segmentation fault。",[981,7112,7113],{},"所以这叫防御性编程：",[1387,7115,7118],{"className":7116,"code":7117,"language":1392},[1390],"与其让错误悄悄污染数据，不如让错误尽早暴露。\n",[1029,7119,7117],{"__ignoreMap":1395},[981,7121,7122],{},"以后你可以养成这个习惯：",[1387,7124,7127],{"className":7125,"code":7126,"language":1607,"meta":1395},[1605],"free(p);\np = NULL;\n",[1029,7128,7126],{"__ignoreMap":1395},[1142,7130],{},[1149,7132,7134],{"id":7133},"p88用-gdb-找崩溃位置","p88：用 gdb 找崩溃位置",[981,7136,7137,7138,2170],{},"这一页展示调试器 ",[1029,7139,7140],{"code":7140},"gdb",[981,7142,7143],{},"流程是：",[1387,7145,7148],{"className":7146,"code":7147,"language":5207,"meta":1395},[5205],"gcc -g DefensiveB.c\ngdb a.out\n(gdb) run\n",[1029,7149,7147],{"__ignoreMap":1395},[981,7151,7152],{},"然后 gdb 输出：",[1387,7154,7157],{"className":7155,"code":7156,"language":1392},[1390],"Program received signal SIGSEGV, Segmentation fault.\n0x0000000100000f76 in main () at DefensiveB.c:11\n11    *a = 55;\n",[1029,7158,7156],{"__ignoreMap":1395},[981,7160,7161,7162,3485],{},"重点是 ",[1029,7163,7164],{"code":7164},"-g",[1387,7166,7169],{"className":7167,"code":7168,"language":5207,"meta":1395},[5205],"gcc -g DefensiveB.c\n",[1029,7170,7168],{"__ignoreMap":1395},[981,7172,7173,7175],{},[1029,7174,7164],{"code":7164}," 会把调试信息放进可执行文件里。这样 gdb 才能告诉你：",[1387,7177,7180],{"className":7178,"code":7179,"language":1392},[1390],"在哪个源文件、哪一行崩了\n",[1029,7181,7179],{"__ignoreMap":1395},[981,7183,7184],{},"这一页你要掌握的不是 gdb 全套命令，而是：",[1387,7186,7189],{"className":7187,"code":7188,"language":1392},[1390],"Segmentation fault 不要瞎猜。\n用 gdb 跑，先定位崩在哪一行。\n",[1029,7190,7188],{"__ignoreMap":1395},[981,7192,7193,7194,7197],{},"p87 通过 ",[1029,7195,7196],{"code":7196},"a = NULL"," 让 bug 更早爆炸；p88 通过 gdb 告诉你爆炸点在哪里。配合起来，就是 C 语言活命二件套。",[1142,7199],{},[1149,7201,7203,7204,7206],{"id":7202},"p89realloc-之后旧别名指针失效","p89：",[1029,7205,6757],{"code":6757}," 之后旧别名指针失效",[981,7208,6909],{},[1387,7210,7213],{"className":7211,"code":7212,"language":1607,"meta":1395},[1605],"#include \u003Cstdio.h>\n#include \u003Cstdlib.h>\n\nint main(void) {\n    int *a = malloc(10 * sizeof(int));\n    int *b = a;       \u002F\u002F b is an \"alias\" for a\n\n    b[0] = 1;\n\n    \u002F\u002F work with a (or b) ... then increase size\n    a = realloc(a, 1000 * sizeof(int));\n\n    \u002F\u002F yet another array\n    int *c = malloc(10 * sizeof(int));\n\n    c[0] = 3;\n    b[0] = 2;\n\n    printf(\"a[0]=%d, b[0]=%d, c[0]=%d\\n\", a[0], b[0], c[0]);\n    printf(\"a = %p\\n\", a);\n    printf(\"b = %p\\n\", b);\n    printf(\"c = %p\\n\", c);\n}\n",[1029,7214,7212],{"__ignoreMap":1395},[981,7216,7217],{},"关键在这几句：",[1387,7219,7222],{"className":7220,"code":7221,"language":1607,"meta":1395},[1605],"int *b = a;\na = realloc(a, 1000 * sizeof(int));\n",[1029,7223,7221],{"__ignoreMap":1395},[981,7225,7226],{},"一开始：",[1387,7228,7231],{"className":7229,"code":7230,"language":1392},[1390],"a 和 b 指向同一块堆内存\n",[1029,7232,7230],{"__ignoreMap":1395},[981,7234,5882,7235,7237],{},[1029,7236,6757],{"code":6757}," 可能会把内存块搬到新位置。调用后：",[1387,7239,7242],{"className":7240,"code":7241,"language":1392},[1390],"a 指向新位置\nb 还指向旧位置\n",[1029,7243,7241],{"__ignoreMap":1395},[981,7245,7246,7249,3630,7253],{},[1515,7247,7248],{},"问题是：旧位置已经不再属于你。",[1515,7250,7251],{},[1029,7252,2654],{"code":2654},[1515,7254,7255],{},"已经变成悬空指针。",[981,7257,7258],{},"课件给出的某次输出是：",[1387,7260,7263],{"className":7261,"code":7262,"language":1392},[1390],"a[0]=1, b[0]=2, c[0]=2\na = 0x7fc53b802600\nb = 0x7fc53b403140\nc = 0x7fc53b403140\n",[1029,7264,7262],{"__ignoreMap":1395},[981,7266,4745,7267,3238,7269,7271,7272,7274,7275,6973],{},[1029,7268,2654],{"code":2654},[1029,7270,1607],{"code":1607}," 地址一样，说明旧块后来可能又被 ",[1029,7273,2469],{"code":2469}," 分给了 ",[1029,7276,1607],{"code":1607},[1387,7278,7281],{"className":7279,"code":7280,"language":1607,"meta":1395},[1605],"b[0] = 2;\n",[1029,7282,7280],{"__ignoreMap":1395},[981,7284,7285,7286,2170],{},"其实破坏了 ",[1029,7287,7288],{"code":7288},"c[0]",[981,7290,7291],{},"但课件也警告：这个结果是巧合，不同运行、不同机器可能完全不同。核心结论是：",[1387,7293,7296],{"className":7294,"code":7295,"language":1392},[1390],"realloc 之后，旧别名指针 b 不再可靠。\n继续使用 b 是 bug。\n",[1029,7297,7295],{"__ignoreMap":1395},[1142,7299],{},[1149,7301,7303],{"id":7302},"p90残酷的现实-great-reality","p90：残酷的现实 Great Reality",[981,7305,7306],{},"这一页是新小节标题。",[981,7308,7309],{},"从这里开始，课件从“C 内存错误”切到更广的系统现实：",[1387,7311,7314],{"className":7312,"code":7313,"language":1392},[1390],"数学模型很美，但机器不是数学。\n高级语言很舒服，但机器执行有底层细节。\n算法复杂度很重要，但真实性能还受内存系统影响。\n",[1029,7315,7313],{"__ignoreMap":1395},[981,7317,7318],{},"后面的 p91–100 就是在讲这些“残酷现实”。",[1142,7320],{},[1149,7322,7324],{"id":7323},"p91残酷现实-1ints-are-not-integers-floats-are-not-reals","p91：残酷现实 #1：Ints are not Integers, Floats are not Reals",[981,7326,7327],{},"这一页的意思是：",[1387,7329,7332],{"className":7330,"code":7331,"language":1392},[1390],"int 不是数学里的整数。\nfloat 不是数学里的实数。\n",[1029,7333,7331],{"__ignoreMap":1395},[1599,7335,7337,7338,7341],{"id":7336},"例子-1x-0-一定成立吗","例子 1：",[1029,7339,7340],{"code":7340},"x² ≥ 0"," 一定成立吗？",[981,7343,7344],{},"数学里当然成立。\n浮点数里通常也能成立。",[981,7346,7347],{},"但整数里不一定，因为整数有固定宽度，会溢出。",[981,7349,7350,7351,7353],{},"例如 32 位有符号 ",[1029,7352,1852],{"code":1852}," 最大大约是：2,147,483,647",[981,7355,7356],{},"所以：40000 * 40000 = 1,600,000,000  还没超",[981,7358,7359],{},"但：50000 * 50000 = 2,500,000,000",[981,7361,7362,7363,7365],{},"超过 32 位有符号 ",[1029,7364,1852],{"code":1852}," 范围了。结果可能变成负数、奇怪值，或者在 C 标准里属于未定义行为。",[981,7367,7368,3630,7371,3630,7375],{},[1515,7369,7370],{},"所以机器里的",[1515,7372,7373],{},[1029,7374,1852],{"code":1852},[1515,7376,7377],{},"更像：有限格子里的数，不是无限整数。",[1599,7379,7381],{"id":7380},"例子-2加法结合律一定成立吗","例子 2：加法结合律一定成立吗？",[981,7383,7384],{},"数学里：",[1387,7386,7389],{"className":7387,"code":7388,"language":1392},[1390],"(x + y) + z = x + (y + z)\n",[1029,7390,7388],{"__ignoreMap":1395},[981,7392,7393],{},"但浮点数里不一定。",[981,7395,5201],{},[1387,7397,7400],{"className":7398,"code":7399,"language":1392},[1390],"(1e20 + -1e20) + 3.14  → 3.14\n1e20 + (-1e20 + 3.14) → ??\n",[1029,7401,7399],{"__ignoreMap":1395},[981,7403,7404],{},"为什么第二个可能不是 3.14？",[981,7406,2466,7407,7410,7411,7414],{},[1029,7408,7409],{"code":7409},"3.14"," 相对于 ",[1029,7412,7413],{"code":7413},"1e20"," 太小了，小到浮点表示时可能被“吃掉”。于是：",[1387,7416,7419],{"className":7417,"code":7418,"language":1392},[1390],"-1e20 + 3.14 ≈ -1e20\n",[1029,7420,7418],{"__ignoreMap":1395},[981,7422,7423],{},"再加：",[1387,7425,7428],{"className":7426,"code":7427,"language":1392},[1390],"1e20 + -1e20 = 0\n",[1029,7429,7427],{"__ignoreMap":1395},[981,7431,7432],{},"所以结果可能变成 0。",[981,7434,7435],{},"这一页的核心：",[1387,7437,7440],{"className":7438,"code":7439,"language":1392},[1390],"机器数有范围、有精度。\n不要把 int 当无限整数，不要把 float 当真实实数。\n",[1029,7441,7439],{"__ignoreMap":1395},[981,7443,7444],{},"课件这里用整数平方和浮点结合律说明机器数与数学数不同。",[1142,7446],{},[1149,7448,7450],{"id":7449},"p92残酷现实-2你必须知道汇编","p92：残酷现实 #2：你必须知道汇编",[981,7452,7453],{},"理解汇编，是理解机器级执行模型的关键。",[981,7455,7456],{},"课件列了几个场景：",[1387,7458,7461],{"className":7459,"code":7460,"language":1392},[1390],"1. 程序出现高级语言模型解释不了的错误\n2. 调优程序性能\n3. 理解编译器做了什么优化、没做什么优化\n4. 操作系统、编译器等系统软件实现\n5. 分析或对抗恶意软件\n",[1029,7462,7460],{"__ignoreMap":1395},[1142,7464],{},[1149,7466,7468],{"id":7467},"p93残酷现实-3内存很重要","p93：残酷现实 #3：内存很重要",[981,7470,7471],{},"这页讲三个事实。",[981,7473,7474],{},"第一，内存不是无限的：",[1999,7476,7477,7480],{},[1747,7478,7479],{},"它必须被分配和管理。",[1747,7481,7482],{},"很多应用程序是内存主导的。",[981,7484,7485],{},"第二，内存引用错误特别有害：",[1999,7487,7488],{},[1747,7489,7490],{},"它可能在时间和空间上造成巨大影响。",[978,7492,7493],{},[981,7494,7495],{},"也就是：你现在写错一个地址，可能污染另一个对象；现在埋雷，十分钟后爆。",[981,7497,7498],{},"第三，内存性能不是均匀的：",[1999,7500,7501,7504],{},[1747,7502,7503],{},"缓存和虚拟内存效应会极大地影响程序性能",[1747,7505,7506],{},"使程序适应存储器系统的特点可以大幅度提升速度",[981,7508,7509],{},"这句话非常关键。抽象上我们觉得：",[1387,7511,7514],{"className":7512,"code":7513,"language":1392},[1390],"访问 memory[i] 和 memory[j] 都差不多\n",[1029,7515,7513],{"__ignoreMap":1395},[981,7517,7518],{},"但真实机器不是这样。离 CPU 近的缓存很快，主存慢很多；连续访问通常比跳着访问快很多。",[981,7520,7521],{},"课件这一页把内存容量、内存错误和内存性能三个问题放在一起强调：随机访问内存只是抽象，真实内存系统有层次、有代价。",[1142,7523],{},[1149,7525,7527],{"id":7526},"p94内存引用错误示例","p94：内存引用错误示例",[981,7529,2812],{},[1387,7531,7534],{"className":7532,"code":7533,"language":1607,"meta":1395},[1605],"typedef struct {\n    int a[2];\n    double d;\n} struct_t;\n\ndouble fun(int i) {\n    volatile struct_t s;\n    s.d = 3.14;\n    s.a[i] = 1073741824; \u002F* Possibly out of bounds *\u002F\n    return s.d;\n}\n",[1029,7535,7533],{"__ignoreMap":1395},[981,7537,7538],{},"结构体里有：",[1387,7540,7543],{"className":7541,"code":7542,"language":1392},[1390],"a[0]\na[1]\nd\n",[1029,7544,7542],{"__ignoreMap":1395},[981,7546,7547],{},"合法访问只有：",[1387,7549,7552],{"className":7550,"code":7551,"language":1607,"meta":1395},[1605],"s.a[0]\ns.a[1]\n",[1029,7553,7551],{"__ignoreMap":1395},[981,7555,7556],{},"但是代码写的是：",[1387,7558,7561],{"className":7559,"code":7560,"language":1607,"meta":1395},[1605],"s.a[i] = 1073741824;\n",[1029,7562,7560],{"__ignoreMap":1395},[981,7564,5621,7565,7568],{},[1029,7566,7567],{"code":7567},"i >= 2"," 时，就越界了。",[981,7570,7571],{},"课件给的结果：",[1387,7573,7576],{"className":7574,"code":7575,"language":1392},[1390],"fun(0) → 3.14\nfun(1) → 3.14\nfun(2) → 3.1399998664856\nfun(3) → 2.00000061035156\nfun(4) → 3.14\nfun(6) → Segmentation fault\n",[1029,7577,7575],{"__ignoreMap":1395},[981,7579,7580],{},"为什么这么怪？",[981,7582,2466,7583,1050,7586,7588,7589,7592,7593,7596],{},[1029,7584,7585],{"code":7585},"a[2]",[1029,7587,3391],{"code":3391}," 已经不是数组元素了，它们会写到结构体后面的内存。刚好结构体后面就是 ",[1029,7590,7591],{"code":7591},"double d"," 的字节，所以越界写可能破坏 ",[1029,7594,7595],{"code":7595},"d"," 的一部分。",[981,7598,2719],{},[1387,7600,7603],{"className":7601,"code":7602,"language":1392},[1390],"i = 0,1：写的是 a[0], a[1]，d 没变\ni = 2,3：写到了 d 的不同部分，d 被改坏\ni = 6：写到更危险的位置，程序崩溃\n",[1029,7604,7602],{"__ignoreMap":1395},[981,7606,7607],{},"这页要记住：",[1387,7609,7612],{"className":7610,"code":7611,"language":1392},[1390],"数组越界不一定立刻崩。\n它可能只是悄悄改坏旁边的数据。\n",[1029,7613,7611],{"__ignoreMap":1395},[981,7615,7616],{},"课件也强调这个结果是系统特定的。",[1142,7618],{},[1149,7620,7622],{"id":7621},"p95解释-p94-的内存布局","p95：解释 p94 的内存布局",[981,7624,7625,7626,7629],{},"这一页用图解释 ",[1029,7627,7628],{"code":7628},"fun(i)"," 到底写到了哪里。",[981,7631,7632],{},[1156,7633],{"alt":1156,"src":7634},"\u002Fimg\u002FComputerOrganization\u002FL3-CMemory-18.png",[1387,7636,7639],{"className":7637,"code":7638,"language":1607,"meta":1395},[1605],"s.a[2] = 1073741824;\n",[1029,7640,7638],{"__ignoreMap":1395},[981,7642,7643,7644,7646,7647,7649,7650,7652,7653,7655],{},"看起来像访问数组第 3 个元素，但 ",[1029,7645,2211],{"code":2211}," 只有两个元素。由于 ",[1029,7648,1852],{"code":1852}," 是 4 字节，",[1029,7651,7585],{"code":7585}," 对应的位置刚好可能落在 ",[1029,7654,7595],{"code":7595}," 的一部分上。",[981,7657,7658],{},"这就是为什么：",[1387,7660,7663],{"className":7661,"code":7662,"language":1392},[1390],"fun(2) → 3.1399998664856\nfun(3) → 2.00000061035156\n",[1029,7664,7662],{"__ignoreMap":1395},[981,7666,7667,7669],{},[1029,7668,7595],{"code":7595}," 被改了一部分，所以结果变成奇怪的小数。",[981,7671,7672],{},"而：",[1387,7674,7677],{"className":7675,"code":7676,"language":1392},[1390],"fun(6) → Segmentation fault\n",[1029,7678,7676],{"__ignoreMap":1395},[981,7680,7681],{},"可能是因为写到了某个关键区域，比如保存程序控制状态的位置，或者不可访问内存。",[1387,7683,7686],{"className":7684,"code":7685,"language":1392},[1390],"越界访问的后果取决于内存布局。\n内存布局又取决于系统、编译器、优化方式。\n",[1029,7687,7685],{"__ignoreMap":1395},[981,7689,7690],{},"所以同一段错代码，在你电脑上“没事”，到别人电脑上炸了，完全正常。它不是没 bug，它只是还没到爆炸时间。",[1142,7692],{},[1149,7694,7696],{"id":7695},"p96内存引用错误总结","p96：内存引用错误总结",[981,7698,7699],{},[1247,7700,7701],{},"C 和 C++ 不提供内存保护机制",[1999,7703,7704,7707,7710],{},[1747,7705,7706],{},"数组越界访问（Out-of-bounds array references）",[1747,7708,7709],{},"无效的指针值（Invalid pointer values）",[1747,7711,7712,7713,7715],{},"错误使用 ",[1029,7714,5932],{"code":5932},"（如重复释放、未释放、释放后继续使用等）",[981,7717,7718],{},[1247,7719,7720],{},"容易导致难以发现的 Bug",[1999,7722,7723,7726],{},[1747,7724,7725],{},"Bug 是否产生影响取决于系统环境和编译器",[1747,7727,7728,7729],{},"Bug 往往与真正出错的位置相距较远\n",[1999,7730,7731,7734],{},[1747,7732,7733],{},"被破坏的对象可能与当前访问对象在逻辑上毫无关联",[1747,7735,7736],{},"Bug 的影响可能在产生很长时间后才会显现",[981,7738,7739],{},[1247,7740,7741],{},"应对方法",[1999,7743,7744,7752,7755],{},[1747,7745,7746,7747],{},"使用具有内存安全机制的编程语言\n",[1999,7748,7749],{},[1747,7750,7751],{},"如 Java、Ruby、Python、ML 等",[1747,7753,7754],{},"深入了解各种可能发生的内存交互和错误",[1747,7756,7757,7758],{},"使用或开发内存检测工具\n",[1999,7759,7760],{},[1747,7761,7762,7763,7766],{},"例如 ",[1247,7764,7765],{},"Valgrind"," 用于检测内存访问错误和内存泄漏",[1142,7768],{},[1149,7770,7772],{"id":7771},"p97残酷现实-4性能比渐近复杂性更复杂","p97：残酷现实 #4：性能比渐近复杂性更复杂",[981,7774,7775],{},"这一页讲性能。",[981,7777,7778],{},"常熟因素很重要",[981,7780,7781,7782,7785],{},"比如两个程序都是 ",[1029,7783,7784],{"code":7784},"O(n)","，一个可能比另一个慢 10 倍。",[981,7787,7788],{},"课件强调：",[1387,7790,7793],{"className":7791,"code":7792,"language":1392},[1390],"常数因素也很重要\n精确操作计数也不能完全预测性能\n代码写法可能造成 10 倍性能差异\n优化要在多个层次进行：\n算法、数据表示、过程、循环\n",[1029,7794,7792],{"__ignoreMap":1395},[981,7796,7797],{},"系统层面还要知道：",[1387,7799,7802],{"className":7800,"code":7801,"language":1392},[1390],"程序如何编译和执行\n如何测量性能、识别瓶颈\n如何在不破坏模块化和通用性的情况下优化\n",[1029,7803,7801],{"__ignoreMap":1395},[981,7805,7806],{},"这页的核心：",[1387,7808,7811],{"className":7809,"code":7810,"language":1392},[1390],"真实性能 = 算法 + 编译器 + CPU + 缓存 + 内存访问模式 + 代码写法\n",[1029,7812,7810],{"__ignoreMap":1395},[981,7814,7815],{},"课件第 97 页明确说，常数因素重要，甚至精确操作计数也不能预测性能，并且要在算法、数据表示、过程和循环多个层次优化。",[1142,7817],{},[1149,7819,7821],{"id":7820},"p98内存系统性能示例二维数组复制","p98：内存系统性能示例——二维数组复制",[981,7823,7824],{},"这一页是非常经典的缓存局部性例子。",[981,7826,7827],{},[1156,7828],{"alt":1156,"src":7829},"\u002Fimg\u002FComputerOrganization\u002FL3-CMemory-19.png",[981,7831,7832],{},"有两个函数：",[1387,7834,7837],{"className":7835,"code":7836,"language":1607,"meta":1395},[1605],"void copyji(int src[2048][2048],\n            int dst[2048][2048])\n{\n    int i, j;\n    for (j = 0; j \u003C 2048; j++)\n        for (i = 0; i \u003C 2048; i++)\n            dst[i][j] = src[i][j];\n}\n",[1029,7838,7836],{"__ignoreMap":1395},[981,7840,7841],{},"和：",[1387,7843,7846],{"className":7844,"code":7845,"language":1607,"meta":1395},[1605],"void copyij(int src[2048][2048],\n            int dst[2048][2048])\n{\n    int i, j;\n    for (i = 0; i \u003C 2048; i++)\n        for (j = 0; j \u003C 2048; j++)\n            dst[i][j] = src[i][j];\n}\n",[1029,7847,7845],{"__ignoreMap":1395},[981,7849,7850],{},"它们做的事一样，都是复制二维数组。\n但课件给出的时间差非常大：",[1387,7852,7855],{"className":7853,"code":7854,"language":1392},[1390],"copyji：81.8 ms\ncopyij：4.3 ms\n",[1029,7856,7854],{"__ignoreMap":1395},[981,7858,7859],{},"为什么？",[981,7861,7862,7863,3630,7868,7871],{},"C 的二维数组是 ",[1515,7864,7865],{},[1247,7866,7867],{},"行优先 row-major",[1515,7869,7870],{},"存储","，也就是说，同一行里的元素在内存中连续。",[981,7873,7874,7877,7878,3485],{},[1029,7875,7876],{"code":7876},"copyij"," 的内层循环是 ",[1029,7879,7880],{"code":7880},"j",[981,7882,7883,7884,7887,7888,7890],{},"固定 ",[1029,7885,7886],{"code":7886},"i","，连续扫 ",[1029,7889,7880],{"code":7880},"，就是沿着一行走。内存访问连续，缓存友好，所以快。",[981,7892,7893,7877,7896,3485],{},[1029,7894,7895],{"code":7895},"copyji",[1029,7897,7886],{"code":7886},[981,7899,7883,7900,7902,7903,7905],{},[1029,7901,7880],{"code":7880},"，变化 ",[1029,7904,7886],{"code":7886},"，相当于沿着一列走。每次跳到下一行同一列，地址跨度很大，缓存很不友好，所以慢。",[981,7907,7908],{},"这页的结论：同样的算法复杂度，同样的数据量，仅仅循环顺序不同，性能可以差十几倍。",[981,7910,7911,7912,3238,7914,7916],{},"课件 p98 就用 ",[1029,7913,7895],{"code":7895},[1029,7915,7876],{"code":7876}," 展示了访问模式对分层存储结构性能的影响。",[1142,7918],{},[1149,7920,7922],{"id":7921},"p99性能不同可视化stride","p99：性能不同可视化——stride",[981,7924,7925],{},[1156,7926],{"alt":1156,"src":7927},"\u002Fimg\u002FComputerOrganization\u002FL3-CMemory-20.png",[1142,7929],{},[1149,7931,7933],{"id":7932},"p100残酷现实-5计算机不只是执行程序","p100：残酷现实 #5：计算机不只是执行程序",[981,7935,7936],{},[1247,7937,7938],{},"程序需要进行输入和输出（I\u002FO）",[1999,7940,7941,7944],{},[1747,7942,7943],{},"输入输出系统对程序的可靠性至关重要",[1747,7945,7946],{},"输入输出系统对程序的性能至关重要",[981,7948,7949],{},[1247,7950,7951],{},"程序需要通过网络进行通信",[1999,7953,7954],{},[1747,7955,7956,7957],{},"网络环境带来了许多系统级问题：\n",[1999,7958,7959,7962,7965,7968],{},[1747,7960,7961],{},"自治进程的并发操作",[1747,7963,7964],{},"应对不可靠的通信媒体",[1747,7966,7967],{},"跨平台兼容性",[1747,7969,7970],{},"复杂的性能问题",[1142,7972],{},[1149,7974,7976],{"id":7975},"p101小结","p101：小结",[981,7978,7979],{},[1247,7980,7981],{},"C 语言有三个主要的内存段",[1999,7983,7984,7997,8007],{},[1747,7985,7986,7989],{},[1247,7987,7988],{},"静态数据（Static Data）",[1999,7990,7991,7994],{},[1747,7992,7993],{},"函数外部的变量",[1747,7995,7996],{},"例如全局变量（global variables）",[1747,7998,7999,8002],{},[1247,8000,8001],{},"堆栈（Stack）",[1999,8003,8004],{},[1747,8005,8006],{},"函数的局部变量（local variables）",[1747,8008,8009,8012],{},[1247,8010,8011],{},"堆（Heap）",[1999,8013,8014],{},[1747,8015,8016,8017,8019],{},"使用 ",[1029,8018,5932],{"code":5932}," 显式分配和释放的内存",[981,8021,8022],{},[1247,8023,8024],{},"堆数据容易产生 Bug",[1999,8026,8027,8030],{},[1747,8028,8029],{},"可能导致内存泄漏（memory leaks）",[1747,8031,8032],{},"可能导致内存损坏（memory corruption）",[981,8034,8035],{},[1247,8036,8037],{},"应对策略",[1999,8039,8040,8061,8076],{},[1747,8041,8042,8045],{},[1247,8043,8044],{},"规划（Planning）",[1999,8046,8047,8053,8056],{},[1747,8048,8049,8050,8052],{},"明确谁“拥有” ",[1029,8051,2469],{"code":2469}," 分配的数据",[1747,8054,8055],{},"通常会有多个“所有者”或指针指向同一块数据",[1747,8057,8058,8059],{},"明确谁可以安全地调用 ",[1029,8060,1119],{"code":1119},[1747,8062,8063,8066],{},[1247,8064,8065],{},"防御性编程（Defensive Programming）",[1999,8067,8068,8073],{},[1747,8069,8070,8071],{},"将已释放的指针赋值为 ",[1029,8072,1969],{"code":1969},[1747,8074,8075],{},"使用常量表示数组大小",[1747,8077,8078,8081],{},[1247,8079,8080],{},"使用工具",[1999,8082,8083,8087],{},[1747,8084,8085],{},[1029,8086,7140],{"code":7140},[1747,8088,8089],{},[1029,8090,7765],{"code":7765},[1142,8092],{},[988,8094,8096],{"id":8095},"第六章c-内存错误专项训练","第六章：C 内存错误专项训练",[981,8098,8099,8100,8103],{},"好，最后 ",[1247,8101,8102],{},"p102–119"," 基本就是“C 内存错误专项训练”。我先给你一个“坑位编号”，后面每道题都对应这些坑：",[992,8105,8106,8119],{},[995,8107,8108],{},[998,8109,8110,8113,8116],{},[1001,8111,8112],{},"坑编号",[1001,8114,8115],{},"坑名",[1001,8117,8118],{},"典型表现",[1014,8120,8121,8132,8143,8163,8178,8193,8204,8226],{},[998,8122,8123,8126,8129],{},[1019,8124,8125],{},"坑 A",[1019,8127,8128],{},"使用未初始化值",[1019,8130,8131],{},"读到垃圾值",[998,8133,8134,8137,8140],{},[1019,8135,8136],{},"坑 B",[1019,8138,8139],{},"使用不属于你的内存",[1019,8141,8142],{},"越界读写、访问已释放内存、访问栈上已失效内存",[998,8144,8145,8148,8154],{},[1019,8146,8147],{},"坑 C",[1019,8149,8150,8151,8153],{},"搞乱 ",[1029,8152,2469],{"code":2469}," 返回的句柄",[1019,8155,8156,8157,8159,8160,8162],{},"指针加减后再 ",[1029,8158,1119],{"code":1119},"，或者 ",[1029,8161,1119],{"code":1119}," 中间地址",[998,8164,8165,8168,8171],{},[1019,8166,8167],{},"坑 D",[1019,8169,8170],{},"内存泄漏",[1019,8172,8173,8175,8176],{},[1029,8174,2469],{"code":2469}," 了，但丢了地址或忘了 ",[1029,8177,1119],{"code":1119},[998,8179,8180,8183,8188],{},[1019,8181,8182],{},"坑 E",[1019,8184,8185,8187],{},[1029,8186,6757],{"code":6757}," 后旧指针失效",[1019,8189,8190,8192],{},[1029,8191,6757],{"code":6757}," 可能移动内存，旧别名不能再用",[998,8194,8195,8198,8201],{},[1019,8196,8197],{},"坑 F",[1019,8199,8200],{},"返回栈内存地址",[1019,8202,8203],{},"函数返回后局部数组\u002F局部变量失效",[998,8205,8206,8209,8217],{},[1019,8207,8208],{},"坑 G",[1019,8210,8211,8212,8214,8215],{},"非法 ",[1029,8213,1119],{"code":1119}," \u002F 重复 ",[1029,8216,1119],{"code":1119},[1019,8218,8219,1050,8222,8225],{},[1029,8220,8221],{"code":8221},"free(&局部变量)",[1029,8223,8224],{"code":8224},"free(p+1)","、double free",[998,8227,8228,8231,8237],{},[1019,8229,8230],{},"坑 H",[1019,8232,8233,8234,8236],{},"字符串结尾 ",[1029,8235,3281],{"code":3281}," 处理错误",[1019,8238,8239],{},"忘记给字符串多留 1 个字节",[981,8241,8242,8243,8245,8246,8249],{},"课件 p85 已经把这些总结过：未初始化、使用不属于你的内存、越界、用 ",[1029,8244,1969],{"code":1969}," 或垃圾指针、打乱 ",[1029,8247,8248],{"code":8248},"malloc\u002Fcalloc"," 返回的指针、内存泄漏，都是常见 C 内存问题。",[1142,8251],{},[1149,8253,8255],{"id":8254},"p103104使用你不拥有的内存","p103–104：使用你不拥有的内存",[1599,8257,8258],{"id":8258},"题目代码",[1387,8260,8263],{"className":8261,"code":8262,"language":1607,"meta":1395},[1605],"int *ipr, *ipw;\n\nvoid ReadMem() {\n    int i, j;\n    ipr = (int *) malloc(4 * sizeof(int));\n    i = *(ipr - 1000);\n    j = *(ipr + 1000);\n    free(ipr);\n}\n\nvoid WriteMem() {\n    ipw = (int *) malloc(5 * sizeof(int));\n    *(ipw - 1000) = 0;\n    *(ipw + 1000) = 0;\n    free(ipw);\n}\n",[1029,8264,8262],{"__ignoreMap":1395},[1599,8266,8268],{"id":8267},"错误-1越界读","错误 1：越界读",[1387,8270,8273],{"className":8271,"code":8272,"language":1607,"meta":1395},[1605],"i = *(ipr - 1000);\nj = *(ipr + 1000);\n",[1029,8274,8272],{"__ignoreMap":1395},[981,8276,8277],{},"这两个都严重越界。",[981,8279,8280,8283,8284,8287],{},[1029,8281,8282],{"code":8282},"ipr - 1000"," 指向申请块前面很远的位置。\n",[1029,8285,8286],{"code":8286},"ipr + 1000"," 指向申请块后面很远的位置。",[981,8289,8290,8291,8293,8294,8296],{},"它们都不属于这次 ",[1029,8292,2469],{"code":2469}," 返回的 4 个 ",[1029,8295,1852],{"code":1852}," 范围。",[1142,8298],{},[1599,8300,8302],{"id":8301},"错误-2越界写","错误 2：越界写",[1387,8304,8307],{"className":8305,"code":8306,"language":1607,"meta":1395},[1605],"*(ipw - 1000) = 0;\n*(ipw + 1000) = 0;\n",[1029,8308,8306],{"__ignoreMap":1395},[1387,8310,8313],{"className":8311,"code":8312,"language":1392},[1390],"坑 B：使用不属于你的内存\n坑 B2：数组\u002F堆块越界写\n",[1029,8314,8312],{"__ignoreMap":1395},[981,8316,8317,8318,8320],{},"这个和前面 p80 对应：",[1029,8319,2469],{"code":2469}," 库在堆里维护空闲块数据结构，写入未分配或已释放的内存可能破坏这些结构。",[1142,8322],{},[1599,8324,8325],{"id":8325},"正确写法",[981,8327,8328],{},"如果只申请 4 个元素，就只能访问 4 个：",[1387,8330,8333],{"className":8331,"code":8332,"language":1607,"meta":1395},[1605],"void ReadMem(void) {\n    int *ipr = malloc(4 * sizeof(*ipr));\n    if (ipr == NULL) return;\n\n    int i = ipr[0];\n    int j = ipr[3];\n\n    free(ipr);\n}\n",[1029,8334,8332],{"__ignoreMap":1395},[1142,8336],{},[1149,8338,8340],{"id":8339},"p105106堆管理故障覆盖全局指针导致泄漏","p105–106：堆管理故障——覆盖全局指针导致泄漏",[1599,8342,8258],{"id":8343},"题目代码-1",[1387,8345,8348],{"className":8346,"code":8347,"language":1607,"meta":1395},[1605],"int *pi;\n\nvoid foo() {\n    pi = malloc(8 * sizeof(int));\n    ...\n    free(pi);\n}\n\nvoid main() {\n    pi = malloc(4 * sizeof(int));\n    foo();\n    ...\n}\n",[1029,8349,8347],{"__ignoreMap":1395},[981,8351,8352,8353,8356,3630,8360,3630,8363,3630,8367,2170],{},"p105 问“这段代码有什么问题”，p106 明确说这是 ",[1247,8354,8355],{},"内存泄漏：",[1247,8357,8358],{},[1029,8359,2469],{"code":2469},[1247,8361,8362],{},"比",[1247,8364,8365],{},[1029,8366,1119],{"code":1119},[1247,8368,8369],{},"多",[1599,8371,8373,8374,8377],{"id":8372},"为什么-freepi-没救回来","为什么 ",[1029,8375,8376],{"code":8376},"free(pi)"," 没救回来？",[1387,8379,8382],{"className":8380,"code":8381,"language":1607,"meta":1395},[1605],"free(pi);\n",[1029,8383,8381],{"__ignoreMap":1395},[981,8385,2706,8386,8389],{},[1029,8387,8388],{"code":8388},"pi"," 指向的是堆块 B，所以释放的是 B。",[981,8391,8392],{},"但堆块 A 已经找不到了，无法释放。",[981,8394,8395],{},"所以整个过程是：",[1387,8397,8400],{"className":8398,"code":8399,"language":1392},[1390],"main malloc A\nfoo malloc B，覆盖 pi，A 泄漏\nfoo free B\nA 仍然泄漏\n",[1029,8401,8399],{"__ignoreMap":1395},[1142,8403],{},[1599,8405,8407],{"id":8406},"这题对应哪个坑","这题对应哪个坑？",[1387,8409,8412],{"className":8410,"code":8411,"language":1392},[1390],"主坑：坑 D，内存泄漏\n副坑：所有权规划不清\n",[1029,8413,8411],{"__ignoreMap":1395},[981,8415,8416],{},"也对应前面 p78 的原则：",[978,8418,8419],{},[981,8420,8421,3238,8423,8425,8426,8428,8429,8431],{},[1029,8422,2469],{"code":2469},[1029,8424,1119],{"code":1119}," 是好朋友。如果调用了 ",[1029,8427,2469],{"code":2469},"，就需要对结果调用 ",[1029,8430,1119],{"code":1119},"，否则就接受内存泄漏。",[1487,8433,8434],{"type":1489},[981,8435,8436,3238,8438,8440],{},[1029,8437,2469],{"code":2469},[1029,8439,1119],{"code":1119}," 的个数一般一样多",[1142,8442],{},[1149,8444,8446],{"id":8445},"p107108堆管理故障改变-malloc-返回的句柄","p107–108：堆管理故障——改变 malloc 返回的句柄",[1599,8448,8258],{"id":8449},"题目代码-2",[1387,8451,8454],{"className":8452,"code":8453,"language":1607,"meta":1395},[1605],"int *plk = NULL;\n\nvoid genPLK() {\n    plk = malloc(2 * sizeof(int));\n    ...\n    plk++;\n}\n",[1029,8455,8453],{"__ignoreMap":1395},[981,8457,8458],{},"p107 问“这段代码有什么问题”，p108 说这是潜在内存泄漏：句柄，也就是块指针，被改变了。",[1142,8460],{},[1599,8462,8464],{"id":8463},"句柄被改变","“句柄被改变”",[981,8466,8467,8469,8470,8472,8473,8475],{},[1029,8468,2469],{"code":2469}," 返回的地址必须保存好。",[1029,8471,1119],{"code":1119}," 只认",[1029,8474,2469],{"code":2469}," 的首地址",[981,8477,8478,8479,8481,8482,8485,8486,2170],{},"这题和前面 p72–74 是同一个坑：循环里把 ",[1029,8480,6156],{"code":6156}," 后，再 ",[1029,8483,8484],{"code":8484},"free(p)"," 是错的；要保留原始地址，最后 ",[1029,8487,8488],{"code":8488},"free(array)",[1142,8490],{},[1149,8492,8494],{"id":8493},"p109110堆管理故障非法-free-和-double-free","p109–110：堆管理故障——非法 free 和 double free",[1599,8496,8258],{"id":8497},"题目代码-3",[1387,8499,8502],{"className":8500,"code":8501,"language":1607,"meta":1395},[1605],"void FreeMemX() {\n    int fnh = 0;\n    free(&fnh);\n}\n\nvoid FreeMemY() {\n    int *fum = malloc(4 * sizeof(int));\n    free(fum + 1);\n    free(fum);\n    free(fum);\n}\n",[1029,8503,8501],{"__ignoreMap":1395},[981,8505,8506],{},"p109 问问题，p110 给出答案：不能释放非堆内存，不能释放未分配的内存。",[1142,8508],{},[1599,8510,8512],{"id":8511},"freememx-的问题","FreeMemX 的问题",[981,8514,8515,8518],{},[1029,8516,8517],{"code":8517},"fnh"," 是局部变量，在栈上。",[981,8520,2552,8521,8523,8524,8526],{},[1029,8522,1119],{"code":1119}," 只能释放由 ",[1029,8525,5966],{"code":5966}," 返回的堆内存。",[981,8528,8529,8530],{},"非法",[1029,8531,1119],{"code":1119},[1142,8533],{},[1599,8535,8537],{"id":8536},"freememy-的第一个问题free-中间地址","FreeMemY 的第一个问题：free 中间地址",[981,8539,8540,2710,8543,8545],{},[1029,8541,8542],{"code":8542},"fum",[1029,8544,2469],{"code":2469}," 返回的原始地址。",[981,8547,6240],{},[1387,8549,8552],{"className":8550,"code":8551,"language":1607,"meta":1395},[1605],"free(fum);\n",[1029,8553,8551],{"__ignoreMap":1395},[981,8555,6249],{},[1387,8557,8560],{"className":8558,"code":8559,"language":1607,"meta":1395},[1605],"free(fum + 1);\n",[1029,8561,8559],{"__ignoreMap":1395},[981,8563,2466,8564,8567],{},[1029,8565,8566],{"code":8566},"fum + 1"," 指向堆块中间，不是整块堆内存的起始地址。",[1387,8569,8572],{"className":8570,"code":8571,"language":1392},[1390],"坑 C：搞乱 malloc 返回的句柄\n坑 G：非法 free\n",[1029,8573,8571],{"__ignoreMap":1395},[1142,8575],{},[1599,8577,8579],{"id":8578},"freememy-的第二个问题double-free","FreeMemY 的第二个问题：double free",[1387,8581,8584],{"className":8582,"code":8583,"language":1607,"meta":1395},[1605],"free(fum);\nfree(fum);\n",[1029,8585,8583],{"__ignoreMap":1395},[981,8587,8588],{},"再次释放同一块已经释放过的内存，非法。",[981,8590,8591],{},"对应：",[1387,8593,8596],{"className":8594,"code":8595,"language":1392},[1390],"坑 G：重复 free\n坑 B：使用\u002F操作已释放内存\n",[1029,8597,8595],{"__ignoreMap":1395},[981,8599,8600],{},"double free 很危险，因为它可能破坏堆管理器的数据结构，造成崩溃或安全漏洞。",[1142,8602],{},[1599,8604,8325],{"id":8605},"正确写法-1",[1387,8607,8610],{"className":8608,"code":8609,"language":1607,"meta":1395},[1605],"void FreeMemY(void) {\n    int *fum = malloc(4 * sizeof(*fum));\n    if (fum == NULL) return;\n\n    ...\n\n    free(fum);\n    fum = NULL;\n}\n",[1029,8611,8609],{"__ignoreMap":1395},[981,8613,2857],{},[1387,8615,8618],{"className":8616,"code":8617,"language":1607,"meta":1395},[1605],"fum = NULL;\n",[1029,8619,8617],{"__ignoreMap":1395},[981,8621,8622],{},"可以降低重复使用已释放指针的风险。",[981,8624,8625],{},"这题对应前面 p78 的原则：",[1387,8627,8630],{"className":8628,"code":8629,"language":1392},[1390],"没有 malloc，就没有 free。\nfree 必须传 malloc 最初返回的地址。\n同一块内存只能 free 一次。\n",[1029,8631,8629],{"__ignoreMap":1395},[1142,8633],{},[1149,8635,8637,8638,8640],{"id":8636},"p111112字符串越界没有给-0-留空间","p111–112：字符串越界——没有给 ",[1029,8639,3281],{"code":3281}," 留空间",[1599,8642,8258],{"id":8643},"题目代码-4",[1387,8645,8648],{"className":8646,"code":8647,"language":1607,"meta":1395},[1605],"void StringManipulate() {\n    const char *name = \"Safety Critical\";\n    char *str = malloc(10);\n    strncpy(str, name, 10);\n    str[10] = '\\0';\n    printf(\"%s\\n\", str);\n}\n",[1029,8649,8647],{"__ignoreMap":1395},[981,8651,8652],{},"p111 问问题，p112 给出：这是超出数组边界的引用，既有越界写，也有越界读。",[1142,8654],{},[1599,8656,8657],{"id":8657},"逐步拆",[1387,8659,8662],{"className":8660,"code":8661,"language":1607,"meta":1395},[1605],"char *str = malloc(10);\n",[1029,8663,8661],{"__ignoreMap":1395},[981,8665,8666],{},"申请了 10 个字节。",[981,8668,8669],{},"合法下标是：",[1387,8671,8674],{"className":8672,"code":8673,"language":1392},[1390],"str[0] ~ str[9]\n",[1029,8675,8673],{"__ignoreMap":1395},[981,8677,8678],{},"没有：",[1387,8680,8683],{"className":8681,"code":8682,"language":1392},[1390],"str[10]\n",[1029,8684,8682],{"__ignoreMap":1395},[1142,8686],{},[1599,8688,8690],{"id":8689},"错误-1越界写","错误 1：越界写",[1387,8692,8695],{"className":8693,"code":8694,"language":1607,"meta":1395},[1605],"str[10] = '\\0';\n",[1029,8696,8694],{"__ignoreMap":1395},[981,8698,8699],{},"这是写第 11 个字节。",[981,8701,8702],{},"但是只申请了 10 字节。",[981,8704,2719],{},[1387,8706,8709],{"className":8707,"code":8708,"language":1392},[1390],"str[10] 越界\n",[1029,8710,8708],{"__ignoreMap":1395},[981,8712,8591],{},[1387,8714,8717],{"className":8715,"code":8716,"language":1392},[1390],"坑 B：使用不属于你的内存\n坑 H：字符串结尾 '\\0' 没有预留空间\n",[1029,8718,8716],{"__ignoreMap":1395},[1142,8720],{},[1599,8722,8724,8725,8728,8729],{"id":8723},"错误-2strncpy-不一定补-0","错误 2：",[1029,8726,8727],{"code":8727},"strncpy"," 不一定补 ",[1029,8730,3281],{"code":3281},[1387,8732,8735],{"className":8733,"code":8734,"language":1607,"meta":1395},[1605],"strncpy(str, name, 10);\n",[1029,8736,8734],{"__ignoreMap":1395},[981,8738,8739,8742],{},[1029,8740,8741],{"code":8741},"name"," 是：",[1387,8744,8747],{"className":8745,"code":8746,"language":1392},[1390],"\"Safety Critical\"\n",[1029,8748,8746],{"__ignoreMap":1395},[981,8750,8751],{},"长度明显超过 10。",[981,8753,8754,8757,8758,2170],{},[1029,8755,8756],{"code":8756},"strncpy(str, name, 10)"," 会复制前 10 个字符，但如果源字符串长度大于等于 10，它不会自动在目标末尾补 ",[1029,8759,3281],{"code":3281},[981,8761,8762,8763,8766],{},"所以复制后，",[1029,8764,8765],{"code":8765},"str"," 里可能是：",[1387,8768,8771],{"className":8769,"code":8770,"language":1392},[1390],"S a f e t y   C r i\n",[1029,8772,8770],{"__ignoreMap":1395},[981,8774,8775],{},"也就是 10 个字符，没有字符串终止符。",[1142,8777],{},[1599,8779,8781,8782,8785],{"id":8780},"错误-3printfs-可能越界读","错误 3：",[1029,8783,8784],{"code":8784},"printf(\"%s\")"," 可能越界读",[1387,8787,8790],{"className":8788,"code":8789,"language":1607,"meta":1395},[1605],"printf(\"%s\\n\", str);\n",[1029,8791,8789],{"__ignoreMap":1395},[981,8793,8794,8797,8798,8800,8801,2170],{},[1029,8795,8796],{"code":8796},"%s"," 会从 ",[1029,8799,8765],{"code":8765}," 开始一直读，直到遇到 ",[1029,8802,3281],{"code":3281},[981,8804,4038,8805,8807,8808,8810],{},[1029,8806,8765],{"code":8765}," 的合法范围里没有 ",[1029,8809,3281],{"code":3281},"，它就会继续往后读堆内存。",[981,8812,8813],{},"这对应：",[1387,8815,8818],{"className":8816,"code":8817,"language":1392},[1390],"坑 B：越界读\n坑 H：字符串没有正确以 '\\0' 结尾\n",[1029,8819,8817],{"__ignoreMap":1395},[1142,8821],{},[1599,8823,8825,8826],{"id":8824},"错误-4没有-freestr","错误 4：没有 ",[1029,8827,8828],{"code":8828},"free(str)",[981,8830,8831],{},"函数结束前没有：",[1387,8833,8836],{"className":8834,"code":8835,"language":1607,"meta":1395},[1605],"free(str);\n",[1029,8837,8835],{"__ignoreMap":1395},[981,8839,8840],{},"所以还有一个附加问题：",[1387,8842,8845],{"className":8843,"code":8844,"language":1392},[1390],"坑 D：内存泄漏\n",[1029,8846,8844],{"__ignoreMap":1395},[981,8848,8849],{},"虽然课件这一页重点讲越界，但你考试\u002F查 bug 时要顺手看到这个泄漏。",[1142,8851],{},[1599,8853,8855],{"id":8854},"正确写法-1申请-11-字节","正确写法 1：申请 11 字节",[981,8857,8858],{},"如果你只想复制最多 10 个字符并加终止符：",[1387,8860,8863],{"className":8861,"code":8862,"language":1607,"meta":1395},[1605],"void StringManipulate(void) {\n    const char *name = \"Safety Critical\";\n\n    char *str = malloc(11);\n    if (str == NULL) return;\n\n    strncpy(str, name, 10);\n    str[10] = '\\0';\n\n    printf(\"%s\\n\", str);\n\n    free(str);\n}\n",[1029,8864,8862],{"__ignoreMap":1395},[981,8866,8867],{},"这里申请 11 字节：",[1387,8869,8872],{"className":8870,"code":8871,"language":1392},[1390],"10 个有效字符 + 1 个 '\\0'\n",[1029,8873,8871],{"__ignoreMap":1395},[1142,8875],{},[1599,8877,8879],{"id":8878},"正确写法-2按源字符串长度申请","正确写法 2：按源字符串长度申请",[1387,8881,8884],{"className":8882,"code":8883,"language":1607,"meta":1395},[1605],"void StringManipulate(void) {\n    const char *name = \"Safety Critical\";\n\n    char *str = malloc(strlen(name) + 1);\n    if (str == NULL) return;\n\n    strcpy(str, name);\n    printf(\"%s\\n\", str);\n\n    free(str);\n}\n",[1029,8885,8883],{"__ignoreMap":1395},[981,8887,8888],{},"这题的核心句：",[1387,8890,8893],{"className":8891,"code":8892,"language":1392},[1390],"C 字符串的内存大小 = 字符数量 + 1 个 '\\0'。\n",[1029,8894,8892],{"__ignoreMap":1395},[1142,8896],{},[1149,8898,8900],{"id":8899},"p113114返回局部数组地址","p113–114：返回局部数组地址",[1599,8902,8258],{"id":8903},"题目代码-5",[1387,8905,8908],{"className":8906,"code":8907,"language":1607,"meta":1395},[1605],"char *append(const char* s1, const char *s2) {\n    const int MAXSIZE = 128;\n    char result[128];\n    int i = 0, j = 0;\n\n    for (j = 0; i \u003C MAXSIZE - 1 && j \u003C strlen(s1); i++, j++) {\n        result[i] = s1[j];\n    }\n\n    for (j = 0; i \u003C MAXSIZE - 1 && j \u003C strlen(s2); i++, j++) {\n        result[i] = s2[j];\n    }\n\n    result[++i] = '\\0';\n    return result;\n}\n",[1029,8909,8907],{"__ignoreMap":1395},[981,8911,8912,8913,8916],{},"p113 问问题，p114 明确说：",[1029,8914,8915],{"code":8915},"result"," 是本地数组名，分配在栈上；函数返回了指向栈内存的指针，函数返回后无效。",[1142,8918],{},[1149,8920,8922],{"id":8921},"主错误返回栈内存地址","主错误：返回栈内存地址",[1387,8924,8927],{"className":8925,"code":8926,"language":1607,"meta":1395},[1605],"char result[128];\n",[1029,8928,8926],{"__ignoreMap":1395},[981,8930,8931,8933,8934,5573],{},[1029,8932,8915],{"code":8915}," 是局部数组，存在当前函数 ",[1029,8935,8936],{"code":8936},"append",[981,8938,8939],{},"函数运行时：",[1387,8941,8944],{"className":8942,"code":8943,"language":1392},[1390],"append 的栈帧存在\nresult 存在\n",[1029,8945,8943],{"__ignoreMap":1395},[981,8947,8948],{},"函数返回后：",[1387,8950,8953],{"className":8951,"code":8952,"language":1392},[1390],"append 的栈帧销毁\nresult 失效\n",[1029,8954,8952],{"__ignoreMap":1395},[981,8956,8957],{},"但代码写：",[1387,8959,8962],{"className":8960,"code":8961,"language":1607,"meta":1395},[1605],"return result;\n",[1029,8963,8961],{"__ignoreMap":1395},[981,8965,8966],{},"这等价于返回：",[1387,8968,8971],{"className":8969,"code":8970,"language":1607,"meta":1395},[1605],"return &result[0];\n",[1029,8972,8970],{"__ignoreMap":1395},[981,8974,8975],{},"也就是把局部数组的地址交给外面。",[981,8977,8978],{},"外面拿到这个指针后，它指向的是已经失效的栈内存。",[981,8980,8813],{},[1387,8982,8985],{"className":8983,"code":8984,"language":1392},[1390],"坑 F：返回栈内存地址\n坑 B：使用不属于你的内存\n",[1029,8986,8984],{"__ignoreMap":1395},[981,8988,8989,8990,8993],{},"这和 p68 的 ",[1029,8991,8992],{"code":8992},"return &x;"," 是同类错误，只不过这里返回的是局部数组地址。",[1142,8995],{},[1149,8997,8999,9000],{"id":8998},"额外错误resulti-0","额外错误：",[1029,9001,9002],{"code":9002},"result[++i] = '\\0'",[981,9004,9005],{},"这个地方也有问题。",[981,9007,9008,9009,9011],{},"前两个循环结束后，",[1029,9010,7886],{"code":7886}," 已经是下一个应该写入的位置。",[981,9013,9014],{},"正确应该是：",[1387,9016,9019],{"className":9017,"code":9018,"language":1607,"meta":1395},[1605],"result[i] = '\\0';\n",[1029,9020,9018],{"__ignoreMap":1395},[981,9022,9023],{},"但代码写的是：",[1387,9025,9028],{"className":9026,"code":9027,"language":1607,"meta":1395},[1605],"result[++i] = '\\0';\n",[1029,9029,9027],{"__ignoreMap":1395},[981,9031,9032,9033,9035],{},"这会先让 ",[1029,9034,7886],{"code":7886}," 加 1，再写终止符。",[981,9037,9038],{},"问题有两个：",[1599,9040,9042],{"id":9041},"情况-1中间跳过一个位置","情况 1：中间跳过一个位置",[981,9044,9045],{},"假设拼接后长度是 5。\n循环结束时：",[1387,9047,9050],{"className":9048,"code":9049,"language":1392},[1390],"i = 5\n",[1029,9051,9049],{"__ignoreMap":1395},[981,9053,9054],{},"正确应该写：",[1387,9056,9059],{"className":9057,"code":9058,"language":1607,"meta":1395},[1605],"result[5] = '\\0';\n",[1029,9060,9058],{"__ignoreMap":1395},[981,9062,8957],{},[1387,9064,9066],{"className":9065,"code":9027,"language":1607,"meta":1395},[1605],[1029,9067,9027],{"__ignoreMap":1395},[981,9069,9070],{},"结果写到：",[1387,9072,9075],{"className":9073,"code":9074,"language":1392},[1390],"result[6]\n",[1029,9076,9074],{"__ignoreMap":1395},[981,9078,9079,9082],{},[1029,9080,9081],{"code":9081},"result[5]"," 没初始化，字符串中间可能出现垃圾字符。",[981,9084,8591],{},[1387,9086,9089],{"className":9087,"code":9088,"language":1392},[1390],"坑 A：使用未初始化值\n坑 H：字符串终止符位置错误\n",[1029,9090,9088],{"__ignoreMap":1395},[1599,9092,9094],{"id":9093},"情况-2可能越界写","情况 2：可能越界写",[981,9096,9097],{},"如果循环结束时：",[1387,9099,9102],{"className":9100,"code":9101,"language":1392},[1390],"i = 127\n",[1029,9103,9101],{"__ignoreMap":1395},[981,9105,9106],{},"因为条件是：",[1387,9108,9111],{"className":9109,"code":9110,"language":1607,"meta":1395},[1605],"i \u003C MAXSIZE - 1\n",[1029,9112,9110],{"__ignoreMap":1395},[981,9114,9115],{},"那么：",[1387,9117,9119],{"className":9118,"code":9027,"language":1607,"meta":1395},[1605],[1029,9120,9027],{"__ignoreMap":1395},[981,9122,9123],{},"会写：",[1387,9125,9128],{"className":9126,"code":9127,"language":1392},[1390],"result[128]\n",[1029,9129,9127],{"__ignoreMap":1395},[981,9131,9132],{},"但合法下标是：",[1387,9134,9137],{"className":9135,"code":9136,"language":1392},[1390],"result[0] ~ result[127]\n",[1029,9138,9136],{"__ignoreMap":1395},[981,9140,9141],{},"所以越界。",[981,9143,8591],{},[1387,9145,9148],{"className":9146,"code":9147,"language":1392},[1390],"坑 B：数组越界写\n坑 H：字符串结尾处理错误\n",[1029,9149,9147],{"__ignoreMap":1395},[1142,9151],{},[1149,9153,9155],{"id":9154},"正确写法-1让调用者提供缓冲区","正确写法 1：让调用者提供缓冲区",[981,9157,9158],{},"这是 C 里很常见、很稳的写法：",[1387,9160,9163],{"className":9161,"code":9162,"language":1607,"meta":1395},[1605],"void append(char *result, size_t maxsize,\n            const char *s1, const char *s2) {\n    size_t i = 0, j = 0;\n\n    for (j = 0; i \u003C maxsize - 1 && s1[j] != '\\0'; i++, j++) {\n        result[i] = s1[j];\n    }\n\n    for (j = 0; i \u003C maxsize - 1 && s2[j] != '\\0'; i++, j++) {\n        result[i] = s2[j];\n    }\n\n    result[i] = '\\0';\n}\n",[1029,9164,9162],{"__ignoreMap":1395},[981,9166,9167],{},"调用者：",[1387,9169,9172],{"className":9170,"code":9171,"language":1607,"meta":1395},[1605],"char buf[128];\nappend(buf, sizeof(buf), \"hello\", \"world\");\n",[1029,9173,9171],{"__ignoreMap":1395},[981,9175,9176],{},"好处是：",[1387,9178,9181],{"className":9179,"code":9180,"language":1392},[1390],"谁提供内存，谁管理内存。\nappend 只负责写，不负责分配。\n",[1029,9182,9180],{"__ignoreMap":1395},[1142,9184],{},[1149,9186,9188],{"id":9187},"正确写法-2在堆上分配让调用者-free","正确写法 2：在堆上分配，让调用者 free",[1387,9190,9193],{"className":9191,"code":9192,"language":1607,"meta":1395},[1605],"char *append(const char *s1, const char *s2) {\n    size_t len1 = strlen(s1);\n    size_t len2 = strlen(s2);\n\n    char *result = malloc(len1 + len2 + 1);\n    if (result == NULL) return NULL;\n\n    memcpy(result, s1, len1);\n    memcpy(result + len1, s2, len2);\n    result[len1 + len2] = '\\0';\n\n    return result;\n}\n",[1029,9194,9192],{"__ignoreMap":1395},[981,9196,9197],{},"调用者必须：",[1387,9199,9202],{"className":9200,"code":9201,"language":1607,"meta":1395},[1605],"char *s = append(\"hello\", \"world\");\n...\nfree(s);\n",[1029,9203,9201],{"__ignoreMap":1395},[981,9205,9206],{},"这题对应：",[1387,9208,9211],{"className":9209,"code":9210,"language":1392},[1390],"主坑：坑 F，返回栈内存地址\n副坑：坑 B，可能越界写\n副坑：坑 H，字符串 '\\0' 位置错误\n副坑：坑 D，如果改成 malloc 版本，调用者必须 free\n",[1029,9212,9210],{"__ignoreMap":1395},[1142,9214],{},[988,9216,9218,9219,9221],{"id":9217},"p115117realloc-返回值被忽略","p115–117：",[1029,9220,6757],{"code":6757}," 返回值被忽略",[1149,9223,8258],{"id":9224},"题目代码-6",[1387,9226,9229],{"className":9227,"code":9228,"language":1607,"meta":1395},[1605],"int* init_array(int *ptr, int new_size) {\n    ptr = realloc(ptr, new_size * sizeof(int));\n    memset(ptr, 0, new_size * sizeof(int));\n    return ptr;\n}\n\nint* fill_fibonacci(int *fib, int size) {\n    int i;\n    init_array(fib, size);\n\n    \u002F* fib[0] = 0; *\u002F\n    fib[1] = 1;\n\n    for (i = 2; i \u003C size; i++)\n        fib[i] = fib[i-1] + fib[i-2];\n\n    return fib;\n}\n",[1029,9230,9228],{"__ignoreMap":1395},[981,9232,9233,9234,9237,9238,9240],{},"p115 问问题，p116 给出提示：这是 ",[1029,9235,9236],{"code":9236},"mem"," 句柄使用不当；记住 ",[1029,9239,6757],{"code":6757}," 可能移动整块内存；如果数组被移动到新位置怎么办？p117 的 “我的东西呢？” 就是在吐槽：你重新分配后的地址去哪了？",[1142,9242],{},[1149,9244,5737,9246],{"id":9245},"先看-init_array",[1029,9247,9248],{"code":9248},"init_array",[1387,9250,9253],{"className":9251,"code":9252,"language":1607,"meta":1395},[1605],"ptr = realloc(ptr, new_size * sizeof(int));\n",[1029,9254,9252],{"__ignoreMap":1395},[981,9256,9257,9259],{},[1029,9258,6757],{"code":6757}," 可能有两种情况：",[1599,9261,9263],{"id":9262},"情况-1原地扩容","情况 1：原地扩容",[1387,9265,9268],{"className":9266,"code":9267,"language":1392},[1390],"旧地址不变\nptr 还是原来的地址\n",[1029,9269,9267],{"__ignoreMap":1395},[981,9271,9272],{},"这种情况下代码可能碰巧能跑。",[1599,9274,9276],{"id":9275},"情况-2搬到新位置","情况 2：搬到新位置",[1387,9278,9281],{"className":9279,"code":9280,"language":1392},[1390],"旧块释放\n新块分配到另一个地址\nptr 指向新地址\n",[1029,9282,9280],{"__ignoreMap":1395},[981,9284,9285],{},"这个才是关键。",[1142,9287],{},[1149,9289,9291,9292,9295],{"id":9290},"问题-1fill_fibonacci-忽略了返回值","问题 1：",[1029,9293,9294],{"code":9294},"fill_fibonacci"," 忽略了返回值",[1387,9297,9300],{"className":9298,"code":9299,"language":1607,"meta":1395},[1605],"init_array(fib, size);\n",[1029,9301,9299],{"__ignoreMap":1395},[981,9303,9304,9305,9307],{},"这里调用了 ",[1029,9306,9248],{"code":9248},"，但没有接住返回值。",[981,9309,4038,9310,9312],{},[1029,9311,6757],{"code":6757}," 把内存搬家了，那么：",[1387,9314,9317],{"className":9315,"code":9316,"language":1392},[1390],"init_array 里的 ptr 指向新地址\nfill_fibonacci 里的 fib 仍然是旧地址\n",[1029,9318,9316],{"__ignoreMap":1395},[981,9320,9321,9324,9325,9327,9328,2170],{},[1029,9322,9323],{"code":9323},"ptr"," 是参数副本。C 函数参数默认按值传递，所以 ",[1029,9326,9248],{"code":9248}," 改的是自己的局部副本，不会自动改外面的 ",[1029,9329,9330],{"code":9330},"fib",[981,9332,9333],{},"于是后面：",[1387,9335,9338],{"className":9336,"code":9337,"language":1607,"meta":1395},[1605],"fib[1] = 1;\n",[1029,9339,9337],{"__ignoreMap":1395},[981,9341,9342],{},"可能是在旧地址上写。",[981,9344,9345,9346,9348],{},"但旧地址可能已经被 ",[1029,9347,6757],{"code":6757}," 释放。",[981,9350,9351],{},"所以这对应：",[1387,9353,9356],{"className":9354,"code":9355,"language":1392},[1390],"坑 E：realloc 后旧指针失效\n坑 B：访问已释放\u002F不属于你的内存\n",[1029,9357,9355],{"__ignoreMap":1395},[1142,9359],{},[1149,9361,9363,9364,9366],{"id":9362},"问题-2如果-fib-原来是-null会更惨","问题 2：如果 ",[1029,9365,9330],{"code":9330}," 原来是 NULL，会更惨",[981,9368,9369],{},"假设调用时：",[1387,9371,9374],{"className":9372,"code":9373,"language":1607,"meta":1395},[1605],"int *fib = NULL;\nfill_fibonacci(fib, size);\n",[1029,9375,9373],{"__ignoreMap":1395},[981,9377,4201,9378,4204],{},[1029,9379,9248],{"code":9248},[1387,9381,9384],{"className":9382,"code":9383,"language":1607,"meta":1395},[1605],"ptr = realloc(NULL, size * sizeof(int));\n",[1029,9385,9383],{"__ignoreMap":1395},[981,9387,9388,9389,9391],{},"这相当于 ",[1029,9390,2469],{"code":2469},"，返回一块新内存。",[981,9393,5882,9394,9396,9397,9399,9400,2170],{},[1029,9395,9294],{"code":9294}," 没有接返回值，所以外面的 ",[1029,9398,9330],{"code":9330}," 还是 ",[1029,9401,1969],{"code":1969},[981,9403,2894],{},[1387,9405,9407],{"className":9406,"code":9337,"language":1607,"meta":1395},[1605],[1029,9408,9337],{"__ignoreMap":1395},[981,9410,9411],{},"就相当于：",[1387,9413,9416],{"className":9414,"code":9415,"language":1607,"meta":1395},[1605],"NULL[1] = 1;\n",[1029,9417,9415],{"__ignoreMap":1395},[981,9419,9420],{},"直接炸。",[981,9422,8591],{},[1387,9424,9427],{"className":9425,"code":9426,"language":1392},[1390],"坑 B：使用 NULL 指针\n坑 E：没有正确接收 realloc 返回的新地址\n",[1029,9428,9426],{"__ignoreMap":1395},[1142,9430],{},[1149,9432,9434,9435,9437],{"id":9433},"问题-3realloc-失败时会出事","问题 3：",[1029,9436,6757],{"code":6757}," 失败时会出事",[1387,9439,9442],{"className":9440,"code":9441,"language":1607,"meta":1395},[1605],"ptr = realloc(ptr, new_size * sizeof(int));\nmemset(ptr, 0, new_size * sizeof(int));\n",[1029,9443,9441],{"__ignoreMap":1395},[981,9445,4038,9446,9448,9449,2170],{},[1029,9447,6757],{"code":6757}," 失败，会返回 ",[1029,9450,1969],{"code":1969},[981,9452,9453],{},"此时：",[1387,9455,9458],{"className":9456,"code":9457,"language":1607,"meta":1395},[1605],"memset(ptr, 0, ...)\n",[1029,9459,9457],{"__ignoreMap":1395},[981,9461,9462],{},"就是：",[1387,9464,9467],{"className":9465,"code":9466,"language":1607,"meta":1395},[1605],"memset(NULL, 0, ...)\n",[1029,9468,9466],{"__ignoreMap":1395},[981,9470,9471],{},"可能直接崩溃。",[981,9473,9474],{},"而且这句：",[1387,9476,9479],{"className":9477,"code":9478,"language":1607,"meta":1395},[1605],"ptr = realloc(ptr, ...)\n",[1029,9480,9478],{"__ignoreMap":1395},[981,9482,9483],{},"还有一个经典危险：如果直接用原指针接返回值，一旦失败，原来的堆块地址可能丢失，造成泄漏。更稳妥应该用临时指针。",[981,9485,8591],{},[1387,9487,9490],{"className":9488,"code":9489,"language":1392},[1390],"坑 D：可能泄漏\n坑 B：可能使用 NULL\n坑 E：realloc 处理不当\n",[1029,9491,9489],{"__ignoreMap":1395},[1142,9493],{},[1149,9495,9497,9498,9501,9502,9505],{"id":9496},"问题-4size-2-时-fib1-越界","问题 4：",[1029,9499,9500],{"code":9500},"size \u003C 2"," 时 ",[1029,9503,9504],{"code":9504},"fib[1]"," 越界",[981,9507,9508],{},"代码写：",[1387,9510,9512],{"className":9511,"code":9337,"language":1607,"meta":1395},[1605],[1029,9513,9337],{"__ignoreMap":1395},[981,9515,4038,9516,9519,9520,9522],{},[1029,9517,9518],{"code":9518},"size"," 是 0 或 1，",[1029,9521,9504],{"code":9504}," 越界。",[981,9524,8591],{},[1387,9526,9529],{"className":9527,"code":9528,"language":1392},[1390],"坑 B：数组越界写\n",[1029,9530,9528],{"__ignoreMap":1395},[1142,9532],{},[1149,9534,8325],{"id":9535},"正确写法-2",[1387,9537,9540],{"className":9538,"code":9539,"language":1607,"meta":1395},[1605],"int* init_array(int *ptr, int new_size) {\n    int *tmp = realloc(ptr, new_size * sizeof(*ptr));\n    if (tmp == NULL) {\n        return NULL;\n    }\n\n    memset(tmp, 0, new_size * sizeof(*tmp));\n    return tmp;\n}\n\nint* fill_fibonacci(int *fib, int size) {\n    if (size \u003C= 0) return fib;\n\n    fib = init_array(fib, size);\n    if (fib == NULL) return NULL;\n\n    fib[0] = 0;\n\n    if (size > 1) {\n        fib[1] = 1;\n    }\n\n    for (int i = 2; i \u003C size; i++) {\n        fib[i] = fib[i-1] + fib[i-2];\n    }\n\n    return fib;\n}\n",[1029,9541,9539],{"__ignoreMap":1395},[981,9543,9544],{},"调用者也要接住返回值：",[1387,9546,9549],{"className":9547,"code":9548,"language":1607,"meta":1395},[1605],"fib = fill_fibonacci(fib, size);\n",[1029,9550,9548],{"__ignoreMap":1395},[981,9552,9553],{},"这题是最后几页里最综合的一题，对应：",[1387,9555,9558],{"className":9556,"code":9557,"language":1392},[1390],"主坑：坑 E，realloc 可能移动内存\n副坑：坑 B，旧指针可能访问已释放内存\n副坑：坑 D，realloc 失败可能泄漏\n副坑：坑 B，size \u003C 2 时越界\n副坑：C 参数按值传递，函数里改 ptr 不会自动改外面的 fib\n",[1029,9559,9557],{"__ignoreMap":1395},[981,9561,5457],{},[1387,9563,9566],{"className":9564,"code":9565,"language":1392},[1390],"realloc 的返回值必须接住，而且要用新地址继续操作。\n",[1029,9567,9565],{"__ignoreMap":1395},[1142,9569],{},[988,9571,9573],{"id":9572},"p118java-中的内存泄漏","p118：Java 中的内存“泄漏”",[981,9575,9576],{},"这一页不是 C 代码题，而是对比 Java 和 C。",[981,9578,9579],{},"课件说，Java 里也可能有“内存泄漏”：如果你意外保留了对未使用对象的引用，垃圾回收器就不会回收它，最终也可能导致内存不足。",[1142,9581],{},[1149,9583,9585],{"id":9584},"java-为什么也会-leak","Java 为什么也会 leak？",[981,9587,3694],{},[1387,9589,9594],{"className":9590,"code":9592,"language":9593,"meta":1395},[9591],"language-java","static List\u003CObject> list = new ArrayList\u003C>();\n\nvoid f() {\n    Object obj = new Object();\n    list.add(obj);\n}\n","java",[1029,9595,9592],{"__ignoreMap":1395},[981,9597,4038,9598,9601],{},[1029,9599,9600],{"code":9600},"list"," 一直存在，里面的对象引用也一直存在，那么 GC 会认为：",[1387,9603,9606],{"className":9604,"code":9605,"language":1392},[1390],"这些对象还能被访问\n不能回收\n",[1029,9607,9605],{"__ignoreMap":1395},[981,9609,9610],{},"哪怕你逻辑上已经不需要它们了，它们也不会被释放。",[981,9612,9613],{},"这就是 Java 的“内存泄漏”。",[981,9615,8591],{},[1387,9617,9620],{"className":9618,"code":9619,"language":1392},[1390],"坑 D 的 Java 版本：逻辑上不用了，但引用还在，GC 无法回收\n",[1029,9621,9619],{"__ignoreMap":1395},[1142,9623],{},[1149,9625,9627],{"id":9626},"java-消除了哪些-c-的坑","Java 消除了哪些 C 的坑？",[981,9629,9630],{},"课件列了几类 Java 基本消除的 C 错误：",[1387,9632,9635],{"className":9633,"code":9634,"language":1392},[1390],"1. 使用无效参数调用 free\n2. 访问已释放内存\n3. 数组越界访问\n4. 访问未分配内存，忘记 new\n",[1029,9636,9634],{"__ignoreMap":1395},[981,9638,9639,9640,9642],{},"Java 没有手动 ",[1029,9641,1119],{"code":1119},"，所以没有：",[1387,9644,9647],{"className":9645,"code":9646,"language":1392},[1390],"free(&局部变量)\nfree(p + 1)\ndouble free\n",[1029,9648,9646],{"__ignoreMap":1395},[981,9650,9651],{},"数组越界会抛异常，而不是悄悄改坏旁边的数据。",[981,9653,9654,9655,9658],{},"空引用会抛 ",[1029,9656,9657],{"code":9657},"NullPointerException","，虽然也是错误，但至少通常不会静默破坏数据。",[981,9660,9661],{},"所以 p118 的核心不是“Java 没有内存问题”，而是：",[1387,9663,9666],{"className":9664,"code":9665,"language":1392},[1390],"Java 仍可能泄漏，但少了很多 C 里会静默损坏内存的坑。\n",[1029,9667,9665],{"__ignoreMap":1395},[981,9669,9670],{},"对应关系：",[1387,9672,9675],{"className":9673,"code":9674,"language":1392},[1390],"Java 仍有：坑 D，内存泄漏\nJava 大幅避免：坑 B、坑 C、坑 E、坑 G\n",[1029,9676,9674],{"__ignoreMap":1395},[1142,9678],{},[988,9680,9682],{"id":9681},"p119使用堆的例子怎么除掉苹果树","p119：使用堆的例子——怎么除掉苹果树？",[981,9684,9685],{},"这一页接的是前面堆上创建树的例子。课件问：",[1387,9687,9690],{"className":9688,"code":9689,"language":1392},[1390],"我们怎样才能除掉苹果树？\n",[1029,9691,9689],{"__ignoreMap":1395},[981,9693,9694,9695,9697],{},"意思是：如果你用 ",[1029,9696,2469],{"code":2469}," 一个节点一个节点创建了一棵树，最后怎么释放整棵树？",[981,9699,9700],{},"p119 明确说，需要一种方法释放不再使用的内存，否则最终可能耗尽。",[1142,9702],{},[1149,9704,9706,9707,6352],{"id":9705},"为什么不能只-freeroot","为什么不能只 ",[1029,9708,9709],{"code":9709},"free(root)",[981,9711,9712],{},"假设树是：",[1387,9714,9717],{"className":9715,"code":9716,"language":1392},[1390],"        10\n       \u002F  \\\n      5    16\n          \u002F\n         11\n",[1029,9718,9716],{"__ignoreMap":1395},[981,9720,9721,9722,9724],{},"每个节点都是单独 ",[1029,9723,2469],{"code":2469}," 出来的。",[981,9726,9727],{},"如果你只写：",[1387,9729,9732],{"className":9730,"code":9731,"language":1607,"meta":1395},[1605],"free(root);\n",[1029,9733,9731],{"__ignoreMap":1395},[981,9735,9736,9737,2170],{},"你只释放了根节点 ",[1029,9738,9739],{"code":9739},"10",[981,9741,9742],{},"但是：",[1387,9744,9747],{"className":9745,"code":9746,"language":1392},[1390],"5、16、11 这些节点还在堆上\n",[1029,9748,9746],{"__ignoreMap":1395},[981,9750,9751],{},"而且根节点释放后，你再也找不到它们了。",[981,9753,9754],{},"这就是：",[1387,9756,9758],{"className":9757,"code":8844,"language":1392},[1390],[1029,9759,8844],{"__ignoreMap":1395},[1142,9761],{},[1149,9763,9765],{"id":9764},"为什么不能先-free-根再-free-子树","为什么不能先 free 根，再 free 子树？",[981,9767,9768],{},"错误写法：",[1387,9770,9773],{"className":9771,"code":9772,"language":1607,"meta":1395},[1605],"void free_tree(Node *root) {\n    free(root);\n    free_tree(root->left);\n    free_tree(root->right);\n}\n",[1029,9774,9772],{"__ignoreMap":1395},[981,9776,9777],{},"这个错得很典型。",[981,9779,9780],{},"因为：",[1387,9782,9784],{"className":9783,"code":9731,"language":1607,"meta":1395},[1605],[1029,9785,9731],{"__ignoreMap":1395},[981,9787,6945,9788,9791],{},[1029,9789,9790],{"code":9790},"root"," 指向的内存已经不属于你。",[981,9793,9794],{},"再访问：",[1387,9796,9799],{"className":9797,"code":9798,"language":1607,"meta":1395},[1605],"root->left\nroot->right\n",[1029,9800,9798],{"__ignoreMap":1395},[981,9802,9803],{},"就是 use-after-free。",[981,9805,8591],{},[1387,9807,9810],{"className":9808,"code":9809,"language":1392},[1390],"坑 B：访问已释放内存\n",[1029,9811,9809],{"__ignoreMap":1395},[1142,9813],{},[1149,9815,9817],{"id":9816},"正确释放树后序遍历","正确释放树：后序遍历",[981,9819,9820],{},"应该先释放左右子树，再释放自己：",[1387,9822,9825],{"className":9823,"code":9824,"language":1607,"meta":1395},[1605],"void free_tree(Node *root) {\n    if (root == NULL) {\n        return;\n    }\n\n    free_tree(root->left);\n    free_tree(root->right);\n\n    free(root);\n}\n",[1029,9826,9824],{"__ignoreMap":1395},[981,9828,9829],{},"为什么是这个顺序？",[1387,9831,9834],{"className":9832,"code":9833,"language":1392},[1390],"先释放 left\n再释放 right\n最后释放 root\n",[1029,9835,9833],{"__ignoreMap":1395},[981,9837,9838,9839,9841],{},"因为在释放 ",[1029,9840,9790],{"code":9790}," 之前，你还需要通过：",[1387,9843,9845],{"className":9844,"code":9798,"language":1607,"meta":1395},[1605],[1029,9846,9798],{"__ignoreMap":1395},[981,9848,9849],{},"找到左右孩子。",[981,9851,9852],{},"这叫后序遍历。",[1142,9854],{},[1149,9856,9858],{"id":9857},"释放后最好把-root-设为-null","释放后最好把 root 设为 NULL",[981,9860,9861],{},"如果你在外面有：",[1387,9863,9866],{"className":9864,"code":9865,"language":1607,"meta":1395},[1605],"Node *root = NULL;\ninsert(10, &root);\ninsert(5, &root);\n...\n",[1029,9867,9865],{"__ignoreMap":1395},[981,9869,9870],{},"释放时可以：",[1387,9872,9875],{"className":9873,"code":9874,"language":1607,"meta":1395},[1605],"free_tree(root);\nroot = NULL;\n",[1029,9876,9874],{"__ignoreMap":1395},[981,9878,9879],{},"这样之后如果误用：",[1387,9881,9884],{"className":9882,"code":9883,"language":1607,"meta":1395},[1605],"root->key\n",[1029,9885,9883],{"__ignoreMap":1395},[981,9887,9888],{},"更容易暴露为空指针错误，而不是悄悄访问已释放内存。",[1142,9890],{},[1149,9892,9894],{"id":9893},"如果想在函数里把-root-也清-null","如果想在函数里把 root 也清 NULL",[981,9896,9897],{},"可以传二级指针：",[1387,9899,9902],{"className":9900,"code":9901,"language":1607,"meta":1395},[1605],"void destroy_tree(Node **rootp) {\n    if (rootp == NULL || *rootp == NULL) {\n        return;\n    }\n\n    Node *root = *rootp;\n\n    destroy_tree(&root->left);\n    destroy_tree(&root->right);\n\n    free(root);\n    *rootp = NULL;\n}\n",[1029,9903,9901],{"__ignoreMap":1395},[981,9905,2839],{},[1387,9907,9910],{"className":9908,"code":9909,"language":1607,"meta":1395},[1605],"destroy_tree(&root);\n",[1029,9911,9909],{"__ignoreMap":1395},[981,9913,9914,9915,9918],{},"这和前面 ",[1029,9916,9917],{"code":9917},"insert(int key, Node **tree)"," 的思路一样：如果函数要修改外面的指针本身，就传指针的地址。",[981,9920,9921],{},"p119 对应的坑：",[1387,9923,9926],{"className":9924,"code":9925,"language":1392},[1390],"主坑：坑 D，树节点不释放会泄漏\n副坑：坑 B，释放根后再访问孩子是 use-after-free\n对应原则：谁 malloc，谁规划 free；复杂结构要递归释放\n",[1029,9927,9925],{"__ignoreMap":1395},[1142,9929],{},[988,9931,9933],{"id":9932},"最后-102119-页总表","最后 102–119 页总表",[992,9935,9936,9951],{},[995,9937,9938],{},[998,9939,9940,9942,9945,9948],{},[1001,9941,1006],{},[1001,9943,9944],{},"题目\u002F内容",[1001,9946,9947],{},"主要错误",[1001,9949,9950],{},"对应坑",[1014,9952,9953,9974,9993,10012,10032,10052,10068,10085,10099],{},[998,9954,9955,9958,9966,9972],{},[1019,9956,9957],{},"p103–104",[1019,9959,9960,1204,9963],{},[1029,9961,9962],{"code":9962},"ipr ± 1000",[1029,9964,9965],{"code":9965},"ipw ± 1000",[1019,9967,9968,9969,9971],{},"超出 ",[1029,9970,2469],{"code":2469}," 范围读写",[1019,9973,8136],{},[998,9975,9976,9979,9988,9991],{},[1019,9977,9978],{},"p105–106",[1019,9980,9981,9982,9984,9985,9987],{},"全局 ",[1029,9983,8388],{"code":8388}," 被第二次 ",[1029,9986,2469],{"code":2469}," 覆盖",[1019,9989,9990],{},"原来的堆块丢失",[1019,9992,8167],{},[998,9994,9995,9998,10003,10009],{},[1019,9996,9997],{},"p107–108",[1019,9999,10000],{},[1029,10001,10002],{"code":10002},"plk++",[1019,10004,10005,10006,10008],{},"改掉 ",[1029,10007,2469],{"code":2469}," 原始句柄",[1019,10010,10011],{},"坑 C、坑 D、坑 G",[998,10013,10014,10017,10026,10029],{},[1019,10015,10016],{},"p109–110",[1019,10018,10019,1204,10022,10025],{},[1029,10020,10021],{"code":10021},"free(&fnh)",[1029,10023,10024],{"code":10024},"free(fum+1)",", double free",[1019,10027,10028],{},"非法释放、重复释放",[1019,10030,10031],{},"坑 G、坑 C",[998,10033,10034,10037,10046,10049],{},[1019,10035,10036],{},"p111–112",[1019,10038,10039,10042,10043],{},[1029,10040,10041],{"code":10041},"malloc(10)"," 后 ",[1029,10044,10045],{"code":10045},"str[10]='\\0'",[1019,10047,10048],{},"字符串终止符越界",[1019,10050,10051],{},"坑 B、坑 H、坑 D",[998,10053,10054,10057,10062,10065],{},[1019,10055,10056],{},"p113–114",[1019,10058,10059],{},[1029,10060,10061],{"code":10061},"return result",[1019,10063,10064],{},"返回局部数组地址",[1019,10066,10067],{},"坑 F、坑 B、坑 H",[998,10069,10070,10073,10079,10082],{},[1019,10071,10072],{},"p115–117",[1019,10074,10075,10076,10078],{},"忽略 ",[1029,10077,6757],{"code":6757}," 返回值",[1019,10080,10081],{},"新地址丢失，旧指针失效",[1019,10083,10084],{},"坑 E、坑 B、坑 D",[998,10086,10087,10090,10093,10096],{},[1019,10088,10089],{},"p118",[1019,10091,10092],{},"Java 内存泄漏",[1019,10094,10095],{},"保留无用引用，GC 无法回收",[1019,10097,10098],{},"坑 D 的 Java 版本",[998,10100,10101,10104,10107,10110],{},[1019,10102,10103],{},"p119",[1019,10105,10106],{},"释放堆上的树",[1019,10108,10109],{},"复杂结构要递归 free",[1019,10111,10112],{},"坑 D、坑 B",[981,10114,10115],{},"你最后可以用这句话收束整章：",[1387,10117,10120],{"className":10118,"code":10119,"language":1392},[1390],"C 内存错误的本质不是“不会写 malloc\u002Ffree”，\n而是没搞清楚：这块内存是谁的、现在还活着吗、原始地址还在吗、谁负责释放。\n",[1029,10121,10119],{"__ignoreMap":1395},[1142,10123],{},{"title":1395,"searchDepth":10125,"depth":10125,"links":10126},4,[10127,10128,10156,10179,10214,10252,10276,10326,10344,10348,10356],{"id":990,"depth":1935,"text":990},{"id":1146,"depth":1935,"text":1147,"children":10129},[10130,10131,10132,10133,10134,10135,10136,10137,10138,10142,10143,10144,10145,10149,10150,10151,10152,10153,10154,10155],{"id":1151,"depth":2452,"text":1152},{"id":1262,"depth":2452,"text":1263},{"id":1277,"depth":2452,"text":1278},{"id":1321,"depth":2452,"text":1322},{"id":1415,"depth":2452,"text":1416},{"id":1500,"depth":2452,"text":1501},{"id":1534,"depth":2452,"text":1535},{"id":1552,"depth":2452,"text":1553},{"id":1571,"depth":2452,"text":1572,"children":10139},[10140,10141],{"id":1601,"depth":10125,"text":1601},{"id":1623,"depth":10125,"text":1623},{"id":1672,"depth":2452,"text":1673},{"id":1707,"depth":2452,"text":1708},{"id":1738,"depth":2452,"text":1739},{"id":1765,"depth":2452,"text":1766,"children":10146},[10147,10148],{"id":1769,"depth":10125,"text":1770},{"id":1785,"depth":10125,"text":1786},{"id":1835,"depth":2452,"text":1836},{"id":1898,"depth":2452,"text":1899},{"id":1916,"depth":2452,"text":1917},{"id":1987,"depth":2452,"text":1988},{"id":2017,"depth":2452,"text":2018},{"id":2037,"depth":2452,"text":2038},{"id":2057,"depth":2452,"text":2058},{"id":2068,"depth":1935,"text":2069,"children":10157},[10158,10159,10160,10161,10162,10163,10164,10168,10169,10173,10174,10178],{"id":2088,"depth":2452,"text":2089},{"id":2186,"depth":2452,"text":2187},{"id":2279,"depth":2452,"text":2280},{"id":2381,"depth":2452,"text":2382},{"id":2402,"depth":2452,"text":2403},{"id":2489,"depth":2452,"text":2490},{"id":2597,"depth":2452,"text":2598,"children":10165},[10166,10167],{"id":2610,"depth":10125,"text":2611},{"id":2620,"depth":10125,"text":2621},{"id":2763,"depth":2452,"text":2764},{"id":2796,"depth":2452,"text":2797,"children":10170},[10171,10172],{"id":2821,"depth":10125,"text":2611},{"id":2830,"depth":10125,"text":2621},{"id":2965,"depth":2452,"text":2966},{"id":3052,"depth":2452,"text":3053,"children":10175},[10176,10177],{"id":3064,"depth":10125,"text":2611},{"id":3073,"depth":10125,"text":2620},{"id":3251,"depth":2452,"text":3252},{"id":3266,"depth":1935,"text":3267,"children":10180},[10181,10182,10183,10184,10185,10187,10188,10189,10190,10191,10192,10193,10195,10196,10197,10198,10199,10204,10205,10206,10210,10212,10213],{"id":3304,"depth":2452,"text":3305},{"id":3360,"depth":2452,"text":3361},{"id":3406,"depth":2452,"text":3407},{"id":3436,"depth":2452,"text":3437},{"id":3516,"depth":2452,"text":10186},"p41：sizeof() 操作符",{"id":3590,"depth":2452,"text":3591},{"id":3642,"depth":2452,"text":3643},{"id":3708,"depth":2452,"text":3709},{"id":3976,"depth":2452,"text":3977},{"id":3996,"depth":2452,"text":3997},{"id":4102,"depth":2452,"text":4103},{"id":4173,"depth":2452,"text":10194},"p48：sizeof(array) 在函数里为什么变了？",{"id":4269,"depth":2452,"text":4270},{"id":4302,"depth":2452,"text":4303},{"id":4578,"depth":2452,"text":4579},{"id":4605,"depth":2452,"text":4606},{"id":4668,"depth":2452,"text":10200,"children":10201},"p53：练习题——int **pp",[10202,10203],{"id":4684,"depth":10125,"text":2611},{"id":4693,"depth":10125,"text":2621},{"id":4872,"depth":2452,"text":4873},{"id":4986,"depth":2452,"text":4987},{"id":5023,"depth":2452,"text":10207,"children":10208},"p56：简洁的 strlen()",[10209],{"id":5057,"depth":10125,"text":5057},{"id":5131,"depth":2452,"text":10211},"p57：main() 的参数",{"id":5197,"depth":2452,"text":5198},{"id":5284,"depth":2452,"text":5285},{"id":5308,"depth":1935,"text":5309,"children":10215},[10216,10217,10218,10219,10220,10221,10222,10226,10227,10228,10229,10231,10232,10234,10235,10237,10238,10239,10241,10242,10244,10246,10248,10249,10251],{"id":5335,"depth":2452,"text":5336},{"id":5415,"depth":2452,"text":5416},{"id":5469,"depth":2452,"text":5470},{"id":5538,"depth":2452,"text":5539},{"id":5603,"depth":2452,"text":5604},{"id":5660,"depth":2452,"text":5661},{"id":5699,"depth":2452,"text":5700,"children":10223},[10224,10225],{"id":5725,"depth":10125,"text":2611},{"id":5734,"depth":10125,"text":2621},{"id":5839,"depth":2452,"text":5840},{"id":5911,"depth":2452,"text":5912},{"id":6022,"depth":2452,"text":6023},{"id":6048,"depth":2452,"text":10230},"p71：malloc()",{"id":6127,"depth":2452,"text":6128},{"id":6211,"depth":2452,"text":10233},"p73：free()",{"id":6288,"depth":2452,"text":6289},{"id":6348,"depth":2452,"text":10236},"p75：为什么调用 free()？",{"id":6424,"depth":2452,"text":6425},{"id":6445,"depth":2452,"text":6446},{"id":6471,"depth":2452,"text":10240},"p78：malloc 和 free 是好朋友",{"id":6535,"depth":2452,"text":6536},{"id":6561,"depth":2452,"text":10243},"p80：malloc\u002Ffree 是如何实现的？",{"id":6615,"depth":2452,"text":10245},"p81：简单的 malloc() 实现",{"id":6661,"depth":2452,"text":10247},"p82：更好的 malloc 实现",{"id":6699,"depth":2452,"text":6700},{"id":6748,"depth":2452,"text":10250},"p84：realloc(p, size)",{"id":6800,"depth":2452,"text":6801},{"id":6845,"depth":1935,"text":6846,"children":10253},[10254,10255,10256,10257,10258,10260,10261,10266,10267,10268,10269,10270,10271,10272,10273,10274,10275],{"id":6866,"depth":2452,"text":6867},{"id":6905,"depth":2452,"text":6906},{"id":7022,"depth":2452,"text":7023},{"id":7133,"depth":2452,"text":7134},{"id":7202,"depth":2452,"text":10259},"p89：realloc 之后旧别名指针失效",{"id":7302,"depth":2452,"text":7303},{"id":7323,"depth":2452,"text":7324,"children":10262},[10263,10265],{"id":7336,"depth":10125,"text":10264},"例子 1：x² ≥ 0 一定成立吗？",{"id":7380,"depth":10125,"text":7381},{"id":7449,"depth":2452,"text":7450},{"id":7467,"depth":2452,"text":7468},{"id":7526,"depth":2452,"text":7527},{"id":7621,"depth":2452,"text":7622},{"id":7695,"depth":2452,"text":7696},{"id":7771,"depth":2452,"text":7772},{"id":7820,"depth":2452,"text":7821},{"id":7921,"depth":2452,"text":7922},{"id":7932,"depth":2452,"text":7933},{"id":7975,"depth":2452,"text":7976},{"id":8095,"depth":1935,"text":8096,"children":10277},[10278,10284,10290,10294,10301,10315,10318,10319,10324,10325],{"id":8254,"depth":2452,"text":8255,"children":10279},[10280,10281,10282,10283],{"id":8258,"depth":10125,"text":8258},{"id":8267,"depth":10125,"text":8268},{"id":8301,"depth":10125,"text":8302},{"id":8325,"depth":10125,"text":8325},{"id":8339,"depth":2452,"text":8340,"children":10285},[10286,10287,10289],{"id":8343,"depth":10125,"text":8258},{"id":8372,"depth":10125,"text":10288},"为什么 free(pi) 没救回来？",{"id":8406,"depth":10125,"text":8407},{"id":8445,"depth":2452,"text":8446,"children":10291},[10292,10293],{"id":8449,"depth":10125,"text":8258},{"id":8463,"depth":10125,"text":8464},{"id":8493,"depth":2452,"text":8494,"children":10295},[10296,10297,10298,10299,10300],{"id":8497,"depth":10125,"text":8258},{"id":8511,"depth":10125,"text":8512},{"id":8536,"depth":10125,"text":8537},{"id":8578,"depth":10125,"text":8579},{"id":8605,"depth":10125,"text":8325},{"id":8636,"depth":2452,"text":10302,"children":10303},"p111–112：字符串越界——没有给 '\\0' 留空间",[10304,10305,10306,10307,10309,10311,10313,10314],{"id":8643,"depth":10125,"text":8258},{"id":8657,"depth":10125,"text":8657},{"id":8689,"depth":10125,"text":8690},{"id":8723,"depth":10125,"text":10308},"错误 2：strncpy 不一定补 '\\0'",{"id":8780,"depth":10125,"text":10310},"错误 3：printf(\"%s\") 可能越界读",{"id":8824,"depth":10125,"text":10312},"错误 4：没有 free(str)",{"id":8854,"depth":10125,"text":8855},{"id":8878,"depth":10125,"text":8879},{"id":8899,"depth":2452,"text":8900,"children":10316},[10317],{"id":8903,"depth":10125,"text":8258},{"id":8921,"depth":2452,"text":8922},{"id":8998,"depth":2452,"text":10320,"children":10321},"额外错误：result[++i] = '\\0'",[10322,10323],{"id":9041,"depth":10125,"text":9042},{"id":9093,"depth":10125,"text":9094},{"id":9154,"depth":2452,"text":9155},{"id":9187,"depth":2452,"text":9188},{"id":9217,"depth":1935,"text":10327,"children":10328},"p115–117：realloc 返回值被忽略",[10329,10330,10335,10337,10339,10341,10343],{"id":9224,"depth":2452,"text":8258},{"id":9245,"depth":2452,"text":10331,"children":10332},"先看 init_array",[10333,10334],{"id":9262,"depth":10125,"text":9263},{"id":9275,"depth":10125,"text":9276},{"id":9290,"depth":2452,"text":10336},"问题 1：fill_fibonacci 忽略了返回值",{"id":9362,"depth":2452,"text":10338},"问题 2：如果 fib 原来是 NULL，会更惨",{"id":9433,"depth":2452,"text":10340},"问题 3：realloc 失败时会出事",{"id":9496,"depth":2452,"text":10342},"问题 4：size \u003C 2 时 fib[1] 越界",{"id":9535,"depth":2452,"text":8325},{"id":9572,"depth":1935,"text":9573,"children":10345},[10346,10347],{"id":9584,"depth":2452,"text":9585},{"id":9626,"depth":2452,"text":9627},{"id":9681,"depth":1935,"text":9682,"children":10349},[10350,10352,10353,10354,10355],{"id":9705,"depth":2452,"text":10351},"为什么不能只 free(root)？",{"id":9764,"depth":2452,"text":9765},{"id":9816,"depth":2452,"text":9817},{"id":9857,"depth":2452,"text":9858},{"id":9893,"depth":2452,"text":9894},{"id":9932,"depth":1935,"text":9933},[903],"C 的编译与解释、寄存器与内存布局、指针与数组、栈与堆内存管理笔记","md",null,{"slots":10362},{},{"text":841,"minutes":842,"time":843,"words":844},{"title":839,"description":10358},{"loc":838,"images":10366},[10367,10368,10369,10370,10371,10372,10373,10374,10375,10376,10377,10378,10379,10380,10381,10382,10383,10384,10385,10386],{"loc":1158},{"loc":1327},{"loc":1558},{"loc":1589},{"loc":1597},{"loc":1713},{"loc":1760},{"loc":2063},{"loc":2097},{"loc":3985},{"loc":4992},{"loc":5341},{"loc":5612},{"loc":6454},{"loc":6651},{"loc":7037},{"loc":7044},{"loc":7634},{"loc":7829},{"loc":7927},[10388,10389],"计算机组成原理","课程笔记","tech","pn-RBTBgnP7a0t3dI8MJT0auq0I-qamnV_z2qqymL8A",[10393,10394],{"title":834,"path":833,"stem":832,"date":653,"type":10390,"children":-1},{"title":848,"path":847,"stem":846,"date":653,"type":10390,"children":-1},1789057280745]