欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

指针基础||一篇文章带你看懂指针

程序员文章站 2022-06-05 18:53:49
...

1. 指针是什么

  • 指针是个类型!,数据类型
  • 类型是干嘛用的,标注数据长度,做语法提示的。
#include <stdio.h>
int main() {
    char a = 1;
    char *b = &a;
    *b = 9999;
    printf("%d\n", *b);
    /*char是-127--127,指针也是数据类型,也会标注长度,大类型转小类型会截取*/
}

2.指针的值

指针的值有三种,句柄,地址,空指针

空指针

char * b = NULL;

空指针就是占位

地址

#include <stdio.h>
int main() {
    char a = 1;
    char *b = &a;
}

指针的值是一个地址,地址就是房间号,大小是八个字节

#include <stdio.h>
int main() {
    char a = 1;
    char *b = &a;
    printf("%d", sizeof(&a));//值是8
}

这个大小,和操作系统有关,32位系统是4字节,64位系统是8字节。

32位操作系统最大内存是4g,大概,32位系统,指针大小是4字节,也就是个int大小,四字节是32位,指针是存地址用的,也就是说这个地址长度是32位,也就是能存的东西是32^2,也就是4294967296 这个数/8,就是536870912 /1024大概事是54288,这是kb,算下来内存4gb左右。

  • 为什么32位的系统指针是四字节, 64位系统的指针是八字节?

    因为32位字长的系统的虚拟地址空间是2^32 - 1为了表示所有的地址,所以指针的大小必须大于等于2^32 - 1, 所以就是4字节(32位)

    64位系统同理

指针就是个数字,八字节,说他是地址,只不过里面存的是数字,大一点而已。

  • long long 也是八字节,指针和long long 有啥区别

    那就是不同类型,对运算符计算结果不一样,

#include <stdio.h>
int main() {
    int a = 1;
    int *p = &a;
    long long lp = (long long)p;
    printf("p = %lld\n", (long long)p);
    printf("lp = %lld\n", lp);
}//一个指针一个long long
#include <stdio.h>
int main() {
    int a = 1;
    int *p = &a;
    long long lp = (long long)p;
    printf("p = %lld\n", (long long)p);
    printf("lp = %lld\n", lp);
    p++;
    lp++;
    printf("new_p = %lld\n", (long long)p);
    printf("new_lp = %lld\n", lp);
    return 0;
}

int型指针,p+1其实是加4

long long类型指针p+1就是+8

加号运算符就是n+m *sizeof(type)

n是原始数字,m是加数,type是指针的类型

比如指针类型是int ,p+2就是p+8

概括:指针也是普通的数字,指针运算符结果和整形不同

*运算符

  • *星号是指针声明的符号

    用int*类型保存了a的地址

    星号出现在等号左边和类型声明结合的话,就是声明的,直接*,就是解引用

    #include <stdio.h>
    int main() {
        int a = 1;
        int *p = &a;
        *p = 2;
        printf("%d\n", *p);
    }//这里操作的是a
    

多级指针

就是一个指针变量存了另一个指针变量的地址,这样一直套娃

指针常量和常量指针

#include <stdio.h>
int main() {
    int a = 1;
    int b = 2;
    const int *p = &a;
    p = &b;
    printf("%d\n", *p);
    return 0;
}//常量指针 一个指针指向了常量

指针不是常量,指针指向的地址是常量,不可以修改

#include <stdio.h>
int main() {
    int a = 1;
    int b = 2;
    const int *p = &a;
    p = &b;
    //*p = 3; //不行
    b = 3 // 正确
    printf("%d\n", *p);
    return 0;
}

可以理解为p是指针,通过p没法给a重新赋值,

const int *p和int const *p都是常量指针

#include <stdio.h>
int main() {
    int a = 1;
    int *const p = &a;
    *p = 3;
    printf("%d\n", *p);
    return 0;
}

//
#include <stdio.h>
int main() {
    int a = 1;
    int b = 2;
    int *const p = &a;
    //p = &b; 不行
    *p = 3; //可以通过p修改他存的值
    printf("%d\n", *p);
    return 0;
} //指针常量 

这个是指针常量,就是const 和p结合,

这里p是常量, p等于&b就不允许了,

const int * const p,就是怎么都不能修改的意思。 指向常量的常量指针

#include <stdio.h>
int main() {
    int a = 1;
    int b = 2;
    const int *const p = &a;
    //p = &b; 不行
    //*p = 3; //不可以通过p修改他存的值
    printf("%d\n", *p);
    return 0;
} //指针常量 

指针数组和数组指针

指针数组

指针基础||一篇文章带你看懂指针

指针数组:就是一个数组,数组的各个元素都是指针值。

#include <stdio.h>
int main() {
    int a = 0;
    int b = 1;
    int c = 2;
    int d = 3;

    int *p[4];

    p[0] = &a;
    p[1] = &b;
    p[2] = &c;
    p[3] = &d;

    printf("%d\n", *p[0]);
    printf("%d\n", *p[1]);
    printf("%d\n", *p[2]);
    printf("%d\n", *p[3]);
} 

在这里我们要注意一下运算符的优先级,由于 * 的优先级比 [] 的优先级低,所以整个表达式中p要先与[4]做结合表示的是一个数组的元素,再与 * 结合表示每一个数组元素的类型是一个指针变量。

多维指针数组

#include <stdio.h>
int main() {
    int array[2][3] = {
        { 0, 1, 2 },
        { 3, 4, 5 }
    };
    int *p[2][3];
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 3; j++) {
        p[i][j] = &array[i][j];
        }
    }
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 3; j++) {
        printf("%d ", *p[i][j]);
        }
    printf("\n");
    }
} 

数组指针

指针基础||一篇文章带你看懂指针

因为*优先级比[]低,所以要这么声明

char (*pa)[4]; //表示一个叫pa的指针存了 char[4]这个数组的首地址

pa是一个指针,里面存的是数组的首地址

数组指针和数组区别

  1. 运算符结果不一样
  2. 大小不一样
#include <stdio.h>
int main() {
    char a[4] = {1, 2, 3, 4};
    char (*pa)[4] = &a;
    printf("a is %p\n", a);
    printf("a[0] is %p\n", &a[0]);
    printf("pa is %p\n", pa);
    pa++;
    printf("new_pa is %p\n", pa);
    printf("数组的步长是%d\n", a[1] - a[0]);
    printf("sizeof pa is %ld, sizeof a is %ld\n", sizeof(pa), sizeof(a));
} 

参考: https://book.itheima.net/course/223/1263669610003230722/1263675595644137474

#include <stdio.h>
int main() {
    int array[2][3] = {
    	{ 0, 1, 2 },
    	{ 3, 4, 5 } 
    };

    //数组指针
    int (*q)[3] = array;
    for (int i = 0; i < 2; i++) {
    	for (int j = 0; j < 3; j++) {
    		printf("%d ", q[i][j]);
    	}
    	printf("\n");
    }
    
    printf("array is %p\n", array);
    printf("a[0][0] is %p\n", &array[0][0]);
    printf("q is %p\n", q);
    
    q++;
    printf("行步长 是%d, 列步长是 %d\n", &array[0][1] - &(array[0][0]), &array[1][0] - &(array[0][0])); //1, 3 3是因为跨了一行
    printf("new_q is %p\n", q);
    printf("sizeof q is %ld, sizeof array is %ld\n", sizeof(q), sizeof(array));

} 

函数指针

指向函数的指针:

不同于指向数据类型的指针,函数指针指向一段可执行的代码的首地址,这段代码仍然占用了一块内存空间。很多人都说C语言是一种面向过程的语言[来源请求],因为它最多只有结构体的定义,而没有的概念。根据本段所述,可以认为C语言能成为面向对象的语言,只是表述比较麻烦而已。[9]事实上很多开源程序都使用这种方式组织他们的代码。

函数指针变量的声明:

type (*function_name)(...args); 
//ps 去掉type就跟函数声明一样了
//相当于定义了个变量,这个变量存了一个函数的地址,参数是arg, 返回值类型是type
#include <stdio.h>
 
int max(int x, int y)
{
    return x > y ? x : y;
}
 
int main(void)
{
    /* p 是函数指针 */
    int (* p)(int, int) = & max; // &可以省略
    int a, b, c, d;
 
    printf("请输入三个数字:");
    scanf("%d %d %d", & a, & b, & c);
 
    /* 与直接调用函数等价,d = max(max(a, b), c) */
    d = p(p(a, b), c); 
 
    printf("最大的数字是: %d\n", d);
 
    return 0;
}
//请输入三个数字:1 2 3
//最大的数字是: 3
#include <stdio.h>

void inc(int *val)
{
    (*val)++;
}

int main(void)
{
    void (*fun)(int *);
    int a=3;
    fun=inc;
    (*fun)(&a);
    printf("%d", a);
    return 0;
}

数组和指针

一、概念

  • 数组:数组是用于储存多个相同类型数据的集合。
  • 指针:指针相当于一个变量,但是它和不同变量不一样,它存放的是其它变量在内存中的地址。

二、赋值、存储方式、求sizeof、初始化等

  1. 赋值

    同类型指针变量可以相互赋值,数组不行,只能一个一个元素的赋值或拷贝

  2. 存储方式

    • 数组:数组在内存中是连续存放的,开辟一块连续的内存空间。数组是根据数组的下进行访问的,多维数组在内存中是按照一维数组存储的,只是在逻辑上是多维的。

      数组的存储空间,不是在静态区就是在栈上。

    • 指针:指针很灵活,它可以指向任意类型的数据。指针的类型说明了它所指向地址空间的内存。

      指针:由于指针本身就是一个变量,再加上它所存放的也是变量,所以指针的存储空间不能确定。

  3. 求sizeof

    • 数组

      数组所占存储空间的内存:sizeof(数组名)
      数组的大小:sizeof(数组名)/sizeof(数据类型)

    • 指针

      在32位平台下,无论指针的类型是什么,sizeof(指针名)都是4,在64位平台下,无论指针的类型是什么,sizeof(指针名)都是8。

  4. 初始化

  5. 传参

    传参: 参考https://blog.csdn.net/cherrydreamsover/article/details/81741459

数组名是什么

  • 数组名出现在表达式中时,绝大多数情况(除了数组名作为sizeof的操作数或者作为取地址&元素符的操作数)会被隐式转换为指向数组的首个元素的指针右值

  • 当数组名作为取地址&运算符的操作数,则表达式的值为指向整个数组的指针右值。

    #include<stdio.h>
    
    int main() {
        char s[]="hello";
        char (*p1)[6]=&s; //OK!
        printf("和&结合是 %p,数组名单独是%p\n", &s, s);
        //char (*p2)[6]=s; //compile error: cannot convert 'char*' to 'char (*)[6]'   
        //char *p3=&s;//compile error: cannot convert 'char (*)[6]' to 'char*' 
    }
    

    根据上述C语言标准中的规定,表达式 &s 的值的类型是char ()[6],即指向整个数组的指针;而表达式 s 则被隐式转换为指向数组首元素的指针值,即 char 类型。同理,表达式 s[4] ,等效于表达式 *(s+4)。