小结 | 指针、数组和指针@_@

来源:互联网 发布:linux重启命令 编辑:程序博客网 时间:2024/06/05 23:59

一、指针

一级指针

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

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),也是函数指针。

七、感想

晚安,各位大佬。
好晕乎

原创粉丝点击