C语言——指针详解

Posted wx60f68848f3833

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言——指针详解相关的知识,希望对你有一定的参考价值。

指针

指针重点

  1. 指针是什么
  2. 指针和指针类型
  3. 野指针
  4. 指针运算
  5. 指针和数组
  6. 二级指针
  7. 指针数组

指针是什么?

int main(){    int a=10;    int* p=&a;//指针变量    return 0 ;}

总结:指针就是变量,存放内存单元的地址(编号)。也可以说指针就是地址,存放在指针中的值都被当成地址处理。

  • 一个小的单元到底是多大?(1个字节)

  • 如何编址?

    经过仔细的计算和权衡我们发现一个字节给一个对应的地址是比较合适的。对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的是产生一个电信号正电1/负电0。

    这里就有2的32次方个地址

    每个地址标识一个字节,那我们就可以给(2N32Byte == 232/1024K8m=2^32/1024/1024MB==2732/1024/1024/1024GB == 4GB)4G的空闲进行编址。同样的方法,那64位机器,如果给64根地址线,那能编址多大空间,自己计算。

总结:

  • 指针是用来存放地址的,地址是唯一标识一块地址空间的。
  • 指针的大小在32位平台上是4个字节,在64位平台上是8个字节。

指针和指针类型

1.指针的解引用操作

int main(){    int a=0x11223344;    int* pa=&a;    char* pc=&a;        printf("%p\\n",pa);//    printf("%p\\n",pc);//pa\\pc打印相同        *pa=0;//00 00 00 00     *pc=0;//00 11 22 33        return 0 ;    }

总结:针类型决定了指针进行解引用操作的时候,能够访问空间的大小。

int p;p能够访问4个字节

char p;p能够访问1个字节

*double p;能够访问8个字节**

2.指针+-操作

int main()
{
    int a=0x11223344;
    int* pa=&a;
    char* pc=&a;

    printf("%p\\n",pa);//0095FB58
    printf("%p\\n",pa+1);//0095FB5C - 0095FB58+4

    printf("%p\\n",pc);//0095FB58
    printf("%p\\n",pc+1);//0095FB59 - 0095FB58+1

    return 0 ;
}

总结:指针类型决定了:指针走一步走多远(指针的步长)

int* p; p+1 --> 4字节

char* p; p+1 --> 1字节

double* p; p+1 --> 8 字节

即指针类型决定了变量的大小,对其+1操作即指向下一个对象,对象大小即为指针类型决定

int main()
{
    //1
    int arr[10]={0};
    int* p=arr;//数组名-首元素的地址-*p指向数组的首元素
    int i=0;
    for(i=0;i<10;i++)
    {
        *(p+i)=1;//遍历-将数组元素都改成1
    }

    //2
    char* p=arr;
    int i=0;
    for(i=0;i<10;i++)
    {
        *(p+i)=1;//不能,只能改10个字节,而数组有40个字节
    }

    return 0 ;
}

野指针

野指针成因

1.指针未初始化

int main()
{
    int a;//局部变量不初始化,默认是随机值
    int *p;//局部的指针变量,就被初始化随机值
    *p=20;//

    return 0 ;
}

2.指针越界访问

int main()
{
    int arr[10]={0};
    int *p=arr;
    int i=0;
    for(i=0;i<=11;i++)
    {
        //当指针指向的范围超出数组arr的范围时,p就是野指针
        *(p++)=i;
    }

    return 0 ;
}

3.指针指向的空间释放

int* test()
{
    int a=10;
    return &a;
}

int main()
{
    int* p=test();
    *p=20;

    return 0 ;
}

如何避免野指针的产生

  1. 指针初始化

    int main()
    {
       int a=10;
       int *pa=&a;//初始化
       int *p=NULL;//不知道应该初始化为何值时定义为 NULL - ((void *)0)
    }
  2. 小心指针越界

  3. 指针指向空间释放即设置NULL

    int main()
    {
       int a=10;
       int *pa=&a;
       *pa=20;
    
       pa=NULL;
    
       if(pa!=NULL)
       {
    
       }
    }
  4. 指针使用之前检查有效性

    int main()
    {
       int *p=NULL;
       int a=10;
       p=&a;
       if(p!=NULL)
       {
           *P=20;
       }
       return 0 ;
    }

指针运算

  • 指针+-整数
  • 指针-指针
  • 指针的关系运算

指针+-整数

int main()
{
    int arr[10]={1,2,3,4,5,6,7,8,9,10};
    int i=0;
    int sz=sizeof(arr)/sizeof(arr[0]);
    int *p=arr;

    //+
    for(i=0;i<sz;i++)
    {
        printf("%d ",*p);//打印第一个元素
        p=p+1;//p++; //指向下一个元素的地址
    }

    //-
    for(i=0;i<5;i++)
    {
        printf("%d ",*p);
        p+=2;
    }
    //结果:13579

    //-=
    int arr[10]={1,2,3,4,5,6,7,8,9,10};
    int i=0;
    int sz=sizeof(arr)/sizeof(arr[0]);
    int *p=&arr[9];
    for(i=0;i<5;i++)
    {
        printf("%d ",*p);
        p-=2;
    }
    //结果:10 8 6 4 2

    return 0 ;
}
#define N_VALUES 5
float values[N_VALUES];
float *vp;
//指针+-整数:指针的关系运算
for(vp=&values[0];vp<&values[N_VALUES];)
{
    *vp++ = 0;
}

指针-指针

int main()
{
    char ch[5]={0};
    int arr[10]={1,2,3,4,5,6,7,8,9,10};
    printf("%d\\n",&arr[9]-&arr[0]);//9
    printf("%d\\n",&arr[9]-&ch[0]);//unkown

    return 0 ;
}
int my_strlen(char* str)
{
    char* start = str;
    char* end = str;
    while(*end != \'\\0\')
    {
        end++;
    }
    return end-start;
}

int main()
{
    //
    //strlen - 求字符串长度
    //递归 - 模拟实现strlen - 计数器的方式1,递归的方式2
    //
    char arr[]="bit";    
    int len = my_strlen(arr);
    printf("%d\\n",len);

    return 0 ;
}

指针的关系运算

#define N_VALUES 5
float values[N_VALUES];
float *vp;
for(vp=&values[N_VALUES];vp>&values[0];)
{
    *--vp = 0;
}

for(vp=&values[N_VALUES-1];vp>=&values[0];vp--)
{
    *vp = 0;
}

实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证它可行。

指针和数组

int main()
{
    int arr[10]={0};
    printf("%p\\n",arr);//地址=首元素的地址00EFF8E0
    printf("%p\\n",arr+1);//00EFF8E4 = 00EFF8E0+4

    printf("%p\\n",&arr[0]);//数组的第一个元素的地址00EFF8E0
    printf("%p\\n",&arr[0]+1);//00EFF8E4 = 00EFF8E0+4

    printf("%p\\n",&arr);//取出的是整个数组的地址00EFF8E0
    printf("%p\\n",&arr+1);//00EFF908 = 00EFF8E0+40

    //1.&arr - &数组名 - 数组名不是首元素的地址-数组名表示整个数组 - &数组名 取出的是整个数组的地址
    //2.sizeof(arr) - sizeof(数组名) - 数组名表示的整个数组 - sizeof(数组名)计算的是整个数组的大小

    return 0 ;
}

总结:数组名表示的是数组首元素的地址

int main()
{
    int arr[10]={0};
    int* p=arr;
    int i=0;

    for(i=0;i<10;i++)
    {
        *(p+i)=i;//将数组赋值
    }
    for(i=0;i<10;i++)
    {
        //printf("%d ",arr[i]);
        printf("%d ",*(p+i));//p+i其实计算的是数组arr下标为i的地址
    }

    /*
    for(i==0;i<10;i++)
    {
        printf("%p ======= %p\\n",p+i,&arr[i]);
    }*/

    return 0 ;
}

二级指针

int main()
{
    int a=10;
    int* pa=&a;
    int** ppa=&pa;//ppa就是二级指针
    //int*** pppa=&ppa;//三级指针

    return 0 ;
}
int main()
{
    int a=10;
    int* pa=&a;
    int** ppa=&pa;
    **ppa=20;
    printf("%d\\n",**ppa);//20
    printf("%d\\n",a);//20

    return 0 ;
}

指针数组

int main()
{
    int a=10;
    int b=20;
    int c=30;
    int* pa=&a;
    int* pb=&b;
    int* pc=&c;
    //整型数组 - 存放整型
    //字符数组 - 存放字符
    //指针数组 - 存放指针

    return 0 ;
}
int main()
{
    int a=10;
    int b=20;
    int c=30;
    int arr[10]={0};
    int* arr2[3]={&a,&b,&c};
    int i=0;
    for(i=0;i<3;i++)
    {
        printf("%d ",*(arr2[i]));//打印数组里每一个元素
    }
}

一些面试真题

练习题1

#include <stdio.h>
int i;//全局变量 - 不初始化时,默认值为0
int main()
{
    i--;
    //-1
    //1000 0000 0000 0000 0000 0000 0000 0001
    //1111 1111 1111 1111 1111 1111 1111 1110
    //1111 1111 1111 1111 1111 1111 1111 1111
    if(i<sizeof(i))//sizeof()-计算变量/类型所占内存的大小 >=0 结果一定为无符号数
    {
        printf(">\\n");
    }
    else
    {
        printf("<\\n");
    }
    return 0 ;
}

//打印结果: > 

当整数和无符号数进行运算时,整型会把i转换为无符号数,整型中负数的无符号形式>无符号数

练习题2

#include <stdio.h>
int main()
{
    int arr[]={1,2,3,4,5};
    short *p=(short*)arr;
    int i=0;
    for(i=0;i<4;i++)
    {
        *(p+i)=0;//这里p+i加的是字节数,short访问字节空间为2,实际访问两个字节,而int型数组一个元素为4字节,所以实际只改变了int数组内的两个元素的值
    }

    for(i=0;i<5;i++)
    {
        printf("%d ",arr[i]);
    }

    return 0 ;
}

//打印结果:0 0 3 4 5

练习题3

#include <stdio.h>
int main()
{
    int a=0x11223344;//在内存块中的存储顺序:44 33 22 11各自占一个字节,共4个字节
    char *pc=(char*)&a;
    *pc=0;//只操作一个字节-将44改成00
    printf("%x\\n",a);

    return 0 ;
}

//打印结果:11223300

练习题4

#include <stdio.h>
int main()
{
    int a,b,c;
    a=5;
    c= ++a;//c=6 , a=6
    b= ++c,c++,++a,a++;//逗号运算符从左往右计算,取最后一个值,c=8,b=7,a=8
    //后缀++的优先级高于+ , +的优先级高于+=
    b+= a++ + c;//b=23,a=9
    printf("a = %d b = %d c = %d\\n",a,b,c);

    return 0 ;
}

//打印结果:a = 9 b = 23 c = 8

练习题5

#include <stdio.h>

/*
//算法1
int count_bit_one(unsigned int n)
{
    while(n)
    {
        if(n%2==1)
        {
            count++;
        }
        n=n/2;
    }
    return count;
}
*/

/*
//算法2
int count_bit_one(int n)
{
    int count=0;
    int i=0;
    for(i=0;i<32;i++)
    {
        if( ((n>>i)&1) == 1)
        {
            count++;
        }
    }

    return count ;
}
*/

//算法3
//n=n&(n-1)
//n
//13
//1101  n
//1100  n-1
//1100  n
//1011  n-1
//1000  n
//0111  n-1
int count_bit_one(int n)
{
    int count=0;
    while(n)
    {
        n=n&(n-1);
        count++;
    }

    return count ;
}

int main()
{
    int a=0;
    scanf("%d",&a);
    //写一个函数求a的二进制表示中有几个1
    //13
    //0000 0000 0000 0000 0000 0000 0000 1101
    int count=count_bit_one(a);
    printf("count=%d",count);

    return 0 ;
}

练习题6

#include <stdio.h>

int get_diff_bit(int m,int n)
{
    int tmp=m^n;
    int i=0;
    int count=0;

    /*
    //算法1
    for(i=0;i<32;i++)
    {
        if( ((tmp>>i)&1) == 1)
        {
            count++;
        }
    }
    */

    //算法2
    while(tmp)
    {
        tmp=tmp&(tmp-1);
        count++;
    }

    return count ;
}

int main()
{
    int m=0;
    int n=0;
    scanf("%d %d",&m,&n);
    int count=get_diff_bit(m,n);
    printf("count=%d",count);

    return 0 ;
}

练习题7

int print(int m)
{
    int i=0;
    printf("奇数位:\\n");
    for(i=30;i>=0;i-=2)
    {
        printf("%d",(m>>i)&1);
    }
    printf("\\n");

    printf("偶数位:\\n");
    for(i=31;i>=1;i-=2)
    {
        printf("%d",(m>>i)&1);
    }
    printf("\\n");

}

int main()
{
    int m=0;
    scanf("%d",&m);
    print(m);

    return 0 ;
}

练习题8

void print(int *p,int sz)
{
    int i=0;
    for(i=0;i<sz;i++)
    {
        printf("%d ",*(p+i));
    }
}

int main()
{
    int arr[]={1,2,3,4,5,6,7,8,9};
    int sz=sizeof(arr)/sizeof(arr[0]);
    print(arr,sz);

    return 0 ;
}

以上是关于C语言——指针详解的主要内容,如果未能解决你的问题,请参考以下文章

C语言函数指针详解

[C语言]详解指针合集

C语言---指针变量详解2

函数指针及其定义和用法,C语言函数指针详解

C语言指针的问题

C++编程知识:什么是万能指针?详解C语言万能指针的妙用