目录
易混淆:
1.
2.
3.
字符型指针可以是数组的数组名,也可以是字符串常量。
指针变量来代替匿名数组的实例。
10.7 数组指针
10.7.1 概念语法形式
指向整个数组的指针。
int a[10] = {1,2,3,4,5,6,7,8,9,0}
int *p;
p = &a
这样是不行的,指针变量p所存放的只能是基类型int型的地址,而a是数组a首元素的地址。&a的基类型为长度为10的一维整型数组。
改正方法:int(*p)[10];*的出现仅仅是说明p是一个指针。去掉变量名剩下类型名,此时定义了一个长度为10的一维整型数组指针。
这种写法与指针数组语义冲突。此处是一次性定义了十个int *型的指针。
p + 1 => p + 1*sizeof(基类型) 。
上述过程中p的基类型是长度为10的一维整型数组,故sizeof(基类型) = 40字节。
10.8 二维数组
二维数组作为函数参数传递,形参是指向数组的指针。
10.8.1 访问形式
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
//a<=> &a[0] 长度为4的一维整型数组的地址
int (*p)[4] //指向元素为长度为4 的一维整型数组 此时指针变量p为野指针
int (*p)[4] = NULL //防止野指针 先把指针变量置零
p = a;
printf("%d\n", *((int *)(a + 3) - 3)); //输出的为10
p += 1; =>p = p + sizeof(基类型) => p = p + 16字节, 故p指向的位置向后移动16个字节。
*(p+1) <=> p[1] 相当于a当中第二个一维数组的数组名,数据类型为 int *型。
*(p + 1) + 2 是*(p + 1)向后偏移8个字节。
*(*(p + 1) + 2) 是*(p + 1) + 2 所指向数组中对应位置的值。
10.8.2 指针方式实现遍历打印
void printArray2D(int (*a)[4], int rows)
{
int i, j;
for(i = 0;i < rows;++i)
{
for(j = 0;j < 4;++j)
{
printf("%2d ", *(*(a + i) + j));
}
puts("");
}
}
*(p + i)是整型指针 ,+j是整形指针向后偏移,再在外整体加* ,*(*(a+i) + j)取该指针所指向的值。
10.8.3 指针方式实现二维数组边缘元素的求和
int acrossArray2D(int (*a)[4], int rows)
{
int i, j;
int sum = 0;
for(i = 0;i < rows;++i)
{
for(j = 0;j < 4;j++)
{
if(i == 0 || i == rows - 1 || j == 0 || j == 3)
{
sum += *(*(a+i) + j);
}
}
}
return sum;
}
与之前所学运算内容区别在于:先找到*(a+i)行的地址,再*(a+i)+j找到具体位置,通过*进行指针运算
10.8.4按行逆序
void swap(int *begin, int *end)
{
int t = *begin;
*begin = *end;
*end = t;
}
void reverse(int *begin, int *end)
{
while(begin < end)
{ //迭代
swap(begin++,end--);
}
}
void reverse2D(int (*a)[4], int rows)
{
int i;
for(i = 0;i < rows;++i)
{
reverse(*(a + i),*(a + i) + 3); //传进reverse函数形参中的是该行首元素的地址
和该行末尾元素的地址
}
}
10.9 malloc函数 动态内存分配
void *malloc(size_t size) 返回值是指针的函数.
size_t size是传你想要的空间的大小。malloc函数会把这段空间的首地址传递给你。并向你保证它肯定是连续的。这段空间被分配在堆区。
调用过程:
分配 ->使用->销毁->置空
10.9.1 斐波拉契数组前十项存储
int fib(int n)
{
if(1 == n || 2 == n)
{
return 1;
}
else
{
return fib(n - 1) + fib(n - 2);
}
}
int main(void)
{
int n = 10;
int *p = malloc(n * sizeof(int));
int i;
if(p != NULL)
{
for(i = 0;i < n;++i)
{
*(p + i) = fib(i + 1);
}
for(i = 0;i < n;++i)
{
printf("%d\n", *(p + i));
}
free(p);
p = NULL;
}
}
若想重新申请空间 则新申请的空间与原来申请的空间不会连续。需要把原来空间上存储的内容拷贝到新空间中。
int main(void)
{
int n = 5;
int *p = malloc(n * sizeof(int));
int i;
for(i = 0;i < n;++i)
{
*(p + i) = fib(i + 1);
}
int m = 20;
int *q = malloc(m * sizeof(int));
memcpy(q, p, n * sizeof(int));
free(p);
p = q;
for(i = n;i < m;i++)
{
*(q + i) = fib(i + 1);
}
for(i = 0;i < m;++i)
{
printf("%d\n", *(p + i));
}
free(p);
p = q = NULL;
}
但是这个过程不需要我们自行编写,只需要理解思路即可。
p = realloc(p, m * sizeof(int));
10.10 指针函数
返回值为指针的函数,绝不能返回局部变量地址,返回static修饰的静态局部变量,全局变量或者是你传进去的变量地址可以。
什么是函数的地址?
函数有汇编语句组成,函数的函数名即为函数的入口地址。
int *p;
p = add;不行的,基类型不同,add是函数add函数的入口地址,p是指向整型变量的指针。
怎么定义一个指针变量,可以存储一个函数的地址?
int (*p)(int a, int b)
说明指针变量p指向一类函数(由两个整型作为参数,返回值是整型)
指向 void fn(void)不行。
此时指针变量是野指针。
p = add; 此时这样赋值就可以啦。类型匹配
实例:
int div3(int n)
{
return n % 3 == 0;
}
int div5(int n)
{
return n % 5 == 0;
}
void printArray(int *a, int len, int (*pfn)(int))
{
int i;
for(i = 0;i < len;++i)
{
if(pfn(a[i]) != 0)
{
printf("%d\n", a[i]);
}
}
}
可以降低函数的耦合性,解耦合运算。
10.10.1 快速排序
qsort(目标,目标元素个数,目标所占的字节数,比较函数(决定排序方式是升序还是降序))
int shortcmp(const void *p1, const void *p2)
{
short *q1 = (short *)p1;
short *q2 = (short *)p2;
if(*q1 > *q2)
{
return 1;
}
else if(*q1 == *q2)
{
return 0;
}
else
{
return -1;
}
}
int main(void)
{
short a[] = {1,2,-3,4,-5,6,7,8,9,10,24};
int len = sizeof(a) / sizeof(*a);
// printArray(a, len, div3);
// printArray(a, len, div5);
qsort(a, len, sizeof(*a), shortcmp);
int i;
for(i = 0;i < len;++i)
{
printf("%d\n", a[i]);
}
return 0;
}