mirror of
https://github.com/Aexiar/c.git
synced 2024-10-22 14:05:45 +02:00
c
This commit is contained in:
parent
78dd9b9355
commit
50a8b60bc5
@ -2110,3 +2110,33 @@ cp perf /usr/bin/
|
||||
![](./assets/196.png)
|
||||
|
||||
![](./assets/197.png)
|
||||
|
||||
## 9.7 C 语言的学习技巧
|
||||
|
||||
* 对于大部分的初学者, 学习 C 语言的目的可能是为了成为一名合格的程序员,开发出优秀的软件。但是,在学习了 C 语言的基本语法后,却发现只能在`控制台`(`黑底白字`)上玩玩,没有漂亮的用户界面以及优化、人性化的交互。于是,开始学习数据结构、算法、数据库、操作系统,越陷越深,越来越迷茫,不知道学习 C 语言能做什么,认为学习编程很难,开始怀疑自己,直到放弃!!!
|
||||
* 其实,C 语言本身就是一门非常简单的语言,提供的实用功能不多,大部分的时候需要`借助`操作系统、第三方库以及以及一些硬件才能发挥它的威力!!!
|
||||
|
||||
> [!IMPORTANT]
|
||||
>
|
||||
> * ① 学习 C 语言仅仅是让你踏上程序员之路的第一步而已,只学习 C 语言也做不了什么。
|
||||
> * ② 系统、扎实的学习 C 语言可以让你了解底层硬件、一些简单的数据结构和算法,并培养计算机思维。
|
||||
|
||||
* C 语言是一门通用性的语言,并没有针对某个领域进行优化,在实际项目中,C 语言主要用于较底层的开发,例如:
|
||||
* Windows、Linux、Unix 等操作系统的内核 90% 以上都使用 C 语言开发。
|
||||
* 开发硬件驱动,让硬件和操作系统连接起来,这样用户才能更有效的使用硬件。
|
||||
* 单片机和嵌入式属于软硬件的结合,是使用 C 语言最多的地方。
|
||||
* 开发系统组件或服务,用于支撑上层应用。
|
||||
* ……
|
||||
* 既然 C 语言的应用很多,为什么感觉学习它还是做不了什么?答案就是`生态`。
|
||||
|
||||
> [!IMPORTANT]
|
||||
>
|
||||
> 现代化的高级编程语言的流行程度,除了和编程语言的设计是否优秀有关,最主要的原因就是`生态`。
|
||||
>
|
||||
> * ① 很多编程语言都自带`标准库`(语言本身提供的,开箱即用),如:Java、Go 等。
|
||||
> * ② 很多编程语言都有自己的`包管理器`(用于管理第三方库)解决方案,如:Java 中的 Maven、Gradle、Go 中的 go modules ,JavaScript 的 npm 等。
|
||||
>
|
||||
> 遗憾的是,C 语言的`标准库`非常简单,如:输入输出、文件操作、日期时间、字符串处理、内存管理等。C 语言的`第三方库`也非常稀少,更别提缺少自己的包管理器。
|
||||
>
|
||||
> 不过,现在 C 语言社区也开始诞生了一些包管理器,如:Conan 和 vcpkg ;也有自己的项目构建工具,如:cmake 、xmake 等。
|
||||
|
||||
|
@ -300,74 +300,105 @@ int main() {
|
||||
>
|
||||
> 下文中提及的`指针`都是`指针变量`,不再阐述!!!
|
||||
|
||||
## 3.3 指针变量的定义
|
||||
|
||||
|
||||
## 3.2 普通变量和指针变量的区别
|
||||
|
||||
在 CLion 中使用 GDB 调试时,可以通过反编译代码来查看指针变量和普通变量的区别。下面是具体的步骤:
|
||||
|
||||
### 设置 GDB 调试器
|
||||
1. 打开 CLion,并加载你的项目。
|
||||
2. 确保你使用的是带有 GDB 支持的工具链(如 GCC 工具链)。
|
||||
3. 在 CLion 的设置中,确保调试器设置为 GDB。
|
||||
|
||||
### 编译你的代码
|
||||
确保在编译你的代码时使用了调试信息生成选项(如 `-g`)。你可以在 CMakeLists.txt 文件中添加以下行:
|
||||
## 3.4 指针的作用
|
||||
|
||||
```cmake
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
|
||||
* 查询数据。
|
||||
* 存储数据。
|
||||
* 参数传递。
|
||||
* 内存管理。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
在 Java 中,引用数据类型的向上类型转换(upcasting)和向下类型转换(downcasting)是面向对象编程中常见的操作。这些转换是 Java 继承体系和多态性的重要部分。我们先分别介绍向上类型转换和向下类型转换,然后讨论它们在 C 语言中指针的类似操作。
|
||||
|
||||
### 向上类型转换(Upcasting)
|
||||
|
||||
向上类型转换是将一个子类对象引用转换为父类对象引用。由于子类继承了父类的所有方法和属性,子类对象也包含父类对象的所有部分,因此这种转换是安全且隐式的。
|
||||
|
||||
**例子:**
|
||||
```java
|
||||
class Animal {
|
||||
void makeSound() {
|
||||
System.out.println("Animal sound");
|
||||
}
|
||||
}
|
||||
|
||||
class Dog extends Animal {
|
||||
void makeSound() {
|
||||
System.out.println("Bark");
|
||||
}
|
||||
}
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
Dog dog = new Dog();
|
||||
Animal animal = dog; // Upcasting, 隐式转换
|
||||
animal.makeSound(); // 输出:Bark
|
||||
}
|
||||
}
|
||||
```
|
||||
在这个例子中,`Dog` 类型的对象 `dog` 被转换为 `Animal` 类型。尽管 `animal` 引用的实际对象是 `Dog`,但在编译时,`animal` 被视为 `Animal` 类型。
|
||||
|
||||
### 开始调试
|
||||
1. 设置断点:在代码中你想要查看变量的地方设置一个断点。
|
||||
2. 启动调试:点击调试按钮启动调试会话。
|
||||
### 向下类型转换(Downcasting)
|
||||
|
||||
### 查看变量
|
||||
当调试器在断点处暂停时,你可以在调试控制台中使用 GDB 命令来查看变量。以下是一些常用的 GDB 命令:
|
||||
向下类型转换是将一个父类对象引用转换为子类对象引用。由于父类对象不一定具有子类的所有方法和属性,因此这种转换需要显式进行,并且在运行时进行类型检查(使用 `instanceof` 关键字来确保安全)。
|
||||
|
||||
- `print variable_name`:打印变量的值。
|
||||
- `info locals`:打印当前作用域中的所有局部变量。
|
||||
- `whatis variable_name`:显示变量的类型。
|
||||
**例子:**
|
||||
```java
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
Animal animal = new Dog(); // Upcasting
|
||||
if (animal instanceof Dog) {
|
||||
Dog dog = (Dog) animal; // Downcasting, 显式转换
|
||||
dog.makeSound(); // 输出:Bark
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
在这个例子中,`animal` 引用的对象实际是 `Dog` 类型,在向下转换之前使用 `instanceof` 检查以确保安全。
|
||||
|
||||
### 区分指针变量和普通变量
|
||||
指针变量和普通变量的主要区别在于它们的类型和存储的内容。指针变量存储的是地址,而普通变量存储的是实际的值。通过 GDB 命令可以很容易地看到这种区别。
|
||||
### 区别
|
||||
|
||||
#### 示例
|
||||
假设有如下代码:
|
||||
- **向上类型转换**:隐式的,安全的,因为子类是父类的扩展。
|
||||
- **向下类型转换**:显式的,可能不安全,需要运行时检查,因为父类不一定具有子类的特性。
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
### C 语言中的指针转换
|
||||
|
||||
在 C 语言中,指针的转换类似于引用类型的转换,但由于 C 语言没有继承和多态的概念,其转换更多是基于内存布局。
|
||||
|
||||
**例子:**
|
||||
```c
|
||||
#include <stdio.h>
|
||||
|
||||
void printInt(void* ptr) {
|
||||
printf("%d\n", *(int*)ptr);
|
||||
}
|
||||
|
||||
int main() {
|
||||
int a = 10;
|
||||
int *p = &a;
|
||||
std::cout << "a: " << a << ", p: " << p << std::endl;
|
||||
int x = 10;
|
||||
void* voidPtr = &x; // Upcasting, 隐式转换
|
||||
printInt(voidPtr); // 输出:10
|
||||
|
||||
int* intPtr = (int*)voidPtr; // Downcasting, 显式转换
|
||||
printf("%d\n", *intPtr); // 输出:10
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
在这个例子中,`void*` 是通用指针类型,可以指向任何类型的数据。将 `int*` 转换为 `void*` 是隐式的,而将 `void*` 转换为 `int*` 是显式的。由于 C 没有类型检查,因此这种转换需要程序员自己确保安全。
|
||||
|
||||
在 CLion 中设置断点并开始调试,程序将在 `std::cout` 行暂停。此时在调试控制台中输入以下命令:
|
||||
|
||||
- `print a`:输出变量 `a` 的值,应该是 `10`。
|
||||
- `print p`:输出指针变量 `p` 的值,即 `a` 的地址。
|
||||
- `print *p`:输出指针 `p` 指向的值,即 `10`。
|
||||
|
||||
通过这些命令,你可以看到指针变量 `p` 实际上存储的是一个地址,而普通变量 `a` 存储的是一个整数值。
|
||||
|
||||
#### 使用反汇编
|
||||
在某些情况下,你可能需要查看反汇编代码来更深入地理解变量的存储方式。使用以下命令可以查看当前函数的反汇编代码:
|
||||
|
||||
- `disassemble`:反汇编当前函数的代码。
|
||||
- `x/4wx &a`:查看变量 `a` 的内存内容。
|
||||
|
||||
### 总结
|
||||
在 CLion 中使用 GDB 调试时,通过查看变量值和反汇编代码,可以清楚地区分指针变量和普通变量。指针变量存储地址,而普通变量存储实际的值,通过适当的 GDB 命令可以轻松辨别两者的区别。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
总结:
|
||||
- Java 中的向上类型转换和向下类型转换是为了支持多态性和继承,向上转换是安全的,向下转换需要显式进行并且进行运行时检查。
|
||||
- C 语言中的指针转换没有多态性和继承的概念,但有类似的指针类型转换操作,程序员需要确保转换的安全性。
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user