当前位置: 首页 > news >正文

c康复训练 01

就这么直白又有点好笑的康复训练专题,课程要求基本还是c的,加油。快快康复!

1 函数内变量名可以和函数名一样吗?

在大多数编程语言中(包括 Java、Python、C++ 等),函数内部的变量名不可以与函数名完全相同,这会导致命名冲突和编译错误。

原因如下:

  1. 作用域冲突函数名在其作用域内是一个唯一标识符,代表函数本身。如果在函数内部定义同名变量,会导致编译器 / 解释器无法区分你是要引用函数还是变量,从而引发歧义。

  2. 具体语言示例以 Java 为例,下面的代码会直接编译报错:

    class Example {// 函数名:testpublic void test() {// 错误:变量名与函数名相同int test = 10; // 编译报错:已在方法 test() 中定义了变量 test}
    }
    
  3. 特殊情况某些语言(如 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"(解析时到达文件末尾)是一个常见的语法错误,通常发生在代码结构不完整的情况下。这意味着解析器在读取代码时,遇到了文件结束但某些代码块还没有正确闭合。

最常见的原因包括:

  1. 缺少闭合括号 / 引号:例如函数定义、循环、条件语句等没有正确关闭

    # 错误示例:缺少闭合括号
    def my_function():print("Hello"  # 这里缺少闭合的括号
    
  2. 代码块不完整:例如 if 语句、循环或函数定义没有对应的主体

    # 错误示例:if语句没有主体且缺少冒号
    if x > 5
    
  3. 多行字符串没有正确结束

    # 错误示例:三引号字符串没有闭合
    long_text = """这是一个很长的文本
    但是没有正确结束
    

解决方法:

  • 仔细检查代码的括号、引号是否成对出现
  • 确保所有代码块(if/else、for/while 循环、函数定义等)都有正确的冒号和缩进
  • 检查是否有未完成的多行结构

通常,错误提示会包含发生错误的行号,可以重点检查该行及附近的代码结构。

3 在 C 语言中,*prices 和数组名 prices 的含义

【c语言里面*prices是不是表示数组指针,prices是数组名?】(随蠢但问)

在 C 语言中,*prices 和数组名 prices 的含义需要结合具体语境分析:

  1. 当 prices 是数组名时:

    • 数组名本身不是指针,但在大多数表达式中会隐式转换为指向数组首元素的指针(即指针指向指向数组第一个元素的指针)
    • *prices 表示对这个指针解引用,结果是数组的第一个元素(prices[0]
  2. 示例说明:

    int prices[5] = {10, 20, 30, 40, 50};
    printf("%d", *prices);  // 输出 10,等价于 prices[0]
    printf("%d", *(prices+1));  // 输出 20,等价于 prices[1]
    
  3. 注意区分「数组指针」:

    • 真正的数组指针是指向整个数组的指针,声明方式为 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 接收的正是这块连续空间的起始地址(首元素的内存地址),这和链表的 "头指针" 概念确实有相似之处 —— 都是通过一个起始指针来访问后续元素。

具体来说:

  1. 数组在内存中是连续存放的,每个元素紧挨着前一个元素
  2. prices 指针指向数组的第一个元素(首地址)
  3. 通过 prices[i] 访问元素时,实际上是计算 首地址 + i * 元素大小 的位置,这就是 "按索引访问" 的本质
  4. 正因为有了 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 大小可能不同)

关键特性

  1. 动态性:内存分配发生在程序运行时,而不是编译时,灵活度更高
  2. 手动管理:分配的内存需要用 free() 函数手动释放,否则会导致内存泄漏
    free(arr);  // 释放之前分配的内存
    arr = NULL;  // 建议将指针置空,避免野指针
    
  3. 初始化malloc 分配的内存不会自动初始化,内容是随机的(垃圾值)
    • 若需要初始化内存为 0,可使用 calloc 函数:calloc(5, sizeof(int))

常见错误

  1. 忘记释放内存:导致内存泄漏,程序长期运行会消耗大量内存
  2. 重复释放内存:同一指针被 free() 多次,会导致程序崩溃
  3. 访问已释放的内存:释放后继续使用指针(野指针),行为不可预测
  4. 不检查返回值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;
}

这里的关键是:

  1. malloc(sizeof(struct Node)) 分配的是单个节点的内存(不是整个链表),大小由 struct Node 决定
  2. 链表的 “长度” 是通过节点的 next 指针逐个连接起来的,而非 malloc 一次性分配
  3. malloc 只是提供了内存块,链表的结构(节点间的连接关系)需要我们手动维护

对比动态数组的创建:

// 用malloc创建动态数组(分配10个int的内存)
int* dynamicArr = (int*)malloc(10 * sizeof(int));

这里 malloc 分配的是连续的 10 个 int 空间,和链表的 “分散节点 + 指针连接” 模式完全不同。

所以可以说:malloc 是创建链表节点的工具,但它的功能远不止于此 —— 任何需要在运行时动态获取内存的场景都能用到它。

http://www.dtcms.com/a/391620.html

相关文章:

  • MLP和CNN在图片识别中的对比——基于猫狗分类项目的实战分析
  • Node-Choice
  • PyQt6之滚动条
  • 使用OpenVINO将PP-OCRv5模型部署在Intel显卡上
  • 【图像处理基石】图像复原方面有哪些经典算法?
  • setTimeout定时器不生效- useRef 的特点/作用
  • 钻井的 “导航仪”:一文读懂单点、多点与随钻测量
  • CKS-CN 考试知识点分享(8) ingress 公开 https 服务
  • ​​[硬件电路-259]:LM4040AIM3 精密电压基准源: 管脚定义、概述、功能、技术指标、使用场景、原理
  • C语言:实现阶乘和计算
  • 鸿蒙应用开发——AppStorageV2和PersistenceV2的使用
  • shell脚本实现docker镜像批量保存并上传至Harbor仓库
  • 用 EzCaptcha 优化 reCAPTCHA 低通过率问题
  • 在docker中构建Vue项目
  • 力扣1895. 最大的幻方
  • Linux入门(四)
  • 以下是与LoRa技术相关的数学公式整理
  • (3) rust和前端交互
  • TextFlux重磅发布:告别复杂控制信号!多语种高保真场景文本编辑新时代
  • A股大盘数据-20250919分析
  • [x-cmd] X-CMD 的依赖非常精简,即装即用,无需额外配置
  • TM52F1363 电子元器件 海速芯 8位高性能微控制器(MCU)技术解析
  • 打工人日报#20250919
  • vue3学习日记(十九):组件实例详解
  • 【开题答辩全过程】以 基于python的订餐系统为例,包含答辩的问题和答案
  • 题解:CF2143E Make Good
  • 鸿蒙:使用worker实现多线程通信
  • Spring MVC 常用注解及代码示例
  • 【DMA】DMA入门:外设数据到内存,以串口DMA接收为例,解析底层实现
  • Java 中 super 和 this关键字总结