致C语言初学者—指针注意项

W170532934 2012-03-19 03:48:19
在论坛里经常见到一些新人对指针提出一些问题,作为一个经历过许多错误后的新手,我想把自己的经历说出来,避免让后来人继续这样的错误。
在讲解指针之前,需要理解一下内存空间。内存是随机存取器,计算机上电后便利用内存进行运转。其有一定的容量,为了标识每个存储单元的位置,我们为内存设置了内存地址。内存的具体组织结构可以参考计算机组成原理。
指针是一种指向某种类型的特殊的型别。一般用*定义。如int *p,这样就定义了一个指向int类型的指针。指针用于指向某块内存空间,该内存空间里面存放了其所指向的内存地址。所以,在使用指针之前,必须明白这指针指向了什么内存空间,给指针赋值可以使用取地址符(&),如p = &a;每次需要访问指针所指向的内容时,便使用解引用符号*进行访问。如:printf(“%d”,*p);
上面简单介绍了下指针的定义,赋值等操作。下面介绍下一些新手容易迷糊的地方吧。
1. char *str1=”abcd”; char str2[]=”abcd”;的区别
C标准没有规定这两种定义字符串的方式的差别。但是,指针类型的字符串一般不允许修改。如:str1[0]=’c’;这样的语句会导致运行时错误。错误类型:不允许写入什么的。据说,在某些编译器中可以设置成可以修改的。
2.指针/数组作为参数进行传递
在C语言中,参数是使用值传递的。
int func(int a );当调用者调用该函数的时候将传递一个值给a,这个a只是你传递进去的参数的一个副本。而数组传递的时候,会退化为指针,其将数组的首地址复制给形参。看下面的一个例子。

void fun(char str[])
{
printf("After transform:%d\n", sizeof(str));
}
int main(){
char strs[]="abcdefg";
printf("Before transform:%d\n",sizeof(strs));
fun(strs);
return 0;
}


输出:
Before transform:8
After transform:4

在传递之前,我们可以获得数组所占用的内存空间的大小;而传递后,我们只能获得一个指针的大小了。

许多初学C指针的同学想在函数内部修改作为参数传递进来的指针的值。看以下代码。

typedef struct Link_Node{
int Elem;
struct Link_Node *next;
}LinkNode,*PLinkList;
void CreateList(LinkNode *header)
{
int i=0;
header = (LinkNode *)malloc(sizeof(LinkNode));
header->Elem = 10;
header->next = NULL;
}

int main()
{
PLinkList head=NULL;
CreateList(head);
if(head!=NULL)
printf("%d\n",head->Elem);
free(head);
return 0;
}


许多人疑惑为什么没有输出呢?请各位谨记:C语言使用值传递进行参数传递。这就是问题所在。在传递指针的时候,调用者传递了原指针所指向的内存地址给形参。也就是说,在传递参数的时候,系统定义了另外一个指针,也就是我们在此定义的header指针。我们把原指针的值传递给了header指针。所以header指向了本例中的null。然后我们去修改header所指向的值。当函数结束后,这个指针的生命周期也结束了。所以对main函数中的head没有任何修改。
下面我们再看一例。

void Trans(int* Arr,int nLength)
{
for(int i=0;i<nLength;i++)
Arr[i] += i+20;
}
int main()
{
int nArr[5]={0};
int i;
printf("Before:\n");
for(i=0;i<5;i++)
printf("%d ",nArr[i]);
Trans(nArr,5);
printf("\nAfter\n");
for(i=0;i<5;i++)
printf("%d ",nArr[i]);
return 0;
}


输出:
Before:
0 0 0 0 0
After
20 21 22 23 24

我们发现我们在函数里面修改了数组的值。结合上面的例子,有新同学开始迷糊了。为什么上面的例子不能修改,为什么下面的例子就能修改了?由于指针存储的是地址值,所以初学比较迷惑。函数传递的是指针变量的值---即该指针所指向的变量的地址。所以,我们可以修改其指向的变量的值,而不能修改指针本身的内容了。为了能够修改指针本身的内容,我们需要传递指针本身的地址。所以在上面那例中,需要传递head指针本身的地址。代码如下:

void CreateList(LinkNode **header)
{
int i=0;
(*header) = (LinkNode *)malloc(sizeof(LinkNode));
(*header)->Elem = 10;
(*header)->next = NULL;
}
int main()
{
PLinkList head=NULL;
CreateList(&head);
if(head!=NULL)
printf("%d\n",head->Elem);
free(head);
return 0;
}


动态开辟数组
指针可以用来分配内存,作为数组来使用。
开辟一维数组:int *pArr = (int*)malloc(10*sizeof(int));释放空间:free(pArr);
开辟二维或者多维数组需要分级申请内存:int **pArr;
pArr = (int **)malloc(sizeof(int*)*3);
for(i=0;i<3;i++)
*(pArr+i) = (int *)malloc(sizeof(int)*5);
当然释放空间的时候也需要分级释放咯; for(i=0;i<3;i++) free(*(pArr+i));

函数指针和指针函数
在计算机内存中,所有的数据都有唯一的地址,当然可运行的程序也是有地址的。函数代码是程序的算法指令部分,它们和数组一样也占用存储空间,都有相应的地址。可以使用指针变量指向数组的首地址,也可以使用指针变量指向函数代码的首地址,指向函数代码首地址的指针变量称为函数指针。定义形式为:函数类型 (*指针变量名)(形参列表);如:int (*func)(char a[],int nlength)。不过,我们经常会遇到这样的定义方式:typedef int (*func)(char a[],int nlength);其实使用typedef主要是为了定义函数指针方便,不需要每次都敲那么的代码。(其实懒也是技术驱动的一种动力啊!)我这里就简单的介绍点东西。在搞算法的时候,函数指针大有用处,可以用它实现很多beautiful算法。比如那些自动机等等。

typedef int (*func)(char a[],int nLength);

int total(char a[],int nLength)
{
int nTotal = 0;
for(int i=0;i<nLength;++i)
nTotal += a[i];
return nTotal;
}

int main()
{
char a[]="abcde";
int nLength = strlen(a);
func fp;
fp = total;
printf("%d\n",fp(a,nLength));
return 0;
}


一个函数不仅可以带回一个整型数据的值,字符类型值和实型类型的值,还可以带回指针类型的数据,使其指向某个地址单元。在这里,我就不多说什么了。仅提醒各位初学者一下:返回的地址必须不能是该函数的栈空间里的。那样待函数返回后,获得到的地址将是一个错误的地址。

以上代码在XP+VS2008下实验通过。
...全文
1126 39 打赏 收藏 转发到动态 举报
写回复
用AI写文章
39 条回复
切换为时间正序
请发表友善的回复…
发表回复
-Alex 2014-04-26
  • 打赏
  • 举报
回复
好帖子!!!!!!!!!!!!!!!
王嗣钧 2014-03-05
  • 打赏
  • 举报
回复
引用 6 楼 cfjtaishan 的回复:
恩,关于参数是可以分为两种的:分为值传递和地址传递吧。
三种吧?按值传递,按地址传递,按别名传递
u010418973 2013-06-03
  • 打赏
  • 举报
回复
c语言入门也有很多不错的书 多看看书还是保险的
gdouchufu 2012-11-21
  • 打赏
  • 举报
回复
引用 35 楼 gdouchufu 的回复:
引用
gdouchufu 2012-11-21
  • 打赏
  • 举报
回复
whoami7788 2012-05-13
  • 打赏
  • 举报
回复
[Quote=引用 33 楼 的回复:]
笨笨熊姐姐,帮我看下这个问题http://topic.csdn.net/u/20120513/11/6b86b91c-1351-4b82-b8fb-7e80e48abafb.html?91650 指导下我吧,嘿嘿!
[/Quote]

http://topic.csdn.net/u/20120513/11/6b86b91c-1351-4b82-b8fb-7e80e48abafb.html?91650
whoami7788 2012-05-13
  • 打赏
  • 举报
回复
笨笨熊姐姐,帮我看下这个问题http://topic.csdn.net/u/20120513/11/6b86b91c-1351-4b82-b8fb-7e80e48abafb.html?91650 指导下我吧,嘿嘿!
whoami7788 2012-05-11
  • 打赏
  • 举报
回复
#include<stdio.h>
bootkit 2012-05-05
  • 打赏
  • 举报
回复
与其纠结这种问题,不如去学学汇编,这花不了几天时间,但对于用C/C++这种编译型语言的人来说很有益处。
清竹小雨 2012-05-05
  • 打赏
  • 举报
回复
呵呵,已经投你一票啦,不错
W170532934 2012-05-04
  • 打赏
  • 举报
回复
其实C语言只支持值传递。只有传递的是指针的值的时候才能去修改数据。
14号选手 2012-05-04
  • 打赏
  • 举报
回复
想问一下,值传递,是实参将自己的副本传递给了形参,在调用函数中形参进行了改变
而这个改变没有影响实参,所以不影响结果,是吗?
然而地址传递,就是会影响结果的,比如
比两个数的大小
期间调用一个函数是swap(&a,&b),这样就可以对其有影响吧?
我这样理解对吗?
[Quote=引用 6 楼 的回复:]

恩,关于参数是可以分为两种的:分为值传递和地址传递吧。
[/Quote]
lj1031 2012-03-28
  • 打赏
  • 举报
回复
给力芬啊
weifanYYao 2012-03-28
  • 打赏
  • 举报
回复
非常的好啊!楼主真是辛苦了。。。
W170532934 2012-03-28
  • 打赏
  • 举报
回复
欢迎大家到http://surveies.csdn.net/survey/comein/409给这篇文章投票哦。谢啦。
raincloud123 2012-03-27
  • 打赏
  • 举报
回复
学习了,谢谢分享
i945800687 2012-03-27
  • 打赏
  • 举报
回复
谢谢楼主分析
jt770520 2012-03-27
  • 打赏
  • 举报
回复
谢谢楼主分享哦
一根烂笔头 2012-03-26
  • 打赏
  • 举报
回复
还是zhao4zhong1比较犀利!
不想回复了,就是此招!
此回复方式,见于多贴啊!
猪头小哥 2012-03-25
  • 打赏
  • 举报
回复
一个打的是自己,一个打的是自己的影子。
加载更多回复(10)

69,374

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧