当前位置:   article > 正文

C语言:指针,指针与数组_09-01-03 结构体指针分数 5入门作者 于延单位 哈尔滨师范大学分析以下代码,理解结

09-01-03 结构体指针分数 5入门作者 于延单位 哈尔滨师范大学分析以下代码,理解结

指针的定义

首先要知道不管你的指针是什么类型,是几级指针,在同一个操作平台编译环境中,所占的内存空间都是一致的。如pc使用的是32位的,那就是 32/8=4,那就是4个字节内存空间。
认真点讲:在C语言中,char,int,long,double这些基本数据类型的长度是由编译器本身决定的。而char*,int*,long*,double*这些都是指针,回想一下,指针就是地址呀,所以里面放的都是地址,而地址的长度当前是由地址总线的位数决定的,现在的计算机一般都是32位的地址总线,也就占4个字节。

例:

#include <stdio.h>
#include <stdlib.h>

int main()
{
     int i = 1;
   	 int *p = &i;//分开写是 int *p;p=&i;

    printf("i = %d\n",i);//简单取值
    printf("&i = %p\n",&i);//取出 i对应的地址
    printf("p = %p\n",p); //p存储的是一个地址,这个地址是&i。
    printf("&p = %p\n",&p);//p自己也有一个地址
    printf("*p = %d\n",*p);//相当于在p的存储的地址中取值,而p的存储的地址是&i对应的地
                    //址,在这个地址里取值,就是i的值。
 return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在这里插入图片描述
解释:通俗点说,i变量是在内存有地址的,这个地址比如说0x2000,实际上对应我的电脑是0x7fff1035aedc,在图上可以看见;而这个i的变量对应的值是1。而指针p是存储i这个变量对应的地址,因为指针是负责存地址的,由上式 int *p = &i可知道,但是p这个指针自己也在内存也存有一块地址,比如说是0x3000,那么这个是指针自身的地址,&p=0x3000,*p = *(&i) = i。上述是一级指针。二级指针继续往下看,

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i = 1;
    int *p = &i;//分开写是 int *p;p=&i;
    int **q =&p;
    
    printf("i = %d\n",i);//简单取值
    printf("&i = %p\n",&i);//取出 i对应的地址
    printf("p = %p\n",p); //p存储的是一个地址,这个地址是&i。
    printf("&p = %p\n",&p);//p自己也有一个地址
    printf("*p = %d\n",*p);//相当于在p的存储的地址中取值,而p的存储的地址是&i对应的地
                        //址,在这个地址里取值,就是i的值。
    printf("q = %p\n",q);
    printf("&q = %p\n",&q);
    printf("*q = %p\n",*q);
    printf("**q = %d\n",**q);
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

在这里插入图片描述
在这里插入图片描述
说到指针,我们也不能忘记了还有两个指针,一个是空指针,一个是野指针。
空指针:一开始不知道这个指针要具体存放啥时,就先将其设为空指针,常见为NULL。
野指针:当前这个指针所指向的空间是不确定的,但还需要使用。

例:

#include "stdio.h"
#include "stdlib.h"

int main()
{
    int *p = NULL;//空指针
    int *q;//野指针

    return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

接下里是指针与一维数组的关系

例:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int a[3] = {1,2,3};
    int i;
    int *p = a;
    
    for(i = 0;i < sizeof(a)/sizeof(a[0]);i++)//sizeof a 表示是整个数组占据的大小,sizeof a0 表示是第一占据大小 相除可以得到具体多少块
     {
     	printf("%p-->%d\n",&a[i],a[i]);
    	printf("%p-->%d\n",a+i,p[i]);
    	printf("%p-->%d\n",p+i,*(a+i));
    	printf("%p-->%d\n",&p[i],*(p+i));
    }
    return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

在这里插入图片描述
潇洒的结论:
a[i]可表示 a[i] = *(a+i) = *(p+i) = p[i]
&a[i]可表示 &a[i] = a+i = p+i = &p[i]

例:来一个观察数组赋值的例子

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int a[3];
    int i;
    int *p = a;
    for(i = 0;i < sizeof(a)/sizeof(*a);i++)
    	printf("%p-->%d\n",&a[i],a[i]);
    
    for(i = 0;i < sizeof(a)/sizeof(*a);i++)
    	scanf("%d",p++);
    	//scanf("%d",&a[i]);
    	//scanf("%d",&p[i]);
    
    p = a;//将自增完的地址重新赋值
    
    for(i = 0;i < sizeof(a)/sizeof(*a);i++,p++)
    	printf("%p-->%d\n",p,*p);
   return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

在这里插入图片描述

例:来一个指针运算

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int a[6] = {5,1,7,3,8,3};
    int y;
    int *p = &a[1];
    
    y = (*--p)++;//在p地址的基础上地址自减一个元素,而p指向是a[1]的数值,--的话就是a[0]对应的值,这是*--p的
    			 //在执行完取值的操作后,进行++操作,取值完成之后的,是a[0]的值自增加1  
    printf("y = %d\n",y);
    printf("a[0] = %d\n",a[0]);
    return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在这里插入图片描述
结论一下:
如:total += *start; start++;
则可以写成一句:total += *start++;
解释:前者,start指向的是一个数组的首地址,第一步是把首元素取值start加上total的值得到一个数据赋值给total,然后start++表示自增加1,指向下一个数组的元素,start指向的是数组的类型指针,比如说int,那么start增加1时,他将增加一个int的大小。
后者,一元运算符※和++具有相同的优先级,但是在结合的时候是从右往左进行的。这就意味着++是应用于start,而不是应用于※start,也就是说指针自增加1,而不是说指针指向的数据增加1。后缀形式start++而不是++start 表示先把指针指向的数据加到total上,然后指针自增1。如果程序使用※++start,则顺序就变为指针先自增1,然后再使用其指向的值。然而如果使用(※start)++,那么会使用start所指向的数据,然后再使该数据自增1,而不是指针自增加1.这样指针指向的地址不变,但其中的元素却变成了一个新的数据。尽管※start++比较好用,但是为了清晰可见,应该使用※(start++)。

例:

#include<stdio.h>

int data[2] = { 100, 200 };
int moredata[2] = { 300, 400 };
int main(void)
{
    int * p1, *p2, *p3;

    p1 = p2 = data;
    p3 = moredata;
    printf("  *p1 = %d,   *p2 = %d,    *p3 = %d\n",*p1, *p2, *p3);
    printf("*p1++ = %d, *++p2 = %d, (*p3)++ = %d\n",*p1++, *++p2, (*p3)++);
    printf("  *p1 = %d,    *p = %d,      *p3 = %d\n",*p1, *p2, *p3);

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

结果:
在这里插入图片描述

接下里是指针与二维数组的关系

例:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int a[2][3] = {1,2,3,4,5,6}; 
    int i,j;
    int *p;
	    
    p = &a[0][0];       //*(a+0),*a
    
    printf("a	-->	%p\n",a);   
    printf("a+1	-->	%p\n",a+1);//a+1:地址增加一个行元素
	printf("p	-->	%p\n",p);   
    printf("p+1	-->	%p\n",p+1);//p+1:地址增加一个一个地址的元素,指针都是占据4个字节,下一个元素就是+4个字节.
    //就是说指针是在不能在列上面移动,不能在行上面移动。
    printf("\n");


    for(i = 0;i < 6;i++)
        printf("%d ",p[i]);
    printf("\n");
    
    for(i = 0;i < 2;i++)
    {
        for(j = 0;j < 3;j++)
        {
            printf("%p	-->	%d\n",*(a+i)+j,*(*(a+i)+j)); //从起始位置a移动i行是a+i,加括号取*,降级变成列指针是 *(a+1),再加上j,相当于在列上移动,如果在括号取*就相当于取值
            //printf("%p	-->	%d\n",&a[i][j],a[i][j]);            
        }
    }
   return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

在这里插入图片描述

接下里是指针与字符数组的关系

例:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    char str[] = "I love china!";
    char *p = str+2;  //*p指针指向字符串的首地址,+2表示向右移动来两位。
    
    puts(str);
    puts(p);
    
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在这里插入图片描述

例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>   //使用到strcpy  man3 查看

int main()
{
    char str1[] = "hello";
    char *str2  = "hello";
    
    printf("str1:%d %d\n",sizeof(str1),strlen(str1));
    printf("str2:%d %d\n",sizeof(str2),strlen(str2));
    
    strcpy(str1,"world");    //将后面的数据复制到指定的位置(前面)
    str2 = "world";
    
    puts(str1);
    printf("\n");
    puts(str2);
    printf("\n");
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

在这里插入图片描述
多说一句:sizeof与strlen是有着本质的区别,sizeof是求数据类型所占的空间大小,而strlen是求字符串的长度,字符串以/0结尾。 sizeof是一个C语言中的一个单目运算符,而strlen是一个函数,用来计算字符串的长度。sizeof求的是数据类型所占空间的大小,而strlen是求字符串的长度。
sizeof求的是类型空间大小,在前面说过,指针型所点的空间大小是8个字节,系统地址总线长度为64位时。

再来看看数组指针

就是说一个指针指向数值,换句话来说指向数组的指针。
int (*p)[3]; --> int[3] *p;
之前学的是 int *p是说指针p指向一个int类型的指针,现在也是指针p指向一个int类型的数组。

1.本身就是一个指针,指针指向数组
2. int *p;这是整型指针。定义一个指针变量,目的是为了指向一个整型元素
  • 1
  • 2

例:

#include <stdio.h>
#include <stdlib.h>
int main()
{   
    int a[2][3] = {1,2,3,4,5,6}; 
    int i,j;
    int *p = *a;
   
    int (*q)[3] = a; //通常情况下,数组的大小,就是那个3,应该与二维数组的列数保持一致,这样方便移动不会报错
       
    printf("%p  %p\n",a,a+1);   //a是一个常量 p则是一个变量
    printf("%p  %p\n",q,q+1);  //和a一样对待
    
    for(i = 0;i < 2;i++)
    {
        for(j = 0;j < 3;j++)
        {
            //printf("%p	-->	%d\n",*(a+i)+j,*(*(a+i)+j)); 
            printf("%p	-->	%d\n",*(q+i)+j,*(*(q+i)+j)); 
            //printf("%p	-->	%d\n",&a[i][j],a[i][j]);            
        }
		printf("\n");
 	   }
   		 return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

在这里插入图片描述

再来看看指针数组

简单的说就是存放指针的数组
如:int *arr[3]; --> int *[3] arr;
[存储类型] 数据类型 * 数组名 [长度]

1.首先这是一个数组 数组就要有数组名 那么定义一个 arr[3]; 数组名为arr内部有3个成员
2.其次这数组里面每一个元素都是一个指针 因此是 int *arr[3];
  • 1
  • 2

例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    int i,j,k;
    char *tmp;
    char *name[5] = {"Fllow me","Basic","Great","Fortran","Computer"};
    
    for(i = 0;i < 4;i++)
    {
        k = i;
        for(j = i+1;j < 5;j++)
        {
            if(strcmp(name[k],name[j]) > 0)
                k = j;
        }
        if(k != i)
        {
            tmp = name[i];
            name[i] = name[k];
            name[k] = tmp;
        }
    }
    
    for(i = 0;i < 5;i++)
    	puts(name[i]);
    
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

结果:
在这里插入图片描述

再看一个const与指针的关系

const作用:把某些内容常量化,约束某些内容不能变化,优点就是检查语法 。
#define PI 3.14
而宏定义是不进行编译检查的,在编译中只是简单替换,不检查语法。

例:

#include <stdio.h>
#include <stdlib.h>

int main()
{
	//float pi = 3.14; //用于一个变量pi来保存3.14这个常量值,而变量实在程序运行中时刻有可能改变的值,
	const float pi = 3.14;// 这是定义。而当去前的变量的pi,const把一个变量常量化,通常用于希望保存一个不需要改变的量。
    //pi = 3.14159; //这样的话会报错,不能向只读的变量赋值
    float *p = &pi;  //间接去改变常量的数值,而此时定义的初始化丢弃了指针目标类型的限定,说直接点就是可以改变了。
    *p = 3.14159;
      printf("%f",pi);
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

const int *p; int const *p ;这两个都是常量指针
int *const p;这个就属于指针常量
const int const p;这个既是常量指针又是指针常量
常量指针定义:const后加
p(p地址存放的具体值),用来保护这个值不被改变
指针常量定义:*const后加p(p为存放值的地址),用来保护这个地址不被改变
说一个简单的区别记法:先看到哪个(const 和 ),哪个在前就按照那个读取,如果是const就是常量指针,如果是 就是指针常量。

例:常量和常量指针

#include <stdio.h>
#include <stdlib.h>

int main()
{
	int i = 1;
	int j = 100;
	const int *p = &i;
    
    i = 10; //这样可以更改,因为定义的i没有定义const,
  // *p = 10; // *p 是一个常量指针,不能改对应的数值
	p = &j; //这样是可以的,因为可以改变指向的地址,那这样的*p就是100.

    printf("%d\n",*p);
        
   return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

例:指针常量

#include <stdio.h>
#include <stdlib.h>

int main()
{
	int i = 1;
	int j = 100;
	int *const p = &i;   //而此时*p的值就是1
    
   *p = 10; // *p 是一个指针常量,这个是可以改变数值的,打印的就是10
	p = &j; //这样是不可以的,不能改变的指向的地址,但是可以改变目的值
    printf("%d\n",*p);      
   return 0;
}   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

例:指针常量和常量指针(地址和数值都不能改变)

#include <stdio.h>
#include <stdlib.h>

int main()
{
  int i = 1;
  int j = 100;
  const int *const p = &i;   //而此时*p的值就是1
  
 // *p = 10; // 不能修改数值
   //p = &j; //不能修改 指向的地址
  printf("%d\n",*p);      
 return 0;
}   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

补充说明一下:

如 man fopen/man 2 open 查看定义
int open (const char *pathmame,int flags);
这表示只能查看传输过来的文件,前面有const,而 *pathname是保护文件的内容,不会改变放心传参。

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号