欢迎光临
我们一直在努力

C语言指针深入学习笔记

1. 引言

今天对 C 语言中的指针内容进行了进一步的学习,从基础概念到实际应用,逐步深入理解了指针的本质与用法。本文记录今天的学习心得与关键知识点,帮助自己巩固记忆,也希望能给同样在学习 C 语言指针的读者一些参考。

2. 指针的基本概念回顾

指针是 C 语言的灵魂,它存储的是变量的内存地址。通过指针,我们可以间接访问和操作内存中的数据。

int a = 10;
int *p = &a; // p 指向变量 a 的地址
printf("%d\\n", *p); // 通过解引用访问 a 的值,输出 10

核心要点:

  • & 取地址运算符,获取变量的内存地址
  • * 解引用运算符,通过地址访问对应内存中的值
  • 指针变量本身也占用内存,存储的是地址值

3. 指针与数组的关系

4. 指针操作整型一维数组

今天重点练习了用指针操作整型一维数组,这是指针最常用的实战场景之一。下面通过几个典型例子来加深理解。

4.1 用指针遍历数组

#include <stdio.h>

int main() {
int arr[5] = {10, 20, 30, 40, 50};
int *p = arr; // 指向首元素

// 方式一:下标访问
for (int i = 0; i < 5; i++) {
printf("arr[%d] = %d\\n", i, arr[i]);
}

// 方式二:指针偏移访问
for (int i = 0; i < 5; i++) {
printf("*(p + %d) = %d\\n", i, *(p + i));
}

// 方式三:指针自增遍历
for (int *q = arr; q < arr + 5; q++) {
printf("%d ", *q);
}
printf("\\n");
return 0;
}

三种方式结果完全一致,方式三最能体现指针的“移动”特性。

4.2 指针作为函数参数操作数组

把数组传给函数时,实际传递的是指向首元素的指针,函数内可以通过指针修改原数组。

#include <stdio.h>

// 用指针遍历并打印数组
void print_array(int *p, int n) {
for (int i = 0; i < n; i++) {
printf("%d ", *(p + i));
}
printf("\\n");
}

// 用指针把数组每个元素翻倍
void double_array(int *p, int n) {
for (int i = 0; i < n; i++) {
*(p + i) *= 2;
}
}

int main() {
int arr[5] = {1, 2, 3, 4, 5};
print_array(arr, 5); // 输出 1 2 3 4 5
double_array(arr, 5); // 通过指针修改原数组
print_array(arr, 5); // 输出 2 4 6 8 10
return 0;
}

要点:

  • 形参 int *p 与 int p[] 在函数参数中是等价的
  • 通过指针修改的是原数组,调用方无需返回值即可看到变化
  • 函数内无法直接得知数组长度,需要额外传入元素个数 n

4.3 指针与数组名的区别

int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;

// 数组名是常量地址,不能重新赋值
// arr = p; // 错误!数组名不是左值

// 指针变量可以重新指向
p = arr + 2; // 合法,p 现在指向 arr[2]

printf("%d\\n", *p); // 输出 3

关键区别:

  • 数组名是常量指针,不能改变指向
  • 指针变量可以随时改变指向
  • sizeof(arr) 得到整个数组的字节数,而 sizeof(p) 只得到指针本身的大小

今天重点学习了指针与数组的紧密联系。数组名在大多数表达式中会退化为指向首元素的指针。

int arr[5] = {1, 2, 3, 4, 5};
int *p = arr; // 等价于 int *p = &arr[0]

// 以下三种写法等价
printf("%d\\n", arr[2]);
printf("%d\\n", *(arr + 2));
printf("%d\\n", *(p + 2));

指针算术的要点:

  • p + 1 不是简单地地址加 1,而是加上 sizeof(int) 个字节
  • 指针的加减运算以所指向类型的大小为单位
  • 数组越界访问是未定义行为,使用指针时要格外小心

5. 指针与函数

今天还学习了指针作为函数参数的用法,这是实现函数修改外部变量的关键手段。

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

int main() {
int x = 3, y = 5;
swap(&x, &y); // 传地址,函数内修改生效
printf("x=%d, y=%d\\n", x, y); // 输出 x=5, y=3
return 0;
}

理解要点:

  • C 语言函数参数默认是值传递
  • 传入指针后,函数内通过解引用可以修改调用方的变量
  • 这是 C 语言实现“引用传递”效果的标准方式

今天还学习了 const 在函数中的用法,它主要用于保护数据不被意外修改,是编写安全代码的重要手段。

#include <stdio.h>

// 用 const 修饰形参,表示函数内不能通过该指针修改所指向的数据
void print_array(const int *p, int n) {
for (int i = 0; i < n; i++) {
printf("%d ", *(p + i));
}
printf("\\n");
}

int main() {
int arr[5] = {1, 2, 3, 4, 5};
print_array(arr, 5); // 只读访问,不会修改数组
return 0;
}

const 修饰函数形参的要点:

  • const int *p 表示指针指向的数据是只读的,函数内不能通过 *p 修改它
  • 这样既能把数组传给函数,又能防止函数意外改动原数据,提高代码安全性
  • 如果函数确实需要修改数据,就不加 const,如前面的 double_array 例子

5. 指针与字符串

今天还学习了用指针表示字符串的方式,这是 C 语言中非常常见的用法。字符串在 C 语言中本质上是字符数组,因此指针操作字符串与操作数组的思路一脉相承。

5.1 用字符指针指向字符串常量

#include <stdio.h>

int main() {
char *str = "Hello, C!"; // str 指向字符串常量首字符
printf("%s\\n", str); // 输出 Hello, C!
return 0;
}

要点:

  • 字符串常量存储在只读区,char *str 只是保存了首字符的地址
  • 通过 %s 打印时,从首地址开始逐个输出,直到遇到 '\\0' 结束符
  • 这种方式不能修改字符串内容,因为指向的是只读的字符串常量

5.2 用字符数组与指针遍历字符串

#include <stdio.h>

int main() {
char str[] = "Hello"; // 字符数组,内容可修改
char *p = str; // p 指向数组首元素

// 用指针逐个访问字符
while (*p != '\\0') {
printf("%c", *p);
p++;
}
printf("\\n");
return 0;
}

要点:

  • 字符数组 str[] 在栈上分配内存,内容可以修改
  • 指针 p 从首字符开始,通过自增逐个移动,直到遇到 '\\0' 结束
  • 这种方式与之前遍历整型数组的思路完全一致,只是结束条件变成了判断结束符

5.3 字符指针与字符数组的区别

char str[] = "Hello"; // 字符数组:内容可修改
char *p = "Hello"; // 字符指针:指向字符串常量,内容只读

str[0] = 'h'; // 合法,数组内容可修改
// p[0] = 'h'; // 错误!p 指向只读的字符串常量,修改会导致未定义行为

关键区别:

  • 字符数组在内存中分配了实际空间,可以修改其中的字符
  • 字符指针只是保存了字符串常量的地址,指向的内容是只读的
  • 用 sizeof(str) 得到整个数组的大小(含 '\\0'),而 sizeof(p) 只得到指针本身的大小

6. 今日学习总结

今天对指针的学习收获颇丰,从基础概念到数组、函数,逐步建立了对指针的整体认识。指针虽然初学时容易困惑,但理解了“地址”与“解引用”的本质后,很多问题都能迎刃而解。

后续还需要继续深入学习的方面:

  • 多级指针(指针的指针)
  • 函数指针
  • 指针与结构体的结合使用
  • 指针与动态内存分配
  • 常见指针错误的排查与调试

7. 参考资料

  • 《C Primer Plus》指针相关章节
  • C 语言标准文档中关于指针的说明
  • 各类 C 语言在线教程与练习平台
赞(0)
未经允许不得转载:171主机测评 » C语言指针深入学习笔记
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址