关于指针的一点摘要

1。一点基础
      int *p = NULL;
      定义了 p是一个指针。
       p这个指针的步长是4,也就是 ++p后,p的内存地址会增加4个byte。
       p的初始值是NULL,NULL的定义一般是(void *)0或者0.
       如果p是全局变量,那么内存单元在link时分配在静态存储区;如果是局部变量,那么内存单元在运行时分配在stack上。
      定义指针时,编译器并不为指针所指向的对象分配空间,它只是分配指针本身的空间,除非在定义时同时赋给指针一个字符串常量进行初始化。
      eg.
      
char *p = "breadfruit";
  注意只有对字符串常量才是如此。另外,在ANSI C中,初始化指针时所创建的字符串常量被定义为只读。如果试图通过指针修改这个字符串的值,程序就
  会出现未定义的行为。该字符串常量,一般存放在数据段,但在有些编译器中,存放在文本段,以防止它被修改。
  与指针相反,由字符串常量初始化的数组是可以被修改的。
  [Parts are referred from <<C Expert Programming>>]

 2。常见用法
      (1). 访问内存数据。
       从底层的观点来看,一个指针就是一个内存地址。
      (2). 动态分配时,储存分配出的内存地址。
       eg.
      
char *p = NULL;
       p = (char *)malloc(MAX_MEM_SIZE);
  (3). 在函数中作为参数传入或传出,在函数内访问传入数据
   eg.
   
    int gid_name(char *name, gid_t *gid)
       {
           [...]
           *gid = ptr->gid = gr->gr_gid;
           return (0);
       }
       如上函數,返回值只用以标记错误条件。
       使用指针类型参数有时也是为了避免函数调用时的大量数据copy,比如结构体,这时使用指针会更有效率。
       eg.
       static double
diffsec(struct timeval *now, struct timeval *then)
       {
           return ((now->tv_sec  -  then->tv_sec)*1.0 + (now->tv_usec  -  then->tv_usec) / 1000000.0);
       }
       函数中通过指针传入的两个结构体的成员值计算结果。
       像上面这种情况,当通过指针传入的参数不需被修改时,最后在前面添上const关键字,表示在函数作用域内,该参数只读。
      (4). 数据成员访问
       访问结构体的成员:p->f;
       访问数组元素:
       
 p = &a[0];
        *(p + 3) = 0;
      (5). 替代数组作为函数参数
       在C中,当数组名作为函数参数时,传入的实际上是这个数组的第一个元素的地址, 即,注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。这时,函数中任何对数组的改动将直接影响到该数组本身。
       同样,C函数中return的数组名也会只返回一个指向该数组第一个元素的指针。所以,当从函数中返回一个数组时,要确保该数组不是从函数中定义的
      局部变量,局部变量内存分配在栈上,否则,一旦函数退出,这个数组的内容会被覆盖,返回的数据会因此无效。避免这种情况的一个方法是将该数组定义为static。
       eg.
      
char *inet_ntoa(struct in_addr ad)
       {
         unsigned long int s_ad;
         int a, b, c, d;
         static char addr[20];
 
         s_ad = ad.s_addr;
         d = s_ad % 256;
         s_ad /= 256;
         c = s_ad % 256;
         s_ad /= 256;
         b = s_ad % 256;
         a = s_ad / 256;
         sprintf(addr, "%d.%d.%d.%d", a, b, c, d);
         return addr;
       }
      如上函数,如果不把数组addr声明为static,当函数inet_ntoa() return时,addr数组的内容会无效。
      (6). 函数指针
           C不允许函数作为参数传入其他函数中,但允许传递一个函数指针给另一个函数。
      (7). 作为别名
           eg.
           struct
output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0};
           [...]
           struct
output *out1 = &output;
       在上例中,指针变量out1可以用在使用变量output的地方。
       一般这样使用有三个原因:
       i. 提高效率
        如果一个数据结构比较大,那么在某些场合,比如赋值语句中使用指针会避免对整个结构体的拷贝。
      
  static struct termios cbreakt, rawt, *curt;
        [...]
        curt = useraw ? &rawt : &cbreakt;
       ii. 访问static的数据
        一个指针常用来指向不同的静态数据, 
        eg.
        char *s;
        [...]
        s = *(opt->bval) ? "True" : "False";
       iii. 在不同的函数中访问同一个全局数据
        eg.
        static long rntb[32] = 
        {
            3, 0x9a319039, 0x32d9c024, 0x9b663182, 0x5da1f342,
            [...]
        };
 
        static long *fptr = &rntb[4];
        [...]
 
        void srrandom(int x)
        {
            [...]
            fptr = &state[rand_sep];
            [...]
        }
 
        long rrandom()
        {
            [...]
            *fptr += *rptr;
            i = (*fptr >> 1) & 0x7fffffff;
        }
        本例中,fptr被初始化指向一个随机数种子表,然后在函数srrandom()中再次被赋值,最后,在rrandom函数中被用来修改所指向的数据。
 
      (8). 处理string, 如strlen函数:
       size_t strlen(const char *str)
       {
           register const char *s;
 
           for (s = str; *s; ++s) ;
           return(s - str);
       }
      (9). 直接内存访问
       既然指针本质上表现为一个内存地址,那么在底层代码中会使用指针去访问特定硬件的内存地址。
       static void vid_wrchar(char c)
       {
           volatile unsigned short *video = NULL;
 
           video = (unsigned short *)(0xe08b8000) +
            vid_ypos * 80 + vid_xpos;

           *video = (*video & 0xff00) | 0x0f00 | (unsigned short)c;
       }
   注意,现代操作系统一般不允许直接硬件访问,所以一般这样的代码只会出现在嵌入式系统或者内核和驱动程序中。
 
     3。 使用规则建议
      这些规则主要是为了防止内存泄漏的
      (1). 用malloc申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL 的内存。
       char *p = (char *)malloc(USER_DEFINE_SIZE);
       if (NULL == p)
       {
            /* Exception Handle */
       }
      (2). 为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。
       For Array:
       int aVar[100] = { 0 };
 
       For Dynamic allocate memory:
       char *p = NULL; /* Initial */
       p = (char *)malloc(USER_DEFINE_SIZE); /* Malloc buffer */
       if (NULL == p)
       {
            /* Exception Handle */
       }
       memset(p, 0, USER_DEFINE_SIZE);
      (3). 避免数组或指针的下标越界,特别要当心发生“多1”或者“少1”操作。
      (4). 动态内存的申请与释放必须配对,防止内存泄漏。
       For free allocate memory:
       if (NULL != p)
       {
            free(p);
       }
      (5). 用free释放了内存之后,立即将指针设置为NULL,防止产生“野指针”。
      [Reference from <<High Quality C/C++ Programming Guide>>]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值