C语言强化——指针

目录

  • 相关概念
  • 数组与函数
  • 栈空间和堆空间的差异
  • 指针常量与常量指针
  • 指针数组与数组指针
  • 二级指针
    • 二级指针的传递
    • 二级指针的偏移(索引式排序)

相关概念
  • 指针的大小,在32系统上是4个字节;在64位系统上,是8个字节。 (32位系统,32根地址线,寻址空间为2^32次方,最大地址值为2^32-1,只需4个字节即32位存储地址。)
void test3()
{//指针的大小,在32系统上是4个字节;在64位系统上,是8个字节
   int number = 10;
   int * p = &number;
   printf("sizeof(p) = %d\n", sizeof(p));
}
  • 野指针:定义指针未初始化或者释放空间后指针未置NULL
    内存泄漏:申请空间后未释放(malloc/free)
    内存踩踏:使用了不属于某一个变量或者指针开辟出来的空间

  • 左值和右值

    lvalue(locator value)代表一个在内存中占有确定位置的对象(换句话说就是有一个地址)。
    rvalue通过排他性来定义,每个表达式不是lvalue就是rvalue。因此从上面的lvalue的定义,rvalue是在不在内存中占有确定位置的表达式。

数组与函数
  • 函数参数传递数组
    1. 一维数组长度无法传递。
    2. 一维数组作为参数,本质上传递的是指针(例子中只会打印一个数字!)
void test0(int arr[]) {
    int *p = arr;
    for (int idx = 0; idx != sizeof(arr) / sizeof(int); ++idx) {
        printf("%2d", *p++);
    }
    printf("\n");
}

int main() {
    int arr[] = { 1,2,3,4,5 };
    test0(arr);   //传入的是指针
    return 0;
}
  • sizeof() 函数:

一维数组打印

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

二维数组打印

#include<stdio.h>
#include<string.h>  

void print(int(*p)[4], int row) {
    int i, j;
    for (int i = 0;i < row;++i) {
        for (int j = 0;j < sizeof(*p) / sizeof(int);++j) {
            printf("%3d", p[i][j]);
        }
        printf("\n");
    }
    
}

int main() {
    int arr[3][4] = { 1,3,5,7,2,4,6,8,10,11,12,13 };
    print(arr, 3);
    return 0;
}
栈空间和堆空间的差异
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

//函数栈空间释放后,函数内所有局部变量消失
char* print_stack() {
    char c[] = "I am a print_stack.";
    puts(c);
    return c;
}

//堆空间不会因函数执行结束而释放
char* print_malloc() {
    char* p;
    p = (char*)malloc(20);
    strcpy(p, "I am a print_malloc.");
    puts(p);
    return p;
}

int main() {
    char* p;
    p = print_stack();   //数据在栈空间
    puts(p);  //打印异常
    p = print_malloc();  //数据在堆空间
    puts(p);

    return 0;
}
指针常量与常量指针
  • 指针常量:不能改变指针的指向,只能改变指向的值。
    int arr[10] = { 1, 2, 3, 4, 5 };
    int * const p = arr;       //指针常量(const pointer)
    int number = 10;
    //p = &number;          //error 不能改变指针的指向
    *p = 10;//ok 可以改变指针所指变量的值
  • 常量指针:指向常量的指针,不能改变指针指向的值,只能改变指针的指向
    const int * p1 = &number;   //常量指针(pointer to const)
    p1 = arr;   //p1可以改变指向
    //*p1 = 10;//error 不能修改p1所指空间的内容
指针数组与数组指针
  • 定义
    int (*p1)[5];//指向一个大小为5的数组,数组里面存放的是整型数据
    int * p2[5];//指针数组 
  • &arr和arr的地址相同,不同的是arr+0是int型指针,而&arr是数组指针。

  • &+数组名的使用

void test1()
{
    int arr[] = { 1, 2, 3, 4, 5 };
    int * p = arr;
    p = &arr;
    printf("%d\n", *(&arr + 1));    //&arr 代表的是数组指针类型,越界
    printf("%d\n", *(arr + 1));     //打印2
    printf("*p = %d\n", *p);        //打印1
}

其中*(&arr+1)偏移了整个数组的大小,解引用后越界!同理如果是二维数组也会偏移整个二维数组的大小。

  • 数组指针的应用

“二维数组的实现是通过二级指针实现的”这是错误的!二维数组通过两次偏移到获取到数组中的某一个元素,所使用的指针是数组指针,数组指针是一级指针

#include<stdio.h>
#include<string.h>  

void print(int(*p)[4], int row) {
    int i, j;
    for (int i = 0;i < row;++i) {
        for (int j = 0;j < sizeof(*p) / sizeof(int);++j) {
            printf("%3d", p[i][j]);
        }
        printf("\n");
    }
}

//数组指针用于二维数组的传递和偏移
int main() {
    int arr[3][4] = { 1,3,5,7,2,4,6,8,10,11,12,13 };
    int b[4] = { 1,2,3,4 };
    int i = 10;
    int(*p)[4];//定义一个数组指针
    p = arr;
    print(p, 3);

    return 0;
}

强转实现4行3列输出

#include<stdio.h>
#include<string.h>  

void print(int(*p)[3], int row) {
    int i, j;
    for (int i = 0;i < row;++i) {
        for (int j = 0;j < sizeof(*p) / sizeof(int);++j) {
            printf("%3d", p[i][j]);
        }
        printf("\n");
    }
}

//数组指针用于二维数组的传递和偏移
typedef int(*ptype)[3];
int main() {
    int arr[3][4] = { 1,3,5,7,2,4,6,8,10,11,12,13 };
    int b[4] = { 1,2,3,4 };
    int i = 10;
    int(*p)[3];//定义一个数组指针
    print((ptype)arr, 4);

    return 0;
}
二级指针
  1. 二级指针的传递
  • 下 面 有 一 个 实 例 , 整 型 指 针 pi, 指 向 整 型 变 量 i, 整 型 指 针 pj 指 向 整 型 变 量 j , 通 过 子

    函 数 change, 我 们 想 改 变 指 针 变 量 pi 的 值 , 让 其 指 向 j, 我 们 知 道 c 语 言 的 函 数 调 用 是 值 传

    递 , 因 此 要 想 在 change 中 改 变 变 量 pi 的 值 , 那 么 必 须 把 pi 的 地 址 传 递 给 change,pi是一级

    指针,&pi的类型即为2级指针。

  • 要 想 在 子 函 数 改 变 一 个 变 量 的 值 , 必 须 把 该 变 量 的 地 址 传 进 去

  • 要 想 在 子 函 数 改 变 一 个 指 针 变 量 的 值 , 必 须 把 该 指 针 变 量 的 地 址 传 进 去,二 级 指 针 传 递

  • 对 于 二 级 指 针 的 传 递 使 用 场 景 , 把 握 两 点 , 第 一 . 二 级 指 针 变 量 定 义 是 在 形 参 , 第 二

    在 调 用 函 数 中 往 往 不 定 义 二 级 指 针 , 如 果 定 义 , 初 始 化 注 意 是 一 级 指 针 的 取 地 址 。

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

void change(int **p2, int *pb) {
    *p2 = pb;
}

//要 想 在 子 函 数 改 变 一 个 变 量 的 值 , 必 须 把 该 变 量 的 地 址 传 进 去
//要 想 在 子 函 数 改 变 一 个 指 针 变 量 的 值 , 必 须 把 该 指 针 变 量 的 地 址 传 进 去
int main() {
    int i = 10;
    int j = 5;
    int *pi, *pj;
    pi = &i;
    pj = &j;
    printf("i=%d, j=%d, *pi=%d, *pj=%d\n", i, j, *pi, *pj);  //10 5 10 5
    change(&pi, pj);
    printf("i=%d, j=%d, *pi=%d, *pj=%d\n", i, j, *pi, *pj);  //10 5 5 5
    return 0;
}
  1. 二级指针的偏移

一 级 指 针 的 偏 移 服 务 于 数 组 , 整 型 一 级 指 针 服 务 于 整 型 数 组 , 所 以 二 级 指 针 的 偏 移 也 服
务 于 数 组 , 服 务 对 象 为 指 针 数 组

  • 应用:索引式排序
#include<stdio.h>
#include<string.h>

//void print(char *p[], int len)  二级指针的偏移服务的是指针数组
void print(char **p, int len) {   //二级指针每次偏移4个字节(因为一级指针在32位系统中占4个字节)
    int i;
    for (i = 0;i < len;++i) {
        puts(p[i]);
    }
}

void printArr(char(*p)[10], int row) {
    int i;
    for (i = 0;i < row;++i) {
        puts(p[i]);
    }
}

int main() {
    char b[5][10] = { "php","python","linux","unix","java" };
    char *p[5];  //指针数组
    int i, j;
    char *tmp;
    for (i = 0;i < 5;++i) {  //让指针数组中的每一个指针指向一个字符串
        p[i] = b[i];
    }
    print(p, 5);
    printf("--------------------\n");
    printArr(b, 5);
    printf("--------------------\n");
    for (int i = 4;i > 0;--i) {
        for (int j = 0;j < i;++j) {
            if (strcmp(p[j], p[j + 1]) > 0) {
                tmp = p[j];
                p[j] = p[j + 1];
                p[j + 1] = tmp;
            }
        }
    }
    print(p, 5);
    
    return 0;
}

主函数内定义二级指针实现:

#include<stdio.h>
#include<string.h>

//void print(char *p[], int len)  二级指针的偏移服务的是指针数组
void print(char **p, int len) {   //二级指针每次偏移4个字节(因为一级指针在32位系统中占4个字节)
    int i;
    for (i = 0;i < len;++i) {
        puts(p[i]);
    }
}

void printArr(char(*p)[10], int row) {
    int i;
    for (i = 0;i < row;++i) {
        puts(p[i]);
    }
}

int main() {
    char b[5][10] = { "php","python","linux","unix","java" };
    char **p;  //等价于char *p[];
    int i, j;
    p = (char**)malloc(20);  //一级指针强制类型转换为二级指针,使用动态的指针数组
    char *tmp;
    for (i = 0;i < 5;++i) {  //让指针数组中的每一个指针指向一个字符串
        p[i] = b[i];
    }

    print(p, 5);
    printf("--------------------\n");
    printArr(b, 5);
    printf("--------------------\n");
    for (int i = 4;i > 0;--i) {
        for (int j = 0;j < i;++j) {
            if (strcmp(p[j], p[j + 1]) > 0) {
                tmp = p[j];
                p[j] = p[j + 1];
                p[j + 1] = tmp;
            }
        }
    }
    print(p, 5);
    
    return 0;
}
  • 传递二级指针修改指针本身的值
#include <stdio.h>

//传递二级指针修改指针本身的值
void swap(int ** p1, int ** p2)
{
    int tmp = *p1;   
    *p1 = *p2;         
    *p2 = tmp;         
    printf("函数内:*p1 = %d, *p2 = %d\n", **p1, **p2);
}

void test6()
{
    int number1 = 3, number2 = 4;
    int * p1 = &number1;
    int * p2 = &number2;

    printf("*p1 = %d, *p2 = %d\n", *p1, *p2);
    swap(&p1, &p2);
    printf("*p1 = %d, *p2 = %d\n", *p1, *p2);
}


int main(void)
{
    test6();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Mered1th/p/10631195.html