C语言:理解指针

一级指针

  • 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
  • 指针的大小是固定的4/8个字节(32位平台/64位平台)。
  • 指针是有类型,指针的类型决定了指针的±整数的步长,指针解引用操作的时候的权限。

二级指针

二级指针本质上是一个一级指针 , 只不过这个一级指针指向的内容也是一个一级指针

指针和数组

指针和数组在C语言中经常被混用,但二者有本质区别:

  • 数组是自带内存空间的,往往是把原来的数据复制到新申请的内存空间
  • 指针变量自身的内存空间就是固定的4个字节 , 不论指向什么内容 , 这4个字节只保存对应的内存空间的地址上 , 不涉及数据拷贝过程

指针数组&数组指针

int *arr[ ] :指针数组,是一个数组,每个元素都是一个指针
int (*p)[ ] :数组指针,是一个指针,指向了一个数组(的首地址)

可以用typedef来帮助理解数组指针:

typedef int(Point)[4]  //(类型叫int[4]) 
  Point* p=int (*p)[4]

二者没有任何联系,只是因为名字相似经常会一起出现,需要明确区分开
**注意:**在某些条件下数组指针可以转换成二级指针 , 二维数组不能转成二级指针
二维数组传参 , 函数形参设计只能省略arr[ ][ ]中的第一个[ ] , 的元素即arr[ ][i] , 对于二维数组,可以不知道有多少行,但是要知道每一行是几个元素(每个数组是几个元素),方便计算

数组元素与解引用:
char* p="hehe"
p[0]=*(p+0)

int (*arr[5])[5] 指针数组,每个元素又是一个指针数组

函数指针

用途:转移表(表驱动方式)
1.void (*pfunc1)()=Func pfunc1是函数指针 , 指向Func , 类型为 void(*)()
2.int (*pfunc2)(int x,int y)=add pfunc2指向Add , 类型为int(*)(int x,int y)
函数指针也可以解引用,解引用得到的不是数据而是一个函数,这个函数可以调用 , 使用()函数调用操作符来进行调用
对于函数指针来说,调用函数是最核心的使命 , 即使不进行解引 , 也能进行函数调用
3.void ( * signl ( int , void(*)(int) ) ) signl是一个函数,参数有两个;第一个参数是int , 第二个是函数指针 这个函数指针参数是int ,返回值是void , signl函数返回值是函数指针
4.typedef void(*h)(int); h signl(int,h);此时定义了一个一个类型h , 这个类型是函数指针类型 , 参数为int ,返回值是void的函数指针(简化函数指针)

回调函数

普通的函数调用时机由程序员自己决定,毁掉函数的调用时机由操作系统或代码框架来决定
典型的例子有回调版的冒泡排序:

#include 

void Swap(int* x,int * y){
    int tmp = *x;
    *x = *y;
    *y = tmp;
}

typedef int(*Cmp)(int,int);

int BulleSort(int arr[],int size,Cmp cmp){
    int bound = 0;
    for (; bound <size ; ++bound) {
        for (int cur = size-1; cur > bound ; -- cur) {
            if(cmp(arr[cur-1],arr[cur])){
                Swap(&arr[cur-1],&arr[cur]);
            }
        }
    }
}

int Less(int x,int y){//升序
    if(x < y){
        return 0;
    }
    return 1;
}

int Greater(int x,int y){//降序
    if(x > y){
        return 0;
    }
    return 1;
}

int  main(){
    int arr1[]={9,5,2,7};
    int arr2[]={9,5,2,7};
    int size1= sizeof(arr1)/ sizeof(arr1[0]);
    BulleSort(arr1,size1,Less);
    for (int i = 0; i <size1 ; ++i) {
        printf("%d ",arr1[i]);
    }
    int size2= sizeof(arr2)/ sizeof(arr2[0]);
    printf("\n");
    BulleSort(arr2,size2,Greater);
    for (int i = 0; i <size2 ; ++i) {
        printf("%d ",arr2[i]);
    }
    return 0;
}

指针和数组组题解析

//一维数组
    int arr[] = {1,2,3,4};
    printf("%d\n",sizeof(arr));// 16 4*4
    printf("%d\n",sizeof(arr+0));// 4 arr1+0 得到了一个指针,32位系统占4个字节
    printf("%d\n",sizeof(*arr));// 4 *arr1=>*(arr+0)=>arr1[0]得到第一个元素1
    printf("%d\n",sizeof(arr+1));// 4
    printf("%d\n",sizeof(arr[1]));// 4
    printf("%d\n",sizeof(&arr));// 4 &arr 数组指针 指针都是占4个字节
    printf("%d\n",sizeof(*&arr));// 16 &arr 数组指针,再*得到整个数组
    printf("%d\n",sizeof(&*arr));// 4 *arr=>int, 再&得到int*
    printf("%d\n",sizeof(&arr+1));// 4 &arr指向整个数组的数组指针,+1还是数组指针
    printf("%d\n",sizeof(&arr[0]));// 4 指针指向第一个元素的地址
    printf("%d\n",sizeof(&arr[0]+1));// 4 指针指向第二个元素的地址
//字符数组
    char arr[] = {'a','b','c','d','e','f'};
    printf("%d\n", sizeof(arr)); // 6
    printf("%d\n", sizeof(arr+0));//4 arr得到指向首元素的指针,+0还是指针
    printf("%d\n", sizeof(*arr));// 1 得到第一个元素
    printf("%d\n", sizeof(arr[1]));// 1得到第二个元素
    printf("%d\n", sizeof(&arr));// 4 数组指针
    printf("%d\n", sizeof(&arr+1));//4 数组指针+1还是数组指针
    printf("%d\n", sizeof(&arr[0]+1)); // 4 arr[0]得到首元素a,&得到指针,+1还是指针
    printf("%d\n", sizeof(arr[0]+1));// 4 arr[0]得到一个char,与1(int)相加,整形提升得到结果为int

    printf("%d\n", strlen(arr)); //未定义行为
    printf("%d\n", strlen(arr+0));//未定义行为
    printf("%d\n", strlen(*arr));//未定义行为 *arr得到char strlen传入的参数是char*,不匹配
    printf("%d\n", strlen(arr[1]));//未定义行为
    printf("%d\n", strlen(&arr));//未定义行为 得到数组指针与字符指针类型不匹配
    printf("%d\n", strlen(&arr+1));//未定义行为
    printf("%d\n", strlen(&arr[0]+1));//未定义行为
char arr[] = "abcdef";
    printf("%d\n", sizeof(arr)); // 7 有\0
    printf("%d\n", sizeof(arr+0));// 4 arr数组名+0变成指针
    printf("%d\n", sizeof(*arr));// 1 *arr取出首元素
    printf("%d\n", sizeof(*arr)+1);// 4 整形提升
    printf("%d\n", sizeof(arr[1]));// 1 第二个元素
    printf("%d\n", sizeof(&arr));// 4 数组指针
    printf("%d\n", sizeof(&arr+1));// 4 数组指针,跳过了这个数组
    printf("%d\n", sizeof(&arr[0]+1));// 4 第二个元素的地址 char*

    printf("%d\n", strlen(arr));// 6 字符串长度不算\0
    printf("%d\n", strlen(arr+0));// 6
    printf("%d\n", strlen(*arr));//未定义行为 UB *arr得到了char 应该传入char*
    printf("%d\n", strlen(arr[1]));// UB
    printf("%d\n", strlen(&arr));// 6 碰巧正确 数组指针值和首元素地址值一致
    printf("%d\n", strlen(&arr+1));// 未定义行为 数组指针+1跳过整个数组
    printf("%d\n", strlen(&arr[0]+1));// 5 指针跳过一个元素地址
char *p = "abcdef";
    printf("%d\n", sizeof(p));// 4
    printf("%d\n", sizeof(p+1));// 4
    printf("%d\n", sizeof(*p));// 1
    printf("%d\n", sizeof(p[0]));// 1
    printf("%d\n", sizeof(&p));// 4 二级指针
    printf("%d\n", sizeof(&p+1));// 4 二级指针
    printf("%d\n", sizeof(&p[0]+1));// 4 第二个元素地址 指针

    printf("%d\n", strlen(p));// 6 p指向的是首元素地址代指整个字符串 求出来的结果就是整个字符串长度
    printf("%d\n", strlen(p+1));// 5
    printf("%d\n", strlen(*p));// UB
    printf("%d\n", strlen(p[0]));// UB
    printf("%d\n", strlen(&p));// UB
    printf("%d\n", strlen(&p+1));// UB
    printf("%d\n", strlen(&p[0]+1));//5
int a[3][4] = {0};
    printf("%d\n",sizeof(arr)); //48
    printf("%d\n",sizeof(arr[0][0]));// 4
    printf("%d\n",sizeof(arr[0]));// 16
    printf("%d\n",sizeof(arr[0]+1));// 4 arr[0]得到了一个数组,数组+1隐式转换成指针,指向第五个元素[0][1]的地址
    printf("%d\n",sizeof(*(arr[0]+1)));// 4 *(arr[0]+1)=>arr[0][1]
    printf("%d\n",sizeof(arr+1));// 4 二维数组=>一维数组 arr+1变成数组指针 int(*)[4]
    printf("%d\n",sizeof(*(arr+1)));// 16 一维数组解引用 a[1]
    printf("%d\n",sizeof(&arr[0]+1));// 4 数组指针
    printf("%d\n",sizeof(*(&arr[0]+1)));// 16 长度为4的数组
    printf("%d\n",sizeof(*arr));// 16 =>*(arr+0)=>arr[0]
    printf("%d\n",sizeof(arr[3]));// 16 sizeof是编译过程中求值,不是下标越界

注意:有两段特殊代码需要单独提一下

char *p ="hello;
*p='a';

这里涉及到一个只读内存,*p无法修改对应内容
C语言:理解指针_第1张图片

char p[ ]="hello";
*p='a';

而定义一个字符指针再取*p就可以对字符串的内容进行修改了
C语言:理解指针_第2张图片

你可能感兴趣的:(C)