C语言指针和数组对比

一、指针

一级指针

我们通常说到指针的时候,指的就是一级指针。

1、提几点指针中容易混淆、不易分辨的知识点:

  • 声明的时候int *p; int * 指的是类型名,此时的*p指的是这个 p 是指针类型,int再进一步说明p是整型的指针变量。当我们在调用的时候,p是指针变量,p = a;p指向变量 a 的空间;*p = 10;此时的*表示解引用,即*p代表 p 指针所指向的空间中的内容——10。
  • 指针就是地址。
  • a = sizeof() 中放一个地址,32位平台 a = 4 byte;64位平台 a = 8 byte。因为32位平台有32根地址线,刚好对应 32 bit = 4 byte。
  • 一个字节的内存配置一个地址,声明的时候分为:int *、 char*、double*指的是指针可以向后访问的字节权限,int* 可以向后访问 4 byte。
  • void *p;是一个没有定义类型的指针变量,他可以接收任何类型的指针,但是在使用的时候必须强转,如果没有类型转换的话,没法进行指向和解引用。如: void* memcpy(void *dest, const void *src, int count); (int *)dest ++;

2、指针的出现就是为了访问内存的时候更加灵活和方便。

下面例子说明的是给指针配置不同类型的影响:

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

int main()
{
    int a = 0x11111111;//十六进制整型
    int* pi = &a;
    char* pc = &a;//此处会警报类型不对,但是我们依然可以操作。

//指针定义的时候需要另外申请一块内存,在申请的内存中存放着目标的地址
    printf("&a = %p\n", &a);
    printf("&pi = %p\n", &pi);
    printf("&pc = %p\n", &pc);

//不同类型的指针,进行+1移动的时候,移动的字节数不同
    printf("pi + 1 = %p\n", pi + 1);
    printf("pc + 1 = %p\n", pc + 1);

//不同类型的指针,向后获取的字节不一样,int4 byte,char 型 1 byte
    printf("*a = %d\n", a);
    printf("*pi = %d\n", *pi);
    printf("*pc = %d\n", *pc);

    system("pause");
    return 0;
}

这里写图片描述

二级指针

1、二级指针是指向一级指针的指针,它用来存放一级指针的地址。

int a = 10;
int* pa = &a;
int* *ppa = &p;

2、本质和一级指针没有大的差别,下面利用 const 修饰指针,进一步理解二级指针。

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

int main()
{
    int a = 10;
    int b = 20;
    int* pa = &a;
    int* pb = &b;

    //1
    const int**pp = &pa;//不可修改的整型数据
    pp = &pb;//可以修改指向
    //**pp = 30;//不可修改的整型数据

    //2
    int** const pp = &pa;//不可修改的二级指针
    //pp = &pb;//不可修改指向

    //3
    int* const *pp = &pa;//不可修改的一级指针的值
    pp = &pb;//可以修改指向
    //*pp = pb;//不可以修改值

    printf("%d\n", &a);
    printf("%d\n", &b);
    printf("%d\n", pa);
    printf("%d\n", *pa);
    printf("%d\n", &pa);
    printf("%d\n", pb);
    printf("%d\n", *pb);
    printf("%d\n", &pb);
    printf("%d\n", pp);
    printf("%d\n", *pp);
    printf("%d\n", &pp);



    int a = 10;
    int b = 20;

    //1
    const int *p = &a;//不可修改的一级指针
    //*p = 30;//整型数据未修改成功
    p = &b;//可以修改指向
    printf("%d\n", a);
    printf("%d\n", *p);

    //2
    int* const p = &a;//不可修改的整型数据
    *p = 30;//可以修改值
    //p = &b;//不可以修改指向
    printf("%d\n", a);
    printf("%d\n", *p);

    system("pause");
    return 0;
}

二、指针数组

指针数组是一个数组,它的每一个元素是指针

int *arr1[10];
char **arr2[10];

上面的例子中,因为 [] 的优先级比 * 优先级高,所以它先是一个数组,然后元素的类型是指针

int a = 10;
int b = 20;

int* arr[2] = {&a, &b}; //&a,&b 的类型是 int * 。
char* arr[3] = {"asdf","qwer","zxzx"};

printf("%d\n",sizeof(arr)); //12 三个字符型地址
printf("%d\n",strlen(arr)); //x 未知值,数组中没有'\0',所以strlen()找不到结束字符

三、数组指针

数组指针是一个指针,它指向的元素是数组。

int (*p)[10];

p先和 * 结合,先是一个指针,然后与 [] 结合,指向类型是数组。它的类型是int (*)[10]

int arr[10] = {0};
int (*p)[10] = &arr;  //用于存放数组的地址

char* arr[10];
char*(*p)[10] = &arr;

int(*p[5])[3];

四、指针和数组的关系

1、数组的小结,请点击:小结 | 一维数组、二维数组

2、在引用字符串的时候,可以用数组或者指针来定义:

char arr[] = "abcdef";
char *p = "abcdef";

两者是有区别的,用数组定义的字符串可以修改,因为它存放在栈中。但是用指针定义的字符串是不可以修改,因为它存放在常量区,是字符串常量。

3、在调用函数是,形参如果利用了数组或者指针,其中有一些这样的转换关系:


//1 一维数组传参
void test(int arr[]);
void test(int arr[10]);//arr[10] 参数数量 10 根本没有影响,因为声明和定义的时候,只关心形参类型。
void test(int *arr);
void test(int *arr[20]);//正确
void test(int **arr); //正确 传递的是首元素的地址,首元素的类型是int* 首元素的地址是 int**

int main()
{
    int arr[10] = {0};  //整型数组
    int *arr2[20] = {0}; //整型指针数组
    test(arr);
    test2(arr2);
}

//2 二维数组传参
void test(int arr[3][5]);
//void test(int arr[][]); //错误的传参,二维数组一定要知道每一行有几列
void test(int arr[][5]);
void test(int* arr[5]);  //错误,传递的是第一行的地址,第一行是数组,所以要用数组指针来接收
void test(int (*arr)[5]); //正确的传参,传过来的是二维数组的第一行的地址,用一维数组指针来接收,类型符合
void test(int **arr);  //错误,同 int* arr[5] 

int main()
{
int arr[3][5] = {0};
test(arr);
}

五、函数指针

函数指针,指向函数地址的指针。

如何获取函数的地址,如何调用函数:

void test()
{
    printf("\n");
}

int main()
{
//加不加 & 均可以获得函数的地址
    printf("%p\n",&test);
    printf("%p\n",test);
//以下四种调用情况都一样,* 符号在这里只是摆设,没有什么价值
    (&test)();
    test();
    (*test)();
    (**test)();

    return 0;
}
char test(const char* str)
{
    printf("%s\n",str);
}

int main()
{
    char (*p)(const char*); //函数指针,其类型为:char (*)(const char *)
    p = &test; //用来接收函数的地址

    p("asdf");  //可以输出 asdf
}

六、操碎心的代码

来自《C 陷阱与缺陷》

1、(*(void(*)())0)(); 整体是一个函数调用,(void()()) 是强转,将 0 的类型强转为函数指针,获得0地址, 解引用,获得0的内容。即调用了 0 地址处的函数。

2、void(*signal(int, void(*)(int)))(int);signal(int, void(*)(int)) = a,则void(* a)(int)。函数是 signal,第一个参数是 int 第二个参数是 void(*)(int),它的类型是函数指针。除去这个函数,即除去 a ,剩下的是函数返回类型 void(*)(int),也是函数指针。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值