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

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

一、前言

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

二、数组基础

2.1 一维数组定义与初始化

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

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

2). 初始化方式

// 1. 全部初始化intarr1[5]={1,2,3,4,5};// 2. 部分初始化,未赋值元素自动补0intarr2[5]={1,2};// 3. 省略数组长度,长度由初始化元素个数决定intarr3[]={1,2,3};// 4. 字符数组(字符串)charstr1[6]={'a','b','c','d','e','\0'};charstr2[]="abcde";// 自动末尾补 '\0' 结束符

2.2 数组访问与下标

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

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

2.3 数组名的含义(重点)

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

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

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

intlen=sizeof(arr)/sizeof(arr[0]);

三、指针基础

3.1 指针概念

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

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

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

inta=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 个「指向类型」的长度
示例:

inta=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),语法合法,不推荐使用。
完整示例:

intarr[3]={10,20,30};inti=0;printf("%d ",arr[i]);printf("%d ",*(arr+i));// 两行代码输出结果一致

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

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

#include<stdio.h>intmain(){intarr[5]={1,2,3,4,5};int*p=arr;// p 指向数组首元素intlen=sizeof(arr)/sizeof(arr[0]);for(inti=0;i<len;i++){printf("%d ",*(p+i));}return0;}

精简遍历写法:

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

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

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

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

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

4.4 二级指针(简单了解)

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

inta=10;int*p=&a;// 一级指针int**pp=&p;// 二级指针printf("%d",**pp);// 输出 10

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

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

voidtest(intarr[]){intlen=sizeof(arr)/sizeof(arr[0]);// 结果错误,arr已是指针}

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

#include<stdio.h>voidprintArr(int*arr,intlen){for(inti=0;i<len;i++){printf("%d ",arr[i]);}}intmain(){intarr[5]={1,2,3,4,5};intlen=sizeof(arr)/sizeof(arr[0]);printArr(arr,len);// 数组名传参,等价传首元素地址return0;}

函数形参两种等价写法:

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

六、经典易错点总结

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

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

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

七、常用代码模板

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

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

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

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

模板 3:指针遍历字符串

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

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

voiddealData(int*buf,intn){// 功能代码}

八、学习总结

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