C语言指针学习

转自:http://blog.sina.com.cn/s/blog_9da24f3b01013199.html   

在自学C语言之后很长一段时间里,我都认为指针是没有用的东西,既然变量就可以操作数据,为什么好要有指针呢?直到最近开了C语言的课,我才发现指针的强大。

    首先从一次上机实验说起吧。在学完数组(我们指针是在数组后面学的)后,老师曾让我们写一个排序的程序。里面要用到将两个数交换, 于是我就想把这个交换功能写成一个函数,于是有了以下代码:

       void  change(int x,int y)
        {
                 int temp;
                 temp = x;
                 x = y;
                 y = temp;
        }
运行结果是:编译通过,但是有逻辑错误。逻辑错误是这个函数一点作用都没起到,两个数完全没有交换。后来学到指针,我才明白。原来函数的执行,是把实参中的数据拷贝到形参中,就像复制了一份,传过去的是复制品,也就是大家所说的单向的传递。再函数里可以对值进行应用,但是对值的操作一点都不会影响到实参值。那该怎样写这个交换函数呢?刚学指针不久我写了以下代码:

       void change(int *x,int *y)
       {
              int *temp;
              temp = x;
               x = y;
              y = temp;
           

这里已经运用了指针,按说已经可以将两个数交换了,但是运行结果同上一次的一样,完全看不到任何作用。后来在爱机器人网上跟大家交流的时候弄明白了这个问题。如果形参是指针,从实参到形参的传递是单向的地址传递,也是先把地址复制一份,然后将复制品传递过去,对复制品的操作不能对实参产生任何影响,即使产生了影响(那得用二级指针了,后面会明白的),也只是将a、b的地址交换了一下,a、b的值依然不变。那要怎么样实现交换的函数呢?这里就可以看出间接访问的优越性了。虽然不能对实参造成影响,我们是可以应用的传递来的值的。于是有了以下代码:

void change(int *x,int *y)
       {
              int temp;
              temp = *x;
               *x = *y;
              *y = temp;
           

这样就完全实现了交换函数。在函数内,我们虽然得到的是实参的复制品,但是我们可以利用这个复制品的数据(也就是地址)。我们已经知道了a、b的地址,自然就可以对a、b的值进行操作了。首先用*取到地址中存放的内容,然后将他们交换。这里直接对内存操作,交换的目的可以轻松达到。

    到这里对指针已经有一点小小的敬佩之情了,没想到它的魅力还没有完全展现出来。后来又学习了指针与数组,以及字符串,知道了数组名有些时候就跟指针一样。同时有产生了一些疑问:

疑问一、我们最常用的printf()函数的第一个参数到底是什么呢?为什么我们可以用"abc..."放在里面?

    这个问题苦思冥想了一阵子,觉得自己找到了答案。这个函数的第一个参数应该是一个字符数组(char a[])。当时觉得很奇怪,为什么会是这样呢?因为一维数组数组名中存放的就是第一个数据的指针,而""的作用就是:1、把里面引起来的东西用和字符数组一样的保存方式保存起来。2、在后面加上\0。3、它的值是存放第一个字符的地址。这样看起来两个东西是等价的,我们用了那个久的printf函数也就有了更深的理解了。但是不久就有出现了新的问题:

疑问二、定义  char *a = "asdasd",*b = "";然后用 strcpy(b,a);编译为异常终止。为什么呢?

    这个问题是向老师请教后得到的答案,其实strcpy的代码内容我们可以自己类似的写出来:

void str_copy(char *to, char *from)
{
 for(;*from!='\0';from++,to++)
  *to = *from; 
 *to ='\0'; 
}

大家可以看到在这一行*to = *from;其实是分别将from字符数据复制给to。大家可以想一想第一次执行循环的时候,就是将'a'赋值给'\0',这里的'a'和'\0'是什么,是代码区的内容,就跟常量123一样,我们在自己写代码改变自己代码的内容,这当然会报错,C语言中是不允许这样的。所以对""的理解还要加上一条:4、引起来的内容是存在代码区的常量。所以说要先把字符串中的数据用字符变量数组保存起来,然后传去变量的指针,就可以实现了,所以这个函数的用法应该是:

char a[] = "asdasd",b[] = "";

strcpy(b,a);

    后来我就在想,既然如此发明C语言的人为什么不把这个函数写的参数即能是变量的指针,又能是常量的指针呢?于是我有了一个想法: char *a = "asdasd"中虽然内容是常量,但是a依然是个指针变量,如果我们能把指针变量a,赋值给b,那就可以实现常量的赋值了。于是有了以下代码:
void str_copy(char **to, char **from)

  *to = *from;  
}

void main()
{
 char *a = "asdasdasd";
 char *b = "";
 str_copy(&b,&a);
 printf("%s\n",a);
 printf("%s\n",b);
}

结果输出结果和想想中的一样:

asdasdasd

asdasdasd

一点小激动后就想测试变量的指针通不通行:
void str_copy(char **to, char **from)

  *to = *from;  
}

void main()
{
 char a[] = "asdasdasd";
 char b[] = "";
 str_copy(&b,&a);
 printf("%s\n",a);
 printf("%s\n",b);
}

结果编译报错: cannot convert parameter 1 from 'char (*)[1]' to 'char **

也就是说:不能把char (*)[1]型的参数赋值给 char **型的。

问题出在传参这一步,为什么会这样呢?这个问题比较难缠,后来查了《C和指针》一书才弄明白:

1、指针中存的不光是内存地址,还有数据类型用来控制分配多少空间。

2、数组名和指针的差别: 例如 int a[5] 跟 int *b

C语言指针学习

a的特点有:

1、开辟了5个连续的内存空间。

2、每个内存空间都是占四个字节的int型

3、把这些空间分配到适当的位置,并有a来指向第一个数据

4、a是常量,不可变,不能执行自加等运算。

C语言指针学习
b的特点有:

1、开辟了占四个字节的空间。

2、具体这个空间在哪里不知道,很可能就是一个野指针。

3、b后面没有继续开辟存放用的空间。

明白了这些以后再来看我们的问题。其实char a[] = "asdasdasd";后&a应该是一个指向数组的指针,而不是指向指针的指针,所以不能赋值给形参 char **from。数组名与指针差别并不很大,那为什么这里不能赋值呢?

再来回顾一个问题 形参 char p[]这样的是不用之名p的长度的,因为传来的实参一定能说明这个长度。但是它必须指明这个指针是指向char行的,因为要计算字节来分配空间。同理char a[] = "asdasdasd";后的&a指向的内容并不是一个简单指针,因为他包含了有10个字符这样用来计算分配多少字节的数据,而char **from指向的内容就是一个简单的指针,所以他俩很不一样,不能相互赋值。那怎么样修复呢?把参数改为:

void str_copy(char **to, char (*from)[10])

char (*from)[10]是一个指向十维char型数组的指针

后面就不用看了,肯定行不通,因为数组名不能拿来做运算。

    最后得出结论,C语言的发明人员还是很厉害的,所以strcpy的使用只有我们自己注意不能放代码区的指针进去。

    现在仿佛看见了一排排的内存,一根根针指着他们每一个,针上写着指向的数据类型,不同的针指向的内存大小也有所不通。这也许就是指针的艺术吧。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值