zoukankan      html  css  js  c++  java
  • c语言基础3

    指针

    指针就是内存地址。通常我们说的指针是指:指针变量。

    因此:指针变量存放指针,指针就是一个内存空间的地址!

    int a

    int *p

    比较:a标示了内存的一个空间,該空间能够存放一个整数。p是一个指针类型变量(通常称为指针),它也标示了内存的一个空间,該空间存放的是一个内存地址(内存地址在32位下为4字节)。

    理解指针:我们需要弄清指针的三方面内容

    1、指针的类型

    2、指针所指空间存储数据的类型

    3、指针的值及存储指针的空间大小

    以下通过例子说明这三方面:

    1、int  *p;

    2、char *p;

    3、int **p;

    4、int (*p)[3]

    1、指针的类型:从语法的角度讲,只要将定义中指针的名字去掉,剩下的部分就是这个指针的类型。

    1、int  *p;     int*

    2、char *p;   char*

    3、int **p;   int**

    4、int (*p)[3]  int(*)[3]

    理解:一个基本的数据类型加上*号,就构成了指针的类型,这个类型定义的变量大小是一定的,与*号前面的数据类型无关。*号前面的数据类型只是说明了指针所指向的内存里存储数据的类型! 如:int* 就是个指针的类型,用該类型定义的变量p大小(32位的为4个字节),int说明該指针所指向的内存里存储的是int类型的数据!

    2、指针所指空间存储数据的类型:语法上讲:只需把指针定义中的指针变量名和名字左边的指针声明符*去掉,剩下的就是指针所指空间存储数据的类型。

    1、int  *p;     int

    2、char *p;   char

    3、int **p;   int*

    4、int (*p)[3]  int [3]

    理解:指针所指空间的数据类型是我们所关心的,对于编译器来说,根据該类型来确定读写从指针地址开始的多少个字节空间的数据。

    3、指针的值以及存储指针值的空间大小

    指针的值是指针变量本身存储的数值,由于指针就是一个内存地址,所以指针的值也就是一个内存地址。在32位系统中,内存地址均为32位长,所以存储指针的空间为4个字节。可以用sizeof()来测试。

    对比一般变量和指针变量的定义和含义:

    int p;  p是整型变量,用于标识内存的一个空间内能存放一个整数

    int *p;  p是指针类型变量,用于标识内存的一个内存空间内能存放一个地址,該地址处能存放一个整数

    int p[5];  p是一个数组(一段连续的存储空间),数组中有5个元素,每个元素为一个整数,p为这段连续存储空间的首地址

    int *p[5];  p是一个数组(一段连续的存储空间),数组中有5个元素,每个元素为一个地址(类型为int *),每个地址处能存放一个整数,p为这段连续存储空间的首      地址       (被成为指针数组,主体是数组,[ ]优先级高于*)

    int (*p)[5];  p是一个指针类型的变量,p指向具有5个元素的数组(类型为int[5]),数组中的元素为整数。(被称为数组指针,主体是指针!)

    int **p;  p是指针类型的变量,用于标识内存的一个空间能存放一个地址(类型为int**),該地址处存放的存放的还是一个地址(类型为int *),这个地址处能存放一      个整数(类型为int)

    int p(int);    p是一个函数,函数有个整型参数,并且函数返回值为整型

    int (*p)(int);  p是一个指针,一个指向函数的指针,函数有一个整型参数,并且函数返回值为整型。

      1 #include<stdio.h>
      2 
      3 main()
      4 {
      5         int a=12,*p,**ptr;
      6         ptr=&p;
      7         p=&a;
      8         **ptr=34;
      9         printf("%d,%d,%d\n",a,*p,**ptr);
     10 }

    输出:34,34,34

    指针和数组:                      (指针和数组是不同的,指针是内存单元的一个地址32位为4字节,数组其大小和元素类型和个数有关。

    对指针和数组元素的访问,可以采用下标法,也可以采用指针法。貌似指针和数组有什么关系,事实上,它两没有关系。指针就是指针,数组就是数组。

    在内存中的数据,一种方法是用数组名加下标的方式读写,另一种方法是通过指针间接的读写。

     1.对数组元素的访问:  int a[10]={0,1,2,3,4,5,6,7,8,9};

    a为数组元素的首地址a[0],现在想读取整数5; (1)指针法: *(a+5) (2)数组法:a[5], 编译器把以下标形式的操作解析为以指针的形式的操作,a作为数组元素的首地址,再加上括号中5个元素的偏移量,a+5*sizeof(int)

    2、对指针所指空间的访问:  char *p=“abcde”;

    p为一个指针变量,大小为4个字节(32位系统),p里面存储了一块内存的首地址,这块内存在静态区(还不明白),其空间大小为6个字节,这快内存空间没有名字,通过匿名访问。    读取字符d  (1)指针法:*(p+3) (2)下标法 p[3]  ,译法和上面相同

    注意:指针加减偏移量时,不是代表指针移动多少个字节,而是代表移动多少个指针所指的元素。

     指针和数组的声明和定义:

    外部变量的引用问题:如果在文件1中定义了一个数组,在文件2中要引用这个数组,那么在这两个文件中怎样声明和定义呢?

    在同一个文件中,特别是数组名作为函数参数时,实参和形参可以用数组,也可以用指针,两种任意组合没有问题(还没有试过)。但是在不同文件间引用数组时,作为外部变量的声明形式必须和另一个文件中的定义形式相同,否则会出错,切记!!!

    例子:   file1.c 中:

      1 #include<stdio.h>
      2 
      3 extern void func(void);
      4 char *a={"ABCDE"};
      5 
      6 void main()
      7 {
      8         func();
      9         printf("%c,%c\n",a[1],a[2]);
     10 }

    file2.c中:

      1 #include<stdio.h>
      2 
      3 extern char *a;
      4 void func()
      5 {
      6         printf("%c\n",a[1]);
      7 }

    gcc -c file1.c file2.c

    gcc -o file file1.o file2.o

    ./file

    输出: B
           B ,C

    若将 file2.c中 extern char *a;改为 extern  char a[ ]; 则上面的输出第一行的变成乱数! why??

    因为:在file2.c中将a声明为外部数组(注意这里声明是不分配空间的,所以无须说明数组有多少个元素,具体是多大可以在其它文件中定义)

    这时编译器认为a是一个数组,其大小为4个字节(因为file1.c中定义了a为指针变量),因此a[1]就输出a这个指针变量中指针的第二个字节内容!是不确定的。。。

    若将file1.c中 char *a={"ABCDE"}; 該为 char a[ ]={"ABCDE"} ; file2.c 中不变,则运行出现段错误。。。 why??

    file1.c中定义数组a,并为其分配6个字符的空间,但在file2.c中,编译器并不知道a是个数组,认为a是个指针变量,而这时候a中存的值为数组{“ABCDE“},由于指针大小为4个字节,所以在file2.c 中a的值变为{”ABCD“},该值被作为指针(画图好理解),这时候输出a[1](相当于*(p+1)),这个地址中的值不确定。。可能这个地址都越界,出现段错误!

    指针的算术运算和关系运算:

    指针的算术运算:因为指针是一个内存地址,所以,其加减一个整数的意义是:指针从当前地址移动多少个元素,元素是指针所指空间存储的数据类型。

      1 #include<stdio.h>
      2 
      3 main()
      4 {
      5         char s[]={"ABCDEFGH"};
      6         int *p; 
      7         p=(int *)s;
      8         p++;//p+sizeof(int)*1,p+4,指向E
      9         printf("%c\n",*p);      
     10         p=p+5;//p+sizeof(int)*5,p+20,越界了,这里p指向的是int类型的数据
     11         printf("%c\n",*p);
     12 }

    输出E和乱码

      1 #include<stdio.h>
      2 
      3 main()
      4 {
      5         char s[]={"ABCDEFG"};
      6         char *p,**ptr;
      7         p=s;
      8         ptr=&p;
      9         printf("%c\n",**ptr);
     10         ptr++;
     11         printf("%c\n",**ptr);
     12 }

    输出A和段错误!

    在第10行,ptr++,ptr指向的类型是char *,是个指针,大小为4字节,所以ptr=ptr+sizeof(char *)*1=ptr+4,这时候ptr的值越界了。。(画图好理解)

    另外:关系运算,两个指针可以进行减法运算,一般是高地址减去低地址,两个指针不能进行加法运算是非法的!关系运算符也都适用。

    数组的首地址和数组元素的首地址:

    注:它两值是相同的,但是类型不同,数组首地址&a;;;数组元素首地址:a或a[0],等价。。。

    例子:

      1 #include<stdio.h>
      2 
      3 /*該程序说明 数组首地址 和 数组首元素的地址 的区别
      4  *数组首元素(a==a[0])和&a值是一样的,但是类型不同
      5  *a/a[0]类型是:int * 
      6  *&a的类型是:int(*)[]
      7  *也就是说下面程序中p1和p2的值是一样的
      8  */
      9 
     10 
     11 main()
     12 {
     13         int a[10]={1,2,3,4,5,6,7,8,9,10};
     14         int *p1;
     15         int (*p2)[10]; 
     16         p1=a;
     17         p2=&a;
     18         printf("%d\n",*p1);
     19         printf("%d\n",*(int *)p2); 
     20         printf("%d\n",*(p1+1)); 
     21         printf("%d\n",*(int *)(p2+1));
     22 }

     要是16行改为:p1=&a; 则编译出现:

    ptr1.c:16: warning: assignment from incompatible pointer type

     原程序输出:

    1
    1
    2
    -1078863728

    21行输出乱码,因为p2+1, p2 的类型为 int  [10](所指向数据的类型), 那sizeof(p2)=4*10 字节了,  p2+1 =p2+ 4*10*1

  • 相关阅读:
    SpringMVC请求参数接收总结(一)
    不用 Spring Security 可否?试试这个小而美的安全框架
    @ConfigurationProperties 注解使用姿势,这一篇就够了
    Spring Aware 到底是什么?
    git rebase VS git merge? 更优雅的 git 合并方式值得拥有
    Spring Bean 生命周期之destroy——终极信仰
    面试还不知道BeanFactory和ApplicationContext的区别?
    Java设计模式学习记录-享元模式
    Java设计模式学习记录-外观模式
    Java设计模式学习记录-装饰模式
  • 原文地址:https://www.cnblogs.com/byking/p/2960419.html
Copyright © 2011-2022 走看看