首页 > 其他分享 >指针详解(day18)

指针详解(day18)

时间:2022-11-29 22:33:01浏览次数:58  
标签:arr int 数组名 地址 详解 数组 day18 指针

目录

指针详解(day18)_数组名

指针基础:指针就是地址,在c语言中指针与指针变量大部分情况下不做区分。指针变量在内存中通常以4/8个字节的空间存储,根据系统位数确定。

实例

int main()
{
int a = 10;
int* pa = &a;
printf("%d\n%d\n", sizeof(a), sizeof(pa));
return 0;
}

结果

4
8

在本机使用的64位系统中,指针变量的大小,即存放地址的变量向内存申请的大小为64bit位,8字节。

1.字符指针

实例

int main()
{
char arr[10] = "abcdef";
char* arr_p = arr; //数组名退化为整个数组的地址,即首元素的地址
printf("%s\n", arr_p);//打印指针变量会直接取出该指针下的内容
return 0;
}

结果

abcdef

注意:此处的数组名虽然退化为首元素的地址,能用相对应的指针变量接收,但体现的仍然是数组整体特征。

实例

int main()
{
const char* arr_p = "abcdef";
printf("%s\n", arr_p);
return 0;
}

“abcdef”在此处为const char型数组,既然是数组,在表达式中传递的仍然是该数组地址,但是相应的根据数据类型需要用 const char型指针变量接收该地址

结果

abcdef

实例

int main()
{
const char* st1 = "abc";
const char* st2 = "abc";
if (st1 == st2)
printf("1\n");
else
printf("0\n");
char st3[] = "abc";
char st4[] = "abc";
if (st3 == st4)
printf("1\n");
else
printf("0\n");
}

结果

1
0

该结果表明:

1)当内存储存一个字符串常量时,对同一常量设置不同的指针变量接收,该指针变量的指针都会指向该常量的地址。即编译器会先分配内存存储该常量,再根据该常量的地址设置接收地址的指针变量。

2)当设置不同数组存储同一字符串时,会首先根据数组开辟空间,然后再向该空间存储值,所以每创建一个数组,无论其储存的值是什么,都会在内存中开辟空间容纳该数组,所以即使数组内的内容相同,显然数组的地址不同。

2.数组指针

实例:遍历整数数组

int main()
{
int arr_1[10] = { 1,2,3 };
int arr_2[10] = { 11,12,13 };
int arr_3[10] = { 21,22,23 };
int* arr[5] = { arr_1,arr_2,arr_3 };
int i;
for (i = 0; i < 3; i++)
{
int j;
for (j = 0; j < 3; j++)
{
printf("%5d", *(arr[i] + j));//j的步长实际由arr[i]决定
}
printf("\n");
}
return 0;
}

结果

    1    2    3
11 12 13
21 22 23

实现逻辑:

1)创建3个整数数组

2)创建一个指针数组接收3个整数数组的地址

3)遍历指针数组,解引用地址(arr【i】+j)。

4)当i= 0时,遍历arr_1数组。打印(arr【i】+j)地址处的数值。步长j由该该指针数组储存的数组类型决定。显然因为arr_1为整型数组,步长j为4个字节。

注意:arr[i]传递的是首元素的地址,步长由数组类型即所存储的元素大小决定。当把arr[i]改为&arr[i]时,指针就会出现越界访问。形成野指针。

实例

int main()
{
int arr_1[10] = { 1,2,3 };
int arr_2[10] = { 11,12,13 };
int arr_3[10] = { 21,22,23 };
int* arr[5] = { arr_1,arr_2,arr_3 };
int i;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 3; j++)
{
printf("%5s", *(&arr[i] + j));
}
printf("\n");
}
return 0;
}

结果

        
(null)
(null)(null)
-1785006136-1785006072-1785006008
-1785006072-1785006008 0
-1785006008 0 0

出现上述错误的原因很可能是本来应该预计指针访问4个字节,即一个整数的内存,但因为直接提取了整个数组的地址,指针类型错误,致使访问的内存为40个字节,打印出来的是整个数组串行所构成的整数,整体越界后刚好覆盖到第二个数组,再往后为空。

指针数组与数组指针

指针数组:储存指针的数组

int* arr[3] ={&a,&b,&c};

数组指针:指向该数组的指针

int(*arr_p)[]= &arr;

3.指针数组

数组指针的创建

指针详解(day18)_数组_02

该创建逻辑为:

1)创建一个指针

2)该指针指向一个拥有5个元素的数组

3)数组内元素为int型

数组名传参时传递的是首元素地址,步长为数组元素类型的字节。

数组指针的应用

实例1(数组名传递整个数组)

void print_1(int arr[3][4], int x, int y)//函数型参接收的是整个数组
{
int i = 0;
int j = 0;
for (i = 0; i < x; i++)
{
for (j = 0; j < y; j++)
{
printf("%4d", arr[i][j]);
}
printf("\n");
}
}

int main()
{
int a[3][4] = { {1,2,3,4},{5,6,7,8},{9,10,11,12} };
print_1(a, 3, 4);
return 0;
}

结果

   1   2   3   4
5 6 7 8
9 10 11 12

注意:该函数传参时,数组名传递的是整个数组。数组名传参的类型和接收时定义的接受类型有关。

实例2(函数传递首元素地址)

void print_2(int(*p_arr)[4], int x, int y)//接收的为数组(一维数组)指针
{
int i = 0;
for (i = 0; i < x; i++)
{
int j = 0;
for (j = 0; j < y; j++)
{
printf("%4d", *(*(p_arr + i) + j));
}
printf("\n");
}
}

int main()
{
int a[3][4] = { {1,2,3,4},{5,6,7,8},{9,10,11,12} };
print_1(a, 3, 4);//数组名传参时传递的是整个数组
print_2(a, 3, 4);//数组名传参时传递的是首元素(一维数组)的地址
return 0;
}

具体来看一下该数组指针是如何取得各元素的

1)向函数传递首元素(第一组一维数组)的地址

2)进入行循环

3)进入列循环

4)*(*(p_arr + i) + j)

p_arr为首行数组的地址,i的步长由该一维数组大小决定,因为解引用得到的是一个存放四个整型元素的数组,所以i的步长为16个字节,所以+i能改变行。

*(p_arr +i)就是解引用第i行的地址,得出的是整个第i行的内容,显然为一个一维数组。

*(*(p_arr + i) + j)第一次解引用得到i行的数组,第二次解引用得到的是i行数组下的第j个元素。j的步长由该数组元素决定,即4个字节。

综上可知

步长显然是由解引用后得到的内容确定,解引用得到一个数组,则步长为该数组的大小,解引用得到一个元素,步长为该元素的大小。

解引用数组名后得到的是该地址下的所有内容,但是在操作符传参时,传递的是数组名所代指的首元素地址,所以解引用能够顺利执行,即解引用解的是地址,得的是内容,传递过程的步长大小由解引用的内容决定。

*(p_arr + i)解出来的是整个数组,但在传参过程中该数组以数组名形式传递,数组名退化为了首元素的地址,即第i行首字节的地址。步长i由解引用的结果确定,为数组大小。

((p_arr + i) + j)解出来是*(p_arr + i)数组的第j个元素。 

以上就是数组指针的一个应用。

      printf("%4d", *(*(p_arr + i) + j));

显然也可以写作

      printf("%4d", (*(p_arr + i))[j];

因为*(p_arr + i)解出来的是整个数组,该数组既可以再次解引用(数组退化为数组名),也可以直接用操作数组的方式操作该数组。

可以简单地理解为:

整个数组内容->数组名->数组内的首元素地址

指针详解(day18)_指针数组_03

1) 一个名为arr的数组,数组有5个int元素的数组

2)一个名为parr1的数组,数组有10个*元素(指针数组),*元素(指针)指向的元素为整型

3)一个名为parr2的指针,指针指向一个数组,该数组有10个int型元素

4)一个名为parr3的数组,该数组存有十个*元素(指针),*元素(指针)指向一个数组,该数组有5个元素,元素为整型。

4.数组传参

一维数组传参

void test_1(int arr[])//数组名传递一维数组,传值函数
{}
void test_1(int arr[10])//数组名传递大小确定的一维数组,传值函数
{}
void test_1(int* arr)//数组名传递地址(首元素),传址函数
{}
void test_2(int* arr[])//数组名传递指针数组,传址函数
{}
void test_2(int** arr)//数组名传递二级指针取值,传值函数
{}

int main()
{
int arr1[10] = { 0 };//整型数组
int* arr2[20] = { 0 };//指针数组
test_1(arr1);
test_2(arr2);

return 0;
}

二级指针,是指向另一个指向目标值的指针,也就是指向指针的指针。这个概念也叫做“多级间址”,或“多级间接地址(multiple indirection)”。普通指针的值是含预期值变量的地址。二级指针中,第一个指针含第二个指针的地址,第二个指针再指向含预期值的变量。

   间接寻址的级数不受限制,但极少需要二级以上的间址。实际上,过深的间址难以理解,容易引起概念错误。作为指向指针的指针变量,必须这样声明,即通过在变量名的前面放置两个星号(**)来实现。

指针详解(day18)_数组_04

**为取值运算,取出的是           ->指向地址->指向内容

二维数组传参

void test(int arr[2][3])//创建与原二维数组大小相同的型参可接收
{}
void test(int arr[][])//未定义大小的二维数组无法接收
{}
void test(int arr[3][4])//创建行列不同的二维数组也无法接收
{}
void test(int arr[][4])//创建行不同列相同的数组可以接收,但不建议
{}

int main()
{
int arr[2][3] = { {1,2,3},{4,5,6} };
test(arr);
return 0;
}

注意:二维数组传值与一维数组有所不同,二维数组的传值函数,其型参必须与原二维数组的列数量保持一致。

标签:arr,int,数组名,地址,详解,数组,day18,指针
From: https://blog.51cto.com/u_15862591/5897181

相关文章

  • 详解支持向量机-基于SVM的ROC曲线和AUC面积【菜菜的sklearn课堂笔记】
    视频作者:菜菜TsaiTsai链接:【技术干货】菜菜的机器学习sklearn【全85集】Python进阶_哔哩哔哩_bilibili手动绘制SVM的ROC曲线对于ROC曲线,我们要注意的是正类的概率和阈......
  • 双指针算法
    双指针算法大致格式如下:for(inti=0;i<n;i++){ while(j<i&&check(i,j))j++; //每道题目的具体逻辑}核心思想:for(inti=0;i<n;i++){ for(int......
  • 计算机网络详解(基础篇)1-3章(韩立刚老师)
    1.lnternet发展网络:让部分电脑实现短距通信(较小范围:如实验室)互联网:路由器连接多个网络形成互联网,实现远距通信。(任意部门或单位或个体都可接入)通讯介质可以是光纤或无......
  • Unity--Cinemachine官方实例详解
    1.2DCamera搭建一个快速场景,MainCamera选择Orthographic。在Cinemachine下有Create2DCamera,在生成的相机中设置follow,同时注意body的设置,如下图所示在虚拟相机中还需要......
  • 插头DP详解
    前言图片来源:我老师的PDF因为我不会画图前排膜拜一波(插头dp虽然模版难度就是黑,但是我认为并不难。我认为dp的难度排序:ddp(动态DP,P4719)>分治dp>插头dp>决策单调性优化dp>......
  • services资源+pod详解
    services资源+pod详解一、Service虽然每个Pod都会分配一个单独的PodIP,然而却存在如下两问题:PodIP会随着Pod的重建产生变化PodIP仅仅是集群内可见的虚拟IP,外部无......
  • Day26:内部类的详解
    内部类1.1内部类概述内部类:就是在一个类中定义另外一个类。例如我们在A类中定义一个B类,那么B类就是A类的内部类,A则是B的外部类。好比我们的手机是一个类,而手机内部的零......
  • Rockwell EDI 855 采购订单确认报文详解
    罗克韦尔自动化与国内12家授权分销商,124家认可的系统集成商,30多家亚太区的Encompass战略合作伙伴和全球战略联盟,共同为制造业企业提供广泛的世界一流的产品、解决方案与......
  • Redis详解(二)——AOF
    转载Redis详解(二)——AOF前言RDB持久化存在一个缺点是一定时间内做一次备份,如果redis意外down掉的话,就会丢失最后一次快照后的所有修改(数据有丢失)。对于数据完整性要......
  • const引用和指针
    1、可以为const引用初始化一个非const的对象、字面值,甚至是一般表达式。2、对引用初始化时必须严格进行类型匹配,但是const引用初始化时不需要类型匹配,只要可以转换为const......