一、C语言初阶:指针

1 指针

1.1 指针的算术运算

  • 指针移动
    int arr[] = {1,2,3,4,5};
    int* p = arr;
    int* q;
    // 移动指针顺序打印
    for(int i = 0;i < 5;++i){
        q = p+i;
        printf("%d ",*q);
    }
    printf("\n");
    // 移动指针倒序打印
    for(int i = 0;i < 5;++i){
        p = q-i;
        printf("%d ",*p);
    }
    printf("\n");
    printf("%d\n",q-p); //计算指针p和指针q的间距
  • 指针自增自减
    int arr[] = {1,2,3,4,5};
    int* p = arr;
    printf("%d\n",*p++);
    for(int i = 0;i < 5;++i){
        printf("%d ",arr[i]);
    }
    printf("\n");
    // 1
    // 1 2 3 4 5

    p = arr;
    printf("%d\n",*++p);
    for(int i = 0;i < 5;++i){
        printf("%d ",arr[i]);
    }
    printf("\n");
    // 2
    // 1 2 3 4 5

    p = arr;
    printf("%d\n",++*p);
    for(int i = 0;i < 5;++i){
        printf("%d ",arr[i]);
    }
    printf("\n");
    // 2
    // 2 2 3 4 5

1、前两种改变变量的值,后一种改变arr元素
2、++、- -和解引用*的结合顺序为自右向左

1.2 指针类型

  • 指针大小
    无论指向什么类型,所有指针的大小都是一样的,都是地址的大小
    char* c;
    int* a;
    long* p;
    printf("%d\t%d\t%d\n",sizeof(c),sizeof(a),sizeof(p));
    // 8  8  8
  • 类型转换
    指针类型转换没有改变指针内的地址,也没有改变指针指向的值,只是改变了移动的单位长度。
   char* str = "abcde";
    int*p = (int*)str;
    printf("%c\n",*++p);
    // e 
    // int型大小为4,所以从arr[0]移动至arr[4]
  • void*
    void*是一种很特别的指针,表示指向未知类型的指针,并不指定它是指向哪一种类型的数据,而是根据需要转换为所需数据类型。
    int a = 10;
    void* p = &a;
    printf("%d\n",*(int*)p);

    char c = 'a';
    p = &c;
    printf("%c\n",*(char*)p); // 强转后解引用
    // printf("%c\n",*p);

1.3 指针作用

1、函数需要多个返回值时,作为返回值参数。
2、传入数组后,对数组做操作。
3、较大数据结构体传入时做参数。
4、动态申请内存。
5、避免使用未初始化指针、空指针和野指针。

1.4 二维指针

  • 指针是变量,变量有地址,指针也有地址。(三段论)
	int n=10;
	int *p = &n; // *p是指向int变量的指针
	int* *pp = &p; // **p是指向int指针的指针
	printf("&p:%p p:%p *p:%d",&p,p,*p);
	printf("pp:%p *pp:%p **pp:%d",pp,*pp,**pp);

1.5 指针数组

存放指针的数组,指针数组是多个指针变量集合

    int a = 1;
    int b = 2;
    int c = 3;
    int* p[] = {&a,&b,&c};
    for(int i=0;i<3;++i){
        printf("%d\n",*p[i]);
    }
    for(int i=0;i<3;++i){
        printf("%d\n",**(p+i));
    }

1.6 二维指针 vs 二维数组 vs 指针数组

    int arr[][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
    for(int i=0; i<3; ++i) {
        for(int j=0; j<4; ++j) {
            printf("%d ",arr[i][j]);
        }
        printf("\n");
    }
    int* parr[] = {arr[0],arr[1],arr[2]};
    for(int i=0; i<3; ++i) {
        for(int j=0; j<4; ++j) {
            printf("%d ",parr[i][j]);
        }
        printf("\n");
    }
    int** p = parr; // 指针数组可以赋值为维指针
    for(int i=0; i<3; ++i) {
        for(int j=0; j<4; ++j) {
            printf("%d ",p[i][j]);
        }
        printf("\n");
    }   
    // int** p2 = arr; //二维数组不可直接赋值给二维指针

1、指针数组与二维数组相同点:访问数组元素方式。
2、arr[0],arr[1],arr[2]是数组名,parr[0],parr[1],parr[2]是指针,所以只是形式上的一直,内存表示上是不同。(数组名是值,指针是变量)
3、指针数组数组名可以直接赋值给二维指针。
4、二维数组数组名不可以直接赋值给二维指针。
5、二维指针操作与指针数组可以认为完全一致;二维数组操作与指针数组部分一致(访问元素上一致;但是指针数组内存放地址可以修改,二维数组数组名表示地址不能修改);

1.7 常量指针 vs 指针常量 vs 常量指针常量

  • 常量指针 const int* p
    可以写作int const* p,p是int*类型,const修饰的是 * p,所以 * p是常量,表示p指向的地址里的值不可修改,也就是说,*p里的值不能再重新赋值了,但是可以修改p指向的地址。
  • 指针常量 int* const q
    q是int*类型,那么const修饰的是q,所以q是常量,表示q指向的地址不可修改,即q不能再指向别的地方了,但是可以修改p指向的这个地址里的值。
  • 常量指针常量 const int* const p
    p是int类型,两个const分别修饰了p和p, 所以p和*p都是常量,表示p指向的地址不可修改,同时p指向的地址里的值也不可修改。
    int a = 10;
    int b = 20;
    // 常量指针
    const int* p = &a;
    p = &b; // p指向的地址可以修改
    // *p = 100; //不可以改指向地址里的值
    printf("%d\n",*p);
    // 指针常量
    int* const q;
    // q = &b; // q指向的地址不能修改
    *q = 200; // 可以修改指向地址里的值
    printf("%d\n",*q);
    // 常量指针常量
    const int* const p1 = &a;
    // p1 = &b; //p1指向地址不能修改
    //*p1 = 100; //地址里的值不能修改

1.8 字符串数组 vs 字符串指针数组

  • 字符串数组
char arr[12][10] = {"January","February","March","April","May","June","July","August","September","October","November","December"};
char arr[][10] = {"January","February","March","April","May","June","July","August","September","October","November","December"};
  • 字符串指针数组
char* arr[12] ={"January","February","March","April","May","June","July","August","September","October","November","December"};
char* arr[] ={"January","February","March","April", "May","June","July","August","September","October","November","December"};
  • 二者的区别
  • 1、大小的区别
    char* arr2[12] = {"January","February","March","April","May","June","July","August","September","October","November","December"};
    printf("%d\n",sizeof(arr2[0]));
    printf("%d\t%d\n",sizeof(arr),sizeof(arr2));
    // 8
    // 120 96
  • 2、二维指针的区别
int main () {
    char arr1[][10] = {"January","February","March","April",
                 "May","June","July","August",
                 "September","October","November","December"};
    char* arr2[] = {"January","February","March","April",
               "May","June","July","August",
               "September","October","November","December"};
     /*
    char** p1 = arr1;
    for(int i=0;i<12;++i){
        printf("%s\n",p1[i]);
    }
    */
    char** p2 = arr2;
    for(int i=0;i<12;++i){
        printf("%s\n",p2[i]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值