目录
一、字符指针
1.1 简易用法
//字符指针
//简易用法
int main()
{
char ch = 'w';
char* pc = &ch;
*pc = 'a';
printf("%c\n", ch);
printf("%p\n", &ch);
printf("%p\n", pc);
return 0;
}
解析:
&ch,将 ch 的地址存放在pc中,因此pc为字符指针类型,指向ch,
*pc解引用pc,相当于 ch ,然后把a赋给ch;
运行结果如下:
1.2 指向字符串的字符指针
先看一段代码:
int main()
{
const char* p = "abcdef";
printf("%c\n", *p);
printf("%s\n", p);
return 0;
}
对于字符串而言,const char* p = "abcdef" 中将字符串第一个字符a的地址存放在p中,类似于数组指针
运行结果如下:
值得一提的是,常量字符串无法被修改,下面这段代码直接报错
再看一段代码:
int main()
{
const char* p1 = "abcdef";
const char* p2 = "abcdef";
char arr1[] = "abcdef";
char arr2[] = "abcdef";
printf("%p\n", &p1);
printf("%p\n", &p2);
if (p1 == p2)
{
printf("p1 == p2\n");
}
else {
printf("p1 != p2\n");
}
if (arr1 == arr2)
{
printf("arr1 == arr2\n");
}
else
{
printf("arr1 != arr2\n");
}
return 0;
}
第一个输出:
p1和p2都是字符指针,都指向abcdef,因此p1,p2的值相同,但存储这两个指针的地址是不相同的;
第二个输出:
arr1和arr2都是字符数组,虽然二者内容相同都为abcdef,但实际为这两个数组开辟的空间是不同的,arr1和arr2均为数组名,代表首元素地址,因此二者值不相同
二、指针数组
2.1 简易用法
//指针数组
int main()
{
int a = 10;
int b = 20;
int c = 30;
int* p1 = &a;
int* p2 = &b;
int* p3 = &c;
int* arr[3] = { &a, &b, &c };//arr就是一个指针数组
for (int i = 0; i < 3; i++)
{
printf("%d ", *(arr[i]));
}
return 0;
}
2.2 进阶用法
值得一提的是,判断一个变量到底是指针还是数组,用就近原则:
[ ] 的优先级比 * 高,因此arr和[ ]结合表示arr是一个数组。
区分变量看后缀:
数组指针是指针;指针数组是数组
整形数组中数组的元素是整形
字符数组中数组的元素是字符
指针数组中数组的元素是指针
看这段代码:
//指针数组 每个元素为指针 指向一个数组
//parr3
int main()
{
int arr1[5] = { 1,2,3,4,5 };
int arr2[5] = { 2,3,4,5,6 };
int arr3[5] = { 3,4,5,6,7 };
int* parr[3] = { arr1, arr2, arr3 };
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
printf("%d", parr[i][j]);
}
puts("\n");
}
}
还有一种表示方法,parr[i] 拿到了第 i +1 个数组名,数组名代表该数组首元素地址,因此
parr[i] + j就表示第 i + 1 个数组的下标为 j 的元素
for (int i = 0; i < 3; i++) { for (int j = 0; j < 5; j++) { //printf("%d", parr[i][j]); printf("%d", *(parr[i] + j)); } puts("\n"); }
值得一提的是,数组名通常为首元素地址,只有在两种情况下例外:
sizeof(arr)//表示整个数组大小 &arr// 拿到的是整个数组的地址
这样就相当于模拟出了一个二维数组,与实际的二维数组的不同之处在于,实际的二维数组的每个一维数组是连续存放的
三、数组指针
3.1如何理解数组指针
int main() { int arr[3][5]; printf("%p\n", arr); printf("%p\n", arr + 1); printf("%p\n", &arr); printf("%p\n", &arr[0]); printf("%p\n", &arr + 1); return 0; }
运行结果:
arr+1,跳过了二维数组arr的首元素,即一维数组{1,2,3,4,5},一共 5 * 4byte = 20 byte
&arr + 1,&arr拿到了整个二维数组的地址,加1相当于跳过了整个二维数组。
3.2数组指针的用法
3.2.1 常规做法
举个例子,写一个打印数组的函数,先上常规用法,方便理解:
void print1(int arr[],int len)
{
for (int i = 0; i < len; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
int arr[8] = { 1,2,3,4,5,6,7,8 };
int len = sizeof(arr) /sizeof(arr[0]);
print1(arr,len);
return 0;
}
3.2.2 进阶用法一
形参为指针的表示法:
void print2(int* arr, int len)
{
for (int i = 0; i < len; i++)
{
printf("%d ", *(arr)+ i);//
}
}
int main()
{
int arr[8] = { 1,2,3,4,5,6,7,8 };
int len = sizeof(arr) /sizeof(arr[0]);
//print1(arr,len);
print2(arr,len);
return 0;
}
//arr为首元素地址,*arr即arr首元素*(arr)+ i即arr第i个元素
3.2.3 进阶用法二
void print3(int (*p)[8], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", *((*p) + i));
}
printf("\n");
}
int main()
{
int arr[8] = { 1,2,3,4,5,6,7,8 };
int len = sizeof(arr) /sizeof(arr[0]);
//print1(arr,len);
//print2(arr,len);
print3(&arr,len);
return 0;
}
注意 这里与前两个函数传参不同,&arr类型为 int(*)[8],arr类型为int *
这种方法依然是为了更好地理解数组指针,且不推荐使用
3.3 二维数组的数组指针进阶用法
写一个函数,打印arr数组
常规做法:
void print4(int arr[3][5], int c, int r)
{
int i = 0;
for (i = 0; i < c; i++)
{
int j = 0;
for (j = 0; j < r; 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} };
print2(arr, 3, 5);
return 0;
}
进阶用法:
void print5(int (*p)[5],int c,int r)
{
int i = 0;
for (int i = 0; i < c; i++)
{
for (int j = 0; j < r; j++)
{
printf("%d", *(*(p + i) + j));
}
printf("%p",p);
puts("\n");
}
}
int main()
{
int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
print5(arr, 3, 5);
return 0;
}
p+i是指向第i行的
*(p+i)相当于拿到了第i行,也相当于第i行的数组名
数组名表示首元素的地址,*(p+i) 就是第i行第一个元素的地址
类比一下一维数组:
arr[i]
*(arr+i) 表示arr第i个元素
arr[i][j]
*(arr+i)[j] ar下标为i的元素,即第i-1行的一维数组名,*(arr+i)[j]即一维数组的下标为j的元素的地址
*(*(arr+i)+j) 即arr下标为i,j 的元素
四、总结
指针这块知识需要反复推敲理解,结合画图才能熟练掌握。如果本文内容给你带来帮助,请留下您的三连,感谢支持!