指针和数组
首先给出结论:数组和指针 在本质类型上是完全不一样的东西。
“数组是常量指针”这种方法只是为了方便理解,因为你可以在功能上利用指针指向数组类型,并进行一些元素的操作,仅此而已
(这也仅仅是因为指针的功能强大,能够到达数组类型的内存单元里)。
那为什么指针能够与数组相匹配,这就要找编译器:
int *pointer = array;
看上去像是数组作为“指针”赋给了pointer,实际上这里有编译器进行的隐式类型转换,你可以看做
int *pointer = &array[0]
只是把指针 指向了 数组(首元素/变量)的地址。
p[item] == array[item] 也只是方便用户的编译器的把戏。
p[item] 实际上就是*(p+item),即使你写成item[p] 也没关系。
这一点在多维数组上也是一样。
——————————————————————
多维"数组"的实现
静态数组
int array[num1][num2] ...
没什么好说的,数据大小的不确定会导致这种实现浪费很多的内存空间,甚至爆栈。
静态指针(指针配合数组)
通过数组存放指针,而这些指针可以指向一连串 存放数据 的空间,或者继续指向数组地址(首元素)——直到降维到单个元素。
//第二维度
int* arr_1[4] = { 1,2,3,4 }, *arr_2[4] = { 0 }, *arr_3[4] = { 5,6,7 }, *arr_4[4] = { 8 };
//第一维度:指针的指针
int** arr[4] = { arr_1,arr_2,arr_3,arr_4 };
动态指针(指针配合指针)
//声明一个 int* 的指针,并分配 NUM1 个 int 长度的空间
int** arr = (char**)malloc(NUM1 * sizeof(int*));
//令每个元素指向一个指针,此指针其后的内存空间被分配了 NUM2 个 int 长度的空间
for (size_t i = 0; i < NUM1; i++)
{
arr[i] = (char*)malloc(NUM2 * sizeof(int));
}
行指针(指针配合数组指针)
int* (*arr)[NUM] = NULL;
中括号中的NUM不是要编译器申请足够的空间大小,(它本身是一个指针的大小),而是提醒该指针要位移NUM大小的数据。(数组指针的位移大小与该数组属性有关)
你可以理解为:指向 数组指针 的 指针。
eg:
int* arr_1[16] = { 1,2,3,4,5,6,7,8 };
int* (*arr)[4] = arr_1;
for (size_t i = 0; i < 4; i++)
{
for (size_t j = 0; j < 4; j++)
{
printf("%d ", arr[i][j]);
}printf("\n");
}
一个非常基础的地方:malloc申请的指针,或者说需要动态申请空间的指针不能用{ }初始化,因为编译器不能预先知道它的空间。(静态数组,数组指针 →可)
——————————————————————
二级指针的传参
1.实参作为普通数组
void func1(array[num1][num2]);
void func2(array[][num]);
void func3(int (*array)[num]);
//——————————————————————————————————————————————
void main()
{
array[num1][num2]={ ...};
}
2.实参作为二级指针
void func(int **array,int num1,int num2);
//1.分配连续内存 (一长条)
int **arr=new int*[num1];
arr[0]=new int[num1*num2];
delete []arr[0];
delete []arr;
//2.二级指针 → 指针数组 →原普通数组 的 行
int n[3][3]={{1,2,3},{4,5,6},{7,8,9}};
int **arr=new int*[3];
arr[0]=n[0];
arr[1]=n[1];
arr[2]=n[2];
func(arr,3,3);
delete []arr;
//3.手动new二级指针
int **arr=new int*[num1];
for(int i=0;i<num1;++i)
arr[num1]=new int[num2]; //在这里有个缺点:数组在内存中的分布不是连续的
func(arr);
for(int i=0;i<3;++i)
delete []arr[num1];//记住new了几个就一定要delete几个
delete []arr;
3.
int arr[3][3]={{1,2,3},{4,5,6},{7,8,9}};
func((int **)arr,3,3); //强转,指针退化
————————————————————
for(int i=0;i<n;++i){
for(int j=0;j<m;++j)
cout<<*((int *)arr+i*m+j)<<" "; //根据地址进行操作
——————————————————————
关于指针
C语言中,不同数据类型的指针,它的本质区别只是在内存中延伸的内存空间大小。
这个延伸的内存空间大小可以称之为引用范围。(范围和被引用数据大小相同)
像某某数据类型只是相对抽象的方便说法,底层来看都是在字节空间上,配合自身指向的数据位移,没有什么不同。
char*为1,int*为4,double*为8
提到引用范围,有必要提一句 空类型指针void* pointer
该指针不能事先知道指向数据的内存大小,没有确定的引用范围,只能指向内存地址,不能直接解地址使用;故使用时要根据场景,进行强制类型转换。
两个例子
eg1:
#include<iostream>
using namespace std;
void memswap(void* arr_1,void* arr_2,size_t size)
{
void * temp=NULL ;
temp = realloc(temp,size);
memcpy(temp,arr_1,size);
memcpy(arr_1,arr_2,size);
memcpy(arr_2,temp,size);
temp = realloc(temp,0);
free(temp);
}
void bublesort(void* array,int item,int count,int (*comp_fun)(void*,void*))
{
int flag=1;
void *start = array;
for(int j=0;flag==1;j++)
{
flag=0;
array = start;
for(int i=0;i<count-1-j;i++,array=(array)+item)
{
if(comp_fun(array,(void*)(array+item))>0)
{
memswap(array,(void*)(array+item),item);
flag=1;
}
}
}
}
int comp(void* arr_1,void* arr_2)
{
return *(int*)arr_1 - *(int*)arr_2;
}
int main()
{
int arr[10]= {1,3,8,2,99,54,6,7,3,2};
bublesort(arr,sizeof(arr[0]),10,comp);
for (int i = 0;i < 10;i++)printf("%d ",arr[i]);
}
一个用到void*,函数指针的冒泡排序(引用)
eg2:
typedef int (* fp)(void *para, void *end);
typedef int (*p)(char *s);
int print(char *s);
int main()
{
p pf;
pf = &print;
pf("阿星");
return 0;
}
int print(char *s)
{
printf("值为:%s\n",s);
return getchar();
}