欢迎光临
我们一直在努力

C语言学习笔记20260601-指针和数组

一、前言

数组和指针是 C 语言核心重难点,二者联系紧密、用法互通。本文从基础语法、关系、常用写法、易错点、实战案例逐一梳理,适合入门复习、笔记归档。

二、数组基础

2.1 一维数组定义与初始化

1). 定义格式:数据类型 数组名[元素个数];
示例:

int arr[5]; // 定义包含5个int元素的数组,未初始化,值为随机垃圾值

2). 初始化方式

// 1. 全部初始化
int arr1[5] = {1,2,3,4,5};

// 2. 部分初始化,未赋值元素自动补0
int arr2[5] = {1,2};

// 3. 省略数组长度,长度由初始化元素个数决定
int arr3[] = {1,2,3};

// 4. 字符数组(字符串)
char str1[6] = {'a','b','c','d','e','\\0'};
char str2[] = "abcde"; // 自动末尾补 '\\0' 结束符

2.2 数组访问与下标

1). 数组下标从 0 开始,下标范围:0 ~ 元素个数-1
2). 访问格式:数组名[下标]
3). 严禁数组下标越界,C 语言不做下标检查,越界会篡改内存、程序崩溃。
示例:

int arr[3] = {10,20,30};
printf("%d", arr[0]); // 输出 10

2.3 数组名的含义(重点)

1). 普通场景:数组名代表数组首元素的地址(常量指针,值不可修改)。
2). 特殊场景(仅有两种):
sizeof(数组名):计算整个数组占用的总字节数
&数组名:取出整个数组的地址
区分示例:

int arr[5] = {1,2,3,4,5};
printf("%p\\n", arr); // 首元素地址
printf("%p\\n", &arr[0]);// 与 arr 地址完全一致
printf("%p\\n", &arr); // 整个数组的地址,数值和上面相同,但类型不同

3.计算数组元素个数公式(通用)

int len = sizeof(arr) / sizeof(arr[0]);

三、指针基础

3.1 指针概念

指针本质是存放内存地址的变量。
1). 普通变量:存放数据值
2). 指针变量:存放另一个变量的内存编号(地址)

3.2 指针定义、取地址、解引用

1)定义格式 :数据类型 * 指针变量名;
*仅表示该变量是指针,不参与类型运算。
2)核心运算符
&:取地址符,取出变量的内存地址
*:解引用符,根据地址找到对应内存中的数据
基础示例:

int a = 10;
int *p = &a; // p 存放变量 a 的地址

printf("%p\\n", &a); // a 的地址
printf("%p\\n", p); // 和 &a 完全一致
printf("%d\\n", *p); // 解引用,取出地址对应的值:10

*p = 20; // 通过指针修改 a 的值
printf("%d\\n", a); // 输出 20

3.3 空指针与野指针

1)空指针 NULL
NULL 本质是地址 0,代表指针不指向任何有效内存
用途:指针初始化、判断指针是否有效
2)野指针(高危错误)
指针指向随机、未知、不受管控的内存
产生原因:指针未初始化、指针指向的变量已销毁、越界访问
后果:程序崩溃、数据异常、内存篡改
解决:指针定义即初始化为 NULL,不使用后再次置空。

3.4 指针加减运算(核心)

指针加减不是单纯加减数字,而是按指向类型的字节跨度移动。
规则:
指针 + n:向后移动 n 个「指向类型」的长度
指针 – n:向前移动 n 个「指向类型」的长度
示例:

int a = 10;
int *p = &a;
p++; // int占4字节,p 地址 +4

四、指针与数组的深度关系(重中之重)

4.1 数组下标等价写法

数组访问 arr[i] 与指针写法 完全等价
公式:

arr[i] <==> *(arr + i)

推导:
1)arr 是首元素地址
2)arr + i:地址向后偏移 i 个元素
3)*(arr+i):解引用取出对应元素
反向写法也成立:i[arr] = *(i + arr),语法合法,不推荐使用。
完整示例:

int arr[3] = {10,20,30};
int i = 0;

printf("%d ", arr[i]);
printf("%d ", *(arr + i));
// 两行代码输出结果一致

4.2 指针遍历数组(最常用写法)

方式 1:数组名 + 下标(常规)
方式 2:指针变量遍历(工程常用)

#include <stdio.h>
int main()
{
int arr[5] = {1,2,3,4,5};
int *p = arr; // p 指向数组首元素
int len = sizeof(arr)/sizeof(arr[0]);

for(int i=0; i<len; i++)
{
printf("%d ", *(p + i));
}
return 0;
}

精简遍历写法:

int *p = arr;
while(*p != '\\0') // 字符数组/字符串专用
{
printf("%c", *p);
p++;
}

4.3 字符数组、字符串与字符指针

1 )字符数组(可修改内容)

char str[] = "hello";
str[0] = 'H'; // 合法,数组内存可改写

2)区分总结:
char str[]:栈内存,可读可写
char *str:指向只读字符串常量,只能读、不能改

4.4 二级指针(简单了解)

一级指针:指向普通变量
二级指针:指向一级指针变量
定义与使用:

int a = 10;
int *p = &a; // 一级指针
int **pp = &p; // 二级指针

printf("%d", **pp); // 输出 10

五、一维数组做函数参数(高频使用)

数组作为函数参数时,会退化为指针,函数内部无法用 sizeof 计算数组总大小。
错误写法(函数内求长度失效):

void test(int arr[])
{
int len = sizeof(arr)/sizeof(arr[0]); // 结果错误,arr已是指针
}

标准规范写法:
数组传参必须额外传递数组长度

#include <stdio.h>

void printArr(int *arr, int len)
{
for(int i=0; i<len; i++)
{
printf("%d ", arr[i]);
}
}

int main()
{
int arr[5] = {1,2,3,4,5};
int len = sizeof(arr)/sizeof(arr[0]);
printArr(arr, len); // 数组名传参,等价传首元素地址
return 0;
}

函数形参两种等价写法:

// 两种写法完全一致,本质都是指针
void func(int arr[]);
void func(int *arr);

六、经典易错点总结

1) 数组名是常量指针,不能被赋值

int arr[5];
arr = NULL; // 错误!数组名地址不可修改

2)下标越界:C 语言不检查下标,运行直接异常。
3)字符串结束符 \\0
字符数组手动赋值必须手动加 \\0,否则乱码
双引号字符串自动补充 \\0
4)指针未初始化 / 空指针解引用:程序直接崩溃。
5)数组传参退化指针:函数内不能用 sizeof 求数组长度。
6)字符串常量指针不可写:char *str = “abc” 不能修改内容。
7)&arr 和 arr 的区别:地址数值相同,类型不同,&arr+1 会跳过整个数组。

七、常用代码模板

模板 1:计算数组元素个数

int arr[] = {1,2,3,4,5};
int length = sizeof(arr) / sizeof(arr[0]);

模板 2:指针遍历整型数组

int *p = arr;
for(int i=0; i<length; i++)
{
printf("%d ", *(p++));
}

模板 3:指针遍历字符串

char str[] = "C language";
char *p = str;
while(*p)
{
printf("%c", *p);
p++;
}

模板 4:数组作为函数参数(标准格式)

void dealData(int *buf, int n)
{
// 功能代码
}

八、学习总结

1)数组是连续内存空间,靠下标访问;指针是存放地址的变量。
2)数组名默认代表首元素地址,因此数组和指针语法高度互通。
3)arr[i] 等价 *(arr+i),是二者互通的核心公式。
4)数组传参退化为指针,务必手动传递长度。
5)野指针、空指针、数组越界、字符串修改是四大高频 BUG。
6)指针和数组是后续结构体、动态内存、数据结构的基础,必须熟练掌握。

赞(0)
未经允许不得转载:171主机测评 » C语言学习笔记20260601-指针和数组
分享到: 更多 (0)

评论 抢沙发

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