--------------------------------------------------------------
------------------------------------------
---------------------
---------
目录
数组名的理解
1.当定义一个一维数组时,其数组名就是该数组的首地址。以下我们做了个实验每个数组的元素都有地址,我们使用&arr[0]取数组的首地址,在跟arr进行对比,我们发现数组名就是首地址。
2.数组名就是首元素地址,有两个例外:
1.sizeof:通常被我们使用来计算数组的长度(sizeof(arr)/sizeof(arr[0])),而sizeof括号里面的arr表示的不是数组名而是整个数组计算的是数组的大小,单位是字节。
2.&arr:这里的取地址数组名表示的是整个数组的地址。
除此之外的数组名arr都表示的是首元素的地址。
我们可以发现sizeof(arr)存储的是40个字节的空间,所以sizeof表示的是整个数组;而&arr[0]和arr增一后内存为其多开辟了4个字节的空间,而&arr则多了40个字节跳过了整个数组的长度,所以&arr表示的是整个数组的地址。
指针数组
1.定义:存放指针的数组(数组中的每一个元素都是指针类型);
2.形式:int*arr[3],数组存放三个元素,每个元素都是整形指针;
指针数组模拟二维数组
#include<stdio.h>
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 2,3,4,5,6 };
int arr3[] = { 3,4,5,6,7 };
int* arr4[] = { arr1,arr2,arr3 };
//数组名是首元素的地址,类型是int*的
//就可以存放在arr4数组中
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
printf("%d ",arr4[i][j]);
}
printf("\n");
}
return 0;
}
arr4[i]访问arr4数组的元素,arr4[i]找到了数组元素指向的一维数组,arr4[i][j]就是数组一维数组中的元素。
以上是模拟的二维数组,但并不是真正的二维数组,因为每行不是连续的。
int* arr4[] = {arr1,arr2,arr3},因为数组名是首元素的大小,所以arr[i]先访问的是首元素地址,在经过循环+j将数组的行数依次打印出来
指针访问数组
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
int* p = arr; //取首元素的地址
int rs = sizeof(arr) / sizeof(arr[0]); //计算数组长度
for (int i = 0; i < rs; i++)
{
scanf_s("%d", p + i); //利用for循环输入,现在的p存储的就是首元素的地址
//也可以这么写arr+i
} //首地址+i相当于跳过i个整形字节并找到对应数组下标的元素
for (int i = 0; i < rs; i++)
{
printf("%d ",*(p+i)); //*(p+i)相当于*(arr+i),而*(arr+i)可以写成arr[i]
} //那么*(p+1)也可以写成p[i],因为加法是满足交换律的还可以写成
//i[p],i[arr],不过最常用的还是*(p+i)或者arr[i]
return 0;
}
数组元素的访问在编译器处理的时候,也是换成首元素地址arr+偏移量i求出数组元素下标地址,然后解引用访问。
一维数组传参的本质
我们之前都是在函数外部计算数组元素个数,那我们可以把函数传给一个函数后,函数内部求数组元素个数吗?
函数内部没有正确的获得数组元素的个数。
数组传参的本质就是传递的是数组首元素的地址。
函数形参理论上应该使用指针变量来接收首元素的地址。
而我们在函数内部我们写sizeof(arr)计算的是一个地址的大小而不是数组的大小。
正因为函数的参数部分本质是指针,所以函数内部是没有办法求数组的元素个数的。
void test(int arr[]) void test(int* arr)
一维数组传参既能写成数组形式也能写成指针形式。
二级指针
指针变量也是变量,是变量就有地址,二级指针的作用就是用来存放一级指针变量的地址,相当与我们数学的二次求导。
#include <stdio.h>
int main()
{
int a = 10;
int* pa = &a;
int** paa = &pa;
printf("%d",**paa);
return 0;
}
我们通过对**paa进行解引用我们找到的是*pa,而*pa访问的是a
所以**paa解引用的结果是10
*ppa前面的*说明ppa是一个指针变量
int*说明paa指向的类型是int*
---------------------------------------------
------------------------------
-----------------------
-----------------
-----------
字符指针变量
1.在指针类型中我们知道一个指针类型char*,我们可以用来存储单个字符:
int main()
{
char ch = 'a';
char *pc = &ch; //pc就是字符指针
printf("%c\n",*pc);
return 0;
}
但是如果指针char*类型存储一个字符串存储的是整个字符串的地址吗?
#include<stdio.h>
int main()
{
char* p = "helloworld";
printf("%c\n", *p);
return 0;
}
我们发现char*类型不是把字符串“helloworld”放入p中,而是将字符串的首地址放进p中存储。
我们可以将字符串想象成一个字符数组,可以写成“helloworld”[3],此时访问的也就是'l'。
想象成的常量字符数组是不能修改的。
#include<stdio.h>
int main()
{
char* p = "helloworld";
printf("%c\n", *p);
printf("%c\n", "helloworld"[3]);
//因为p存储的"helloworld"的首元素地址,我们前面说到数组名就是首元素地址
//所以还可以这么写
printf("%c\n", p[3]);
return 0;
}
我将想象的字符数组的下标3的元素修改成a,但是编译器打印不了,所以是不可修改的。
为了避免这种情况可以在 char*类型前面加上const。
指向同一字符串的指针(地址)相同
1.指向相同元素的指针地址是相同的(指针类型相同的字符串只会保留一个地址);
2.而面对相同的字符串,数组会在开辟一个空间进行存储。
#include<stdio.h>
int main()
{
char ch1[] = "hello world";
char ch2[] = "hello world";
const char* ch3 = "hello world";
const char* ch4 = "hello world";
if (ch1 == ch2)
{
printf("地址相同\n");
}
else
{
printf("地址不同\n");
}
if (ch3 == ch4)
{
printf("地址相同\n");
}
else
{
printf("地址不同\n");
}
return 0;
}
通过编译器的监视我们可以发现:ch3和ch4的地址是相同的,ch1和ch2的地址是不相同的。
数组指针
定义:数组指针就是存放数组的指针。
与指针数组的区别:指针数组是数组,存放指针的数组。
int *p1[10];
//p1是数组,数组类型10个元素,每个元素的类型是int*,所以p1是指针数组
int (*p2)[10];
//p2是指针,指针指向的是数组,数组有10个元素,每个元素的类型是int,所以p2是数组指针
1.[ ]的优先级要高于*号,所以必须加上()来保证p先和*结合,变成指针类型。p先和*结合,说明p是一个指针变量。
数组指针的初始化
#include<stdio.h>
int main()
{
int arr[5] = { 1,2,3,4,5 };
&arr; //得到数组的地址
int(*arr)[5] = &arr;
//将数组的地址放在数组指针中
return 0;
}
int (*p)[5] = &arr
| | |
| | |
| | p指向的数组元素个数
| |p是数组指针变量名
|p指向的数组元素类型
数组指针的运用
数组指针主要运用在多维数组中:
二维数组传参的本质就是传递一维数组的指针
#include<stdio.h>
void test(int arr[3][5], int a, int b)
{
for (int i = 0; i < a; i++)
{
for (int j = 0; j < b; j++)
{
printf("%d ",arr[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
test(arr, 3, 5);
return 0;
}
根据数组名是数组首元素地址的这一规则,二维数组的数组名表示的就是第一行的地址,是一维数组的地址。根据上面的例子,第一行的一维数组的类型就是int [5],所以第一行的地址类型就是数组的指针类型int(*)[5]。二维数组的传参本质就是传递了一个一维数组的指针。
#include<stdio.h>
void test(int (*p)[5], int a, int b)
{
for (int i = 0; i < a; i++)
{
for (int j = 0; j < b; j++)
{
printf("%d ",*(*(p+i)+j));
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
test(arr, 3, 5);
return 0;
}
所以可以把传参的数组写成指针的形式:int(*p)[5]。
----------------------------------------------------------
-----------------------------------
-------------------------
-----------------
-----------
------
-------
------------------
-------------------------
----------------------------------------
------------------------------------------------------
感谢老铁支持!