C语言学习笔记系列(3)指针

说起C语言的难点,我想很多人的第一念头就是指针,各种不同类型的指针指来指去,左偏移一下右偏移一下,经常搞得人头昏脑胀,不知所措。然而C语言的灵活性也恰恰体现在指针上,只要你能够不被指针迷惑,不被吓倒,能够清楚地知道自己在做怎样的处理,那么指针的运用,会让你更加得心应手,随心所欲,指哪打哪。从本质上讲指针与数组是同一种类型,数组可以做的操作,指针基本上也都可以。

我们先来了解一下使用操纵内存时容易出错的地方,这样在使用指针时才能够尽量避免出错。

1:未初始化

这也是使用数组是容易出现的问题,在声明一个数组后或者是malloc分配一块空间后,没有及时地进行初始化,造成使用数组或者指针时取得不可预知的数据。

2:内存读写越界覆盖

每个数组或者指针,都有其大小,在编码时要时刻注意不能操作到其范围外的地址空间。因为那样会有可能影响到其他的变量,造成不可预知的错误。C语言不像Java等编程语言,在数组越界时会提示错误,C语言的灵活性就在于开发人员可以自由操纵内存,因此即使你的数组或者指针越界了,也不会得到编译器的人和回应。这种越界有可能不会造成任何问题,但也许会在任意时刻造成致命的错误。

3:空指针

这个是相对较容易发现的问题,也是出现问题较多的地方。我们需要做的就是,在使用任意指针做操作之前(当然初始化操作或者内存释放操作除外),都需要保证该指针指向非空(!=NULL)

 

以上就是操纵内存时容易出现问题的地方,也是我们在学习指针时需要重点关注的地方。

关于指针的操作,重点在于偏移。偏移的形式有多种,类似于数组的下标偏移方式比较简单,而用++或者——进行多次偏移时则要复杂的多。关于指针的难点,那就是多重指针了。基础是单一指针,我们只有把多重指针分解成一个个的单一指针,才能更好地理解和应用多重指针。

下面针对偏移和多重指针举例进行说明。

#include <stdio.h>

#include <string.h>

 

int main()

{

      int i = 0;

      int numlist[4] = {0x123456,0x2111,0x78,0x23765432};

      int *pnlist = NULL;

      short *pilist = NULL;

      char *pclist = NULL;

 

      pnlist = numlist;

      for(i = 0; i < sizeof(numlist)/sizeof(*pnlist); i++) {

               printf("pnlist[%d]: %x/n", i, *(pnlist++));

      }

      //out of numlist

      printf("pnlist[%d]: %x/n", i, *(pnlist++));

 

      pilist = (short *)numlist;

      for(i = 0; i < sizeof(numlist)/sizeof(*pilist); i++) {

               printf("pilist[%d]: %x/n", i, *(pilist++));

      }

 

      pclist = (char *)numlist;

      for(i = 0; i < sizeof(numlist)/sizeof(*pclist); i++) {

               printf("pclist[%d]: %x/n", i, *(pclist++));

      }

 

      return 0;

}

运行结果为:

pnlist[0]: 123456

pnlist[1]: 2111

pnlist[2]: 78

pnlist[3]: 23765432

pnlist[4]: 4

pilist[0]: 12

pilist[1]: 3456

pilist[2]: 0

pilist[3]: 2111

pilist[4]: 0

pilist[5]: 78

pilist[6]: 2376

pilist[7]: 5432

pclist[0]: 0

pclist[1]: 12

pclist[2]: 34

pclist[3]: 56

pclist[4]: 0

pclist[5]: 0

pclist[6]: 21

pclist[7]: 11

pclist[8]: 0

pclist[9]: 0

pclist[10]: 0

pclist[11]: 78

pclist[12]: 23

pclist[13]: 76

pclist[14]: 54

pclist[15]: 32

其中pnlist[4] 为越界后取得的值,程序不会提示出错。从结果可以看出来,指针的偏移是以本身类型为单位偏移量的,int指针++后,偏移了一个int4byte),short指针++后,偏移了一个short2byte),char指针++后偏移了一个char1byte)。还有就是,指针本身没有明确它所指定的范围,但是它所指向的空间有着明确的大小范围。

#include <stdio.h>

int main()

{    

      char *pletter[] = {"abc", "de", "fghi"};

     

      //first string

      printf("string1: %s/n", *pletter);

 

      //first string'first letter

      printf("letter1: %c/n", **pletter);

 

      return 0;

}

运行结果为:

string1: abc

letter1: a

其中*pletter 等效于pletter[0]**pletter等效于pletter[0][0]或者*pletter[0]

 

以上例子只是用来帮助理解指针的操作,复杂度上可能无法与实际应用相比。但是如果理解了这些,那么即使再复杂的指针也可以分解成简单的指针操作,也就不难理解了。

关于函数指针,实际应用中比较少见,比较容易见到的场合可能就是在创建线程时,不是太复杂,这里仅做简单说明。

     int thr_create(void  *stack_base,  size_t  stack_size,  void

     *(*start_func)  (void*),  void  *arg,  long  flags, thread_t *new_thread_ID)

其中第三个参数指明了创建线程所需函数的特征:void*(*start_func)  (void*)

第一个void*表明所需函数的返回值为void,括号内的*表明所需函数要声明成指针类型,后面的void*表明参数类型为void*,根据要求我们可以定义下面的函数:void *threadtestvoid *argv)或者void *threadtest()。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值