zoukankan      html  css  js  c++  java
  • C09 指针

    目录

    • 指针相关概念
    • 指针变量
    • null指针
    • 指针的算术运算
    • 指针数组 
    • 指向指针的指针 
    • 传递指针给函数 
    • 从函数返回指针

    指针相关概念

    变量

    如果在程序中定义了一个变量,在对程序进行编译时,系统就会为这个变量分配内存单元。编译系统根据程序中定义的变量类型分配一定长度的空间。内存的基本单元是字节,一字节有8位。每字节都有一个编号,这个编号就是“地址”。

    直接访问变量

    在程序中一般是通过变量名来对内存单元进行存取操作的。其实程序经过编译以后已经将变量名转换为变量的地址,对变量值的存取都是通过地址进行的。这种按变量地址存取变量的方式称为直接访问方式。

    间接访问变量

    间接访问的方式,即变量中存放的是另一个变量的地址。也就是说,变量中存放的不是数据,而是数据的地址。就

    按C语言的规定,可以在程序中定义整型变量、实型变量、字符型变量,也可以定义这样一种特殊的变量,它是存放地址的。

    指针

    数据在内存中的地址也称为指针,如果一个变量存储了一份数据的指针,我们就称它为指针变量。

    在C语言中,允许用一个变量来存放指针,这种变量称为指针变量。指针变量的值就是某份数据的地址,这样的一份数据可以是数组、字符串、函数,也可以是另外的一个普通变量或指针变量。

    现在假设有一个 char 类型的变量 c,它存储了字符 'K'(ASCII码为十进制数 75),并占用了地址为 0X11A 的内存(地址通常用十六进制表示)。另外有一个指针变量 p,它的值为 0X11A,正好等于变量 c 的地址,这种情况我们就称 p 指向了 c,或者说 p 是指向变量 c 的指针。如下图:

    “指针”和“指针变量”的区别:指针是一个地址;而指针变量是存放地址的变量。

    通常也将“指针变量”简称为“指针”。

    指针变量

    概念

    一个变量专门用来存放另一个变量的地址,那么就称它为“指针变量”。即指针变量里面存放的是指针,即地址。 

    指针变量的定义

    语法格式:

    基类型  *指针变量名;
    

     

    基类型:数据类型和*组合再一起构成指针类型变量类型。

    不同数据类型在内存中所占用的字节数是不同的,指针一般指向数据所在内存的第一个字节地址。

    指针的运算中,地址加减的基本单位是数据类型的字节数。例如整数的指针变量+1,实际上在内存中是移动4个字节。

    示例代码:

    #include <stdio.h>
    
    int main ()
    {
        int *p, *q;
        int k;  //k用来存放两个地址数相减的结果
        int i = 3, j = 4;
        p = &i;
        q = &j;
        k = p-q;
        printf("p内存地址:%#x
    q内存地址:%#x
    地址间隔:k = %d
    ", p, q, k);
        return 0;
    }
    

    示例代码说明:

    • #x表示输出16进制数据,并且数据前添加0x。
    • &为取地址符。指针变量p和q的值为内存地址,需要使用取地址符号&。
    • 定义指针变量时必须带*,给指针变量赋值时不能带*。

    输出结果:

    修改指针变量的值

    和普通变量一样,指针变量也可以被多次写入。

    示例代码:

    #include <stdio.h>
    
    int main ()
    {
    	//定义普通变量
    	float a = 99.5, b = 10.6;
    	char c = '@', d = '#';
    	//定义指针变量
    	float *p1 = &a;
    	char *p2 = &c;
    	//修改指针变量的值
    	p1 = &b;
    	p2 = &d;
        return 0;
    }
    

      

    示例说明:

    注意:p1、p2 的类型分别是float*和char*,而不是float和char,它们是完全不同的数据类型。如果让p1指向char*,p2指向float*,结果如何?

    结果:指针指向的数据类型不相关,编译出错:

    Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
    

      

     通过指针变量取得数据

    指针变量存储了数据的地址,通过指针变量能够获得该地址上的数据。

    语法格式:

    *pointer;
    

      

    这里的*称为指针运算符,用来取得某个地址上的数据。

    示例代码: 

    #include <stdio.h>
    
    int main(){
        int a = 15;
        int *p = &a;
        printf("%d, %d
    ", a, *p);  //两种方式都可以输出a的值
        return 0;
    }
    

      

    输出结果:

    分析:示例中,假设 a 的地址是 0X1000,p 指向 a 后,p 本身的值也会变为 0X1000,*p 表示获取地址 0X1000 上的数据,也即变量 a 的值。从运行结果看,*p 和 a 是等价的。

    CPU 读写数据必须要知道数据在内存中的地址,普通变量和指针变量都是地址的助记符,虽然通过 *p 和 a 获取到的数据一样,但它们的运行过程稍有不同:a 只需要一次运算就能够取得数据,而 *p 要经过两次运算,多了一层“间接”。

    即:用指针是间接获取数据,使用变量名是直接获取数据,前者比后者的代价要高。

     NULL 指针

    在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。

    赋为 NULL 值的指针被称为空指针。

    NULL 指针是一个定义在标准库中的值为零的常量。

    示例代码:

    在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的。

    然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。

    但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。

    指针的运算

     C 指针是一个用数值表示的地址。因此,可以对指针执行算术运算:++、--、+、-。

    指针支持关系运算,如 ==、< 和 >。如果 p1 和 p2 指向两个相关的变量,比如同一个数组中的不同元素,则可对 p1 和 p2 进行大小比较。

    假设 ptr 是一个指向地址 1000 的整型指针,是一个 32 位的整数,让我们对该指针执行下列的算术运算:

    ptr++; //移动到下一个位置
    ptr--;  //移动到上一个位置
    ptr1==ptr2; //是否指向同一个地址

    说明:  

    在执行完ptr++后,ptr 将指向位置 1004,因为 ptr 每增加一次,它都将指向下一个整数位置,即当前位置往后移 4 个字节。

    指针数组

    定义

    定义用来存储指针的数组。

    语法格式:

    数据类型  *指针数组变量名[数组大小]
    

      

    例如:

    int *ptr[100];
    

      

    如上示例,把 ptr 声明为一个数组,由 100个整数指针组成。因此,ptr 中的每个元素,都是一个指向 int 值的指针。

    示例:将整数保存在整数指针数组中

    #include <stdio.h>
     
    const int MAX = 3;//常量
     
    int main ()
    {
       int  nums[] = {10, 100, 200};
       int  *ptr[MAX];
       int i;
       for ( i = 0; i < MAX; i++)
       {
          ptr[i] = &nums[i]; /* 赋值为整数的地址 */
       }
       for ( i = 0; i < MAX; i++)
       {
          printf("数组元素nums[%d] = %d
    ", i, *ptr[i] );
       }
       return 0;
    }
    

      

    输出结果:

     示例:指向字符的指针数组来存储一个字符串列表

    #include <stdio.h>
     
    const int MAX = 4;
     
    int main ()
    {
       const char *names[] = {
                       "张三",
                       "李四",
                       "王五",
                       "赵六",
       };
       int i = 0;
     
       for ( i = 0; i < MAX; i++)
       {
          printf("数组元素names[%d] = %s
    ", i, names[i] );
       }
       return 0;
    }
    

      

    输出结果:

    指向指针的指针

    概念

    指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。

    通常,一个指针包含一个变量的地址。

    当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。

    如下图所示:

    语法格式:

    datatype  **ptr
    

      

    同样,当一个目标值被一个指针间接指向到另一个指针时,访问这个值需要使用两个星号运算符。

    示例代码

    #include <stdio.h>
     
    int main ()
    {
       int  var;
       int  *ptr;
       int  **pptr;
    
       var = 3000;
    
       /* 获取 var 的地址 */
       ptr = &var;
    
       /* 使用运算符 & 获取 ptr 的地址 */
       pptr = &ptr;
    
       /* 使用 pptr 获取值 */
       printf("var的值为 = %d
    ", var );
       printf("*ptr指向的值为 = %d
    ", *ptr );
       printf("**pptr指向的值为 = %d
    ", **pptr);
    
       return 0;
    }
    

    输出结果:

    传递指针给函数

    指针作为函数参数

    C 语言支持传递指针给函数,只需将函数参数声明为指针类型即可。

    示例代码:

    #include <stdio.h>
    #include <time.h>
     
    void getSeconds(unsigned long *par);
    
    int main ()
    {
       unsigned long sec;
    
    
       getSeconds( &sec );//实参为sec的地址
    
       /* 输出实际值 */
       printf("Number of seconds: %ld
    ", sec );
    
       return 0;
    }
    
    void getSeconds(unsigned long *par)
    {
       /* 获取当前的时间戳 */
       *par = time( NULL );
       return;
    }
    

      

    数组作为函数参数

     指针能够作为函数参数,也能将数组指针作为参数。

    对于一维数组而言,数组名就是指向该数组首地址的指针。

    示例代码:

    #include <stdio.h>
    
    /* 函数声明 */
    double getAverage(int *arr, int size);
    
    int main ()
    {
    	/* 带有 5 个元素的整型数组  */
    	int balance[5] = {1000, 2, 3, 17, 50};
    	double avg;//平均值 
    	/* 传递一个指向数组的指针作为参数 */
    	avg = getAverage( balance, 5 ) ;
    	
    	/* 输出返回值  */
    	printf("Average value is: %f
    ", avg );
        
    	return 0;
    }
    
    double getAverage(int *arr, int size)
    {
    	int    i, sum = 0;       
    	double avg;          
    	
    	for (i = 0; i < size; ++i)
    	{
    		sum += arr[i];
    	}
    	
    	avg = (double)sum / size;
    	
    	return avg;
    }
    

      

      

    从函数返回指针

     C 允许从函数返回指针。为了实现该,必须声明一个返回指针的函数,语法格式如下:

    int * myFunction()
    {
    	//函数体
    }
    

     

    此外,C 语言不支持在调用函数时返回局部变量的地址,除非定义局部变量为 static 变量。

    示例:

    示例函数会生成 10 个随机数,并使用表示指针的数组名(即第一个数组元素的地址)来返回它们。

    #include <stdio.h>
    #include <time.h>
    #include <stdlib.h> 
     
    /* 要生成和返回随机数的函数 */
    int * getRandom( )
    {
       static int  r[10];
       int i;
     
       /* 设置种子 */
       srand( (unsigned)time( NULL ) );
       for ( i = 0; i < 10; ++i)
       {
          r[i] = rand();
          printf("%d
    ", r[i] );
       }
     
       return r;
    }
     
    /* 要调用上面定义函数的主函数 */
    int main ()
    {
       /* 一个指向整数的指针 */
       int *p;
       int i;
     
       p = getRandom();
       for ( i = 0; i < 10; i++ )
       {
           printf("*(p + [%d]) : %d
    ", i, *(p + i) );
       }
     
       return 0;
    }
    

      

    输出结果:

  • 相关阅读:
    HDU 4165 pills
    POJ 2125 Destroy The Graph
    POJ 2075 Tangled in Cables
    NYOJ Number SequenceProblem F
    PHP Eval研究笔记
    PHP 学习备忘(更新中)
    iOS学习笔记1 版本控制
    PHP全局缓存上栽的坑及其他【反面教材系列】
    iOS学习笔记2 Code Format
    iOS学习笔记4 垃圾回收初涉
  • 原文地址:https://www.cnblogs.com/rask/p/10178157.html
Copyright © 2011-2022 走看看