我们在学习C语言的时候都会接触到关于指针的问题,大体我们都会知道指针在函数传递参数的过程中很灵活,我们可以将指针变量传递进去实现多个变量的值的改变,从而可以在一定程度上弥补我们的C函数返回值是一个变量的现象。
在C Primer Plus中在指针的简介开始出是这么说的“究竟什么是指针?一般来讲,指针是一个数值为地址的变量”。我们总会去想指针是传递地址的,其实细想一下其实都是传的数值,比如我们定义两个函数
#include <stdio.h> void showData(int a); void displayData(int * a); int main(void) { int aa = 5; printf("&aa = %p\n", &aa); printf("\nThe showData Progress:\n"); showData(aa); printf("\nThe DisplayData progress:\n"); displayData(&aa); return 0; } void showData(int a) { printf("a = %d, &a = %p\n", a, &a); displayData(&a); return ; } void displayData(int * b) { printf("b = %p\t, *b = %d, &b = %p\n", b, *b, &b); return ; } /* &aa = 0012FF7C The showData Progress: a = 5, &a = 0012FF2C b = 0012FF2C , *b = 5, &b = 0012FED4 The DisplayData progress: b = 0012FF7C , *b = 5, &b = 0012FF2C Press any key to continue */
其中我们通常习惯于说showData的方式是传值,displayData的方式是传地址。这样的说法估计就是缘于作为int *类型的b的值是地址吧。但是实际上我们在主调函数向被调函数函数传递参数的过程中本质就是将变量a和b的数值传给了被调函数。只是我们在displayData中我们可以通过地址访问(*b)的方式来讲主调函数中传入的变量代表的值改变了。而showData中a是局部变量,作为主调函数的一个拷贝代表的不是主调函数中变量,所以不能改变主调函数的值。而且我们要注意的是变量a和变量b都是局部变量,是他们有自己的地址即&a,
&b。但是我们会看见在主调函数中的aa的地址和b的值是一样的。
最近自己在看东西的时候但是如果我们使用所谓的传地址来进行操作的时候并不是一定可以实现对我们传入的地址进行正确的操作的(这个地方我不知道该怎么正确表达, 就比如我们虽然是传进去了地址变量,可以通过 *a的方式获取地址),但是我们如果在被调函数内部误操作将局部指针变量的值改变了就得不到我们预期的操作
#include <stdio.h> void modify(int * a); int main(void) { int aa = 5; printf("原始的 aa = %d\n", aa); modify(&aa); printf("改后的 aa = %d\n", aa); return 0; } void modify(int * a) { int b; printf("请输入更改后的数据:"); scanf("%d", &b); a = &b; return ; } /* 原始的 aa = 5 请输入更改后的数据:65 改后的 aa = 5 Press any key to continue */
这样我们可以看见,原本我们是希望通过传入地址来更改aa变量的值的,但是我们却并没有达到预计的目的,当然平时我们肯定不会去犯这样的错误,只是用这样的例子来说明这样一个问题。而正确的做法应该是直接操作*a,因为那才是主调函数传来的变量的真实地址,只有对正确的地址进行操作才可以得到正确的操作效果。还有一种情况就是在主调函数中没有对传入的参数进行初始化就直接传入却想通过在被调函数中完成改变变量的值。这样一来如果在被调函数中不进行初始化而操作就会运行时直接报错,如果在被调函数中进行了初始化并进行了后续的正确操作也是不能达到理想效果的。正如前面说过的我们主调函数的
#include <stdio.h> #include <stdlib.h> typedef struct _Node { int data; _Node * next; }NODE, * pNode; void CreateLink(pNode pHead, int n); void ShowLink(pNode pHead, int n); void FreeMem(pNode pHead, int n); int main(void) { pNode pHead = (pNode)malloc(sizeof(NODE)); int n; printf("Pls enter the num of nodes: "); scanf("%d", &n); CreateLink(pHead, n); ShowLink(pHead, n); FreeMem(pHead, n); return 0; } void CreateLink(pNode pHead, int n) { pNode ptail, pNew; ptail = pHead; for(int i=0; i<n; i++) { pNew = (pNode)malloc(sizeof(NODE)); printf("Pls enter your data: "); scanf("%d", &pNew->data); ptail->next = pNew; //将新建立的节点挂到链表的最后的节点上去 ptail = ptail->next; //将ptail移动到现存链表的最后的节点上 } ptail->next = NULL; return; } void ShowLink(pNode pHead, int n) { for(int i=0; i<n; i++) { pHead = pHead->next; printf("%d\t", pHead->data); } printf("\n"); return; } void FreeMem(pNode pHead, int n) { pNode tmp; for(int i=0; i<n; i++) { tmp = pHead; pHead = pHead->next; free(tmp); } return; } /* Pls enter the num of nodes: 4 Pls enter your data: 12 Pls enter your data: 34 Pls enter your data: 23 Pls enter your data: 76 12 34 23 76 Press any key to continue */
有一次曾见过有人在使用结构体的时候也遇见过这种情形,定义一个带头节点的链表,如果采用上面的代码去执行的话,我们可以得到正确的结果,因为虽然我们在CreateLink中使用的是局部变量pHead,但是pHead的值(即地址变量)在主调函数中已经被初始化了。而且进来后ptail = pHead;以及ptail->next = pNew;将所有节点都链起来了,所以通过主调函数的pHead可以进行访问。但是如果我们在外部没有初始化pHead(全局的),则外部的pHead与内部初始化的pHead是不一样的地址,而在被调函数中操作的那个链表并没有被挂到全局的pHead上,造成了局部pHead在被调函数终止时被注销,但是在堆上分配的内存还在,但是却找不到而内存被浪费。所以头节点如果是在被调函数中进行调用的话就要将局部的pHead以函数返回值的形式返回给全局的pHead才可以。
#include <stdio.h> #include <stdlib.h> typedef struct _Node { int data; _Node * next; }NODE, * pNode; pNode CreateLink(int n); void ShowLink(pNode pHead, int n); void FreeMem(pNode pHead, int n); int main(void) { // pNode pHead = (pNode)malloc(sizeof(NODE)); int n; printf("Pls enter the num of nodes: "); scanf("%d", &n); pNode pHead = CreateLink(n); ShowLink(pHead, n); FreeMem(pHead, n); return 0; } pNode CreateLink(int n) { pNode pHead = (pNode)malloc(sizeof(NODE)); pNode ptail, pNew; ptail = pHead; for(int i=0; i<n; i++) { pNew = (pNode)malloc(sizeof(NODE)); printf("Pls enter your data: "); scanf("%d", &pNew->data); ptail->next = pNew; //将新建立的节点挂到链表的最后的节点上去 ptail = ptail->next; //将ptail移动到现存链表的最后的节点上 } ptail->next = NULL; return pHead; } void ShowLink(pNode pHead, int n) { for(int i=0; i<n; i++) { pHead = pHead->next; printf("%d\t", pHead->data); } printf("\n"); return; } void FreeMem(pNode pHead, int n) { pNode tmp; for(int i=0; i<n; i++) { tmp = pHead; pHead = pHead->next; free(tmp); } return; } /* Pls enter the num of nodes: 4 Pls enter your data: 12 Pls enter your data: 34 Pls enter your data: 23 Pls enter your data: 76 12 34 23 76 Press any key to continue */
或者在外部定义pNode pHead,并将pHead的地址传进去也是可以的,这样就是对pHead的值进行改变实现的
#include <stdio.h> #include <stdlib.h> typedef struct _Node { int data; _Node * next; }NODE, * pNode; void CreateLink(pNode * pHead, int n); void ShowLink(pNode pHead, int n); void FreeMem(pNode pHead, int n); int main(void) { // pNode pHead = (pNode)malloc(sizeof(NODE)); int n; printf("Pls enter the num of nodes: "); scanf("%d", &n); pNode pHead; CreateLink(&pHead, n); ShowLink(pHead, n); FreeMem(pHead, n); return 0; } void CreateLink(pNode * pHead, int n) { * pHead = (pNode)malloc(sizeof(NODE)); pNode ptail, pNew; ptail = * pHead; for(int i=0; i<n; i++) { pNew = (pNode)malloc(sizeof(NODE)); printf("Pls enter your data: "); scanf("%d", &pNew->data); ptail->next = pNew; //将新建立的节点挂到链表的最后的节点上去 ptail = ptail->next; //将ptail移动到现存链表的最后的节点上 } ptail->next = NULL; return; } void ShowLink(pNode pHead, int n) { for(int i=0; i<n; i++) { pHead = pHead->next; printf("%d\t", pHead->data); } printf("\n"); return; } void FreeMem(pNode pHead, int n) { pNode tmp; for(int i=0; i<n; i++) { tmp = pHead; pHead = pHead->next; free(tmp); } return; } /* Pls enter the num of nodes: 4 Pls enter your data: 12 Pls enter your data: 34 Pls enter your data: 23 Pls enter your data: 76 12 34 23 76 Press any key to continue */