c康复训练 01
就这么直白又有点好笑的康复训练专题,课程要求基本还是c的,加油。快快康复!
1 函数内变量名可以和函数名一样吗?
在大多数编程语言中(包括 Java、Python、C++ 等),函数内部的变量名不可以与函数名完全相同,这会导致命名冲突和编译错误。
原因如下:
作用域冲突函数名在其作用域内是一个唯一标识符,代表函数本身。如果在函数内部定义同名变量,会导致编译器 / 解释器无法区分你是要引用函数还是变量,从而引发歧义。
具体语言示例以 Java 为例,下面的代码会直接编译报错:
class Example {// 函数名:testpublic void test() {// 错误:变量名与函数名相同int test = 10; // 编译报错:已在方法 test() 中定义了变量 test} }
特殊情况某些语言(如 JavaScript)在非严格模式下可能允许类似写法,但会导致变量覆盖函数引用,引发难以预料的 bug,这是强烈不推荐的做法。
最佳实践:
变量名应与函数名保持语义区分,例如函数名用动词(如 calculateProfit
),变量名用名词(如 profit
),既避免冲突,又增强代码可读性。
在这种情况下呢?(见代码)
class Solution {public int maxProfit(int[] prices) {if (prices.length <= 1) {return 0;}int lowPrice = prices[0];int maxProfit = 0; for (int i = 1; i < prices.length; i++) {// 先判断是否更新最低价(应该用当前价格比较)if (prices[i] < lowPrice) {lowPrice = prices[i];} // 计算当前价格与最低价的利润int profit = prices[i] - lowPrice;// 保留最大利润if (profit > maxProfit) {maxProfit = profit;}}return maxProfit;}
}
在你提供的这段代码中,maxProfit
既作为函数名,又作为函数内部的变量名,这种情况在 Java 中是允许的,不会导致编译错误。
原因是:
- 函数名(方法名)和局部变量名处于不同的命名空间
- Java 编译器能够区分它们:当你在方法内部使用
maxProfit
时,默认引用的是局部变量;如果要引用方法本身,需要通过特殊方式(如反射),这种情况非常罕见
不过,虽然语法上允许,但从代码可读性角度考虑,这种命名方式并不推荐:
- 可能会让阅读者产生混淆,需要额外判断当前
maxProfit
指的是方法还是变量 - 不符合 "标识符语义化" 的最佳实践
更好的命名方式可以是:
public int maxProfit(int[] prices) {// ...int maximumProfit = 0; // 用同义词或不同形式命名变量// ...
}
这样既避免了潜在的混淆,也保持了代码的清晰性。
2 reached end of file while parsing
"reached end of file while parsing"(解析时到达文件末尾)是一个常见的语法错误,通常发生在代码结构不完整的情况下。这意味着解析器在读取代码时,遇到了文件结束但某些代码块还没有正确闭合。
最常见的原因包括:
缺少闭合括号 / 引号:例如函数定义、循环、条件语句等没有正确关闭
# 错误示例:缺少闭合括号 def my_function():print("Hello" # 这里缺少闭合的括号
代码块不完整:例如 if 语句、循环或函数定义没有对应的主体
# 错误示例:if语句没有主体且缺少冒号 if x > 5
多行字符串没有正确结束:
# 错误示例:三引号字符串没有闭合 long_text = """这是一个很长的文本 但是没有正确结束
解决方法:
- 仔细检查代码的括号、引号是否成对出现
- 确保所有代码块(if/else、for/while 循环、函数定义等)都有正确的冒号和缩进
- 检查是否有未完成的多行结构
通常,错误提示会包含发生错误的行号,可以重点检查该行及附近的代码结构。
3 在 C 语言中,*prices
和数组名 prices
的含义
【c语言里面*prices是不是表示数组指针,prices是数组名?】(随蠢但问)
在 C 语言中,*prices
和数组名 prices
的含义需要结合具体语境分析:
当
prices
是数组名时:- 数组名本身不是指针,但在大多数表达式中会隐式转换为指向数组首元素的指针(即指针指向指向数组第一个元素的指针)
*prices
表示对这个指针解引用,结果是数组的第一个元素(prices[0]
)
示例说明:
int prices[5] = {10, 20, 30, 40, 50}; printf("%d", *prices); // 输出 10,等价于 prices[0] printf("%d", *(prices+1)); // 输出 20,等价于 prices[1]
注意区分「数组指针」:
- 真正的数组指针是指向整个数组的指针,声明方式为
int (*p)[5]
(指向包含 5 个 int 的数组) - 而
prices
隐式转换后的指针是「元素指针」(int *
类型),指向单个元素
- 真正的数组指针是指向整个数组的指针,声明方式为
简单说:*prices
是数组第一个元素的值,而不是数组指针。数组名在表达式中会退化为元素指针,而非数组指针。
4 int* prices
int maxProfit(int* prices, int pricesSize) {}
在这个函数中,int* prices
是函数的参数,它表示一个指向整数的指针。结合上下文(函数名 maxProfit
和逻辑),这个指针实际上指向的是一个存储股票价格的数组的首元素。
具体来说:
- 当你传入一个数组名(比如
prices_array
)作为参数时,数组名会隐式转换为指向数组首元素的指针,也就是&prices_array[0]
- 因此
*prices
就等价于数组的第一个元素prices_array[0]
- 函数中通过
prices[i]
或*(prices+i)
的方式访问数组中的第 i 个元素
举个调用例子:
int main() {int prices[] = {7,1,5,3,6,4};int size = 6;int result = maxProfit(prices, size); // 这里传入的是数组名,会自动转换为指针return 0;
}
在这个例子中,函数参数 prices
接收的是指向数组 {7,1,5,3,6,4}
首元素(即 7)的指针,通过这个指针可以访问整个数组的元素。
简单说,int* prices
在这里的作用就是让函数能够访问外部传入的价格数组,是 C 语言中传递数组的标准方式。
【也就是说在内存空间里,数组的第一个内存被传入参数的第一个指针指引,按照size可以接下去访问元素,也就是我们熟悉的索引访问,只不过这里int * 数组名是提供了解包数组的一个头指针(我不知道能不能这么说,链表可以说头指针吧)】
可以这样类比:
在内存中,数组是一块连续的存储空间,int* prices
接收的正是这块连续空间的起始地址(首元素的内存地址),这和链表的 "头指针" 概念确实有相似之处 —— 都是通过一个起始指针来访问后续元素。
具体来说:
- 数组在内存中是连续存放的,每个元素紧挨着前一个元素
prices
指针指向数组的第一个元素(首地址)- 通过
prices[i]
访问元素时,实际上是计算首地址 + i * 元素大小
的位置,这就是 "按索引访问" 的本质 - 正因为有了
pricesSize
,函数才知道数组的边界,避免访问越界
和链表的区别在于:
- 数组的 "头指针" 指向连续空间,通过指针算术就能直接定位任意元素(O (1) 时间)
- 链表的头指针指向第一个节点,必须通过每个节点的指针域依次访问(O (n) 时间)
所以你说 "int* 数组名提供了解包数组的头指针" 是很形象的说法,这个指针确实起到了 "入口" 的作用,配合数组大小就能完整访问整个数组。
5 malloc
malloc
是 C 语言中用于动态分配内存的函数,全称为 "memory allocation",位于 <stdlib.h>
头文件中。它的核心作用是在程序运行时向系统申请一块指定大小的连续内存空间,并返回指向该空间起始地址的指针。
基本用法
#include <stdlib.h> // 包含 malloc 函数的头文件// 函数原型:void* malloc(size_t size);
// 参数:需要分配的内存大小(以字节为单位)
// 返回值:成功时返回指向分配内存的指针;失败时返回 NULL// 示例:分配能存储 5 个 int 类型元素的内存(假设 int 为 4 字节)
int* arr = (int*)malloc(5 * sizeof(int)); // 5*4=20 字节
- 注意:
malloc
返回的是void*
类型指针,需要根据实际存储的数据类型进行强制转换(如(int*)
、(char*)
等) sizeof(int)
用于计算单个int
类型的字节数,这样写可移植性更强(不同系统中int
大小可能不同)
关键特性
- 动态性:内存分配发生在程序运行时,而不是编译时,灵活度更高
- 手动管理:分配的内存需要用
free()
函数手动释放,否则会导致内存泄漏free(arr); // 释放之前分配的内存 arr = NULL; // 建议将指针置空,避免野指针
- 初始化:
malloc
分配的内存不会自动初始化,内容是随机的(垃圾值)- 若需要初始化内存为 0,可使用
calloc
函数:calloc(5, sizeof(int))
- 若需要初始化内存为 0,可使用
常见错误
- 忘记释放内存:导致内存泄漏,程序长期运行会消耗大量内存
- 重复释放内存:同一指针被
free()
多次,会导致程序崩溃 - 访问已释放的内存:释放后继续使用指针(野指针),行为不可预测
- 不检查返回值:
malloc
可能分配失败(返回 NULL),不判断会导致后续操作出错int* arr = (int*)malloc(5 * sizeof(int)); if (arr == NULL) { // 检查是否分配成功// 处理错误(如打印提示、退出程序)return -1; }
与数组的对比
- 静态数组(如
int arr[5]
):大小固定,编译时分配内存,函数结束后自动释放 malloc
动态分配:大小可在运行时确定(如根据用户输入),需要手动释放,更灵活
例如,根据用户输入的大小创建数组:
int n;
printf("请输入数组大小:");
scanf("%d", &n);
int* arr = (int*)malloc(n * sizeof(int)); // 动态确定大小
// 使用数组...
free(arr); // 记得释放
malloc
是 C 语言中管理动态内存的基础,理解它的工作原理和使用规范,对编写灵活高效的程序非常重要,尤其在处理不确定大小的数据(如文件内容、用户输入)时必不可少。
【malloc就是创建链表吧,指定多少长度的什么数据类型的?】
你的理解有一定道理,但不完全准确。malloc
本质是动态分配一块连续内存,它不仅能用于创建链表,还能用于各种需要动态内存的场景(比如动态数组、结构体实例等)。
用 malloc
创建链表是它的典型应用之一,因为链表的节点数量在运行前往往不确定,需要动态创建:
// 定义链表节点结构体
struct Node {int data;struct Node* next; // 指向下一个节点的指针
};// 创建新节点(用malloc分配内存)
struct Node* createNode(int value) {// 分配一个Node大小的内存块struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));if (newNode != NULL) { // 检查内存分配是否成功newNode->data = value; // 初始化数据newNode->next = NULL; // 初始化指针(暂时指向空)}return newNode;
}
这里的关键是:
malloc(sizeof(struct Node))
分配的是单个节点的内存(不是整个链表),大小由struct Node
决定- 链表的 “长度” 是通过节点的
next
指针逐个连接起来的,而非malloc
一次性分配 malloc
只是提供了内存块,链表的结构(节点间的连接关系)需要我们手动维护
对比动态数组的创建:
// 用malloc创建动态数组(分配10个int的内存)
int* dynamicArr = (int*)malloc(10 * sizeof(int));
这里 malloc
分配的是连续的 10 个 int 空间,和链表的 “分散节点 + 指针连接” 模式完全不同。
所以可以说:malloc
是创建链表节点的工具,但它的功能远不止于此 —— 任何需要在运行时动态获取内存的场景都能用到它。