指针的回顾
在C语言中,指针是一个变量,与其他数据不同的是,它的作用是用来存储其它变量的内存地址。指针可以指向不同类型的数据,包括整数、浮点数
、字符、数组等。通过使用指针,我们可以直接访问和修改存储在内存中的数据,而不需要进行复制或者传递大量的数据。
这是指针的大概情况,下面就要对指针进一步研究。
字符指针
对于字符来说,我们可以用char类型来创建变量,那么就有char*类型的指针,通常我们称之为字符指针。
先看以下代码:
int main()
{
char ch = 'w';
char *pc = &ch;
*pc = 'w';
return 0;
}
创建一个字符变量ch,字符指针pc指向ch的地址,对pc解引用就是'w'。
这是常规的用法,但在大多数情况下,以下情况更加普遍使用:
int main()
{
const char* pstr = "hello cc.";
printf("%s\n", pstr);
return 0;
}
在这里,打印出来的就是“hello cc",但pstr存的是字符串的首元素地址
,也就是说,对ptsr解引用,得到的是'h'。
在这里,会发现我在指针pstr加入了const,这是因为字符串的地址本来就不可改变,加上const,增加程序的可读性和维护性。看下面例子:
#include <stdio.h>
int main()
{
char str1[] = "hello cc.";
char str2[] = "hello cc.";
const char *str3 = "hello cc.";
const char *str4 = "hello cc.";
if(str1 ==str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");
if(str3 ==str4)
printf("str3 and str4 are same\n");
else
printf("str3 and str4 are not same\n");
return 0;
}
结果:
对于str1和str2,它们是字符数组,对于数组来说,会在内存中开辟空间,而str1和str2是两个不同的字符数组,虽然它们的内容相同,但开辟的空间肯定是不一样的,而数组名一般又是指首元素的地址,所以各不相同;对于str3和str4,它们是字符指针,相当与常量字符串自己开辟了空间,然后两个指针都会指向内容相同的字符串首元素。
指针数组
顾名思义,它是一个数组,只是每个数组里面存放的是指针;
我们可以用指针数组实现二维数组
int main()
{
int arr1[] = { 1,2,3,4,5 };//arr1 - int*
int arr2[] = { 2,3,4,5,6 };
int arr3[] = { 3,4,5,6,7 };
//指针数组
int* arr[3] = { arr1, arr2, arr3 };
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
对于一维数组来说,本质上就是一维指针,也就是arr1 类型为int*.
数组指针
指针的定义
数组指针,是一个指针,指向数组的指针。
int *p1[10];//指针数组
int (*p2)[10];//数组指针
这里要注意:[]的优先级要高于号的,所以必须加上()来保证p先和结合。
&数组名VS数组名
int arr[10];
我们知道,arr是指首元素地址,&arr指的是整个数组的地址;
int main()
{
int arr[10] = { 0 };
printf("%p\n", arr);
printf("%p\n", &arr[0]);
printf("%p\n",&arr);
return 0;
}
上面虽然的地址都是一样,这是因为刚好都指向了首元素地址
int main()
{
int arr[10] = { 0 };
printf("%p\n", arr);//
printf("%p\n", arr+1);//
printf("%p\n", &arr[0]);//
printf("%p\n", &arr[0]+1);//
printf("%p\n", &arr);//
printf("%p\n", &arr+1);//
return 0;
}
但我们把16进制转换为10进制后&arr+1就增加了40个bit,但arr+1相对于arr只增加了4bit;
根据上面的代码我们发现,其实&arr和arr,虽然值是一样的,但是意义应该不一样的。
实际上: &arr 表示的是数组的地址,而不是数组首元素的地址.
数组指针的使用
直接看以下代码:
#include <stdio.h>
void print_arr1(int arr[3][5], int row, int col)
{
int i = 0;
for(i=0; i<row; i++)
{
for(j=0; j<col; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
void print_arr2(int (*arr)[5], int row, int col)
{
int i = 0;
for(i=0; i<row; i++)
{
for(j=0; j<col; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][5] = {1,2,3,4,5,6,7,8,9,10};
print_arr1(arr, 3, 5);
print_arr2(arr, 3, 5);
return 0;
}
对于一维数组来说,数组名表示首元素地址,而二维数组中,首元素表示的是第一行的地址,所以把数组指针看作是二维数组中,指向一维数组的指针。也就是所,二维数组可以由数组指针来表示。
数组参数、指针参数
一维数组传参
#include <stdio.h>
void test(int arr[])
{}
void test(int arr[10])
{}
void test(int *arr)
{}
void test2(int *arr[20])
{}
void test2(int **arr)
{}
int main()
{
int arr[10] = {0};
int *arr2[20] = {0};
test(arr);
test2(arr2);
}
对于以上所有test函数来说,所有形式参数都是可以接收到实参的;数组本质来说就是指针,所以可由指针来接收;而指针数组来说,就要用二维指针来接收。
二维数组传参
void test(int arr[3][5])
{}
void test(int arr[][5])
{}
//对于二维数组来说,行的数字可以省略,列的数字不能省略
void test(int *arr)
{}
void test(int (*arr)[5])
{}
//数组指针是可以的,但对于指针数组来说,数组中包含的是指针,虽然说
//数组本质就是指针,但是由于数组的限制,指针的地址可能会出现连续的
//状态,无法确定一行中有多少列,故不可使用。
一级指针传参
#include <stdio.h>
void print(int *p, int sz)
{
int i = 0;
for(i=0; i<sz; i++)
{
printf("%d\n", *(p+i));。
}
}
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9};
int *p = arr;
int sz = sizeof(arr)/sizeof(arr[0]);
//一级指针p,传给函数
print(p, sz);
return 0;
}
对于数组来说,元素的地址是连续存在的,所以可以用指针叠加的方式进行循环遍历。
二级指针传参
#include <stdio.h>
void test(int** ptr)
{
printf("num = %d\n", **ptr);
}
int main()
{
int n = 10;
int*p = &n;
int **pp = &p;
test(pp);
test(&p);
return 0;
}
num=10
num=10
对于实参来说,只要是一级指针的地址,就可以传参,而函数中的形参一般只用二级指针接收即可。
总的来说,对于数组传参,形参部分可以是指针,也可以是数组;
而对于指针传参,实参部分只要是地址,都可以当作实参,形参只用指针来接收。