目录
一、指针与数组
(一)数组名
- 数组名是一个地址常量,不能进行++、–操作,但是可以"数组名+i"
- 获取整个数组的地址:&数组名
- 数组名是数组的首个元素的地址
(二)数组+/-运算
1、数组地址
int arr[3]={11,22,33};
arr:数组名,数组的首元素地址,是一个地址常量
arr[0]:数组的第0个元素,对应的值是一个变量
&arr:数组的整个地址,值为首元素地址
&arr[0]:数组首个元素的地址
2、加减运算
int arr[3]={11,22,33};
arr+1:数组名arr是首个元素的地址,+1移动一个元素,4个字节
&arr[0]+1:&arr[0]是数组首个元素的地址,+1移动一个元素,4个字节
&arr+1:移动了整个数组的大小,12个字节
arr[0]+1:arr[0]里面的数值+1,值为12
*(arr+0):获取arr[0]地址中的值,值为11
*(arr+1):获取arr[1]地址中的值,值为22
- 注:[ ] 下标运算符
arr[i] == *(arr + i)
//等价关系
二、二维数组与指针
(一)数组地址
逻辑结构
int arr\[3]\[2] = {{11,22},{33,44},{55,66}};
arr:二维数组的数组名称,也是首元素地址(首个元素为嵌套内层的数组)
arr[0]:第0行首个元素的首地址,相当于内嵌的第0个数组的数组名
arr[0][0]:二维数组的第0个元素
&arr[0][0]:二维数组第0个元素的地址
&arr[0]:二维数组第0行地址
&arr:取到整个数组的地址,它的地址
(二)加减运算
arr+1
:移动二维数组一行的大小,可以将arr理解为是数组类型元素的数组,+1就是移动第一个元素,即第一个内套数组的大小
arr[0] +1:arr[0]相当于第一个内层数组的数组名,+1移动4个字节
&arr[0][0] + 1:移动4个字节大小
&arr[0] + 1:移动8个字节大小
&arr + 1:移动整个二维数组的大小,移动24个字节的大小
eg: 单层嵌套遍历整个二维数组
#include <stdio.h>
int main(int argc, const char *argv[])
{
int arr[3][2]={{11,22},{33,44},{55,66}};
int len=sizeof(arr)/sizeof(arr[0])*sizeof(arr[0])/sizeof(arr[0][0]);
int *p= (int *)arr; //将二维数组强制类型转换为一维数组
for(int i=0;i<len;i++)
{
printf("%d ",*(p+i));
}
putchar(10);
return 0;
}
三、数组名传参
数组名作为函数参数进行传递时,会降维成为指针
(一)遍历二维数组
法一:下标
for(int i=0;i<line;i++)
{
for(int j=0;j<col;j++)
printf("%d ",arr[i][j]);
putchar(10);
}
法二:
for(int i=0;i<line;i++)
{
for(int j=0;j<col;j++)
printf("%d ",(*(arr+i))[j]);
putchar(10);
}
法三:
for(int i=0;i<line;i++)
{
for(int j=0;j<col;j++)
printf("%d ",*(arr[i]+j));
putchar(10);
}
法四:
for(int i=0;i<line;i++)
{
for(int j=0;j<col;j++)
printf("%d ",*((*(arr+i))+j));
putchar(10);
}
法五:
int *p=(int *)arr;
for(int i=0;i<line*col;i++)
{
printf("%d ",*(p+i));
putchar(10);
}
法六:
int *k=&arr[0][0];
for(int i=0;i<line*col;i++)
{
printf("%d ",p[i]);
putchar(10);
}
return 0;
四、二级指针
(一)定义
1. 格式
(1)二级指针是指向一级指针的地址
(2)定义时需要使用**,但是只起到标识作用,p才是指针
(3)二级指针在内存中,如果是32位计算机占用4个字节;如果是64位计算机占用8个字节。
(4)二级指针+1,即移动一个一级指针的大小个数,即如果是32位计算机移动4个字节;如果是64位计算机移动8个字节。
(5)二级指针的数据类型是 int**
eg: 通过二级指针访问变量
#include <stdio.h>
int main(int argc, const char *argv[])
{
int number = 10;
int *p = &number;
int **q = &p;
//通过二级指针读变量的值
printf("%d\n",**q);
//通过二级指针写变量
**q = 200;
printf("%d\n",**q);
return 0;
}
输出结果:
(二)二级指针传参
eg: 分配一块内存空间,并将分配的空间的首地址传递给指针p
#include <stdio.h>
#include <stdlib.h>
void memory(int **p)
{
*p = (int *)malloc(128); //malloc函数申请空间完毕,会返回申请的空间的首地址
}
int main(int argc, const char *argv[])
{
int *p = NULL;
memory(&p);
printf("%p\n",p);
return 0;
}
输出结果:
- 注:为什么memory函数传递参数是int**,而非int*类型?
- 析:假设memory函数中传递的参数是int* 类型的参数,p也是int*类型,当调用该函数时,“memory§;”,此时在memory中的操作均是对形参的操作,对实参p没有影响,则达不到向p中写入地址的目的。
- 而如果传递int**类型的参数,则需要传入p指针的地址,即"memory(&p);",此时再对*p进行操作,则可以改变p的值。
(三)多级指针
int a;
//变量
int *p = &a;
//一级指针存放变量的地址
int **pp = &p;
//二级指针存放一级指针的地址
int ***ppp = &pp;
//三级指针存放二级指针的地址
五、数组指针
(一)概念
数组指针,本质是一个指针,指针指向的是一个数组。
(二)练习
eg: 数组指针
#include <stdio.h>
int main(int argc, const char *argv[])
{
int arr[5]={1,2,3,4,5};
int (*p)[5]=&arr; //&arr是整个数组的地址
for(int i=0;i<5;i++)
{
printf("%d ",(*p)[i]);
}
putchar(10);
return 0;
}
eg: 使用数组指针遍历二维数组
#include <stdio.h>
int main(int argc, const char *argv[])
{
int arr[2][2]={{1,2},{3,4}};
int (*p)[2]=arr; //arr就是第一个元素的首地址,arr是int [2]类型
for(int i=0;i<2;i++)
{
for (int j=0;j<2;j++)
printf("%d ",(*(p+i))[j]);
}
putchar(10);
return 0;
}
eg: 定义一个函数,找到数组中的最大值和最小值
#include <stdio.h>
void function(int (*a)[2],int line,int col,int *max,int *min)
{//此处传参必须写"int (*a)[2]",不能写"int (*a)[]",会报错
*max = (*a)[0];
*min = a[0][0];//两种方式
for(int i=0;i<line;i++)
{ for(int j=0;j<col;j++)
{
if(((*(a+i))[j]) > (*max))
*max = (*(a+i))[j];
if(a[i][j] < (*min))
*min = (*(a+i))[j];
}
}
}
int main(int argc, const char *argv[])
{
int arr[3][2];
for(int i=0;i<3;i++)
{
for (int j=0;j<2;j++)
scanf("%d ",&arr[i][j]);
}
int max=arr[0][0];
int min=arr[0][0];
function(arr,3,2,&max,&min);
printf("max:%d;min:%d\n",max,min);
return 0;
}
六、指针数组
(一)概念
指针数组本质上是一个数组,这个数组中存放的元素的数据类型是指针。
(二)练习
eg: 通过指针数组遍历多个变量
#include <stdio.h>
int main(int argc, const char *argv[])
{
int a=100;
int b=200;
int c=300;
int *pa = &a;
int *pb = &b;
int *pc = &c;
int *arr[3] ={pa,pb,pc};
for(int i=0;i<3;i++)
{
printf("%d ",*arr[i]);
}
putchar(10);
return 0;
}
(三)特殊的字符串定义方法
char *str="Hello world";
指针指向常量区的一个值
- 注意:此时指针指向的是常量区的值,不允许更改
char *arr[3]={"Hello world","hello python","hello baidu"};
eg: 使用上述方法定义一个字符数组,并打印出来
#include <stdio.h>
int main(int argc, const char *argv[])
{
char *s = "this is a string";
char *arr[3]={"Hello world","I love C","Work hard"};
printf("%s\n",s);
for(int i=0;i<3;i++)
{
printf("%p:%s\n",arr[i],arr[i]);
printf("%d:%c\n",i,*arr[i]);
}
return 0;
}
输出结果:
- 注:
-
- arr中存放的是指针,指针存放的是各个字符串的首个字符的地址
-
- 当输出格式是%p时,打印出指针存放的地址
-
- 当输出格式是%s时,与之对应的参数是一个指向字符串的指针,打印时找到指针指向的空间的值,一直打印直到遇到’\0’
-
- *arr[i] 取出的是指针指向的空间的值;如果此时使用%s,会将取到的值当作一个指针去寻址,然后到该地址去取得值。编译时会报警告,运行时程序会崩溃。现象如下:
七、练习
有一个二维数组 int a[5][4],二维数组的首地址是100,int (*p)[4] = a,问 *(p+2)+3是多少?
答案:144
#include <stdio.h>
int main(int argc, const char *argv[])
{
int arr[5][4];
printf("%p\n",arr[0]);
int (*p)[4]=arr;
printf("%p\n",*(p+2)+3);
printf("%#u\n",(*(p+2)+3)-(arr[0]));
//相差多少个元素大小
printf("%#u\n",(char)(*(p+2)+3)-(char)(arr[0]));
//强制类型转换为char,相差多少个字节
return 0;
}