相信很多人都听过指针是c语言的灵魂!
对于指针的学习,往往也是最难的,也是最难搞懂的,那么今天我们一起来学习分析c语言的指针,体会c语言的魅力!
*指针和指针应用大概分为一下几类:
1、字符指针
2、数组指针
3、指针数组
4、数组传参和指针传参
5、函数指针
6、函数指针数组
7、指向函数指针数组的指针
*
*这里我就不列举常见数据类型的指针(如:整形类型的指针int * 双精度类型的指针double 等等), 讲字符指针我会连带说下他们。
字符指针
字符指针它的功能有:
1、指向字符串常量(char str = “abdefg”)
2、指向字符数组(有一个字符数组char str1[20] = {0}; char str = str1;)
3、当字符数组作为实参数传递时,形参采用字符指针接受。
(1)、在这里,我要提一下第1个功能, 当指向字符串常量时,是不能修改字符串里面的值的,因为常量无法修改,你可以访问它,但不可以修改。
(2)、字符串不同于字符,他后面是有结束符’\0’的,千万不能忽略!*
接下来请大家做一道习题考察下自己的能力吧!
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
char str1[20] = "wozhenshuai";
char *str2 = "wozhenshuai";
printf("%d, %d\n", sizeof(str1), strlen(str1));
printf("%d, %d\n", sizeof(str2), strlen(str2));
system("pause");
return 0;
}
正确答案是:
20,11
4,11
有没有作对呢?
数组名,他一共有3个含义,1、指向数组首元素的地址,2、sizeof()单目操作符,计算该类型的字节数。当被sizeof()处理时,它默认为指向整个数组,一共20个元素,每个元素都是char类型,char占用一个字节,所以一共为20*1 = 20字节。3、当对数组名取地址符&默认是对整个数组取地址,因此&str1+1 相当横跨整个数组。
字符指针还是很简单的!
数组指针&指针数组
**数组指针和指针数组是比较容易搞混的,区分它们很容易,谁在后面就是什么,所以数组指针是指针,指向的是数组t,
它的形式是char (*pa)[20]
那么同理,指针数组就是数组,数组存放的值是指针!它的形式为char pb[20]
在这里,我强调一下优先级,[]的优先级是该与的,但是小于()的优先级, 在判断是什么类型的时候,就完全取决于它的优先级排布,
举例:char (pa)[20] ()优先级高于[],我们可以观察到()里面是指针,因此这是一个指针,然后看指针指向什么,
去掉(pa)后剩下char [20],很明显这是一个字符数组, 故这是一个指向字符数组的指针,也就是数组指针。同理指针数组。
**接下来我们来做几道例题,巩固下自己,分析一下是什么类型?指向什么?
(1)、int *p1[6];
(2)、int (*p3)[6];
**
*(1)、第一道题 观察优先级,很明显[]大于,因此这是一个数组,去掉pa[6],剩下int *,因此这是一个存放int *类型的数组,数组里面有6个元素,每个元素都是int *型的
(2)、观察优先级,()大于[],()里面是指针,因此这是一个指针,去掉(*p3),剩下int [6], 因此这是一个指向int [6]数组的指针,(int [6]代表数组整体, 也就是刚才讲的对数组名取地址&)
**
数组传参和指针传参
数组作为实参传递,接受的形参其实是指针,指针传参,则用对应的指针接受。
下面我们做来做几道习题,通过习题来讲解传参问题
#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]);
print(arr, sz);
return 0;
仔细观察,整形数组传参过程,不仅传递了数组名(首元素地址),而且还传递sz,这里sz计算的是数组的长度,
为什么呢?这是因为你传递的数组名(也就是数组首元素地址),他是用指针接受的,切忌:指针不等同数组。有相似之处,但是是两个
不同的类型。 才打印函数里面需要数组长度,因此我们需要传递数组长度sz。
#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;
}
这是传递二级指针,没有任何问题,那么接下来看下面一道题!
#include <stdio.h>
void test(int** ptr)
{
printf("num = %d\n", **ptr);
}
int main()
{
int a[3][4] = {0};
test(a);
return 0;
}
*上面的代码对吗? 请各位小伙伴运行下这段代码,看看会出现什么错误。
很明显,实参和形参类型不匹配,二维数组的数据类型不是二级指针,而是数组指针,因此形参写成int (a)[4]
函数指针
函数是由地址的,和指针一样。那么有地址,我们对它进行取地址会出现什么呢? 结果是还是它本身,也就是对函数去地址还是它本身。
如果对函数取地址,我们用什么接收它呢? 对它去取地址会是什么类型呢?
下面看一段程序
void test()
{
printf("hehe\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)();
void *pfun2();
**这是一个自定义函数test(),我们采用void (pfun1)();来接收它的地址,观察这个函数指针,()里面是指针,这是一个指针类型,指针指向的是一个函数,去掉(pfun),剩下的是void ();。是一个函数,没有形参。 和上面数组指针和指针数组是一样的性质。
那么它有什么用呢?我们可以调用函数,传递函数指针,然后解引用函数指针然后调用函数,使用函数内部的功能。虽说可以直接调用,但是函数指针可以构建成函数指针数组,数组里面都是函数指针,我们调用就像使用数组一样方便。而且更加系统化!
函数指针数组指针&&函数指针数组
void test(const char* str)
{
printf("%s\n", str);
}
int main()
{
//函数指针pfun
void (*pfun)(const char*) = test;
//函数指针的数组pfunArr
void (*pfunArr[5])(const char* str); pfunArr[0] = test;
//指向函数指针数组pfunArr的指针ppfunArr
void (*(*ppfunArr)[10])(const char*) = &pfunArr;
return 0;
}
**void (pfunArr[5])(const char str); 首先先找到变量名pfunArr,观察发现它是一个指针数组,去掉pfunArr[5],
剩下void ()(const char str), 观察发现()里面是,所以它是指针,而且是函数类型的指针,因此这里pfunArr是一个长度为5的数组,
数组里面的元素为函数指针,函数的参数为(const char str),返回值为void。
**
**
void((ppfunArr)[10])(const char),首先先找到ppfunArr,观察发现它是一个指针,去掉ppfunArr,剩下void(* [10])(const char*)
()里面是(* [10]),观察发现它是指针数组,因此它是指向指针数组的指针, 去掉[10], 剩下void ()(const char), 观察发现它是
函数指针, 故这是一个指向指针数组的指针,数组里面存放的是函数指针,函数的参数为const char*,返回值为void。
**
**
函数指针数组和函数指针数组指针是对函数指针更好的调用,更加方便地使用函数功能,他的使用和数组指针和指针数组地使用比较相似,就只是解引用后是函数调用而已!大家多多品味就可以了,很好理解的!
好啦,今天就分享到这里了,最近忙着期末考试,随后空闲时候,我会继续分享和大家共同学习,感受c语言的魅力!
**