指针入门:概念、应用与实践
立即解锁
发布时间: 2025-08-20 01:16:49 阅读量: 1 订阅数: 4 


Arduino C语言编程入门与实践
### 指针入门:概念、应用与实践
#### 指针运算与数组偏移
在编程中,指针运算和数组偏移是很重要的概念。比如下面的代码:
```cpp
Serial.print(greet[i]);
Serial.print(greet + i);
Serial.print(2200 + 0);
Serial.print(2200);
```
这似乎能正确处理数组的第一个元素。但当 `i = 1` 时:
```cpp
Serial.print(greet[i]);
Serial.print(greet + 1);
Serial.print(2200 + 1);
Serial.print(2201);
```
`2201` 并不是数组中第二个元素的左值(lvalue)。这是因为编译器在计算数组偏移时,会根据数据类型的标量(scalar)来缩放偏移量。对于 `int` 类型,我们可以这样验证:
```cpp
Serial.print(*(greet + i)); // 等同于下面的写法
Serial.print(*(greet + i * scaler));
Serial.print(*(greet + i * sizeof(int)));
Serial.print(*(greet + i * 2));
Serial.print(*(2200 + 1 * 2));
Serial.print(*(2202)); // 当 i = 1 时的左值
```
这里 `2202` 是数组第二个元素的左值,说明这种计算方式是正确的。另外,使用 `Serial.print(*ptr++);` 也能正常工作,因为所有指针运算都会根据底层数据类型进行缩放。在 `int` 类型的情况下,每次递增偏移量会增加 2,因为 `int` 类型每个元素需要 2 个字节的内存。而且,当我们改变程序中使用的数据类型时,编译器会自动调整标量。
#### 值传递与引用传递
为了证明值传递(pass-by-value)和引用传递(pass-by-reference)的区别,我们来看下面的代码示例。
**值传递示例**:
```cpp
void setup() {
Serial.begin(9600);
int number = 10;
Serial.print("lvalue for number is ");
Serial.print((int) &number);
Serial.print(" rvalue for number is ");
Serial.println(number);
SquareIt(number);
Serial.print("After call: rvalue for number is ");
Serial.println(number);
}
void loop() {}
void SquareIt(int temp)
{
Serial.print("In SquareIt(), lvalue for temp is ");
Serial.print((int) &temp);
Serial.print(" rvalue for temp is ");
Serial.println(temp);
temp *= temp;
Serial.print("The new rvalue for temp is ");
Serial.println(temp);
}
```
运行这个程序,在 `setup()` 函数中 `number` 的左值是 8694,右值(rvalue)是 10。调用 `SquareIt()` 函数时,`temp` 接收了 `number` 的值。在 `SquareIt()` 函数内部,`temp` 的左值是 8687,右值是 10。对 `temp` 进行平方运算后,其右值变为 100。但回到 `setup()` 函数后,`number` 的右值仍然是 10,说明这里是值传递,传递的是值的副本,函数内部对副本的修改不会影响原始变量。
**引用传递示例**:
将 `setup()` 函数中的调用语句从 `SquareIt(number);` 改为 `SquareIt(&number);`,并修改 `SquareIt()` 函数如下:
```cpp
void SquareIt(int *temp)
{
Serial.print("In SquareIt(), lvalue for temp is ");
Serial.print((int) &temp);
Serial.print(" rvalue for temp is ");
Serial.println((int) temp);
*temp = *temp * *temp;
Serial.print("The new rvalue for temp is ");
Serial.println(*temp);
}
```
这里使用了取地址运算符 `&` 传递 `number` 的左值,而不是其右值的副本。在 `SquareIt()` 函数内部,`temp` 的右值是 8694,与 `number` 的左值相同。通过间接运算符 `*` 对 `temp` 进行操作,实际上是对 `number` 进行操作。执行 `*temp = *temp * *temp;` 后,`number` 的右值被永久改变为 100。这就是引用传递的效果,函数可以直接访问和修改原始变量的值。
#### 编程实践:控制双 LED 电路
现在我们来解决一个编程问题:使用双 LED 电路,编写一个程序,通过 `Serial` 监视器从用户那里获取一个一位数字。用户输入的数字有以下含义:
| 输入值 | LED 状态 |
| ---- | ---- |
| 0 | 两个 LED 都不亮 |
| 1 | LED1 亮 |
| 2 | LED2 亮 |
| 3 | 两个 LED 都亮 |
| 其他 | 保持当前 LED 状态 |
我们需要使用两个新函数:`GetInput()` 用于获取用户输入,返回值为 0 表示输入无效,1 表示输入有效;`LightLEDs()` 用于根据用户输入的值控制 LED 的开关。
**初始化步骤**:
```cpp
#define LED1 11
#define LED2 10
void setup() {
Serial.begin(9600);
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
}
```
这里定义了两个符号常量 `LED1` 和 `LED2` 表示 LED 对应的引脚。不使用引脚 0 和 1 是因为它们用于 USB 连接的数据传输,避免使用引脚 2 和 3 是因为它们是许多 Arduino 板上仅有的外部中断引脚。
**循环函数**:
```cpp
void loop() {
int goodBadFlag; // 输入是否有效
int LEDValue;
Serial.println();
goodBadFlag = GetInput(&LEDValue);
Serial.print("flag = ");
Serial.print(goodBadFlag);
Serial.print
```
0
0
复制全文
相关推荐









