C语言指针总结
2018-05-10 18:13
246 查看
起源
想学一下UNIX系统编程,所以把C重学一遍,C中指针关联甚多,是重点也是难点。下面是自己做的一个总结,希望对你有所帮助!一、何为指针
1、C的特殊性
C是一门很特殊的语言,特殊的地方在于它可能对计算机是友好的,对程序员并不太友好。常识上来说,我们是不需要知道一个变量在内存中的实际地址的,对于我们实现业务没有意义。但早期编写C的这帮计算机科学家个个聪明绝顶,而且做得是底层研究,他们熟悉汇编,更熟悉计算机硬件原理,他们只是想做一个抽象层,编出一门语言来简化系统软件开发。
现在来看,对于学习JS、Java或是Python的人来说,C的编写过于复杂了,不够简洁。不过反过来看,我们也不能太苛刻,相比汇编语言来说,C已经好太多了。
2、初识指针
说到指针,它的定义就是:用于存储变量的内存地址。比如一个变量int num = 0;来说,代码执行的时候必然会给他分配内存,如果你是一个高端玩家,想要知道num储存在内存具体的那块地址上,那么可以用
&num来获得实际地址。
执行代码:
int num = 10; printf("%d %p", num, &num);
执行结果大致为:
10 0060FF0C
那么获得一个内存地址有什么用呢?
我们先来看它在应用方面来讲最大的作用。
3、函数中变更变量的值
如果要实现两个int值互换,那么非常简单:int num1 = 10; int num2 = 20; int temp = num2; num2 = num1; num1 = temp; printf("%d %d",num1,num2);
如果要实现一个interchange函数呢,你可能想到:
void interchange(int num1, int num2){ int temp = num2; num2 = num1; num1 = temp; }
在main函数中调用:
int main(void){ int num1 = 10; int num2 = 20; interchange(num1,num2); printf("%d %d",num1,num2); }
打印出来的结果很有可能不符合预期哦,实际上两个数并没有进行交换。
这是怎么回事儿呢?这是由C语言的特性所决定的,num1和num2实际上只传递了值,传递了副本,所以可以这么看:在interchange()函数中的num1和num2已经是独立的值,已经和main()中的两个数没啥关系了,他们怎么改变也不会影响到main()中的num1和num2了。
说道这里,我们遇到了第一个问题,怎么解决呢?需要指针登场了!
4、指针的基础知识
先看下指针的基础知识,然后我们可以用这些知识来解决上述问题。先有一个表达式:
int num = 10;,指针的知识:
获得一个变量的指针:
&num
指针声明:
int * pnum = &num
解指针:
*pnum = num
这里容易让人迷惑的地方是指针声明和解指针用的都是
*号。
简单来说,
&num也是一个类型,是一个什么类型呢?指针类型。那么如何声明指针类型:
int * pnum;,
pnum就是一个指针类型。
pnum是一个指针,那么我想获取它的实际表达值怎么办?也就是怎么解这个指针呢?也用到
*号。所以
*pnum其实和
num是相等的。
如果感觉有点绕,请多读两遍。
5、利用指针解决问题
所以改良后的interchange()函数如下:void interchange(int * num1, int * num2){ int temp = *num2; *num2 = *num1; *num1 = temp; }
调用:
interchange(&num1, &num2);
interchange()函数用了指针和解指针的方式巧妙的改变了
num1和
num2变量的值。
二、数组与指针
定义一个数组,非常简单:int powers[5] = {3,4,5,6,7};
下面开始引入数组与指针的关系了,如果你感觉魔幻,那么不是你的错,我刚开始也一样。
主要的规则有以下几点:
数组名本身就是一个指针,是数组首元素的地址。也就是说
*powers等于
powers[0]
指针+1,则指针的值递增它所指向类型的大小。也就是说
powers+1等于
&powers[1]
对于指针来说,可以用++操作,比如powers++
多举几个例子,感受一下:
powers = &powers[0] powers + 2 = &powers[2] *(powers + 2) = dates[2]
1、函数形参、指针与数组
数组可以理解为是一种特殊的指针,所以下面的四种函数原型是等价的:int sum(int *ar, int n); int sum(int *,int); int sum(int ar[], int n); int sum(int [],int);
2、指针与多维数组
先看一个多维数组的定义:int zippo[4][2];
对这个多维数组进行分析:
zippo[0]是一个占用一个int大小对象的地址,而
zippo是一个占用两个int大小对象的地址。他们两个都开始于同一个地址,所以
zippo和
zippo[0]的值相同
解指针可以用
[]运算符,也可以用
**运算符.
*zippo等于
zippo[0],
**zippo等于
zippo[0][0]
下面开始烧脑的内容,请说明以下两个表达式的不同:
int (* pz)[2]; //1 int * pax[2]; //2
对于表达式1来说,
pz指向了一个内含两个int类型值的数组。可能不太好理解,先来看
int *pz和
int apz[2]有何联系,
pz是一个指针类型,
apz也是一个指针类型,而且他们都是指向int类型值的指针,所以这样赋值也不会有什么错误:
pz=apz。通过这个实验,可以更加深刻的理解前面所说的数组可以理解为一种特殊的指针。
回来看,
int (* pz)=int pz[*],那么
int (* pz)[2]=int pz[*][2]。
那么对于表达式2呢,分解来看,
pax[2]声明了一个包含了两个int类型值的数组,前面加上了指针声明的符号,所以
int * pax[2]=int pax[2][*]
3、多维数组与函数
如果多维数组作为参数来传递,那么函数原型声明上也有要注意的一些地方,先来看正确的声明方式:int sum(int (* ar)[4], int rows); int sum(int ar[][4], int rows);
再来看错误的方式:
int sum(int ar[][], int rows);
为什么下面是错的呢,是因为编译器会把数组表示法转换为指针表示法,那么就必须知道ar所指向的对象大小。
三、字符串与指针
在C中,字符串是以空字符(\0)结尾的char类型数组。那么我们很容易想到他的定义:
char mesg[11] = "hello world"; char * pmesg = "hello world";
根据前面数组的内容,这两种定义都是ok的。
那么他们有什么异同呢?还是说使用的时候任何情况下都可以看成是含义相同的?
这里又看出来C的复杂性,他们还是有很大差异的。主要的差异就是:char数组字符串声明的时候已经分配了指针空间的大小,在这里是sizeof(char) * 11;而char指针字符串没有分配具体的空间大小,这在某些情况下可能造成内存溢出。
可以先简单的记住这个规则:scanf()最好使用char数组字符串的声明方式。
四、结构与指针
C中的结构如:struct node{ int num; };
跟Java中的Class相似。
这里要讲到结构指针,作用和前面的函数中变更变量的值差不多。
先思考一个问题,如果一个结构作为入参传入一个函数中,函数中修改结构的值会影响到main()函数中结构的值么?
可能答案你已经猜出来了,是不能的。这里还是要借助指针来实现:
void struct_demo(){ struct node tn; tn.num = 1; struct_demo_swap(&tn); printf("%d",tn.num); } void struct_demo_swap(struct node * temp){ temp->num = 10; }
打印出来的结果是
tn.num=10。
注意,这里对于指针访问结构成员有两种方式:
(*temp).num或
temp->num
五、函数与指针
在C中,原始就支持JS的闭包或Java8的Lamda表达式,也就是支持函数作为参数来传递。在C中,函数也有地址,指向函数的指针中存储着函数代码的起始处的地址。
一个规则:函数名可以用于表示函数的地址。看代码:
void ToUpper(char *); void (*pf)(char *); pf = ToUpper;
可以看到第二行声明了一个函数指针叫
pf,这个函数指针定义了返回值和形参列表,只要是和他结构一样的,都可以赋值给他。这里
pf = ToUpper;没有什么问题。
这样的规则会存在一些小问题:*pf实际上表示ToUpper函数,而pf和函数名又可以互换,所以(*pf)("abc")和pf("abc")、ToUpper("abc")又都是等价的。
下面看一个函数指针最常用的用法,就是作为入参:
void show(void (* fp)(char *), char * str);
关于指针的知识就总结完了,希望大家有所收获!
相关文章推荐
- 关于C语言中的指针的总结与运算符的优先级的总结
- C语言指针总结
- 简图记录-C语言总结(二) 指针与数组
- C语言指针总结之程序举例分析
- C语言内存管理总结-野指针
- C语言指针和数组知识总结(下)
- JNA调用C语言动态链接库学习实践总结(指针模拟)
- C语言中 指针的基础知识总结, 指针数组的理解
- 【C语言总结】C语言指针
- C语言的指针、数据、结构体关系总结
- 【C语言】指针小总结
- 2016年11月30日学习总结----C语言中野指针的定义、危害及规避
- 读陈浩的《C语言结构体里的成员数组和指针》总结,零长度数组
- C语言指针总结
- [转]C语言指针学习经验总结浅谈
- C语言指针学习经验总结
- 对C语言指针的总结
- 对C语言指针的总结
- C语言指针学习总结
- 重温C语言 总结指针遗忘点