目录
1.字符指针(char*)
(1).指向字符
char ch = 'w';
char *pc = &ch;
创建了一个字符变量ch给它赋值了一个字符w,然后创建一个字符指针变量pc,将ch的地址赋给pc
*pc = 'a';
通过对pc的解引用我们就可以对字符变量c的内存进行访问从而间接改变c的内容
(2).指向字符串
<1>
我们可以看到虽然我们的输出是abcdef,但是在内存中指针变量p只存放了该字符串的首地址
我们可以在试一试
我们将指针变量解引用后可以看到输出的是a
经过以上分析我们可以知道将字符串赋给指针变量就是将字符串的首地址赋给了指针变量
提示:部分编译器在<1>的情况下可能会报错因为"abcdef"是一个常量字符串,将它放入p中可能会报警告,原因是对p做赋值语句会改变字符串。即:使用第一种情况时最好在语句前面加上一个const,保护常量字符串
const char *p = "abcdef";
<2>
char arr[] = "abcdef";
将字符串全部放入了数组中
<3>
我们来看两组数据
->1
const char *p1 = "abcdef";
const char *p2 = "abcdef";
->2
char arr1[] = "abcdef";
char arr2[] = "abcdef":
那么问题来了
p1 == p2?arr1[ ] == arr2 [ ]?
如上图我们可以看到
p1 == p2:为什么p1等于p2,"abcdef"是常量字符串谁也改不了,系统就将这个字符串放在只读数据区,谁需要用直接在里面拿出来用就好了,所以p1的地址等于p2的地址
arr1 != arr2: arr1和arr2是两个不同的数组,它们会创建两个不同的内存空间来存放数据,所以arr1不等于arr2
2.指针数组
指针数组是数组,用来存放指针的数组
example:
int* arr2[6] //存放int* int* ....存放6个int*的内容
char* arr3[6] //存放char* char* ....存放6个char*的内容
需要用什么类型的指针数组视情况而定
我们现在用整型指针数组来模拟二维数组
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 2,3,4,5,6 };
int arr3[] = { 3,4,5,6,7 };
int* parr[] = { arr1,arr2,arr3 };
return 0;
}
三行四列的二维数组模拟好了,我们应该怎么使用这个数组指针呢?
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 2,3,4,5,6 };
int arr3[] = { 3,4,5,6,7 };
int* parr[] = { arr1,arr2,arr3 };
int i, j;
for(i=0;i<3;i++)
{
for(j=0;j<5;j++)
{
printf("%d", *(parr[i]+j))
}
}
return 0;
}
*(a+i)和a[i], parr[i][j]和*(parr[i]+j)原理相同
3.数组指针
<1>
没有任何操作之前它们都是一样的(都是同一个地址)
将它们全部加1之后第一种和第二种是相同的,只有&arr加1前后对比差了40个字节
<2>
可以看到打印出来的是整个数组的元素字节的总和
总结
数组名通常表示的首地址
除两个情况之外:
(1) sizeof(数组名),在sizeof中的数组名指整个数组
(2) &数组名,被&修饰的数组名指整个数组
->数组指针
int (*p1)[10] p是数组指针,去掉变量名就是它的类型:int (*)[10] 。该指数组的意思是p1指向一个数组,该数组有10个元素,每个元素的类型是int
例:
int arr[] = {1,2,3,4,5};
int (*p)[5] = &arr //取地址数组名arr就是整个arr数组的地址
创建一个数组将数组的地址给数组指针
用法1:
这个用法不常用
int sz = sizeof(arr)/sizeof(arr[0]);
int i = 0;
for(i = 0; i < sz; i++)
{
printf("%d", *(*p+i));
}
*p为指针数组的数组名,即首元素地址,对*p进行加i操作就是改变*p的指向,遍历数组
用法2:
#include <stdio.h>
void print(int (*p)[5], int x, int y)
{
int i = 0, j = 0;
for(i = 0; i < x; i++)
{
for(j = 0; j < y; 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}};
print(arr, 3, 5);
return 0;
}
tips:
int (*p)[5] p+1跳过一个有5个int型元素的数组,就是跳过20个字节
指针数组的下标不能省略,系统不会像帮一维数组二维数组一样帮你算出它的元素个数
int arr[5]; //类型:int
int *parr1[10]; //类型:int*[10]int (*parr2)[10]; //类型:int(*)[10]
int (*parr3[10])[5]; //类型:1.int(*)[5] 2.parr3[10] 指针数组的数组
parr3存放数组指针的数组,每个数组中存放5个int型的元素
4.函数指针 - 函数名也有地址
我们来试试看数组名是否有地址
取地址函数名看是否有地址
可以看到函数名确实也有地址
去取地址函数名有地址那我们在来看看函数名不加取地址符它是否还有地址
居然也有地址,并且是一样的,即&add和add都表示的是这个函数的地址
函数名既然是地址的话那我们是不是可以定义一个指针来存放这个地址
我们需要用一个指针来存放地址*pf,在加上函数这个类型:int (*pf)(int, int)
该形式就是这个函数的函数指针
函数指针的用法
用法:
#include <stdio.h>
int add(int a, int ,b)
{
return a + b;
}
int main()
{
int (*pf)(int,int) = add;
int ret = (*pf)(2,3);
printf("%d", ret);
return 0;
}
用函数指针也可以得出2和3的和
再函数调用的时候也可以将(*pf)(2,3);语句简写成pf(2,3),但是要记住这只是一种简写形式
5.一维数组的数组名传参
#include <stdio.h>
int main()
{
int arr[10] = {0};
test(arr);
return 0;
}
1.void test(int arr[ ])
2.void test(int arr[10])
3.void test(int *arr)
以上这三种方式接收一维数组数组名均可
6.数组指针传参
#include <stdio.h>
int main()
{
int* arr[20] = {0};
test(arr2);
return 0;
}
1,void test(int *arr[20])
2,void test(int **arr)
以上两种方式接收数组指针数组名均可
7.二维数组数组名传参
#include <stdio.h>
int main()
{
int arr[3][5] = {0};
test(arr);
return 0;
}
1.void test(int arr[3][5])
2.void test(int arr[ ][5])
3.void test(int (*arr)[5])
以上三种方式都可以接收二维数组数组名