C语言——指针进阶

本文详细探讨了指针、数组、数组指针、多级指针、函数指针、数组指针数组、回调函数以及各种指针运算,包括数组名和指针的特殊含义,以及如何在实际编程中运用这些概念。涵盖了内存管理、字符串操作、数据结构和算法应用等内容。
摘要由CSDN通过智能技术生成

指针进阶

int main()
{
    char ch = 'q';
    char* pc = &ch;

    //本质上是把"hello bit"这个字符串的首字符的地址存储在了ps中
    const char* ps = "hello bit";
    char arr[] = "hello bit";
    printf("%s\n", ps);
    printf("%s\n", arr);
    return 0;
}

int main()
{
    char str1[] = "hello bit.";
    char str2[] = "hello bit.";
    const char* str3 = "hello bit.";  //常量字符串!!
    const char* str4 = "hello bit.";
    //3.4指向的"hello bit."只有一份,指向同一个位置 

 
    //str2[1] = 's';
    //*str3 = 's'; //报错(常量字符串的内容不可以更改)


    if (str1 == str2)
        printf("str1 and str2 are same.\n");
    else
        printf("str1 and str2 are not same.\n");
    if (str3 == str4)
        printf("str3 and str4 are same.\n");
    else
        printf("str3 and str4 are not same.\n");
    return 0;
}


int main()
{
    //指针数组
    //数组 - 数组中存放的是指针

    //int a = 10;
    //int b = 20;
    //int c = 30;
    //int* arr[3] = { &a,&b,&c };//存放整形指针的数组
    //int i = 0;
    //for (i = 0; i < 3; i++)
    //{
    //    printf("%d\n", *arr[i]);
    //}

    int a[5] = { 1,2,3,4,5 };
    int b[] = { 2,3,4,5,6 };
    int c[] = { 3,4,5,6,7 };
    int* arr[3] = { a,b,c };
    int i = 0;
    int j = 0;
    for (i = 0; i < 3; i++)
    {
        for (j = 0; j < 5; j++)
        {
            //printf("%d ", *(arr[i] + j));
            //printf("%d ", arr[i][j]);
            //模拟二维数组,但不真的是(因为这三个数组元素不是连续存放的)
            printf("%d ", (*(arr+i))[j]);
        }
        printf("\n");
    }
    return 0;
}

数组指针
是一种指针 - 是指向数组的指针

整型指针 - 是指向整型的指针
字符指针 - 是指向字符的指针

int main()
{
    int a = 10;
    int* pa = &a;
    char ch = 'w';
    char* pc = &ch;

    double* d[5];
    double*(*pd)[5] = &d;  //pd就是一个数组指针
    int arr[10] = { 1,2,3,4,5 };
    int(*parr)[10] = &arr;//取出的是数组的地址
    //parr就是一个数组指针 - 其中存放的是数组的地址
    
    //arr;//数组名是首元素的地址,不是数组的地址 - arr[0]的地址
    
    return 0;
}


int main()
{
    int arr[10] = { 0 };

    int* p1 = arr;
    int(*p2)[10] = &arr;
    printf("%p\n",arr);
    printf("%p\n",&arr);

    printf("%p\n", p1+1);
    printf("%p\n", p2+1);

    //数值相同,数组的起始地址和数组首元素的地址相同
    return 0;
}

数组名是数组首元素的地址
但是有两个例外
1.sizeof(数组名) - 表示整个数组,计算的是整个数组的大小,单位是字节
2.&数组名 - 数组名表示整个数组,取出的是整个数组的地址

int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int (*pa)[10] = &arr;
    //*pa;  //*(&arr) -> arr
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        printf("%d ", *(*(pa)+i));
    }
    return 0; 
}

void print1(int arr[][5], int r, int c)
{
    int i = 0;
    int j = 0;
    for (i = 0; i < r; i++)
    {
        for (j = 0; j < c; j++)
        {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
}

p是一个数组指针
void print2(int(*p)[5], int r, int c)
{
    int i = 0;
    int j = 0;
    for (i = 0; i < r; i++)
    {
        for (j = 0; j < c; j++)
        {
            printf("%d ", * (*(p + i) + j));
        }
        printf("\n");
    }
}
int main()
{
    //int a[5];  //&a
    int arr[3][5] = { {1,2,3,4,5},{2.3,4,5,6},{3,4,5,6,7} };
    //print1(arr, 3, 5);
    print2(arr, 3, 5); //arr数组名,表示数组首元素的地址
    //二维数组的数组名表示首元素的地址
    //二维数组的首元素是:第一个元素(第一行)!
}

int(*parr3[10])[5]
int(*         )[5]
parr3是一个存放数组指针的数组,该数组能够存放10个数组指针
每个数组指针能够指向一个数组,数组5个元素,每个元素是int类型


int i;  //i是全局变量,不初始化,默认是0
int main()
{
    i--;
    if (i > sizeof(i)) //-1和4进行比较
    //sizeof这个操作符,算出结果的类型是unsigned int
    //有符号整型和无符号整形运算的时候,有符号整型被转换为无符号整型。int被转换为unsigned int
    //11111111111111111111111111111111 - 当做无符号数,极其大的整数>4
    {
        printf(">\n");
    }
    else
    {
        printf("<\n");
    }
    return 0;
}

void print(int* ptr, int sz)
{
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%d ", *(ptr + i));
    }
}

void test(char* p)
{
    ;
}

int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int* p = arr;
    int sz = sizeof(arr) / sizeof(arr[0]);
    //p是一级指针

    print(p,sz);
    char ch = 'w';
    char* p1 = &ch;
    test(p1);
    test(&ch);  //char*可以传什么进函数?取地址或者指针变量
    return 0;
}

void test(int** p2)
{
    **p2 = 20;
}
int main()
{
    int a = 10;
    int* pa = &a; //pa是一级指针
    int** ppa = &pa; //ppa是二级指针
    //把二级指针进行传参呢?
    test(ppa);
    //设计为**p2的二级指针参数,可以传什么参数呢?
    test(ppa);
    test(&pa); //传一级指针变量的地址
    int* arr[10] = { 0 };
    test(arr); //传存放一级指针的数组

    //test(&(&a)); //不可以!&a是一个数,没有内存空间
    //数组名是首元素int*的地址,因此是int**
    printf("%d\n", a);
    return 0;
}

一级指针
int* pi; - 整形指针 - 指向整型的指针
char* pc; - 字符指针 - 指向字符的指针
void* pv; - 无类型的指针

//二级指针
char** ppc;
int** pi;

//数组指针:指向数组的指针
int(*p)[4];

数组:
一维数组
二维数组
指针数组 - 存放指针的数组

!!!函数指针:指向函数的指针 - 存放函数地址的指针
int Add(int a, int b)
{
    return a + b;
}
int main()
{
    int a = 10;
    int* pa = &a;
    char ch = 'w';
    char* pc = &ch;

    int arr[10] = { 0 };
    int (*parr) [10] = &arr;  //parr是指向数组的指针,存放的是数组的指针

    //函数指针 - 存放函数地址的指针
    //&函数名 - 取到的是数组的地址
    //pf就是函数指针变量
    int (*pf)(int,int) = &Add;
    printf("%p\n", &Add);
    printf("%p\n", Add);
    //&数组名 != 数组名
    // 函数名 == &函数名

    return 0;
}


int Add(int a, int b)
{
    return a + b;
}
int main()
{
    
    //int (*pf)(int,int) = &Add;
    int (*pf)(int,int) = Add;  //Add等价于pf
    //int ret = (*pf)(3, 5);
    //int ret = (********pf)(3, 5);  - 函数指针的*没有实际意义,但加*需要加括号
    //int ret = Add(3, 5);
    //int ret = pf(3, 5);

    /*int ret = *pf(3, 5);*/  //错误!先调用函数pf(3,5),再解引用8,不可以

    printf("%d\n", ret);
    return 0;
}


int main()
{
    (*(void (*)())0)();
    //把0转换为(void (*)())类型的函数指针,再(*)调用,最后一个括号代表不传参
    //结果是调用0地址处的函数,该函数无参,返回类型是void
    //1.void(*)() - 函数指针类型
    //2.(void(*)())0 - 对0进行强制类型转换,被解释为一个函数地址
    //3.*(void(*)())0 - 对0地址进行了解引用操作
    //4.(*(void(*)())0)() - 调用0地址处的函数
    return 0;
}

写法一
void (*signal(int, void(*)(int)))(int);

不支持把返回值为函数指针的函数定义返回类型的时候把后面的圆括号放在前面。
因此,void(*)(int)signal(int, void(*)(int))错误


写法二
typedef - 对类型进行重定义
typedef void(*pfun_t)(int);  //对void(*)(int)的函数指针类型重命名为pfun_t
pfun_t signal(int, pfun_t);

1.signal先和()结合,说明signal是函数名
2.signal函数的第一个参数类型是int,第二个参数的类型是函数指针
  该函数指针指向一个参数是int,返回类型是void的函数
3.signal函数的返回类型也是一个函数指针,指向一个参数是int,返回类型是void的函数
因此,本语句是函数signal的声明

函数指针数组 - 存放函数指针的数组
整型指针 int*
整形指针数组 int* arr[5];
int Add(int x, int y)
{
    return x + y;
}

int Sub(int x, int y)
{
    return x - y;
}

int Mul(int x, int y)
{
    return x * y;
}

int Div(int x, int y)
{
    return x / y;
}

int main()
{
    int (*pf1)(int,int) = Add;
    int (*pf2)(int,int) = Sub;

    int(*pfArr[2])(int, int) = { Add,Sub };  //pfArr就是函数指针数组
    return 0;
}

void menu()
{
    printf("****************************\n");
    printf("*******1.add    2.sub*******\n"); 
    printf("*******3.mul    4.div*******\n");
    printf("*******    0.exit    *******\n");
    printf("****************************\n");
}
int main()
{
    //计算器 - 加、减、乘、除
    int input = 0;
    do {
        menu();
        printf("请选择:>");
        scanf("%d",&input);
        int x = 0; 
        int y = 0;
        int ret = 0;
        switch (input)
        {
        case 1:
            printf("请输入两个操作数:>");
            scanf("%d %d", &x, &y);
            ret = Add(x,y);
            printf("ret = %d\n", ret);
            break;
        case 2:
            printf("请输入两个操作数:>");
            scanf("%d %d", &x, &y);
            ret = Sub(x, y);
            printf("ret = %d\n", ret);
            break;
        case 3:
            printf("请输入两个操作数:>");
            scanf("%d %d", &x, &y);
            ret = Mul(x, y);
            printf("ret = %d\n", ret);
            break;
        case 4:
            printf("请输入两个操作数:>");
            scanf("%d %d", &x, &y);
            ret = Div(x, y);
            printf("ret = %d\n", ret);
            break;
        case 0:
            printf("退出程序\n");
            break;
        default:
            printf("选择错误,重新选择!\n");
            break;
        }
    } while (input);
    return 0;
}

int main()
{
    //计算器 - 加、减、乘、除
    int input = 0;
    //pfarr就是函数指针数组
    //转移表 - 给一个下表,找到数组某个元素,调用数组这个元素(函数)
    int(*pfarr[5])(int, int) = { null,add,sub,mul,div };
    do {
        menu();
        int x = 0;
        int y = 0;
        printf("请选择:>");
        scanf("%d", &input);

        if (input >= 1 && input <= 4)
        {
            printf("请输入两个操作数>:");
            scanf("%d %d", &x, &y);
            printf("ret = %d\n", (pfarr[input])(x, y));
        }
        else if (input == 0)
            printf("退出程序\n");
        else
            printf("输入错误,请重新选择\n");
    }while (input);
    return 0;
}

函数指针的数组 - 数组
取出函数指针数组的地址

整型数组
int arr[5];
int (*p1)[5] = &arr;

整型指针的数组
int* arr[5];
int* (*p2)[5] = &arr;
p2是指向【整型指针数组】的指针

int (*p)(int, int);  //函数指针
int (*p2[4])(int, int);  //函数指针数组
int (*(*p3)[4])(int,int) = &p2;  //p3就是一个指向【函数指针数组】的指针

int main()
{
    int arr[10];
    //arr数组元素类型: int(去掉数组名,元素个数)
    //arr数组类型:int [10](去掉数组名)
    return 0;
}


回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数
不直接在函数中调用这个函数,而是在某种时间传这个指针调用函数
 A函数      B函数(A函数的地址)
                  A函数的指针,返回来调用A函数
int Add(int x, int y)
{
    return x + y;
}

int Sub(int x, int y)
{
    return x - y;
}

int Mul(int x, int y)
{
    return x * y;
}

int Div(int x, int y)
{
    return x / y;
}

void menu()
{
    printf("****************************\n");
    printf("*******1.add    2.sub*******\n");
    printf("*******3.mul    4.div*******\n");
    printf("*******    0.exit    *******\n");
    printf("****************************\n");
}

int Calc(int(*pf)(int,int))
{
    int x = 0;
    int y = 0;
    printf("请输入两个操作数:>");
    scanf("%d %d", &x, &y);
    return pf(x, y);
}

int main()
{
    //计算器 - 加、减、乘、除
    int input = 0;
    do {
        menu();

        int ret = 0;
        printf("请选择:>");
        scanf("%d", &input);
        switch (input)
        {
        case 1:
            ret = Calc(Add);
            printf("ret = %d\n", ret);
            break;
        case 2:
            ret = Calc(Sub);
            printf("ret = %d\n", ret);
            break;
        case 3:
            ret = Calc(Mul);
            printf("ret = %d\n", ret);
            break;
        case 4:
            ret = Calc(Div);
            printf("ret = %d\n", ret);
            break;
        case 0:
            printf("退出程序\n");
            break;
        default:
            printf("选择错误,重新选择!\n");
            break;
        }
    } while (input);
    return 0;
}

void bubble_sort(int* arr, int sz)
{
    int i = 0;
    int j = 0;
    for (i = 0; i < sz - 1; i++)
    {//冒泡排序趟数
        for (j = 0; j < sz - i - 1; j++)
        {//一趟冒泡排序
            if (arr[j] > arr[j + 1])
            {
                arr[j] = arr[j] ^ arr[j + 1];
                arr[j+1] = arr[j] ^ arr[j + 1];
                arr[j] = arr[j] ^ arr[j + 1];
            }
        }
    }
}

void print(int* arr, int sz)
{
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

int main()
{
    int arr[10] = { 1,3,4,2,5,9,8,6,7,0 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    print(arr, sz);
    //10个数字 - 9趟冒泡排序
    bubble_sort(arr, sz);
    print(arr, sz);
}

假设让你写一个函数,排序字符串
bubble_sort_string();
strcpy; 与比较整型的方法有所差异


库函数  --  qsort(); //快速排序
整型数据 字符串数组 结构体数据
void qsort(void* base,  //base存放的是待排序数据中第一个对象的地址,void*可以传入任意类型指针的参数
           size_t num,  //排序的数据元素个数
           size_t width,  //数据中一个元素的大小
           int(__cdecl* compare)(const void* elem1, const void* elem2));  
           用来比较待排序数据中的两个元素的函数


void print(int* arr, int sz)
{
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

int cmp_int(const void* e1, const void* e2)
{
    //升序
    return *(int*)e1 - *(int*)e2;
    //降序
    //return *(int*)e2 - *(int*)e1;
}

struct Stu
{
    char name[20];
    int age;
};

int sort_by_age(const void* e1, const void* e2)
{
    return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}

int sort_by_name(const void* e1, const void* e2)
{
    return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}

void print_by_age(struct Stu* arr, int sz)
{
    struct Stu* p = arr;
    while(p<arr+sz)
    {
        printf("%d ", p++->age);
    }
    printf("\n");
}

void print_by_name(struct Stu* arr, int sz)
{
    struct Stu* p = arr;
    while (p < arr + sz)
    {
        printf("%s ", p++->name);
    }
    printf("\n");
}


void test1()
{
    //整型数据的排序
    int arr[] = { 1,5,3,2,4,8,9,6,0,5,7 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    //排序
    qsort(arr, sz, sizeof(arr[0]), cmp_int);
    //打印
    print(arr, sz);
}

void test2()
{
    //使用qsort函数排序结构体数据
    struct Stu s[] = { {"张三",30},{"李四",34},{"王五",20}};
    int sz = sizeof(s) / sizeof(s[0]);
    //按照年龄来排序
    //qsort(s, sz, sizeof(s[0]), sort_by_age);
    //按照名字来排序
    qsort(s, sz, sizeof(s[0]), sort_by_name);
    //按照名字打印
    //print_by_age(s, sz);
    //按照年龄打印
    //print_by_name(s,sz);
}


//模仿qsort实现一个冒泡排序的通用算法
void Swap(char* buf1, char* buf2, size_t width)
{
    int i = 0;
    for (i = 0; i < width; i++)
    {
        char tmp = *buf1;
        *buf1 = *buf2;
        *buf2 = tmp;
        buf1++;
        buf2++;
    }
}
void qbubble_sort(void* base,
                  size_t num,
                  size_t width,
                  int(*cmp)(const void*,const void*))
{
    int i = 0;
    int j = 0;
    for (i = 0; i < num - 1; i++)
    {//冒泡排序趟数
        for (j = 0; j < num - i - 1; j++)
        {//一趟冒泡排序
            if (cmp(((char*)base + j * width) , ((char*)base + (j+1) * width))>0)
            {
                //交换
                Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
            }
        }
    }
}

void test3()
{
    //模拟qsort整型数据的排序
    int arr[] = { 1,5,3,2,4,8,9,6,0,5,7 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    //排序
    qbubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
    //打印
    print(arr, sz);
}

void test4()
{
    //使用qsort函数排序结构体数据
    struct Stu s[] = { {"张三",30},{"李四",34},{"王五",20} };
    int sz = sizeof(s) / sizeof(s[0]);
    //按照年龄来排序
    //qsort(s, sz, sizeof(s[0]), sort_by_age);
    //按照名字来排序
    qbubble_sort(s, sz, sizeof(s[0]), sort_by_name);
    print_by_name(s, sz);
}

int main()
{
    //test1();
    //test2();
    //test3();
    //test4();
    return 0;
}

int main()
{
    int a = 10;
    char ch = 'w';
    void* p = &a;
    p = &ch;  //p无具体类型,可以存放任意类型的指针
    *p;  //无法解引用,访问字节不确定
    p++;  //无法加法,不知道加几个字节
    //因此,void使用时强制转换为某种类型即可
    //(char*)p+j - 可以逐个访问字节
    return 0;
}

题目练习
sizeof(数组名) - 数组名是表示整个数组的 - 计算的是整个数组的大小
&数组名 - 数组名表示整个数组,取出的是整个数组的地址
除此之外,所有的数组名都是数组首元素的地址

一、
int main()
{
    int a[] = { 1,2,3,4 };  //4*4=16
    printf("%d\n", sizeof(a));  //16
    printf("%d\n", sizeof(a+0));  //4或8 - a+0是第一个元素的地址,sizeof(a+0)计算的是数组的大小
    printf("%d\n", sizeof(*a));  //4 - *a是数组的第一个元素,sizeof(*a)计算的是第一个元素的大小
    printf("%d\n", sizeof(a+1));  //4或8 - a+1是第二个元素的地址,sizeof(a+1)计算的是地址的大小
    printf("%d\n", sizeof(a[1]));  //4 - 计算的是第二个元素的大小
    
    printf("%d\n", sizeof(&a));  //4或8 - 虽然是整个数组的地址,但是也是地址,sizeof(&a)计算的是地址的大小  
    printf("%d\n", sizeof(*&a));  //16 - &a - int(*p)[4](取数组的地址解引用得到数组)
    printf("%d\n", sizeof(&a+1));   //4或8 - 跳过本数组后空间的地址(是一个数组指针)
    printf("%d\n", sizeof(&a[0]));   //4或8
    printf("%d\n", sizeof(&a[0]+1);  //4或8
    return 0;
}

字符数组
int main()
{
    char arr[] = { 'a','b','c','d','e','f' };
    printf("%d\n", sizeof(arr));  //6
    printf("%d\n", sizeof(arr + 0));  //4或8
    printf("%d\n", sizeof(*arr));  //1
    printf("%d\n", sizeof(arr[1]));  //1
    printf("%d\n", sizeof(&arr));  //4或8
    printf("%d\n", sizeof(&arr+1));  //4或8
    printf("%d\n", sizeof(&arr[0] + 1));  //4或8
    return 0;
}
 
int main()
{
    char arr[] = { 'a','b','c','d','e','f' };
    printf("%d\n", strlen(arr));  //随机值
    printf("%d\n", strlen(arr + 0));  //随机值
    //printf("%d\n", strlen(*arr));  //err - char和char*
    //printf("%d\n", strlen(arr[1]));  //err - char和char*
    printf("%d\n", strlen((char*)&arr));  //随机值 本来类型是char(*)[6],转换为char*
    printf("%d\n", strlen((char*)(&arr+1)));  //随机值 4或8
    printf("%d\n", strlen(&arr[0] + 1));  //4或8
    return 0;
}
 
int main()
{
    char arr[] = "abcdef";
    printf("%d\n", sizeof(arr));  //7
    printf("%d\n", sizeof(arr + 0));  //4或8
    printf("%d\n", sizeof(*arr));  //1
    printf("%d\n", sizeof(arr[1]));  //1
    printf("%d\n", sizeof(&arr));  //4或8  char(*)[7]
    printf("%d\n", sizeof(&arr + 1));  //4或8
    printf("%d\n", sizeof(&arr[0] + 1));  //4或8
    return 0;
}

int main()
{
    char arr[] = "abcdef";
    printf("%d\n", strlen(arr));  //6
    printf("%d\n", strlen(arr + 0));  //6
    //printf("%d\n", strlen(*arr));  //err - char和char*
    //printf("%d\n", strlen(arr[1]));  //err - char和char*
    printf("%d\n", strlen((char*)&arr));  //6
    printf("%d\n", strlen((char*)(&arr + 1)));  //随机值
    printf("%d\n", strlen(&arr[0] + 1));  //5
    return 0;
}

int main()
{
    const 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  -  p[0] <==> *(p+0)
    printf("%d\n", sizeof(&p));  //4
    printf("%d\n", sizeof(&p+1));  //4
    printf("%d\n", sizeof(&p[0] + 1));  //4
}

int main()
{
    const char* p = "abcdef";
    printf("%d\n", strlen(p));  //6
    printf("%d\n", strlen(p + 1));  //5
    //printf("%d\n", strlen(*p));  //err
    //printf("%d\n", strlen(p[0]));  //err
    printf("%d\n", strlen((char*)&p));  //随机值
    printf("%d\n", strlen((char*)(&p + 1)));  //随机值,与前面没有关系,可能p的4个字节里就有了\0
    printf("%d\n", strlen(&p[0] + 1));  //5
}

二维数组
int main()
{
    int a[3][4] = { 0 };
    printf("%d\n", sizeof(a));  //48 - sizeof(数组名)表示整个数组 - 4*(3*4)=48
    printf("%d\n", sizeof(a[0][0]));  //4 - 第一行第一个元素
    printf("%d\n", sizeof(a[0]));  //16 - a[0]可以理解为第一行的数组名a[0]:a[0][i]是每个元素
                                   //因此sizeof(a[0])单独放在sizeof内部,表示的是整个第一行数组,第一行大小4*4=16
    printf("%d\n", sizeof(a[0]+1));  //4 - a[0]作为数组名并没有单独放进sizeof内部,也没有取地址
                                     //因此a[0]代表首元素a[0][0]的地址,a[0]+1就是a[0][1]的地址
    printf("%d\n", sizeof(*(a[0]+1)));  //4 - a[0]+1是第一行第二个元素的地址*(&a[1][1])
    printf("%d\n", sizeof(a+1));  //4 - a作为二维数组的数组名,并没有取地址,也没有单独放在sizeof内部
                                  //所以a代表二维数组首元素的地址,即第一行。a+1就是二维数组第二行的地址,即&a[1]
    printf("%d\n", sizeof(*(a+1)));  //16 - a+1是第二行地址,所以*(a+1)表示第二行,所以计算的是第二行的大小
                                     //*(a+1) <--> a[1],sizeof(a[1])计算的是第二行的大小
    printf("%d\n", sizeof(&a+1));  //4 - 跳过整个二维数组之后的地址
    printf("%d\n", sizeof(&a[0] + 1));  //4 - a[0]是第一行的数组名,&a[0]取出的是整个第一行的地址
                                       //&a[0]+1就是表示第二行a[1]的地址,即&a[1]
    printf("%d\n", sizeof(*(&a[0]+1)));  //16 - &a[0]+1就是第二行的地址,因此就代表整个第二行,即*(&a[1])
    printf("%d\n", sizeof(*a));  //16 - a作为二维数组名没有取地址&,没有单独放在sizeof内部,a就是首元素的地址,即第一行的地址
                                 //因此*a等价于*(&a[0]),因此sizeof(a[0]),数组名单独放在sizeof内部,计算的是整个数组的大小
    printf("%d\n", sizeof(a[3]));  //16 - a[3]其实是第四行的数组名(如果有的话)
                                   //虽然第四行不存在,但是每个表达式有值属性和类型属性,a[3]的类型是int[4],其实可以根据类型就可以算出大小
                                   //注意:sizeof()内部的表达式是不算的! 不真正访问第四行
    printf("%d\n", sizeof(a[-1]));
    return 0;
}

int main()
{
    short s = 5;
    int a = 4;
    printf("%d\n", sizeof(s = a + 6));  //2
    //sizeof内部表达式不计算,只是模拟如果int类型的a+6真的放到s里面去的话结果是short类型
    printf("%d\n", s);  //5
    return 0;
}
int main()
{
    int a = 0;
    printf("%d\n", sizeof(printf("%d\n", a++)));
sizeof(函数)根据函数返回类型确定大小
    printf("%d\n", a);
    return 0;
}

二、笔试题

1.
int main()
{
    int a[5] = { 1,2,3,4,5 };
    int* ptr = (int*)(&a + 1);  //从 int(*)[5]类型转换成了int*类型
    printf("%d,%d", *(a + 1), *(ptr - 1));
    return 0;
}

2.
考察的是:指针类型决定了指针的运算!
struct Test
{
    int Num;
    char* pcName;
    short sDate;
    char cha[2];
    short sBa[4];
}*p;
假设p的值为0x100000,如下表达式的值是多少?
已知结构体Test类型的变量大小是20个字节
int main()
{
    printf("%p\n", p + 0x1);
    //0x100000跳过1*20=20个字节
    //0x100014
    printf("%p\n", (unsigned long)p + 0x1);
    //转换为整型,与指针不同,整型+1就是+1.把0x100000+1这个unsigned long类型的值当作地址来打印
    //0x100001
    printf("%p\n", (unsigned int*)p + 0x1);
    //0x100000跳过1*4个字节
    //0x100004
    return 0;
}

3.
int main()
{
    int a[4] = { 1,2,3,4 };
    int* ptr1 = (int*)(&a + 1);
    int* ptr2 = (int*)((int)a + 1);
    //a地址这个数值+1(整数+1就是+1) 例如,0x0012ff44变成0x0012ff45,只差了1个字节
    //小端存储:
    //01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
    //   |ptr2指向这里                    |ptr1-1           |ptr1指向这里
    printf("%x,%x\n", ptr1[-1], *ptr2);
    //*(ptr1-1) -> 00 00 00 04 -> 4
    //*ptr2 - 往后访问4个字节 -> 02 00 00 00 -> 2000000
}


4.
int main()
{
    int a[3][2] = { (0,1),(2,3),(4,5) }; //小括号,说明是逗号表达式
    //a[0] 1,3
    //a[1] 5,0
    //a[2] 0,0
    int* p;
    p = a[0];  //a[0]代表首元素a[0][0]的地址,&a[0][0]
    printf("%d", p[0]);  //*(p+0) -> *(&a[0][0])
    return 0;
}


5.
int main()
{
    int a[5][5];
    int(*p)[4];
    p = (int(*)[4])a;  //a[0]的地址
    printf("%p,%d\n", &p[4][2] - &a[4][2],&p[4][2] - &a[4][2]);
    //*(*(p+4)+2) - *(*(a+3)+3)
    //4*4+3-(5*4+3) = -4
    //10000000000000000000000000000100(原码) - %d形式打印的是肉眼可见的原码
    //11111111111111111111111111111011(反码)
    //11111111111111111111111111111100(补码) - %p打印的话把他当作地址,得到这个补码
    //FFFFFFFC

    //a的元素(连续存储)
    //a[0][0] a[0][1] a[0][2] a[0][3] a[0][4]
    //a[1][0] a[1][1] a[1][2] a[1][3] a[1][4]
    //a[2][0] a[2][1] a[2][2] a[2][3] a[2][4]
    //a[3][0] a[3][1] a[3][2] a[3][3] a[3][4]
    //a[4][0] a[4][1] a[4][2] a[4][3] a[4][4]

    //p的元素
    //a[0][0] a[0][1] a[0][2] a[0][3] 
    //a[0][4] a[1][0] a[1][1] a[1][2]
    //a[1][3] a[1][4] a[2][0] a[2][1]
    //a[2][2] a[2][3] a[2][4] a[3][0]
    //a[3][1] a[3][2] a[3][3] a[3][4]
    //a[4][0] a[4][1] a[4][2] a[4][3]
    //a[4][4]
    return 0;
}


6.
int main()
{
    int aa[2][5] = { 1,2,3,4,5,6,7,8,9,10 };
    //1,2,3,4,5  6,7,8,9,10
    int* ptr1 = (int*)(&aa + 1);
    int* ptr2 = (int*)(*(aa + 1));
    printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
    //10,5
    return 0;
}


7.
char* p = "abcdef";  //首字符的地址
int main()
{
    const char* a[] = { "work","at","alibaba" };
    //work\0  at\0  alibaba\0
    //|p0     |p1   |p2(p里面存的是字符串首字符的地址)
    const char** pa = a;  //&p0
    pa++;  //&p1
    printf("%s\n", *pa);  //*(&p1) <--> p1 <--> &"at"
    return 0;
}

8.
int main()
{
    const char* c[] = { "ENTER","NEW","POINT","FIRST" };
    //ENTER\0  NEW\0  POINT\0  FIRST\0
    //p1       p2     p3       p4  (相差4个字节)
    //c <--> &p1
    const char** cp[] = { c + 3,c + 2,c + 1,c };
    //&p4   &p3   &p2   &p1
    //pp4   pp3   pp2   pp1  (相差4个字节)
    //cp <--> &(&p4) <--> &(pp4)
    const char*** cpp = cp;

    printf("%s\n", **++cpp);  
    //++cpp后代表&(&p3) <--> &(pp3),指向pp3
    //**(&pp3) <--> *(pp3) <--> *(&p3) <-->  p3
    //POINT
    printf("%s\n", *--*++cpp+3);
    //*(--(*++cpp))+3
    //++cpp后代表&(pp2),指向pp2
    //*(++cpp)代表了pp2
    //--(*++cpp)代表了pp2--,代表pp1
    //*pp1 <--> p1
    //p1+3 <--> &(ENTER)+3
    //ER
    printf("%s\n", *cpp[-2]+3);
    //*(*(cpp-2))+3
    //cpp-2指向pp4
    //*(cpp-2)代表*(&pp4) <--> pp4
    //*(*(cpp-2)) <--> *(pp4) <--> p4
    //*cpp[-2]+3 <--> p4+3 <--> &(FIRST)+3
    //ST
    printf("%s\n", cpp[-1][-1]+1);
    //*(*(cpp-1)-1)+1
    //cpp-1指向pp3
    //*(cpp-1) <--> *(&pp3) <--> pp3
    //*(cpp-1)-1 <--> pp3-1 <--> &p3-1 <--> &p2
    //*(*(cpp-1)-1) <--> p2
    //*(*(cpp-1)-1)+1 <--> p2+1 <--> &(NEW)+1
    //EW
    return 0;
}

int main()
{
    //申请内存后无法调整大小
    int a = 10;  //4
    int arr[10] = { 0 };  //40

    动态内存开辟
    p = malloc();
    申请:malloc
    使用
    释放:free(p);
  free释放完p指向的空间,p不会主动被置为空指针,需要手动去修改
    p = NULL;
}

int main()
{
    void* p = malloc(40);

    free(p);  //把p开辟的空间释放掉,还给操作系统,但是p存的是这个位置的地址,是野指针
    p = NULL;
}


编程题
某个矩阵,每行从左到右递增,矩阵从上到下递增
编写程序在这样的矩阵中查找某个数字是否存在
要求:时间复杂度小于O(N)
O(N) - 查找的次数最坏的情况下是N次
N*N   N*(N-1)    ---O(N^2)
1/2/5/k次    --O(1)
#define ROW 3
#define COL 3
int main()
{
    int arr[ROW][COL] = { 1,2,3,4,5,6,7,8,9 };  
    int i = 0;
    int j = 0;
    int num = 0;
    int isbigger = 0;
    scanf("%d", &num);
    for (i = 0; i < ROW; i++)   //每行最右侧元素是一行里最大,一列里最小,因此一次查找可以去掉一行或者一列
    {
        if (arr[i][COL - 1] >= num)
        {
            isbigger = 1;
            for (j = COL - 1; j >= 0; j--)
            {
                if (arr[i][j] == num)
                {
                    printf("%d's position is arr[%d][%d].\n", num, i, j);
                    break;
                }
            }
                printf("%d isn't in that arrow.\n", num);
                break;
        }
    }
    if (!isbigger)
    {
        printf("%d isn't in that arrow.\n", num);
    }
    return 0;
}

int find_num(int arr[ROW][COL], int* px, int* py, int key)
{
    int row = *px;
    int col = *py;
    int x = 0;
    int y = col - 1;
    while (x < row && y >= 0)
    {
        if (arr[x][y] < key)
        {
            x++;
        }
        else if (arr[x][y] > key)
        {
            y--;
        }
        else
        {
            *px = x;
            *py = y;
            return 1;  //找到了
        }
    }
    return 0;  //找不到
}
int main()
{
    int arr[ROW][COL] = { 1,2,3,4,5,6,7,8,9 };
    int key = 9;
    int x = 3;
    int y = 3;
    if (find_num(arr, &x, &y, key))
    {  //&x,&y
       //1.传入参数
       //2.带回结果
        printf("找到了,坐标是[%d][%d]\n",x,y);
    }
    else
    {
        printf("找不到\n");
    }
    return 0;
}

编程题:左旋字符串
法一
void string_left_rotate(char* str, int k)
{
    int i = 0;
    char copy = 0;
    char* move = str;
    for (i = 0; i < k; i++)  //左旋k次
    {
        //1.每次左旋转一个字符(拿出来放到最后)
        copy = *str;
        //2.把后面的n-1个字符往前挪
        move = str;
        while (*move)
        {
            *move = *(move + 1);
            move++;
        }
        //3.copy放最后
        *(move - 1) = copy;
    }
}

法二
A B C D E F  - 2
(AB)(CDEF)
B A F E D C
C D E F A B
void reverse_string(char* left, char* right)
{
    assert(left);
    assert(right);
    while (left < right)
    {
        char tmp = *left;
        *left = *right;
        *right = tmp;
        left++;
        right--;
    }
}
void string_left_rotate(char* str, int k)
{
    int len = strlen(str);
    //三步翻转法
    //1.左边逆序
    reverse_string(str, str + k - 1);
    //2.右边逆序
    reverse_string(str + k, str + len - 1);
    //3.右边逆序
    reverse_string(str, str + len - 1);
}

int main()
{
    char ch[] = "ABCDEF";
    int k = 10;
    string_left_rotate(ch, k%strlen(ch));
    puts(ch);
    return 0;
}

编程题:判断一个字符串是不是另一个字符串的旋转结果
法一
int is_rotate(char* string1, char* string2)
{
    char* start = string1;
    char* compare_1 = string1;  //用来比较的
    char* compare_2 = string2;  //用来比较的
    int flag = 0;
    while (*string1)
    {
        if (*string1 == *string2)
        {
            flag = 1;
            compare_1 = string1;
            compare_2 = string2;
            while (*compare_1)
            {
                if (*compare_1++ != *compare_2++)
                {
                    flag = 0;
                }
            }
            compare_1 = start;
            {
                while (*compare_2)
                {
                    if (*compare_1++ != *compare_2++)
                    {
                        flag = 0;
                    }
                }
            }
        }
        if (flag)
            return 1;
        string1++;
    }
    return 0;
}
int main()
{
    char string1[] = "ABCDEF";
    char string2[] = "EFABCD";
    if (is_rotate(string2, string1))
    {
        printf("%s is the rotation of %s", string2, string1);
    }
    else
    {
        printf("%s is not the rotation of %s", string2, string1);
    }
    return 0;
}

法二
void reverse_string(char* left, char* right)
{
    assert(left);
    assert(right);
    while (left < right)
    {
        char tmp = *left;
        *left = *right;
        *right = tmp;
        left++;
        right--;
    }
}
int is_rotate(char* str1 , const char* str2 , int n)
{
    int i = 0;
    char copy = 0;
    char* move = str1;
    for (i = 0; i < n; i++)  //左旋n次,每次左旋一个字符,看是否能够相等
    {
        //1.每次左旋转一个字符(拿出来放到最后)
        copy = *str1;
        //2.把后面的n-1个字符往前挪
        move = str1;
        while (*move)
        {
            *move = *(move + 1);
            move++;
        }
        //3.copy放最后
        *(move - 1) = copy;
        if (strcmp(str1, str2) == 0)
        {
            return 1;
        }
    }
    return 0;
}

int main()
{
    char string1[] = "EFABCD";
    char string2[] = "ABCDEF";
    char copy_string1[1000];
    strcpy(copy_string1, string1);
    if (is_rotate(copy_string1 , string2 , strlen(string2)))
    {
        printf("%s is the rotation of %s", string1, string2);
    }
    else
    {
        printf("%s is not the rotation of %s", string1, string2);
    }
    return 0;
}

法三!!
ABCDA
BCDAA
CDAAB
DAABC
AABCD
AABCDAABCD
既要是接长字符串子串,又要长度相同
void my_strcat(char* str1)  //自己后面追加自己
{
    int len = strlen(str1);
    int i = 0;
    for (i = 0; i < len; i++)
    {
        *(str1 + len + i) = *(str1 + i);
    }
}
char* my_strstr(char* str1,const char* str2)
{
    char* compare = str1;
    while (*compare)
    {
        if (*compare == *str2)
        {
            if (strncmp(compare, str2, strlen(str2)) == 0)
            {
                return compare;
            }
        }
        compare++;
    }
}
int is_rotate(char* str1, const char* str2)
{
    //长度不等,一定不是旋转而来的
    if (strlen(str1) != strlen(str2))
    {
        return 0;
    }

    //1.str1字符串的后面追加1个str1
    my_strcat(str1);
    //int len = strlen(str1);
    //strncat(str1, str1, len);

    //2.判断str2是否为str1的字串
    //库函数:strstr,判断str2是不是str1的字串,如果是字串则返回这个字串在str1中的位置(地址),否则返回空指针
    //char* ret = strstr(str1, str2);
    char* ret = my_strstr(str1, str2);
    return ret != NULL;  //是空指针就返回0(找不到),不是空指针就返回1
}
int main()
{
    char arr1[1000] = "ABCDE";
    char arr2[500] = "CDEAB";
    if (is_rotate(arr1, arr2))
    {
        printf("YES!");
    }
    else
    {
        printf("NO!");
    }
    return 0;
}

总结:
指针                  数组                  指针运算:
一级指针              一维数组              解引用
二级指针              二维数组              指针+-整数(与类型有关)
                                            指针-指针(同一个数组才有意义,元素个数)
整型指针              整型数组              指针的关系运算
字符指针              字符数组
..                    ..
数组指针              指针数组
函数指针(回调函数)

******************************
      指针进阶完结撒花!
******************************

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值