目录
指针回顾
指针的本质是一个用来存放地址的变量,大小在三十二位系统下是4字节,六十四位系统下是8字节。
指针有不同类型,主要作用是用于计算步长,比如在对int*变量加减1是,总会跳过四个字节,对于char*则是一个字节。
一、字符指针
字符指针,顾名思义,即用来存放字符地址的变量,如下:
#include<stdio.h>
int main()
{
char c = 'w';
char* pc = &c;
*pc = 's';
printf("%c\n", c);
return 0;
}
值得注意的是如果代码写作:char* pstr = "hello world" 则指针变量的本质是存放了首元素的地址,而不是整个字符串,且由于这是一个常量字符串,故而内容不可以通过指针解应用修改内容。请看下面一道面试题目:
#include <stdio.h>
int main()
{
char str1[] = "hello bit.";
char str2[] = "hello bit.";
const char *str3 = "hello bit.";
const char *str4 = "hello bit.";
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;
}
其中str3和str4指向的是不可以修改的常量字符串,因此系统会把他们分配到同一块内存中去,也无需另外开辟空间。但是数组的内容是可以修改,对于不同的数组只不过是在初始化的时候赋予了同样的值,但本质上他们指向了不同的内存区域。故该题运行结果如下:
二、指针数组
指针数组本质上是数组,只不过其元素是地址,例如:
int* arr1[10] 这是一个有着十个元素的数组,数组中每一个元素都是一个整型指针;
char* arr2[10] 这是一个存放字符指针的数组,总共是个元素
三、数组指针
基本概念
数组指针不同于指针数组,从字面上来看,数组指针的概念与整型指针、字符指针等概念相统一,他也是一种指针,只不过指针指向的是整个数组,如下:
int (*p)[10] = &arr int (*)[10] 是数组指针的类型, 括号中的*先和p结合,表明p是一个指针,int表明数组中元素的类型是int。注意:由于[ ]的优先级高于*,所以必须加上(),注意区分int* p1[10](指针数组)。
&arr 与 arr
了解了数组指针的概念,我们有必要了解&arr 与 arr之间的区别,请看如下代码:
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
printf("arr = %p\n", arr);
printf("&arr= %p\n", &arr);
printf("arr+1 = %p\n", arr + 1);
printf("&arr+1= %p\n", &arr + 1);
return 0;
}
根据以上代码及其运行结果,我们可以发现,尽管arr和&arr 在结果上是一样的,但他们本质上的意义却不同,其中arr本质意义是首元素的地址,在加减操作时,跳过的长度与元素大小一致;而&arr是数组指针,代表整个数组的地址,在加减操作时会步长是一整个数组。
数组指针的使用
数组指针一般不在一维数组中使用
二维数组的使用如下:
#include<stdio.h>
void print1(int arr[3][5], int row, int col)
{
int i = 0,j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
void print2(int(*arr)[5], int row, int col)
{
int i, j;
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},
{3,4,5,6,7},
{6,7,8,9,10} };
print2(arr, 3, 5);
return 0;
}
上述代码中,print2函数是利用数组指针进行操作的,其中arr就相当于二维数组第一行的地址,因此将其作为实参就相当于一个数组指针,在打印时 arr+i 获得元素所在行的整个数组的地址,对其进行解应用获得所在行首元素的地址,+j获得元素所在列的地址,解应用获得该地址存放的元素。
区分几种表示形式
int arr[5]; 有五个元素的整型数组
int *parr1[10]; 存放是个整型指针的数组
int (*parr2)[10]; 数组指针
int (*parr3[10])[5] 存放数组指针的数组,parr3是一个有十个元素的数组,每一个元素都是一个数组指针,数组指针指向的数组有5个整型元素。
对于以上形式的区分最重要的是要认识到,int* , int (*)[]等都是类型,不要因为放置的顺序问题而搞混。
二位数组的传参
void test(int arr[3][5])//ok?//可以,数组传参的标准形式
{}
void test(int arr[][])//ok? //不可以,不能省略列数
{}
void test(int arr[][5])//ok? //可以,可以省略行
{}
void test(int *arr)//ok? //不可以,实参是数组指针,而形参是一个整型指针
{}
void test(int* arr[5])//ok? //不可以,实参是指针,形参是数组,类型不匹配
{}
void test(int (*arr)[5])//ok? //可以,实参可以表示二维数组第一行的地址,即一个数组指针
{}
void test(int **arr)//ok? //不可以,第一行的地址并不是一个二级指针
{}
int main()
{
int arr[3][5] = {0};
test(arr);
}
总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。这样才方便运算。