C语言学习笔记(五)——指针【C语言的灵魂】

来源:互联网 发布:r语言 单独 数据挖掘 编辑:程序博客网 时间:2024/04/27 21:59


          第五章                               指针【C语言的灵魂】

 

指针【C语言的灵魂】

        实质:

                  指针的实质就是地址,地址就是指针

                  地址就是内存单元的编号

                  指针变量是存放地址的变量

                  指针和指针变量是两个不同的概念

                  但是要注意:通常我们叙述时会把指针变量简称为指针,实际它们的含义并不一样。

                  学习指针后,应将变量空间看作两部分内容:变量的内容和变量的地址。

                  实质:不在同一函数的两个指针,都可以指向同一变量。

                  注意:&取地址符不能取常量和表达式的地址。

        指针的重要性:

                           1.表示一些复杂的数据结构

                           2.快速的传递数据,减少内存耗用

                                    使用指针指向主函数中的变量,从而直接修改主函数中的变量,且只发送4个字节的地址,速                                              度快,效率高。

                           3.使函数返回一个以上的值

                           4.能直接访问硬件

                           5.能够方便地处理字符串

                           6.是理解面向对象语言中引用的基础

                  指针的分类:

                           1.基本类型指针

                                    详见经典指针程序_互换两个数.cpp

                           2.指针和数组

                           3.指针和函数

                           4.指针和结构体

                           5.多级指针

 

                  指针与函数

                           函数的形参与实参永远不可能是同一个变量,通过指针指向实参可以使 *形参与实参为同一变量,在

                          函数内部将实参的值改变。

                           【指针:改写主函数中变量的值】

                           要想改变主函数中变量的值,实参必须发送地址。

通过传值,形参永远不可能改变实参的值,只能返回一个值,但可以通过指针,发送(复制)地址给形参,从而改变实参的值(即返回多个值)。

 

                  指针与数组(地址的运算)

                           一维数组名

                                    是个指针常量,存放的是数组中第一个元素的地址,固定指向数组中第一个元素,不可被赋值                                             改变。

 

                                    inta[5]; //aint *类型

                                    *a= 9; //一维数组名即表示数组中第一个元素的地址,此处元素a[0]被赋值9

                                    *(a+1)= 10; //依次类推,a+1a+2...a+i分别表示第2个、第3...i+1个元素。

                                    地址+1即为数组中下一连续元素的地址。

                           下标与指针

                                    如果p是个指针变量(或数组名),则 p[i] 永远等价于 *(p+i)

                           指针变量加下标即表示数组元素,正如a[i]表示数组元素一样,a本身就是指针变量。

                           函数对数组进行处理,需要接收两个参数:函数第一个元素的地址(即数组名)和数组元素的个数。

                           即确定一个数组需要两个参数(首地址与长度),只要知道函数第一个元素的地址(即数组名)和数组                      元素的个数,那么数组中所有元素的信息都可以知道。

 

                           指针的运算

                                    指针变量不能相加,不能相乘,也不能相除。

                                    如果两个指针变量指向的是同一块连续空间中的不同存储单元,

                                    则这两个指针变量才可以相减。

                                    正如,在同一小区的门牌号才可以相减,不在同一小区的门牌号相减无意义。

                                    指针的相减运算就是十六进制数的相减运算。

                                    inti = 3;

                                    intj = 5;

                                    inta[50];

                                    int* p = &i;

                                    int* q = &j;

 

                                    q- p; //不在同一存储空间的两个变量ij的地址相减无意义。

 

                                    p= &a[1];

                                    q= &a[44];

        

                                    printf("%d\n",q-p); //即为十六进制数的相减,然后以十进制输出。

                  动态内存分配

                           内存分配头文件:<malloc.h>

                                    malloc.h文件包括malloc()函数、free()函数、realloc()函数等操作内存函数。

                                    在使用时应注意声明<malloc.h>头件。

                                    mallocmemory(内存)allocate(分配)的缩写。

                                    inti = 3; //静态分配内存,给变量i分配了4个字节空间。

                                    int* p = (int *)malloc(20);

                                                       /*

                                                                1.动态分配内存,要使用malloc函数,必须添加malloc.h头文件                                                                  (函数声明)。malloc memory(内存) allocate(分配)的缩写。

                                                                2.malloc函数只有一个形参,并且形参是整型。

                                                                3.20表示请求系统为本程序分配20个字节内存空间。

                                                                4.malloc函数的返回值为第一个字节的内存地址,赋值给指针变         

                                                       p

                                                                 malloc函数的返回值为void *类型,void *类型也叫干地址              

                                                              ,即无实际意义的地址。

                                                                  并且需要强制类型转换(int *),将所分配的内存转换为指针变         

                                                            p所定义的int * 类型。

                                                                  共分配了20个字节,每个int类型变量占4个字节,所以共生成                                                             5个变量(动态数组)。

                                                                  第一个变量为*p,第二个为*(p+1),依次类推,第i个为*(p+i)                                                                  5.注意:指针变量p所占的内存是4个字节,为静态分配的,p所指                                                            向的内存为动态分配的。

                                                                6.如果内存分配失败,则malloc函数会返回地址NULL,所以经常在调用                                                                   malloc函数后写上if (NULL == p);语句来判断是否分配成功。

                                                       */

 

                                    *p= 3; //为第一个变量赋值3

                                    *(p+1)= 5; //为第二个变量赋值5

 

                                   printf("%d,%d\n", *p, *(p+1));

 

                                    free(p);//free()函数,将p指向的内存空间释放掉。注意:p本身的内存空间是静态的,不                                                    会被释放,只能在程序结束后被系统释放。

                           动态构造一维数组:

                                              inta[5]; //静态构造了5个元素的一维数组。

                                              int* pArr;

                                              intlen;

                                              inti;

 

                                              printf("请输入需要存放的元素的个数:");

                                              scanf("%d",&len);

                                              pArr= (int *)malloc(4*len); 

/*

动态构造了一维数组,长度为len,数组类型为                

        int类型,数组名即为pArr。类似于 int a[5];                               数组名PArra相同。

                                                                               动态数组的操作与静态数组相同,数组名pArr                 

                                                                               放了数组第一个元素的地址。

                                                                          */

                                            for (i=0;i<len; ++i)

                                              {

                                                       printf("请输入第%d个元素的值:", i);

                                                       scanf("%d",&pArr[i]); //与静态数组操作相同。

                                              }

 

                                              printf("一维数组中的内容为:\n");

                                              for(i=0; i<len; ++i)

                                                       printf("a[%d]= %d\n", i, pArr[i]);

 

realloc(pArr,100); /*

                           realloc()重新分配内存函数,用于改变数组的长度,后跟两个参数,                                             第一个是数组名,第二个是重新分配的数组字节数,

                                                                    如此函数即将pArr数组所占内存改为100字节。

                                                                     当内存改变时,增长:如50字节变为100字节,前50                                                                                  节内容保留不变,再增加50字节内存。

                                                                    缩小:如150字节变为100字节,前100字节内容保留,     

                                                                   50字节内容丢失。

                                                                          */

                                              free(pArr); //free()函数释放指针变量所指向的变量内存,当释放数组名时,将全                                                      //部释放整个数组所占的内存,而不是只释放数组名所指向的第一个元                                                                 //素变量内存。

 

                                              printf("一维数组中的内容为:\n");

                                              for(i=0; i<len; ++i)

                                                       printf("a[%d]= %d\n", i, pArr[i]);

 

                           动态内存与静态内存比较:

                                    静态内存是由系统自动分配,由系统自动释放。

                                    静态内存是在栈中分配的。

 

                                    动态内存是由程序员手动分配,手动释放。

                                    动态内存是在堆中分配的。

 

                                    内存分为五大区,其中在栈中分配的内存调用时压栈,完毕后出栈,内存被释放,不能够跨函                                            数使用,而在堆中分配的内存不存在这种情况,可以跨函数使用。

 

                           多级指针

                                    多级指针,即为指针的指针,取指针变量的地址。

                                    inti = 10;

                                    int* p = &i;

                                    int** q = &p; //pint *类型,&p即为int ** 类型。

                                    int*** r = &q;

 

                                    //p= &r; //错误,rint *** 类型,&r即为int **** 类型,而pint * 类型,类型不一                                                      //致,无法赋值。

                                    printf("i= %d\n", i);

                                    printf("i= %d\n", *p);

                                    printf("i= %d\n", **q);

                                    printf("i= %d\n", ***r); //*rq**rp,等价于*q***ri,等价于*p

                                    int** p如何理解:pint ** 类型,*p就是int * 类型,**p就是int类型。(星号的移动)

                                    注意:要在另一函数中修改谁,就应该发送谁的地址,以其指向(包含多次指向)来修改,而                                       不可能修改发送的变量内容。

                                              如: int * p;

                                                  change(p); //错误,要修改p的指向,应该发送p的地址,而不能发送p本身。

                                              即: int * p;

                                                  change(&p); //*q == p*q指向来实现修改p的指向。

 

                           跨函数使用内存

                                    上已介绍,静态内存是在栈中分配的,函数运行结束后内存被释放,不能跨函数使用。

                                    动态内存是在堆中分配的,函数运行结束内存空间仍然存在,所以可以跨函数使用。

                                    学习指针的重要用途就是为了跨函数使用动态内存变量,在跨函数使用中,传递的都是地址。

                                    

 

                           指针变量所占字节

                                              sizeof()函数:

                                                       sizeof(数据类型或变量名)

                                              sizeof()函数返回值为该数据类型或该变量所占字节数。

                                              如:sizeof(int) = 4; sizeof(double) =8; sizeof(char) = 1;

                                                 sizeof(int * ) = 4; sizeof(double * ) = 4;sizeof(char * ) = 4;

                                              结论:一个指针变量无论是什么类型指针变量,指针变量都占4个字节。

                                              原因:

                                              在内存中,8位二进制数为一个字节,一个字节为一个基本单元,有一个编号。

                                              如:double类型数据,占8个字节,所有有8个编号,但C语言中规定,一个变量地址                                              的编号以其第一个编号作为其编号,所以无论是什么类型数据,其编号都只有一个,                                                  一个十六进制地址编号占4个字节,所以指针变量也就占4个字节。

                                    拓展:

                                              32CPU32根地址总线,2^32=4G,所以32CPU最大支持4G内存。

                                              每个地址编号都是由32位二进制数组成,所以占4个字节,转换为十六进制表示。

                                              地址总线进行寻址是由32根地址总线确定的。

 

                                    //本函数功能是输出任意一维数组。

                                    voidstring(int * p, int l) //函数对数组进行处理,需要接收两个参数:函数第一个元        

                                    {                                素的地址(即数组名)和数组元素的个数。

                                              inti;

 

                                            for (i=0;i<l; ++i)

                                                       printf("a[%d]= %d\n",i, *(p+i));

                                    }

        【空指针NULL

                  NULL代表的是内存单元的地址编号00000...0

                  计算机中规定了以0为编号的内存存储单元的内容不可读,不可写。

                  操作系统中很重要的数据会放在以0为编号的内存存储单元中。

 

                  int* p; p = NULL; //就不可再对*p中的内容进行操作。

                  经常这样写:free(p); p = NULL; //p所指向的变量空间已被释放,不能再读写,为防止程序出错崩溃,写

                  p = NULL;防止再对*p进行操作。

                  注意:空指针NULL是四个大写字母,不能写成小写。

                  

        指针常见问题

 

1.若普通变量未被初始化,则会存放垃圾值;

                             但若指针未被初始化,则会出错,因为指针变量存放了未知的地址,指向了未知的变量,

                             指针变量可使用,即地址可使用,但 *指针变量的值不属性本程序,无权读取或访问。

 

                      2.普通变量不同类型之间可以相互赋值,虽然可能会有精度丢失;

                            但指针变量不同类型之间即使不会丢失精度也不能够相互赋值,如:long * p;即定义p只能存放                       long类型变量地址,不能存放double类型变量地址,也不能存放int类型变量地址,因为指针变量                                 只保存了变量第一个字节的地址,即只指向第一个字节,但int * p规定了p指向的变量占4个字节,                            以此来确定变量。

 

                      3. &:取地址符,即取变量的地址,不仅可以取普通变量的地址,也可以取指针变量的地址,即指针                                      变量也是变量,只要分配了变量空间,就可以取其地址。

 

                  星号*的三种含义:

                                    1.乘号

                                    2.定义指针变量

                                    3.指针运算符(为取地址符 &的逆运算)

 

                  查看变量的地址

                           把变量的地址以十六进制输出(地址编号都是以十六进制表示的)

                           %xX:输出为十六进制整型(字母大小写) %#x(X):输出为Ox(X)十六进制整型

                           printf("%#X",&i); //以十六进制整型输出变量i的地址。

                           或用%p32位地址十六进制控制输出。

 

 

        1.

          注意:指针变量pint * 类型,*pint类型,即p存放了整形变量的地址,*p代表整形变量。(*&为互逆运算)

        int* p; //定义整形指针变量,p是指针变量的名字,int * 表示p变量存放的是int类型变量的地址。

                   //p变量为int *类型,即存放int变量地址的类型。

        inti = 3;

        

        p= &i;

        /*

                  1. p存放了i的地址,因此p指向i,通过p可以找到i

                  2. p不是ii也不是ppi是两个不同的变量。更准备地说:修改p的值不影响i的值,修改i的值也不                           影响p的值。

                  3.如果一个指针变量指向了某个普通变量,则

                           *指针变量 就完全等同于 普通变量

                           例:如果p是个指针变量,并且p存放了普通变量i的地址

                                    p指向普通变量i

                                    *p 就完全等同于 i *p == i*是取地址&的逆运算,可认为是取出指针所指向的普通变                                         量。)            

                                    或者说:在所有出现*p的地方都可以替换成i(除定义声明外)

                                               在所有出现i的地方都可以替换成*p

                                    *p表示以p的内容为地址的变量

                                    所以,*p就是 i,修改 *p的值就是修改i的值。

        */

 

        printf("i= %d\n", i);

        printf("i= %d\n", *p);

        

        *p= 99; //即修改了i的值。

 

        printf("i= %d\n", i);

        printf("i= %d\n", *p);

 

        2.

                  inta[5] = {1,2,3,4,5};

                  inti;

 

                  for(i=0;i<5; ++i)

                  printf("a[%d]= %d\n", i, *(a+i)); //此处写*(a+i)a[i]都是完全等价的。

 

        3.

                  voidarr(int * p, int len)

                  {

                           inti;

                  

                           for(i=0; i<len; ++i)

                                    printf("a[%d]= %d\n", i, p[i]);//此处写*(p+i)p[i]都是完全等价的。                            

                                                                                         //*(p+i)p[i]a[i]*(a+i)都是同一个变量。

                           return;                                                //p[i] == *p(p+i) ==a[i] == *(a+i)

                  }

 

                  intmain(void)

                  {

                           inta[10] = {1,2,3,4,5};

                           arr(a,10);

                           return0;

                  }

 

1 0
原创粉丝点击