☰
C语言Day10复盘:数组、字符串与指针入门实战
2026/10/7 5:05:54 网站建设 项目流程

不用刻意写一个完整的大标题,直接从第一篇的学习日志切入。这个“Day10”不是某一天的流水账,而是一个C语言初学者在语法关和思维关交界处的真实记录:前面学了变量、分支、循环、函数,到了第十天终于要正面碰数组、字符串和指针。今天这篇就把我当天学到的核心内容、踩过的坑和顺手做掉的小练习题完整复盘一遍。

1. Day10在C语言学习路线里的位置:告别“玩具代码”的分水岭

前九天我基本是按照市面上最常见的C语言课程节奏在走:先搞懂数据类型和变量定义,再啃printf和scanf的格式控制,然后是if/else、switch、while和do-while的区别、for循环的嵌套,最后学了函数的基本写法。说实话,到昨天为止,我写出来的所有程序都还是“顺序执行+分支+循环”的套路,处理的数据量最多也就是几个变量来回倒腾。

第十天之所以特殊,是因为我要第一次面对真正意义上的“批量数据”和“内存操作”了。从这一天开始,程序不再只是“输入一个数、算一下、输出一个结果”,而是要处理一整组数、一整串字符,甚至是直接去读写内存地址。如果你也在学C语言,你会发现一个规律:前面几天的内容再怎么学,总感觉还是在用“高级语言思维”写程序,只有当你开始碰数组和指针,你才真正开始用“C语言思维”写程序。

今天的核心任务其实可以拆成三块:数组的定义与遍历、字符串的本质(字符数组)、指针的入门概念。这三块内容在谭浩强第六版里差不多是第5章到第8章的内容,但在翁恺老师的课程里,第十天左右恰好正是数组和指针交替出现的高峰期。如果你用的是其他教材,大体节奏也类似——语法上它们互相依赖,学习上也必须放在一起理解。

我之前一直有个误解:觉得数组很简单,不就是“一堆同类型变量的集合”吗?真上手才发现,数组的难点根本不在“定义”和“初始化”,而在“下标到底从几开始”“数组越界了会怎样”“数组名和指针到底什么关系”这些细节上。今天一天下来,我的直观感受是:数组这个知识点本身不难,但它牵扯出来的内存布局和指针概念才是真正的门槛。这也是为什么几乎所有C语言学习路线都会把Day10前后安排成“理解程序的存储方式”的关键节点。

如果你现在正好也学到这里,我建议你先有个心理准备:从今天起,单纯“背语法”已经不够了,你得学会在脑子里画内存图。每定义一个变量,都要知道它在内存里占了几个字节、地址是多少、能存什么范围的值。这种思维一旦建立起来,后面的链表、结构体、文件操作都会顺很多。

2. 数组定义与遍历:从“一个变量”到“一整组数据”的思维切换

2.1 定义分类和初始化方式:先搞清楚有多少种写法

今天上午第一件事就是系统过了一遍数组的“定义分类”。C语言里数组定义虽然看着简单,但实际组合起来有几种容易混淆的变体:

  • 一维数组的基本定义:int arr[10];——定义一个能存10个int的数组,下标从0到9。
  • 指定长度的初始化:int arr[5] = {1,2,3,4,5};——长度由初始化列表决定。
  • 部分初始化:int arr[5] = {1,2};——后面的三个元素自动补0。
  • 不写长度的初始化:int arr[] = {1,2,3,4,5};——编译器根据初始化列表自动确定长度为5。
  • 字符数组的特殊写法:char str[] = "hello";——注意这里实际占了6个字节,末尾自动加\0。

这些写法你要是第一次看,肯定会觉得“这不是差不多的意思吗?为什么要有这么多种?”实际上,它们对应的是不同的应用场景。比如部分初始化,在写计数器数组时特别常用——你需要一个长度为26的int数组统计每个字母出现的次数,直接int count[26] = {0};一行搞定,不用写循环去清零。而“不写长度”的初始化方式,在字符串处理里几乎是标配,因为字符串本来就不想被固定长度束缚。

今天光是数组定义我就整理了满满一页笔记,后来发现核心其实就一句话:数组名是这块连续内存的起始地址,中括号里的数字是元素的个数或下标,数组的大小在定义时必须是编译期能确定的常量(C99之前),或者用变长数组(C99之后)。初学者最容易栽的跟头,就是把arr[10]里那个10当作最大下标,明明定义了10个元素却去访问arr[10],结果编译器不报错,程序运行起来要么输出一个莫名其妙的值,要么直接崩溃。这就是典型的“数组越界”,是C语言最臭名昭著的问题之一,也是很多安全漏洞的根源。

2.2 遍历与下标偏移:为什么我建议你亲手画一遍内存格子

数组的遍历是今天的第一个实操练习。一个简单的需求:输入10个数,按逆序输出。我的第一版代码是这样的:

#include <stdio.h> int main() { int arr[10]; for (int i = 0; i < 10; i++) { scanf("%d", &arr[i]); } for (int i = 9; i >= 0; i--) { printf("%d ", arr[i]); } return 0; }

代码本身没什么问题,但我在写的时候脑子里其实模糊了一下:倒序输出的循环变量到底是从9开始还是从10开始?为什么结束条件是i >= 0而不是i > 0?为了彻底搞明白,我在纸上画了这样一个表格:

下标0123456789
地址偏移基准+0基准+4基准+8基准+12基准+16基准+20基准+24基准+28基准+32基准+36

这里有个很重要的细节:数组元素的下标对应的是“从首地址开始的第几个元素”,而实际上编译器计算地址时是“首地址 + 下标 × 每个元素的大小”。比如int通常是4字节,那么arr[5]的地址就是基地址 + 5*4 = 基地址 + 20。我头一次真正理解数组时,就是这个“乘以元素大小”的瞬间——原来数组下标不是简单的“第几个”,而是“偏移了多少个字节”。一旦你理解了偏移量,后面学指针、学结构体数组、学二维数组,全都一通百通。

我当时为了验证理解,还写了一段小代码打印每个元素的地址:

#include <stdio.h> int main() { int arr[5] = {10, 20, 30, 40, 50}; for (int i = 0; i < 5; i++) { printf("arr[%d] = %d, address = %p\n", i, arr[i], &arr[i]); } return 0; }

输出结果大概是arr[0] = 10, address = 0x7ffd...,每次地址加4。亲眼看到地址跳变之后,数组的“连续内存”概念才真正在我脑子里立住了。我强烈建议每个初学者都做一遍这个实验,光凭想象去理解“连续内存”,远不如直接打印地址来得直观。

2.3 数组变量与普通变量的类型差异:几个容易混淆的概念

今天还有一个特别容易混淆的点,就是“数组变量”和“普通变量”在赋值和传参上的行为完全不一样。普通变量可以直接赋值:int a = 10; int b = a;,但数组不能整体赋值:int arr1[5]; int arr2[5]; arr2 = arr1;这种写法是编译错误的。原因是数组名在表达式里会“退化”为指向首元素的指针,本质上是一个地址值,而不是一个可以整体移动的“数据块”。要想把一个数组的内容复制到另一个数组,必须用循环逐个元素赋值,或者用memcpy函数。

这种“退化为指针”的规则,是C语言里让新人最崩溃的一条。一个简单的性子:

int arr[5] = {1,2,3,4,5}; int *p = arr; // 合法:arr退化为指向arr[0]的指针

后面学了指针之后回头看,数组和指针的关系可以用一句话概括:数组是“一段内存的声明”,指针是“指向这段内存的变量”。它们在访问形式上几乎可以互换,但在语义上完全不同。今天先把“数组不能整体赋值”这个坑记下来,后面学函数传参时还会碰到——函数参数里写int arr[]本质是int *arr,那时候再回头看这句话,会更有感触。

3. 字符串与字符数组:\0这个看不见的哨兵决定了你的bug数量

3.1 字符串的本质:它不过是一个以空字符结尾的字符数组

今天的重头戏之一是“字符串”。学之前我以为字符串是一种独立的类型,学完发现C语言里根本没有字符串类型,所谓字符串其实就是char数组,只不过约定最后一个元素是'\0'(空字符,ASCII码为0)。这个设计在当年是为了节省内存和简化语言实现,但代价就是所有处理字符串的函数都要自己判断终止位置。

这里有一个所有新手都会掉的坑:定义一个字符数组来存字符串时,长度到底该给多少?比如要存"hello",你需要char str[6]而不是char str[5],因为5个字符后面还要留一个位置给\0。如果你写了char str[5] = "hello";,编译器会直接报错,因为存不下6个字节。但如果你用char str[] = "hello"这种写法,编译器会自动算好长度,这也是我推荐初学者优先使用的写法——让编译器帮你数数,别自己去数,数错的概率实在太高了。

我实际验证过一次:把字符串逐个字符输出,看看到第几个字符会出现\0:

#include <stdio.h> int main() { char str[] = "hello"; for (int i = 0; i < 10; i++) { printf("%d: %c (code: %d)\n", i, str[i], str[i]); } return 0; }

输出到第5个字符之后,str[5]的代码就是0,再往后打印出来是空白或乱七八糟的东西。这个实验帮我理解了一个重要事实:printf遇到字符串时,用%s输出,它会一直读内存,直到碰到\0才停下来。如果你构造字符串时忘了加\0,printf("%s", str)就不知道什么时候停,会一口气把后面内存里的随机字节全打印出来,直到运气好碰到一个0为止。这个“缓冲区读取越界”的问题,是所有字符串处理bug的根源。

3.2 字符串函数库里的常用操作:strlen并不“算长度”

下午做练习时,我把常用的字符串函数逐个梳理了一遍,主要是这几个:strlen、strcpy、strcmp、strcat。每个函数都有坑:

  • strlen(str)返回的是字符串长度,但不包含末尾的\0。也就是说"hello"的strlen是5。这个函数是逐个字符扫描,直到遇到\0为止,时间复杂度是O(n),如果你在一个大循环里反复调用strlen,性能会肉眼可见地变差。
  • strcpy(dest, src)把src的内容复制到dest,包括末尾的\0。这里最容易犯的错误是dest空间不够大,结果把数据写到dest后面去了——所谓的“缓冲区溢出”。我建议在复制之前先用strlen(src)确认一下长度,或者直接用strncpy指定最大复制长度。
  • strcmp(a, b)逐个字符比较两个字符串,返回值是第一个不相等字符的ASCII码差值,而不是简单的“大于0就是a大”。初学者经常会误以为它返回1或0。
  • strcat(dest, src)把src接到dest末尾,它会自动找到dest的\0位置,然后从那里开始追加。同样的缓冲区溢出风险。

我顺手写了一个常见的“字符串逆序”练习(很多学校PTA和OJ上都有这道题)。我的第一版代码是这样的:

#include <stdio.h> #include <string.h> int main() { char str[100]; gets(str); int len = strlen(str); for (int i = len - 1; i >= 0; i--) { putchar(str[i]); } return 0; }

这里有个老生常谈的问题:gets函数已经被C11标准废除了,因为它没有办法限制输入长度,非常容易造成缓冲区溢出。正确做法是用fgets(str, sizeof(str), stdin)——它会读入最多sizeof(str)-1个字符,并自动在末尾补\0。但fgets有个小坑:如果输入不足一行,它会把末尾的换行符也保留下来。这个换行符经常导致字符串比较失败或输出多空一行,解决办法是手动把最后一个字符换成\0:

fgets(str, sizeof(str), stdin); str[strcspn(str, "\n")] = '\0'; // 把换行符替换成字符串结束符

说真的,字符串这块内容看起来不难,但它是后续所有C项目里bug的高发区。今天如果能把\0这个概念彻底吃透,后面学文件读写、学链表存储用户数据时,会少踩很多坑。

3.3 字符串逆序之外的另一个小练习:字符频率统计

下午为了巩固数组和字符串的组合使用,我又做了一个经典的统计练习:输入一行字符串,统计每个英文字母出现的次数。核心逻辑就是数组下标和字符ASCII码之间的转换:

#include <stdio.h> #include <string.h> #include <ctype.h> int main() { char str[200]; int count[26] = {0}; fgets(str, sizeof(str), stdin); for (int i = 0; str[i] != '\0'; i++) { if (isalpha(str[i])) { char lower = tolower(str[i]); count[lower - 'a']++; } } for (int i = 0; i < 26; i++) { if (count[i] > 0) { printf("%c: %d\n", 'a' + i, count[i]); } } return 0; }

这个练习的价值在于:lower - 'a'这个操作充分利用了字符在ASCII表中连续排列的特性。你不需要去背'a'到底是多少(97),只要知道字母之间是连续的,用相对偏移量就能作为数组下标。这种“字符转数值下标”的思路,是后面各种查表法、哈希表初阶的基础。比如以后做词频分析、压缩算法,基本思路都和这个一模一样。

4. 指针初体验:从“有两个变量”到“两个变量都指向同一个内存”的震惊

4.1 为什么第十天必须引入指针:它是C语言的灵魂

很多人会问:第十天就上指针,是不是太快了?我的观点是:不快,而且正好。因为如果你已经理解了数组在内存中的连续布局,再学“指针就是保存地址的变量”这个概念,就只是顺水推舟。指针本身并不难,难的是从“值”的思维切换到“地址”的思维。

指针的定义形式五花八门,但核心就一句话:int *p;——p是一个变量,它里面存的不是int值,而是一个int变量的地址。你可以用&取地址运算符取一个变量的地址,也可以用*解引用运算符通过地址访问它指向的变量。

我今天的第一个指针练习是这样写的:

#include <stdio.h> int main() { int a = 42; int *p = &a; printf("a = %d\n", a); printf("&a = %p\n", &a); printf("p = %p\n", p); printf("*p = %d\n", *p); *p = 100; printf("after *p = 100, a = %d\n", a); return 0; }

运行之后,我盯着输出看了好一会儿:*p = 100之后,a的值也变成了100。我明明没有给a赋值,为什么它的值变了?因为p存着a的地址,*p就是通过地址直接操作a本身。这就像你手上有一把钥匙,钥匙上写着某个储物柜的编号,你用钥匙打开柜子换了里面的东西,柜子里的东西当然就变了,而不是“另有一个柜子被改了”。这个“通过地址间接修改变量”的机制,是后面学函数参数传递(传值vs传址)、链表、动态内存分配时最基础的思想。

4.2 指针类型到底有什么意义:决定你读写几个字节

今天最颠覆我认知的一个知识点是“指针的算术运算”。刚开始我以为指针就是一个地址数字,那p + 1就应该等于地址值加1呗。实际跑了一下才发现完全不是:

int arr[5] = {10, 20, 30, 40, 50}; int *p = arr; printf("p = %p\n", p); printf("p + 1 = %p\n", p + 1);

输出结果是:如果p等于0x7ffd...0,那么p + 1等于0x7ffd...4,而不是...1。因为编译器知道p是一个int*,所以p + 1在原地址基础上加了“一个int的大小”,也就是4个字节。同理,char*的p + 1只加1,double*的p + 1加8。

指针类型的意义,在于告诉编译器“这个地址上的数据占多大空间、该怎样解释”。它决定了当你读写这个地址时,是取1个字节还是一个int,是把它当作有符号数还是无符号数。这也是为什么C语言里指针类型转换(比如把int*强转为char*)会从根本上改变对同一块内存的解释方式。

这里我遇到一个特别反直觉的例子:用一个int*去遍历字符串,能输出吗?试试看:

char str[] = "abc"; int *p = (int *)str; // 强制把char*转成int* printf("%c\n", *p); // 会输出'a'吗?

结果是完全没把握。因为int*一次会读4个字节,然后把那4个字节解释成一个int值,你再用%c输出,输出的其实是这个int值的最低1个字节的ASCII字符。虽然运气好可能还是'a',但本质上已经是“对内存的不同解释”。搞懂这个例子之后,我彻底明白了“指针类型决定解释方式”这句话的含义。这也是为什么C语言不做强制类型检查很容易出错,但也正是这种灵活性,给了底层编程(嵌入式、操作系统、网络协议栈)极大的控制能力。

4.3 指针与函数参数:为什么交换两个变量的值需要传指针

今天另一个经典练习是“交换两个变量的值”。如果写成这样:

void swap(int a, int b) { int temp = a; a = b; b = temp; }

在主函数里调用swap(x, y),你发现x和y根本没有变化。原因是C语言函数的参数传递是“传值”的,函数内部拿到的a和b是主函数里x和y的拷贝,函数里修改拷贝根本不影响原始变量。如果要真正改变主函数里的变量,必须传入指针:

void swap(int *a, int *b) { int temp = *a; *a = *b; *b = temp; }

调用时写swap(&x, &y)。这里的关键区别:swap内部拿到的不是x和y的值,而是它们的地址。通过地址去操作,才能真正修改原始变量的内存单元。

这个知识点如果你今天没想明白,后面学链表的增删节点、学二叉树的插入、学文件指针的移位,全部都会卡壳。因为所有“需要修改调用方数据”的场景,都必须通过指针传递。今天我在笔记本上记了一句话:“传值改不了原值,传地址才能改原值。”虽然直白到有点像口诀,但真的实用。

5. 今天做过的练习与踩坑:翁恺练习题、PTA乙级、乱码与缓冲区

5.1 一道翁恺风格的练习题:输入日期算这是第几天

今天做完基础练习之后,我挑了一道典型的日期计算题:输入一个年月日,计算并输出这是这一年的第几天。这道题在很多OJ上都有变体,翁恺老师的练习题里也包含类似逻辑。我一开始用了最笨的方法:判断月份,累加前面所有月份的天数,最后再加上当月天数。然后又想“优化”成switch的方式。实际写下来,最简洁的做法是先把每个月的天数放到一个数组里:

int days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

然后判断闰年,如果是闰年且月份大于2,总天数再加1。这个解法用到了今天的数组知识点,比堆一长串if/else清爽多了。题目本身不难,但我在这里踩了个隐蔽的坑:数组下标从0开始,而月份从1开始。访问days_in_month[month]时如果忘了减1,就会取到下一个月的天数。这种“下标偏移一”的问题,在真实项目中也是常见的bug来源,往往肉眼检查半天发现不了,调试器一看到month=2取到31才恍然大悟。

5.2 PTA乙级1037:“在霍格沃茨找零钱”与进制换算思路

今天练习题里的另一个老朋友是PTA乙级的“1037 在霍格沃茨找零钱”。题目大概是说在魔法世界里,货币单位是Galleon、Sickle、Knut,进制关系和现实世界不太一样(17 Knut = 1 Sickle,29 Sickle = 1 Galleon),让你计算找零。

这道题其实和进制转换、借位减法有关。我的第一反应是直接按位减法,结果发现借位处理特别繁琐。后来我换了个思路:把所有金额统一换算成最小的Knut单位,做一次整数减法,再换算回去。这样写不仅代码量少,而且不容易出错:

#include <stdio.h> int main() { int g1, s1, k1, g2, s2, k2; scanf("%d.%d.%d %d.%d.%d", &g1, &s1, &k1, &g2, &s2, &k2); int total1 = g1 * 17 * 29 + s1 * 29 + k1; int total2 = g2 * 17 * 29 + s2 * 29 + k2; int diff = total2 - total1; if (diff < 0) { printf("-"); diff = -diff; } printf("%d.%d.%d\n", diff / (17 * 29), diff % (17 * 29) / 29, diff % 29); return 0; }

这个思路在“时间差”“日期差”等场景里其实是通用的:先全部换算成最小单位,再相减,最后格式化输出。我在书上还看到过类似的题:算日期差、算两个时刻的时间差,本质都是一样的。学C语言到一定阶段,你会慢慢发现很多题刚开始觉得是“算法题”,实际上只是“单位换算思路”的应用。这个“统一到最小单位再计算”的思想,值得记在笔记本上。

5.3 实操中的常见坑:scanf缓冲区、乱码、vscode配置

今天在代码运行过程中还遇到了几个环境层面的问题,顺手记录一下。第一个是scanf的缓冲区问题。我写了一个循环读取多个整数,运行后发现第一次输入后程序会跳过后续输入,或多读一个空行。这是因为scanf在处理完数据后,缓冲区里可能还留着换行符,紧接着的下一次%c会把它读走。解决办法是在%c前加空格,比如scanf(" %c", &ch),或者用getchar()把换行符吃掉。

另外一个问题是乱码。我用VSCode写C语言时,程序里含中文注释,一编译运行中文就变成乱码。原因是Windows下的VSCode终端默认编码可能是GBK,而源代码文件保存成了UTF-8。解决办法有两个:一是把文件保存成GBK编码,二是在程序开头设置setlocale(LC_ALL, "zh_CN.UTF-8");并包含locale.h头文件。更省事的方案是把代码文件统一为UTF-8,在VSCode设置里把终端编码改成UTF-8。这个问题在学习阶段特别容易遇到,但它不是C语言本身的问题,而是编码环境的经典坑。

第三个是VSCode怎么运行C语言代码。我这里用最简单的方式说明一下:装好C/C++扩展和Code Runner插件后,新建.c文件,点击右上角的运行按钮,Code Runner会调用gcc编译并运行。如果你用的是虚拟机里的Ubuntu,那就更简单了,终端里直接:

gcc -o hello hello.c ./hello

我自己的建议是:不管用不用IDE,都要自己能在命令行里用gcc编译一次,这样你对“源文件 → 目标文件 → 可执行文件”这条链路会有直观认识。环境配置这块内容虽然不算C语言语法本身,但它直接影响你是不是能顺利跑通练习题,所以每次都值得多花十分钟把它调好。

5.4 今天用过的调试手段:printf大法与gdb简单操作截图

说到调试,今天还真帮我抓住了一个隐蔽bug。我在写字符统计程序时,输出结果里多了很多空白字符,我就一个个加printf看每个步骤的变量值,这招俗称“printf大法”,虽然土但在学习阶段足够用。不过遇到真正难以定位的内存问题,还是得上调试器。

今天试了一下gdb(在虚拟机的Ubuntu里),最基本的操作流程是:

gcc -g -o debug debug.c # 编译时加-g参数,保留调试信息 gdb ./debug break main # 在main函数设置断点 run # 运行到断点 print a # 查看变量a的值 next # 单步执行 continue # 继续运行

我首次跑gdb时最大的困惑是不知道print到底应该打什么,后来明白了:print 变量名直接看变量值,print &变量名看地址,print *指针看指针指向的值。调试器这东西,刚开始用会觉得很麻烦,但当你面对“程序崩溃但不知道在哪一行”的情况时,它几乎是唯一的高效手段。今天我用gdb定位了一个数组越界的问题——程序跑完后崩溃,backtrace命令直接指到越界的那一行循环,效率比肉眼查代码快太多了。

6. Day10后的三个自我提醒与扩展学习方向

6.1 数组和指针不是一码事:别再被“指针就是数组”带偏

今天的学习内容下来,我最大的感受是:数组和指针的关系非常微妙,但千万不能简单粗暴地理解为“等价”。数组是内存里的一段连续空间,数组名在大多数表达式里退化为指向首元素的指针,但在sizeof运算里,数组名代表的是整个数组的大小,而不是一个指针的大小。比如:

int arr[10] = {0}; printf("%zu\n", sizeof(arr)); // 输出40,10 * 4字节 printf("%zu\n", sizeof(arr + 0)); // 输出8,指针大小

这两个结果不一样,这个差异特别能帮人理解“数组是一个实体,指针是一个指向实体的变量”。我建议你亲手跑一次这段代码,体会一下两个输出值为什么不同。

6.2 记住三个“万万不可”的常见错误

今天一天下来,我给自己总结了三条必须牢记的红线:

  • 字符串数组长度不够,塞不下结尾的\0,造成缓冲区溢出。
  • 数组下标从0开始,循环遍历时注意边界条件的i < nではなくi <= n这种错误。
  • 用gets读取字符串,几乎必定造成未定义行为,现在全用fgets代替。

这三条是我实际踩过的坑,写在这里提醒自己,也提醒一起学C语言的各位。C语言不像Java或Python那样会帮你在运行时报一个“越界异常”,它会在你越界后静默地读取或写入相邻内存,后果可能是乱码、崩溃,也可能是程序看似正常但数据已经悄悄被改坏了。这种“静默错误”最难排查,所以最好的策略是从一开始就写出防守型的代码——比如严格检查循环边界、给数组多留一点空间、使用带长度限制的字符串函数。

6.3 后面几天的学习线索:从数组到结构体、函数递归与文件

Day10之后,我的学习路线大致是这样规划的:第11天到第12天把指针的算术运算、指针和数组的混合使用(二维数组、指针数组、数组指针)彻底打牢;第13天开始学函数指针和递归;第14天左右会接触结构体,把多个不同类型的数据打包成一个整体;第15天到第16天进入动态内存分配(malloc/free)和链表。再往后就是文件操作了。

这些内容其实都和今天的知识挂钩。比如结构体的本质不过是一个“更大块的内存”,而链表节点之间的连接靠的就是指向下一个节点的指针。今天打好的底子,直接决定了后面能不能跟上。所以我的经验是:第十天到第十二天这段时间,宁可放慢一点,把数组、字符串、指针这三个东西在纸面上反复画内存图,也不要赶进度。很多C语言学到一半放弃的人,基本都是在这几天没有把指针想清楚,后面一路稀里糊涂。

我个人学习时还有一个习惯,就是每道练习题做完后,在代码旁边写一行注释,说明“这道题我用到数组的哪个特性”或者“这里为什么要用指针”。看似多花了半分钟,但回头复习的时候,你能很快从一堆代码里回忆出每道题的核心考点。这个方法帮我撑过了后面好几周的密集训练,也推荐给正在啃C语言的你。

Day10这一天学完,我对C语言有了完全不同的感觉:原来写程序不是在“告诉机器要做什么”,而是在“安排数据的存放与流转”。这个转变不发生在第一天,也不发生在写了大几百行代码之后,恰恰就是发生在你第一次认真研究数组和指针的那几天。今天把这些笔记整理出来,既是给自己的复盘,也希望给正在同样进度上挣扎的你一点参照:别怕卡壳,把内存图画明白,慢慢就通透了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询