news 2026/4/16 10:13:32

指向数组的指针变量

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
指向数组的指针变量

一、引言:数组的本质是什么?

在C语言中,数组和指针有着密不可分的关系。很多人认为数组就是一段连续的内存空间,这没错,但更重要的是要理解:数组名本质上就是一个指向数组第一个元素的常量指针。

想象一下,数组就像一栋公寓楼,每个房间(元素)都有门牌号(下标)。而指针就像这栋楼的地址牌,它告诉我们这栋楼在哪里。更重要的是,通过这个地址牌,我们可以找到楼里的任何一个房间。

intapartments[5]={101,102,103,104,105};// 一栋5个房间的公寓楼// 数组名apartments就是这栋楼的地址

二、数组名:被伪装的指针

2.1 数组名的真实身份

让我们通过代码揭示数组名的本质:

#include<stdio.h>intmain(){intnumbers[5]={10,20,30,40,50};printf("=== 数组名的真面目 ===\n");// 证明1:数组名就是一个地址printf("数组名numbers的值: %p\n",(void*)numbers);printf("第一个元素的地址: %p\n",(void*)&numbers[0]);// 证明2:数组名可以像指针一样使用printf("\n通过数组名访问元素:\n");printf("numbers[0] = %d\n",numbers[0]);printf("*numbers = %d\n",*numbers);// 看!可以解引用// 证明3:数组名可以参与指针运算printf("\n指针运算:\n");printf("*(numbers + 1) = %d\n",*(numbers+1));// 相当于numbers[1]printf("*(numbers + 2) = %d\n",*(numbers+2));// 相当于numbers[2]// 但是!数组名不是普通的指针变量printf("\n重要区别:\n");// numbers = numbers + 1; // 错误!数组名是常量,不能赋值printf("数组名是常量指针,不能修改它的值\n");return0;}

2.2 数组与指针的关系总结

特性 数组名 指针变量
本质 常量指针 变量
能否修改指向 不能 能
存储的内容 数组首地址 可以存储任何地址
sizeof 整个数组大小 指针本身大小
&运算符 整个数组的地址 指针变量的地址

#include<stdio.h>intmain(){intarr[5]={1,2,3,4,5};int*ptr=arr;// ptr指向数组的第一个元素printf("sizeof区别:\n");printf("sizeof(arr) = %zu (整个数组的大小: 5*4=20)\n",sizeof(arr));printf("sizeof(ptr) = %zu (指针的大小,通常是4或8)\n",sizeof(ptr));printf("\n&运算符区别:\n");printf("&arr = %p (整个数组的地址)\n",(void*)&arr);printf("arr = %p (第一个元素的地址)\n",(void*)arr);printf("&ptr = %p (指针变量本身的地址)\n",(void*)&ptr);return0;}

三、指向一维数组的指针变量

3.1 如何声明指向一维数组的指针?

指向一维数组的指针有两种声明方式:

#include<stdio.h>intmain(){intarr[5]={10,20,30,40,50};// 方式1:指向数组元素的指针(常用)int*ptr1=arr;// 指向int的指针// 或者 int *ptr1 = &arr[0];// 方式2:指向整个数组的指针(不常用,用于多维数组)int(*ptr2)[5]=&arr;// 指向包含5个int的数组的指针printf("方式1 - 指向元素的指针:\n");printf("ptr1 = %p, *ptr1 = %d\n",(void*)ptr1,*ptr1);printf("ptr1+1 = %p, *(ptr1+1) = %d\n",(void*)(ptr1+1),*(ptr1+1));printf("\n方式2 - 指向整个数组的指针:\n");printf("ptr2 = %p\n",(void*)ptr2);printf("*ptr2 = %p (数组名)\n",(void*)*ptr2);printf("**ptr2 = %d (第一个元素)\n",**ptr2);printf("ptr2+1 = %p (跳过了整个数组!)\n",(void*)(ptr2+1));return0;}

3.2 指针遍历一维数组

使用指针遍历数组比使用下标更高效,也更"C语言风格":

#include<stdio.h>voidtraverse_array(){printf("=== 指针遍历一维数组的三种方法 ===\n");intscores[6]={85,92,78,90,88,95};int*ptr;// 方法1:指针作为游标printf("方法1 - 指针游标:\n");for(ptr=scores;ptr<scores+6;ptr++){printf("%d ",*ptr);}printf("\n");// 方法2:使用下标和指针printf("\n方法2 - 指针加下标:\n");ptr=scores;// 重新指向开始for(inti=0;i<6;i++){printf("scores[%d] = %d, *(ptr+%d) = %d\n",i,scores[i],i,*(ptr+i));}// 方法3:指针与数组名混合printf("\n方法3 - 数组名作为指针:\n");for(inti=0;i<6;i++){printf("%d ",*(scores+i));// 数组名就是指针}printf("\n");// 实际应用:计算数组平均值printf("\n应用:计算平均值\n");ptr=scores;intsum=0;for(inti=0;i<6;i++){sum+=*(ptr+i);}printf("平均分: %.2f\n",sum/6.0);}

3.3 指针运算的深入理解

指针运算不是简单的数学加减,而是基于类型的地址计算:

#include<stdio.h>voidpointer_arithmetic_demo(){printf("=== 指针运算的本质 ===\n");charchars[5]={'A','B','C','D','E'};intints[5]={10,20,30,40,50};doubledoubles[5]={1.1,2.2,3.3,4.4,5.5};char*cp=chars;int*ip=ints;double*dp=doubles;printf("初始地址:\n");printf("cp = %p\n",(void*)cp);printf("ip = %p\n",(void*)ip);printf("dp = %p\n",(void*)dp);printf("\n指针加1后的地址:\n");printf("cp + 1 = %p (增加了 %ld 字节)\n",(void*)(cp+1),(char*)(cp+1)-(char*)cp);printf("ip + 1 = %p (增加了 %ld 字节)\n",(void*)(ip+1),(char*)(ip+1)-(char*)ip);printf("dp + 1 = %p (增加了 %ld 字节)\n",(void*)(dp+1),(char*)(dp+1)-(char*)dp);printf("\n验证:\n");printf("sizeof(char) = %zu\n",sizeof(char));printf("sizeof(int) = %zu\n",sizeof(int));printf("sizeof(double) = %zu\n",sizeof(double));printf("\n实际计算元素位置:\n");// 访问第3个元素(下标2)printf("chars[2] = %c, *(cp + 2) = %c\n",chars[2],*(cp+2));printf("ints[2] = %d, *(ip + 2) = %d\n",ints[2],*(ip+2));printf("doubles[2] = %.1f, *(dp + 2) = %.1f\n",doubles[2],*(dp+2));}

四、指向多维数组的指针变量

4.1 二维数组的内存布局

理解二维数组指针的关键是理解二维数组在内存中是按行连续存储的:

#include<stdio.h>voidtwo_dimension_memory(){printf("=== 二维数组的内存布局 ===\n");intmatrix[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};printf("逻辑视图:\n");for(inti=0;i<3;i++){printf("行 %d: ",i);for(intj=0;j<4;j++){printf("%2d ",matrix[i][j]);}printf("\n");}printf("\n物理内存视图:\n");printf("整个数组占用 %zu 字节\n",sizeof(matrix));printf("每个元素: %zu 字节\n",sizeof(matrix[0][0]));// 验证连续性printf("\n内存地址连续性验证:\n");for(inti=0;i<3;i++){for(intj=0;j<4;j++){printf("matrix[%d][%d] 地址: %p, 值: %2d\n",i,j,(void*)&matrix[i][j],matrix[i][j]);}}// 重要概念:二维数组名是什么?printf("\n二维数组名的含义:\n");printf("matrix = %p (指向第一行的指针)\n",(void*)matrix);printf("matrix[0] = %p (指向第一个元素的指针)\n",(void*)matrix[0]);printf("&matrix[0][0] = %p\n",(void*)&matrix[0][0]);printf("*matrix = %p (也是第一个元素的地址)\n",(void*)*matrix);}

4.2 指向二维数组的指针声明

声明指向二维数组的指针有多种方式,理解它们很重要:

#include<stdio.h>voidtwo_dimension_pointers(){printf("=== 指向二维数组的指针声明 ===\n");intarr[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};// 方式1:指向整个二维数组的指针int(*ptr1)[3][4]=&arr;printf("方式1 - 指向整个二维数组:\n");printf("ptr1 = %p\n",(void*)ptr1);printf("*ptr1 = %p (数组名)\n",(void*)*ptr1);printf("**ptr1 = %p (第一行的地址)\n",(void*)**ptr1);printf("***ptr1 = %d (第一个元素)\n",***ptr1);// 方式2:指向一维数组(行)的指针int(*ptr2)[4]=arr;// 或 &arr[0]printf("\n方式2 - 指向行的指针(常用):\n");printf("ptr2 = %p (指向第一行)\n",(void*)ptr2);printf("ptr2 + 1 = %p (指向第二行,跳过4个int)\n",(void*)(ptr2+1));printf("*(ptr2 + 1) = %p (第二行的首地址)\n",(void*)*(ptr2+1));printf("*(*(ptr2 + 1) + 2) = %d (第二行第三列: arr[1][2])\n",*(*(ptr2+1)+2));// 方式3:指向元素的指针int*ptr3=&arr[0][0];// 或 (int*)arr 或 *arrprintf("\n方式3 - 指向元素的指针:\n");printf("ptr3 = %p (第一个元素的地址)\n",(void*)ptr3);printf("ptr3 + 1 = %p (第二个元素的地址)\n",(void*)(ptr3+1));// 用一维方式访问二维数组printf("\n用一维指针遍历二维数组:\n");for(inti=0;i<3*4;i++){printf("%2d ",*(ptr3+i));if((i+1)%4==0)printf("\n");}}

4.3 用指针访问二维数组元素

有四种方法可以用指针访问二维数组元素:

#include<stdio.h>voidaccess_2d_array(){printf("=== 访问二维数组的四种方法 ===\n");intmatrix[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};printf("原始数组:\n");for(inti=0;i<3;i++){for(intj=0;j<4;j++){printf("%2d ",matrix[i][j]);}printf("\n");}printf("\n方法1: 数组下标法\n");for(inti=0;i<3;i++){for(intj=0;j<4;j++){printf("matrix[%d][%d]=%d ",i,j,matrix[i][j]);}printf("\n");}printf("\n方法2: 指针偏移法\n");int*p=&matrix[0][0];for(inti=0;i<3;i++){for(intj=0;j<4;j++){printf("*(p+%d)=%d ",i*4+j,*(p+i*4+j));}printf("\n");}printf("\n方法3: 行指针法\n");int(*row_ptr)[4]=matrix;for(inti=0;i<3;i++){for(intj=0;j<4;j++){printf("*(*(row_ptr+%d)+%d)=%d ",i,j,*(*(row_ptr+i)+j));}printf("\n");}printf("\n方法4: 混合法\n");for(inti=0;i<3;i++){for(intj=0;j<4;j++){printf("%d ",*(matrix[i]+j));}printf("\n");}}

五、三维及更高维数组的指针

5.1 三维数组的理解

三维数组可以理解为"数组的数组的数组":

#include<stdio.h>voidthree_dimension_demo(){printf("=== 三维数组与指针 ===\n");// 三维数组:2个平面,每个平面3行4列intcube[2][3][4]={{// 第一个平面{1,2,3,4},{5,6,7,8},{9,10,11,12}},{// 第二个平面{13,14,15,16},{17,18,19,20},{21,22,23,24}}};printf("三维数组大小: %zu 字节\n",sizeof(cube));printf("元素总数: 2 * 3 * 4 = %d\n",2*3*4);printf("\n内存地址验证:\n");// 验证内存连续性int*p=(int*)cube;for(inti=0;i<2*3*4;i++){printf("元素 %2d: 地址 %p, 值 %2d\n",i,(void*)(p+i),*(p+i));}printf("\n不同层次的指针:\n");// 指向整个三维数组的指针int(*ptr1)[2][3][4]=&cube;// 指向平面的指针(二维数组)int(*ptr2)[3][4]=cube;// 指向第一个平面// 指向行的指针(一维数组)int(*ptr3)[4]=cube[0];// 指向第一个平面的第一行// 指向元素的指针int*ptr4=&cube[0][0][0];printf("ptr1 (整个数组): %p\n",(void*)ptr1);printf("ptr2 (平面): %p\n",(void*)ptr2);printf("ptr3 (行): %p\n",(void*)ptr3);printf("ptr4 (元素): %p\n",(void*)ptr4);printf("\n访问元素 cube[1][2][3]:\n");printf("下标法: %d\n",cube[1][2][3]);printf("指针法: %d\n",*(*(*(cube+1)+2)+3));}

5.2 高维数组的通用访问模式

#include<stdio.h>voidhigh_dimension_pattern(){printf("=== 高维数组访问的通用模式 ===\n");// 四维数组示例inthyper[2][2][2][2]={{// 第一个三维块{// 第一个平面{1,2},{3,4}},{// 第二个平面{5,6},{7,8}}},{// 第二个三维块{// 第一个平面{9,10},{11,12}},{// 第二个平面{13,14},{15,16}}}};printf("四维数组访问模式:\n");// 通用规则:*(*(...*(数组名 + i) + j) + k) ...for(inta=0;a<2;a++){for(intb=0;b<2;b++){for(intc=0;c<2;c++){for(intd=0;d<2;d++){printf("hyper[%d][%d][%d][%d] = %2d ",a,b,c,d,hyper[a][b][c][d]);printf("指针访问: %2d\n",*(*(*(*(hyper+a)+b)+c)+d));}}}}printf("\n内存计算:\n");// 计算 hyper[1][1][1][1] 的位置int*base=(int*)hyper;intoffset=1*(2*2*2)+1*(2*2)+1*(2)+1;printf("hyper[1][1][1][1] 在偏移 %d 处\n",offset);printf("值应为: %d\n",*(base+offset));}

六、数组指针的实际应用

6.1 矩阵运算

#include<stdio.h>#defineROWS3#defineCOLS3// 矩阵相加voidmatrix_add(int(*a)[COLS],int(*b)[COLS],int(*result)[COLS]){for(inti=0;i<ROWS;i++){for(intj=0;j<COLS;j++){// 三种等价写法:// result[i][j] = a[i][j] + b[i][j];// *(*(result + i) + j) = *(*(a + i) + j) + *(*(b + i) + j);// 使用指针运算*(*(result+i)+j)=*(*(a+i)+j)+*(*(b+i)+j);}}}// 矩阵相乘voidmatrix_multiply(int(*a)[COLS],int(*b)[COLS],int(*result)[COLS]){for(inti=0;i<ROWS;i++){for(intj=0;j<COLS;j++){result[i][j]=0;for(intk=0;k<COLS;k++){result[i][j]+=a[i][k]*b[k][j];}}}}// 打印矩阵voidprint_matrix(int(*matrix)[COLS]){for(inti=0;i<ROWS;i++){for(intj=0;j<COLS;j++){printf("%4d ",*(*(matrix+i)+j));}printf("\n");}}voidmatrix_operations(){printf("=== 矩阵运算 ===\n");intA[ROWS][COLS]={{1,2,3},{4,5,6},{7,8,9}};intB[ROWS][COLS]={{9,8,7},{6,5,4},{3,2,1}};intC[ROWS][COLS];// 结果矩阵printf("矩阵A:\n");print_matrix(A);printf("\n矩阵B:\n");print_matrix(B);printf("\n矩阵相加 A + B:\n");matrix_add(A,B,C);print_matrix(C);printf("\n矩阵相乘 A × B:\n");matrix_multiply(A,B,C);print_matrix(C);}

6.2 图像处理示例

#include<stdio.h>#defineHEIGHT5#defineWIDTH5// 简单的图像处理函数voidinvert_image(int(*image)[WIDTH]){for(inti=0;i<HEIGHT;i++){for(intj=0;j<WIDTH;j++){// 反转像素值(假设0-255范围)*(*(image+i)+j)=255-*(*(image+i)+j);}}}// 模糊处理voidblur_image(int(*image)[WIDTH]){inttemp[HEIGHT][WIDTH];// 复制原图像for(inti=0;i<HEIGHT;i++){for(intj=0;j<WIDTH;j++){temp[i][j]=*(*(image+i)+j);}}// 应用简单的3x3模糊核for(inti=1;i<HEIGHT-1;i++){for(intj=1;j<WIDTH-1;j++){intsum=0;for(intdi=-1;di<=1;di++){for(intdj=-1;dj<=1;dj++){sum+=*(*(temp+i+di)+j+dj);}}*(*(image+i)+j)=sum/9;// 平均值}}}// 旋转图像90度voidrotate_image(int(*image)[WIDTH],int(*result)[HEIGHT]){// 注意:旋转后宽高交换for(inti=0;i<HEIGHT;i++){for(intj=0;j<WIDTH;j++){*(*(result+j)+(HEIGHT-1-i))=*(*(image+i)+j);}}}// 打印图像voidprint_image(int(*image)[WIDTH],intheight,intwidth){for(inti=0;i<height;i++){for(intj=0;j<width;j++){printf("%3d ",*(*(image+i)+j));}printf("\n");}}voidimage_processing_demo(){printf("=== 图像处理演示 ===\n");// 创建一个简单的5x5图像(灰度值)intimage[HEIGHT][WIDTH]={{200,200,200,200,200},{200,100,100,100,200},{200,100,50,100,200},{200,100,100,100,200},{200,200,200,200,200}};printf("原始图像:\n");print_image(image,HEIGHT,WIDTH);printf("\n反转图像:\n");invert_image(image);print_image(image,HEIGHT,WIDTH);printf("\n模糊处理:\n");blur_image(image);print_image(image,HEIGHT,WIDTH);printf("\n旋转90度:\n");introtated[WIDTH][HEIGHT];// 注意宽高交换rotate_image(image,rotated);print_image((int(*)[HEIGHT])rotated,WIDTH,HEIGHT);}

七、动态分配的数组指针

7.1 一维动态数组

#include<stdio.h>#include<stdlib.h>voiddynamic_1d_array(){printf("=== 一维动态数组 ===\n");intsize;printf("请输入数组大小: ");scanf("%d",&size);// 动态分配内存int*arr=(int*)malloc(size*sizeof(int));if(arr==NULL){printf("内存分配失败!\n");return;}// 初始化数组for(inti=0;i<size;i++){*(arr+i)=i*10;// 使用指针访问}// 打印数组printf("动态数组内容:\n");for(inti=0;i<size;i++){printf("arr[%d] = %d (地址: %p)\n",i,*(arr+i),(void*)(arr+i));}// 重新调整数组大小printf("\n重新调整数组大小...\n");intnew_size=size+3;arr=(int*)realloc(arr,new_size*sizeof(int));// 添加新元素for(inti=size;i<new_size;i++){*(arr+i)=i*10;}printf("调整后的数组:\n");for(inti=0;i<new_size;i++){printf("%d ",*(arr+i));}printf("\n");// 释放内存free(arr);arr=NULL;// 避免悬空指针}

7.2 二维动态数组

#include<stdio.h>#include<stdlib.h>voiddynamic_2d_array(){printf("=== 二维动态数组 ===\n");introws,cols;printf("请输入行数和列数: ");scanf("%d %d",&rows,&cols);// 方法1:使用指针数组(每行独立分配)printf("\n方法1: 指针数组方式\n");int**matrix1=(int**)malloc(rows*sizeof(int*));for(inti=0;i<rows;i++){matrix1[i]=(int*)malloc(cols*sizeof(int));// 初始化for(intj=0;j<cols;j++){*(*(matrix1+i)+j)=i*cols+j;}}// 访问for(inti=0;i<rows;i++){for(intj=0;j<cols;j++){printf("%3d ",*(*(matrix1+i)+j));}printf("\n");}// 释放内存for(inti=0;i<rows;i++){free(matrix1[i]);}free(matrix1);// 方法2:使用单个连续内存块printf("\n方法2: 连续内存方式\n");int*matrix2=(int*)malloc(rows*cols*sizeof(int));// 初始化for(inti=0;i<rows;i++){for(intj=0;j<cols;j++){*(matrix2+i*cols+j)=i*cols+j;}}// 访问for(inti=0;i<rows;i++){for(intj=0;j<cols;j++){printf("%3d ",*(matrix2+i*cols+j));}printf("\n");}free(matrix2);}

八、数组指针的高级技巧

8.1 指针数组 vs 数组指针

这两个概念容易混淆,但非常重要:

#include<stdio.h>voidpointer_array_vs_array_pointer(){printf("=== 指针数组 vs 数组指针 ===\n");printf("1. 指针数组 - 数组的元素是指针\n");inta=10,b=20,c=30;int*ptr_array[3];// 包含3个int指针的数组ptr_array[0]=&a;ptr_array[1]=&b;ptr_array[2]=&c;printf("指针数组内容:\n");for(inti=0;i<3;i++){printf("ptr_array[%d] = %p, *ptr_array[%d] = %d\n",i,(void*)ptr_array[i],i,*ptr_array[i]);}printf("\n2. 数组指针 - 指向数组的指针\n");intarr[3]={100,200,300};int(*array_ptr)[3]=&arr;// 指向包含3个int的数组的指针printf("数组指针:\n");printf("array_ptr = %p\n",(void*)array_ptr);printf("*array_ptr = %p (数组名)\n",(void*)*array_ptr);printf("(*array_ptr)[0] = %d\n",(*array_ptr)[0]);printf("(*array_ptr)[1] = %d\n",(*array_ptr)[1]);printf("\n3. 常见应用:\n");// 指针数组常用于字符串数组char*fruits[]={"Apple","Banana","Cherry","Date"};printf("水果列表:\n");for(inti=0;i<4;i++){printf("%s\n",fruits[i]);// fruits[i]是char指针}// 数组指针常用于二维数组intmatrix[2][3]={{1,2,3},{4,5,6}};int(*mat_ptr)[3]=matrix;// 指向包含3个int的数组的指针printf("\n矩阵访问:\n");for(inti=0;i<2;i++){for(intj=0;j<3;j++){printf("%d ",*(*(mat_ptr+i)+j));}printf("\n");}}

8.2 复杂声明解析

理解复杂的指针声明是C语言的重要技能:

#include<stdio.h>voidcomplex_declarations(){printf("=== 复杂声明解析 ===\n");// 规则:从变量名开始,向右看,向左看,交替进行printf("1. int *arr[5];\n");// 解析:arr[5] 是一个数组,有5个元素// *arr[5] 数组的元素是指针// int *arr[5] 指针指向int// 结论:arr是一个包含5个int指针的数组(指针数组)printf("2. int (*arr)[5];\n");// 解析:(*arr) arr是一个指针// (*arr)[5] 指针指向一个数组,数组有5个元素// int (*arr)[5] 数组的元素是int// 结论:arr是指向包含5个int的数组的指针(数组指针)printf("3. int *(*func)(int, int);\n");// 解析:(*func) func是一个指针// (*func)(int, int) 指针指向一个函数,函数接受两个int参数// *(*func)(int, int) 函数返回一个指针// int *(*func)(int, int) 指针指向int// 结论:func是一个函数指针,该函数接受两个int参数,返回int指针printf("\n实际示例:\n");// 示例1:指针数组intx=1,y=2,z=3;int*ptr_arr[3]={&x,&y,&z};// 示例2:数组指针intarr[5]={10,20,30,40,50};int(*arr_ptr)[5]=&arr;// 示例3:函数指针数组intadd(inta,intb){returna+b;}intsubtract(inta,intb){returna-b;}intmultiply(inta,intb){returna*b;}int(*func_array[3])(int,int)={add,subtract,multiply};char*op_names[]={"加","减","乘"};for(inti=0;i<3;i++){printf("5 %s 3 = %d\n",op_names[i],func_array[i](5,3));}}

九、常见错误与调试技巧

9.1 数组指针常见错误

#include<stdio.h>voidcommon_errors(){printf("=== 数组指针常见错误 ===\n");intarr[5]={1,2,3,4,5};printf("错误1: 数组越界\n");int*p=arr;// printf("arr[5] = %d\n", arr[5]); // 越界访问// printf("*(p+5) = %d\n", *(p+5)); // 越界访问printf("正确做法: 始终检查边界\n");for(inti=0;i<5;i++){printf("arr[%d] = %d\n",i,arr[i]);}printf("\n错误2: 混淆指针类型\n");intmatrix[3][4];// int *wrong_ptr = matrix; // 警告:类型不匹配int(*correct_ptr)[4]=matrix;// 正确printf("\n错误3: 对数组名赋值\n");// arr = arr + 1; // 错误:数组名是常量printf("\n错误4: 返回局部数组的地址\n");// int* bad_function() {// int local[5] = {1,2,3,4,5};// return local; // 错误:局部数组在函数结束时销毁// }printf("\n错误5: 错误的指针运算\n");inta[3][4];int*p1=&a[0][0];int(*p2)[4]=a;printf("p1 + 1 移动 %ld 字节\n",(char*)(p1+1)-(char*)p1);printf("p2 + 1 移动 %ld 字节\n",(char*)(p2+1)-(char*)p2);printf("注意:不同类型的指针运算步长不同\n");}

9.2 调试技巧

#include<stdio.h>#include<assert.h>#defineARRAY_SIZE(arr)(sizeof(arr)/sizeof(arr[0]))voiddebug_techniques(){printf("=== 数组指针调试技巧 ===\n");intmatrix[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};printf("技巧1: 使用宏计算数组大小\n");printf("行数: %zu\n",ARRAY_SIZE(matrix));printf("列数: %zu\n",ARRAY_SIZE(matrix[0]));printf("\n技巧2: 使用assert检查边界\n");introw=2,col=3;assert(row>=0&&row<ARRAY_SIZE(matrix));assert(col>=0&&col<ARRAY_SIZE(matrix[0]));printf("安全访问: matrix[%d][%d] = %d\n",row,col,matrix[row][col]);printf("\n技巧3: 打印调试信息\n");int*base=(int*)matrix;for(inti=0;i<3;i++){for(intj=0;j<4;j++){int*elem_ptr=&matrix[i][j];printf("matrix[%d][%d]: 地址=%p, 偏移=%ld, 值=%d\n",i,j,(void*)elem_ptr,(char*)elem_ptr-(char*)base,matrix[i][j]);}}printf("\n技巧4: 边界检查函数\n");intsafe_get(int(*arr)[4],introw,intcol,introws){if(row<0||row>=rows||col<0||col>=4){printf("错误: 访问越界 [%d][%d]\n",row,col);return-1;}return*(*(arr+row)+col);}printf("安全访问测试:\n");printf("matrix[1][2] = %d\n",safe_get(matrix,1,2,3));printf("matrix[3][0] = %d\n",safe_get(matrix,3,0,3));// 越界}

十、实战项目:学生成绩管理系统

#include<stdio.h>#include<stdlib.h>#include<string.h>#defineMAX_STUDENTS100#defineMAX_SUBJECTS5#defineSUBJECT_NAMES{"语文","数学","英语","物理","化学"}// 学生结构typedefstruct{intid;charname[20];floatscores[MAX_SUBJECTS];floataverage;floattotal;}Student;// 全局学生数组Student students[MAX_STUDENTS];intstudent_count=0;// 使用指针操作学生数组voidadd_student(){if(student_count>=MAX_STUDENTS){printf("学生数量已达上限!\n");return;}Student*stu=&students[student_count];// 指向当前学生printf("请输入学生信息:\n");printf("学号: ");scanf("%d",&stu->id);printf("姓名: ");scanf("%s",stu->name);stu->total=0;char*subjects[]=SUBJECT_NAMES;for(inti=0;i<MAX_SUBJECTS;i++){printf("%s成绩: ",subjects[i]);scanf("%f",&stu->scores[i]);stu->total+=stu->scores[i];}stu->average=stu->total/MAX_SUBJECTS;student_count++;printf("添加成功!\n");}// 使用指针数组排序voidsort_by_average(){// 创建指针数组Student*ptr_array[MAX_STUDENTS];for(inti=0;i<student_count;i++){ptr_array[i]=&students[i];}// 使用指针数组排序(不移动原始数据)for(inti=0;i<student_count-1;i++){for(intj=0;j<student_count-1-i;j++){if(ptr_array[j]->average<ptr_array[j+1]->average){Student*temp=ptr_array[j];ptr_array[j]=ptr_array[j+1];ptr_array[j+1]=temp;}}}printf("\n按平均分排序:\n");printf("%-10s %-10s ","学号","姓名");char*subjects[]=SUBJECT_NAMES;for(inti=0;i<MAX_SUBJECTS;i++){printf("%-8s ",subjects[i]);}printf("%-8s %-8s\n","总分","平均分");for(inti=0;i<student_count;i++){Student*stu=ptr_array[i];printf("%-10d %-10s ",stu->id,stu->name);for(intj=0;j<MAX_SUBJECTS;j++){printf("%-8.1f ",stu->scores[j]);}printf("%-8.1f %-8.1f\n",stu->total,stu->average);}}// 统计各科平均分voidsubject_statistics(){floatsubject_totals[MAX_SUBJECTS]={0};floatsubject_averages[MAX_SUBJECTS];char*subjects[]=SUBJECT_NAMES;// 使用指针遍历Student*stu_ptr=students;for(inti=0;i<student_count;i++){for(intj=0;j<MAX_SUBJECTS;j++){subject_totals[j]+=stu_ptr->scores[j];}stu_ptr++;// 指向下一个学生}printf("\n各科平均分:\n");for(inti=0;i<MAX_SUBJECTS;i++){subject_averages[i]=subject_totals[i]/student_count;printf("%s: %.2f\n",subjects[i],subject_averages[i]);}// 找出最高和最低平均分的科目intmax_index=0,min_index=0;for(inti=1;i<MAX_SUBJECTS;i++){if(subject_averages[i]>subject_averages[max_index]){max_index=i;}if(subject_averages[i]<subject_averages[min_index]){min_index=i;}}printf("\n最高平均分科目: %s (%.2f)\n",subjects[max_index],subject_averages[max_index]);printf("最低平均分科目: %s (%.2f)\n",subjects[min_index],subject_averages[min_index]);}// 查找学生Student*find_student_by_id(intid){for(inti=0;i<student_count;i++){if(students[i].id==id){return&students[i];// 返回指向学生的指针}}returnNULL;// 未找到}voidsearch_student(){intid;printf("请输入要查找的学号: ");scanf("%d",&id);Student*stu=find_student_by_id(id);if(stu==NULL){printf("未找到学号为 %d 的学生\n",id);return;}printf("\n找到学生:\n");printf("学号: %d\n",stu->id);printf("姓名: %s\n",stu->name);printf("成绩: ");for(inti=0;i<MAX_SUBJECTS;i++){printf("%.1f ",stu->scores[i]);}printf("\n总分: %.1f, 平均分: %.1f\n",stu->total,stu->average);}voidgrade_management_system(){printf("=== 学生成绩管理系统 ===\n");intchoice;// 添加一些测试数据student_count=3;for(inti=0;i<student_count;i++){students[i].id=1001+i;sprintf(students[i].name,"学生%d",i+1);for(intj=0;j<MAX_SUBJECTS;j++){students[i].scores[j]=60+rand()%40;students[i].total+=students[i].scores[j];}students[i].average=students[i].total/MAX_SUBJECTS;}do{printf("\n菜单:\n");printf("1. 添加学生\n");printf("2. 按平均分排序\n");printf("3. 统计各科成绩\n");printf("4. 查找学生\n");printf("5. 显示所有学生\n");printf("0. 退出\n");printf("请选择: ");scanf("%d",&choice);switch(choice){case1:add_student();break;case2:sort_by_average();break;case3:subject_statistics();break;case4:search_student();break;case5:sort_by_average();// 显示所有学生break;case0:printf("退出系统\n");break;default:printf("无效选择\n");}}while(choice!=0);}intmain(){grade_management_system();return0;}

十一、总结:数组与指针的核心要点

11.1 关键概念回顾

  1. 数组名是常量指针:指向数组的第一个元素
  2. 数组在内存中连续存储:这是指针访问数组的基础
  3. 指针运算基于类型:ptr + n 移动 n * sizeof(类型) 字节
  4. 多维数组的指针:需要理解数组的数组的概念
  5. 动态数组:使用malloc/free管理内存

11.2 访问方式的等价性

对于数组 arr 和指针 p = arr:

访问方式 一维数组 二维数组
下标法 arr[i] arr[i][j]
指针法 *(arr + i)((arr + i) + j)
混合法 *(p + i) *(p[i] + j)

11.3 学习建议

  1. 从一维开始:先掌握一维数组指针,再学多维
  2. 画图理解:画出内存布局图,标注地址和指针
  3. 实际编码:多写代码,多调试,观察内存变化
  4. 理解而不是记忆:理解原理,不要死记硬背语法
  5. 安全第一:始终检查边界,避免数组越界
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/13 18:59:59

29、Linux文件系统全面解析与操作指南

Linux文件系统全面解析与操作指南 1. 引言 在Linux系统中,文件系统是管理和组织文件的核心机制。它不仅决定了文件的存储方式,还影响着系统的性能和稳定性。本文将深入探讨Linux文件系统的各个方面,包括基本概念、目录结构、设备管理、挂载操作、文件系统检查与修复,以及…

作者头像 李华
网站建设 2026/4/13 22:24:21

实现RNDIS USB网络连接的必备配置与步骤!

RNDIS协议通过USB模拟以太网接口&#xff0c;实现即插即用的网络共享。要成功建立连接&#xff0c;必须在设备端开启RNDIS功能&#xff0c;并确保主机系统具备相应驱动支持&#xff0c;再通过标准网络配置完成IP通信。本文以Air780EPM系列核心板/开发板为例&#xff0c;分享在W…

作者头像 李华
网站建设 2026/4/3 2:03:38

机器人工程毕设 基于单片机的红外热视仪(源码+硬件+论文)

文章目录 0 前言1 主要功能2 硬件设计3 核心软件设计4 实现效果5 最后 0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往达不到毕业答辩的要求&#xff0c;这两年不断有学弟学妹告诉学长自己…

作者头像 李华
网站建设 2026/4/16 6:37:20

Android多屏显示终极优化:SecondScreen完整配置实战指南

Android多屏显示终极优化&#xff1a;SecondScreen完整配置实战指南 【免费下载链接】SecondScreen Better screen mirroring for Android devices 项目地址: https://gitcode.com/gh_mirrors/se/SecondScreen SecondScreen是一款专为Android设备设计的显示优化神器&…

作者头像 李华
网站建设 2026/4/15 9:18:20

相机动画总结-相机直线运动动画、相机圆周运动动画

相机动画总结&#xff08;.position 和 .lookAt()&#xff09; 核心概念 相机动画主要通过控制相机对象的 .position 属性和 .lookAt() 方法来实现&#xff1a; 1. .lookAt() 方法 功能&#xff1a;设置相机观察的焦点&#xff0c;相当于调整相机镜头的指向参数&#xff1a;可以…

作者头像 李华
网站建设 2026/4/10 23:08:48

VibeVoice-1.5B终极指南:零基础打造专业级AI播客

想要用AI技术制作专业播客却不知从何入手&#xff1f;VibeVoice-1.5B为你提供了完美的解决方案。这款前沿的开源文本转语音模型能够生成长达90分钟的多说话人对话音频&#xff0c;彻底改变了传统语音合成的局限性。 【免费下载链接】VibeVoice-1.5B 项目地址: https://ai.gi…

作者头像 李华