06.C(指针)

作者: 任振铭 | 来源:发表于2018-03-30 18:44 被阅读18次

    (创建于2017/8/17)

    指针的含义
    
    #include<stdio.h>
    
    int main() 
    {
        int a = 10;
        int *p = &a;  //这行代码等同于 int *p; p = &a;
    
        //int *p;
        //*p = &a;    //这种方式是错误的,这两行的意思就是定义以一个指针,但是没有
                      //具体的指向,也就是一个野指针,然后给这个野指针指向的内容(*p就是指向的内容的意思)
                      //赋值,也就是将a变量的地址赋值给p指向的内容,但实际上,p没有具体的指向,所以这样是错误的
        
                      //*p //代表指针所指内存的实际数据,切记,指针只能存放地址,不能将一个int
                      //型的变量直接赋值给一个指针,这样是没有意义的 比如 int *p = 100;
        return 0;        
    
    }
    
    指针的步长

    指针运算,不是简单的整数加减法,而是指针指向的数据类型在内存中占用字节数做为倍数的运算

    #include <stdio.h>
    
    int main() {
        int a;
        int *p = &a;
        printf("p: %d,p+1:%d\n",p,p+1);
    
        char b;
        char *q = &b;
        printf("q: %d,q+1:%d\n", q, q + 1);
        system("pause");
    }
    打印
    p: 5962300,p+1:5962304
    q: 5962279,q+1:5962280
    请按任意键继续. . .
    
    #include<stdio.h>
    
    int main() 
    {
        int a = 100000000;
        printf("%d\n",sizeof(a));    //得到4,int占四个字节,也就是说sizeof得到的是字节数
        return 0;        
    }
    
    
    1.取地址符 &
    
    #include<stdio.h>
    
    int main() 
    {
        int a = 190;
        printf("%p\n", &a);    //取出a的地址,%p输出指针的地址
        return 0;
    }
    
    #include<stdio.h>
    
    int main() 
    {
        int a = 190;
        printf("%p\n", &a); 
        int *p = &a;
        printf("%X\n", p);  //a的地址赋值给变量p,将地址格式化为十六进制形式,以大写表示,
        int *p1;            //第一一个变量,名字叫p1,他可以指向一个int类型的地址
        p1=&a;                //指针变量的值一般不能直接赋值一个整数,而是通过取变量地址的方式
    
        return 0;
    }
    
    2.p代表的意思(号,定义指针的时候表示一种类型,使用的时候表示操作指向的内存)
    #include<stdio.h>
    
    int main()  
    {
        int a = 1;
        int *p;
        p = &a;
        int b = *p;          //p指向a的地址,*p的意思是指针变量指向的内容
        printf("%X\n", b);  
    
        *p = 2;                  //通过指针间接修改制定内存地址的值,
        return 0;
    }
    

    通过指针修改了a的值

    #include<stdio.h>
    
    int main() 
    {
        int a = 10;
        int *p;
        p = &a;
        *p = 1;
        printf("%d\n",a);
        return 0;
    }
    

    输出结果a=1

    3.数组中元素地址的特点
    
    int main() 
    {
        char buf[10];
        printf("%u,%u,%u,%u\n", buf, &buf[0], &buf[1], &buf[2]);
        printf("---------------------------------------------------\n");
        int buf1[10];
        printf("%u,%u,%u,%u\n", buf1, &buf1[0], &buf1[1], &buf1[2]);
        return 0;
    }
    

    输出结果:(char每个元素占一个字节,int每个元素占四个字节,可以从地址的连续性上看出来)
    16645708, 16645708, 16645709, 16645710 //可见数组的地址就是数组中第一个元素的地址


    16645660, 16645660, 16645664, 16645668
    请按任意键继续. . .

    4.无类型指针(万能指针)

    void p
    定义一个指针变量,但不指定他指向的哪种数据类型,可以通过强制转换将void
    转换为其他指针类型,也可以用void将其他指针类型转换为void指针类型
    1 不可以定义void类型的普通变量,不能确定类型
    2 可以定义void
    变量,void指针也叫万能指针
    3 void
    可以指向任何类型的变量,使用指针所指向的内存时,最后转换为它本身的指针类型

    #include <stdio.h>
    
    int main() {
        void *p = NULL;
        int a = 10;
        p = &a;
        *((int*)p) = 20;
        printf("*p = %d\n", *((int*)p));
        printf("a = %d\n", a);
        system("pause");
    }
    打印
    *p = 20
    a = 20
    请按任意键继续. . .
    
    
    5.指针的大小

    指针不管指向哪种数据类型,他的大小都是一定的,不过与编译器有关
    32位编译器用32位大小(4字节)保存
    64位编译器用64位大小(8字节)保存

    
    #include<stdio.h>
    
    int main()
    {
        int a = 3;
        char c = 0;
        
        int *p = &a;
        char *p2 = &c;
        printf("%x\n", sizeof(p));
        printf("%x\n", sizeof(p2));
        return 0;
    }
    
    输出结果:
    4
    4
    请按任意键继续. . .
    
    6.空指针与野指针
    1. 只有定义后的变量,此变量的地址才是合法的
    2. 野指针就是保存没有意义地址的指针变量或者干脆没有保存地址
    3. 操作野指针本身不会有任何问题
    4. 操作野指针所指向的内存才导致错误,比如你打印一个随手指定的地址没有错,但是操作这个地址上的内存就会有问题
    #include<stdio.h>
    
    int main() 
    {
        int *p;
        p = NULL;  //空指针,NULL就是数字0
        return 0;
    }
    
    
    #include<stdio.h>
    #pragma warning(disable:4700)  
    int main() 
    {
        int a = 10;
        int *p;                    //p没有具体的指向(或者你随手指定一个地址给他),是野指针(这个无法运行)
        *p = 1;  
        printf("%d\n",a);
        return 0;
    }
    
    7.指针的兼容性

    指针之间赋值比普通数据类型赋值检查更为严格,例如:不能把double赋值给int
    原则上一定是相同类型的指针指向相同类型的变量地址,不能用同一种类型的指针指向另一种类型的变量地址

    #include<stdio.h>
    
    int main() 
    {
        float a = 3.14;
        int i = a;                //自动数据类型转换,舍去小数点部分
        printf("%d\n", i);
        int *p = &a;            //错误,指针类型不兼容,打印结果无厘头
        printf("%d\n",*p);
        return 0;
    }
    输出结果:
    
    3
    1078523331
    请按任意键继续. . .
    
    8.指向常量的指针和指针常量

    指向常量的指针
    const int* p = &a const 修饰,代表指针所指向的内存是只读的,无法通过指针操作这个内存,比如说不能通过p修改内存的值,但是这个地址可以改变,p可以指向另一个变量的内存
    int* const p2 = &a const修饰指针变量,代表指针变量的值为只读,表示指针指向的地址是不能改变的,但是这个地址的值可以改变

    #include <stdio.h>
    int main() {
        int a = 10;
        int b = 10;
        int *p1 = &a; 
        *p1 = 100;   //操作a的内存
        p1 = NULL;   //操作指针变量p,不会影响到a的内存
        printf("a = %d\n", a);
    
        //const 修饰*,代表指针所指向的内存是只读的
        //(等价于int const *p2 = &a,两种写法是相同的)
        //表示p2指向地址的值不能改变,但是这个地址可以改变
        const int *p2 = &a;  
        //*p2 = 200;无法修改内存的值,相当于
        p2 = NULL;
    
        //const修饰指针变量,代表指针变量的值为只读
        //表示p4指向的地址是不能改变的,但是这个地址的值可以改变
        int* const p4 = &a;
        //设置p4指向的地址的值为100
        *p4 = 100;
        //修改p4指向的地址的值,这时错误的
        //p4 = NULL; //err
        system("pause");
    }
    
    #include<stdio.h>
    
    int main() 
    {
        int a = 10;
        const int *p = &a;   
        //*p = 30;                //不能通过*p的方法修改一个const指针,运行会提示错误,
                                //表达式必须是可修改的左值,const修饰则无法修改(有一种方式可以达到修改的目的,这是C语言的一个不够严谨的地方,看下边)
        a = 20;
        printf("%d\n",*p);        //*p是个只读的值,不能修改
        return 0;
    }
    
    #include<stdio.h>
    int main() 
    {
        int a = 100;
        const int *p = &a;
        int *p2 = p;            //(p = &a) 我们用另一个指针指向p,也就是指向了p所指向的a的地址
                                //然后修改*p2的值达到修改a的值的目的(这是C语言的一个不够严谨的地方,c++中是不可以的)
        *p2 = 200;
        printf("%d\n", a);
        return 0;        
    }
    
    

    同样道理

    #include<stdio.h>
    
    int main() 
    {
        const int a = 100;
        //a = 200;错误,无法修改
        int *p = &a;
        //在Windows上修改成功,Mac上xcode无法修改成功,打印的仍是100,并且会有警告
        *p = 200;          
        printf("%d\n", a);
        system("pause");    
    }
    

    指针常量(一旦定义它所指向的地址无法发生改变,但是该地址的值可以改变,这和上边指向常量的指针恰恰相反,指针常量可以修改值不能修改地址,指向常量 的指针可以修改地址,但是不能修改值)

    #include<stdio.h>
    
    int main() 
    {
        int a = 10;
        int b = 20;
        int *const p = &a;    //定义一个常量指针,可以通过常量指针修改或者
        *p = 20;            //读取一个变量的值
        //p = &b;                //常量指针一旦定义了,就不能修改其指向的变量
        printf("%d\n",*p);        
        return 0;
    }
    
    9.指针与数组的关系

    注意,二级指针不是二维数组

    #include <stdio.h>
    int main() {
        int a[3] = {1,2,3};
        //指针数组,是数组,每个元素都是指针
        int *p[3];
    
        int n = sizeof(p) / sizeof(p[0]);
        int i = 0;
        for (; i < n; i++) {
            //等价于p[i]=a+i;
            p[i] = &a[i];
        }
    
        for ( i = 0; i < n; i++)
        {
            //p[i]存储的是地址,所以*p[i]是这个地址的值
            printf("*p[i] = %d,p[i]=%d\n", *p[i],p[i]);
        }
        
        system("pause");
    }
    
    #include <stdio.h>
    int main() {
        int a = 10;
        int *p = &a;
        //通过指针间接操作a的内存
        *p = 111;
        printf("a = %d\n",a);
        printf("*p = %d\n", *p);
        printf("p[0] = %d\n", p[0]);
        //*p等价于*(p+0),同时等价于p[0]
        p[0] = 250;
        printf("a2=%d\n",a);
    
        //等价于*(p+1),操作野指针所指向的内存,可能会报错
        p[1] = 111;
        printf("p[1] = %d\n", p[1]);
        system("pause");
    }
    
    #include<stdio.h>
    
    void printbuf(char buf[])
    {
        for (int i = 0; i < 10; i++)
        {
            printf("buf[%d]=%d\n", i, buf[i]);
        }
    }
    
    int main() 
    {
        char buf[10] = {1,2,3,4,5,6};
        //int n = sizeof(buf)/sizeof(*buf);可以得到数组的长度
        char *p = buf;
        char *p1 = &buf[0];
        char *p2 = &buf[1];
        char *p3 = &buf[2];
    
        *p2 = 7;  //将数组中的第二个值修改为7
    
        p3 += 1;  //数组在内存中的地址是连续的,char数组每个元素一个字节,p3
        *p3 = 100;        //表示buf[2]的地址,加一就是表示buf[3]的地址,然后将buf[3]的值修改为100
        p3 -= 2;
        *p3 = 12;
        printbuf(buf);
        return 0;
    }
    
    打印
    buf[0]=1
    buf[1]=12
    buf[2]=3
    buf[3]=100
    buf[4]=5
    buf[5]=6
    buf[6]=0
    buf[7]=0
    buf[8]=0
    buf[9]=0
    请按任意键继续. . .
    
    #include<stdio.h>
    
    int main() 
    {
        int buf[10] = {1,2,3,4,5,6,7,8,9};
        int *p = buf;      //指向这个数组,也就是指向数组中的第一个元素的地址
        p++;                //p = p +1; 指向了数组中第二个元素
        //buf++;            //这样写是错误的,它等同于buf = buf + 1;但是buf是一个常量,不能做左值
        int *p2 = buf + 1;  //这样是可以的,等同于p2++,p2会指向数组中第二个元素
        //int *p2 = buf++;  //这样也是错误的,同buf++
        return 0;        
    }
    
    #include<stdio.h>
    
    int main() 
    {
        int buf[10] = {1,2,3,4,5,6,7,8,9};
        int *p = buf;      
        int i;
        for ( i = 0; i < 10; i++)
        {
            printf("%d\n",p[i]);    //C语言允许指针通过数组下标的方法访问数组成员
            printf("%d\n", *(p + i)); //等同于上边的代码
        }
        return 0;        
    }
    
    #include<stdio.h>
    
    int main() 
    {
        int a = 100000000;
        char *p = &a;
        printf("%x,%x,%x,%x\n",*p,*(p+1),*(p+2),*(p+3));
        printf("%x,%x,%x,%x\n", p[0], p[1],p[2],p[3]);    //两者等同
        return 0;        
    }
    
    10.指针求字符串的长度和合并字符串
    #include<stdio.h>
    
    int main() 
    {
        char a[100] = "hello";
        char b[100] = "world";
        char *p1 = a;
        char *p2 = b;
        int len = 0;
        while (*p1)          //值=0的时候循环退出,=0说明到达了字符串末尾,因为字符串以0结尾
        {
            p1++;
            len++;
        }
        printf("%d\n", len);
    
        while (*p2)
        {
            *p1 = *p2;            //将p2所在地址的值赋给当前p1所在的位置
            p2++;
            p1++;
        }
        printf("%s\n", a);    //p1指向的是数组a,修改的值是数组a的值
        return 0;
    
    }
    
    
    值传递

    不会改变实参的值

    #include <stdio.h>
    #include <string.h>
    void printbuf(char buf[])
    {
        for (int i = 0; i < 10; i++)
        {
            printf("buf[%d]=%d\n", i, buf[i]);
        }
    }
    
    void swap(int m, int n) {
        int temp;
        temp = m;
        m = n;
        n = temp;
        printf("m=%d,n=%d\n", m, n);
    }
    int main() {
        
        int a = 11;
        int b = 12;
        swap(a, b);
        printf("a=%d,b=%d\n", a, b);
        system("pause");
    }
    
    地址传递

    传递的是地址,会改变实参的值

    #include <stdio.h>
    
    void swap(int* m, int* n) {
        int temp;
        temp = *m;
        *m = *n;
        *n = temp;
        printf("m=%d,n=%d\n", *m, *n);
    }
    int main() {
        
        int a = 11;
        int b = 12;
        swap(&a, &b);
        printf("a=%d,b=%d\n", a, b);
        system("pause");
    }
    
    形参中传递的数组

    1.形参中的数组,是指针变量
    2.形参数组:int a[10000], int a[],int *a,对编译器来讲没有任何区别,都是当作int指针处理
    3.形参中的数组和非形参中的数组区别:形参中数组是指针,非形参数组是数组

    //#define _CRT_SECURE_NO_WARNINGS
    #include <stdio.h>
    #include <string.h>
    void print_array(int *buf, int n)
    {
        for (int i = 0; i < n; i++)
        {
            printf("buf[%d]=%d\n", i, buf[i]);
        }
    }
    
    //形参中的数组不是数组,是指针变量
    void pintf_array2(int buf[10])
    {
        int n = sizeof(buf) / sizeof(buf[0]);
        for (int i = 0; i < n; i++)
        {
            printf("n=%d,sizeof(buf)=%d,sizeof(buf[0])=%d\n", n,sizeof(buf)), sizeof(buf[0]);
            printf("buf[%d]=%d\n", i, buf[i]);
        }
    }
    
    int main() {
        int a[] = { 1,-2,3,-4,5,-6,7,-7,8,-8,9 };
        //在这里这种方式可以得到数组的长度,但是放在上边的位置,通过形参中的数组是获取不到的
        //因为形参中的数组只是一个指针,那么sizeof(buf),buf是指针变量,32位操作系统中,指针变量buf的
        //大小为4,sizeof(buf[0]),buf[0]是int型的变量,也是4,所以sizeof(buf) / sizeof(buf[0])得到的是1并非
        //数组的长度,所以通过指针传递数组操作数组,一定要将数组长度提前得到传递到方法中
        int n = sizeof(a) / sizeof(a[0]);
        pintf_array2(a);
        printf("-----------------------------------------\n");
        print_array(a, n);
        system("pause");
    }
    
    返回局部变量的地址

    下边的代码在visual studio和qt中都可以编译通过,也可以获取到p指向的地址,*p的值也的确是所赋的值,然而实际上,这是有问题的,由于方法执行后销毁掉,a的地址其实已经变成了非法地址,这时候去操作这个地址也是非法的,在Linux 64位gcc执行,无法获取到这个a的地址。这跟编译器有关系

    #include <stdio.h>
    //返回a的地址
    int* fun() {
        int a;
        printf("&a = %p\n", &a);
        return &a;
    }
    
    int main() {
        int *p = NULL;
        //p指向a的地址        
        p = fun();
        //fun方法执行完后,自动销毁a变量,所以此时这个a的地址已经是非法地址了        
        printf("p = %p\n", p);
        *p = 100;
        printf("*p = %d\n", *p);
        system("pause");
    }
    
    返回全局变量的地址
    #include <stdio.h>
    int a;
    int* fun() {
        printf("&a = %p\n", &a);
        return &a;
    }
    
    int main() {
        int *p = NULL;
        p = fun();
        printf("p = %p\n", p);
        *p = 100;
        printf("*p = %d\n", *p);
    
        //直接操作地址,不需要定义中间变量
        printf("*fun = %d\n", *fun());
        system("pause");
    }
    
    打印字符串说明
    #include <stdio.h>
    
    int main() {
        char str[] = {"hello world"};
    
        //以%s为占位符打印一个字符数组(字符串),得到的是这个字符串的内容
        printf("str = %s\n",str);
        //以%p为占位符打印一个字符数组(字符串),得到的是这个字符串的地址
        printf("str = %p\n", str);
        //这样打印是会报错的,因为%s打印字符串就已经做了相应的处理了,不用加*
        //printf("str = %s\n",*str);
        //str表示字符串中第一个字符的地址,*str表示第一个字符,所以用%c打印可以正常输出
        printf("str = %c\n", *str);
    
        //以%s为占位符打印一个字符数组(字符串),其实内部实现就是这样
        int i = 0;
        while (str[i] != '\0') {
            printf("%c", str[i]);
            i++;
        }
        printf("\n");
        system("pause");
    }
    
    打印:
    str = hello world
    str = 0055FB1C
    str = h
    hello world
    请按任意键继续. . .
    
    
    自己实现字符串拷贝函数strcpy
    #include <stdio.h>
    void my_strcpy(char *dst, char* src) {
        int i = 0;
        while (*(src + i) != '\0') {
            *(dst + i) = *(src + i);
            i++;
        }
        *(dst + i) = 0;
    }
    int main() {
        char src[] = "hello world";
        char dst[100];
        char *p = dst;
        my_strcpy(p, src);
        printf("%s\n", p);
        system("pause");
    }
    
    字符串常量

    每个字符串都是一个地址,这个地址是指字符串首元素地址,字符串常量放在data区,文字常量区

    #include <stdio.h>
    
    int main() {
        //每个字符串都是一个地址,这个地址是指字符串首元素地址
        //每个字符串常量放在data区,文字常量区
        
        //直接打印一个字符串,以p为占位符得到的是地址
        printf("s1 = %p\n", "hello");
    
        //因为每个字符串都是代表首地址,所以+1之后就是把地址向后移动了一步,
        //看打印结果,这两行 代码恰恰证明了每个字符串都是地址这个结论
        printf("s1 = %s\n", "hello");
        printf("s1 = %s\n", "hello"+1);
        //文字常量区的字符串,是只读的不能修改
        char *p = "good morning";
        //这里会报错
        *p = "nice";
        printf("*p = %s\n", *p);
        system("pause");
    }
    
    查找匹配字符串出现的位置
    
    #include <stdio.h>
    
    int main() {
        char *p = "11abcd121232abcd23232abcd98y7abcdqqq";
        int i = 0;
        char *temp = NULL;
        while (1) {
            //查找匹配的字符串,如果找到,返回匹配字符串的地址,没有找到返回空
            temp = strstr(p,"abcd");
            if (temp == NULL) {
                break;
            }
            else {
                i++;
                //  找到一个之后,设置这个字符串的后边位置为下一次寻找的起始位置
                p = temp + strlen("abcd");
            }
        }
        printf("个数 = %d\n", i);
        system("pause");
    }
    
    两头堵模式实现拷贝字符串并去除两端的空字符
    #include <stdio.h>
    
    int main() {
        char *p = "       1234567        ";
        //start指针指向第一个元素地址
        char *start = p;
        //end指向最后一个元素地址
        char *end = p + strlen(p) - 1;
    
        //从左到右,找到第一个不是空的字符位置
        while (*start == ' ' && *start != '\0') {
            start++;
        }
        //从右到左,找到第一个不是空的字符位置
        while (*end == ' ' && end != p) {
            end--;
        }
    
        //得到不包含空字符的字符串长度
        int n = end - start + 1;
        printf("n = %d\n", n);
    
        char buf[100] = "aaaaaaaaaaaaaa";
        //拷贝
        strncpy(buf, start, n);
        buf[n] = 0;
        printf("buf = %s\n", buf);
        system("pause");
    }
    
    11.游戏作弊器
    
    __declspec(dllexport)      //添加这个关键字代表go函数是可以在其他程序中调用的dll函数
    void go()
    {
        int *p = 0x1CF88808;    //得到植物大战僵尸中阳关变量的地址,这个地址是
        while (1)                //植物大战僵尸的地址,我自己写的程序没有权利修改其他程序的地址的内容
        {
            if (*p < 200)
            {
                *p = 400;
            }
        }
    }
    
    //操作步骤:目前此方法只针对单机游戏有效
    /*
        1.生成dll动态链接库,项目->属性->配置属性->项目默认值->配置类型 选择动态库(.dll)
        2.在顶部添加关键字__declspec(dllexport)
        3.修改返回值类型为void,函数名随便改个(这一步非必需)
        4.点击本地windows调试器,会在指定目录位置生成.dll文件
        5.需要两个工具辅助a:Cheat Engine 6.3
        用于获取游戏中相应变量的地址b.DllInject.exe用于将dll文件注入到你要修改的游戏中
        7.注入之后游戏修改成功
    */
    
    12.指针的运算
    #include<stdio.h>
    int main()
    {
        int buf[10];
        int *p = buf;                      //注意指针指向数组不需要取地址符,但是指向数组中的某一个元素,需要区地址符
        int *p1 = &buf[1];
        printf("%d,%d\n", p, p1);
        p++;
        printf("%d\n", p);
        return 0;
    }
    

    输出结果:
    15465412,15465416
    15465416
    请按任意键继续. . .

    为什么p++之后得到的地址比原本增加了4呢?
    指针运算不是简单的整数加减法,而是指针指向的数据类型在内存中占用字节数作为倍数的运算
    char *p;
    p++ 移动了sizeof(char)这么多个字节数

    int p;
    p++ 移动了sizeof(int)这么多个字节数
    p+=3 移动了3
    sizeof(int)个字节(注意这个int指的是他本身类型的int)

    如果此时
    p = (int)p+3; 将p强转为一个整数,那么就会按照整数的加法去计算,得到的也就是增加3后的结果,但是这样
    对指针是没有意义的

    #include<stdio.h>
    int main()
    {
        int buf[10];
        int *p = buf;
        int *p1 = &buf[1];
        printf("%d,%d\n", p, p1);
        p++;
        printf("%d\n", p);
    
        int *pp1 = &buf[1];
        int *pp2 = &buf[3];
        printf("%d\n", pp2 - pp1);    //结果是2  两个指针相减得到两个数组元素 的相对距离
    
        char *pp3 = &buf[1];
        char *pp4 = &buf[3];
    
        printf("%d\n", pp4 - pp3);      //结果是8  相差两个int,一个int4个字节,也就是相差八个字节,char类型
                                      //的指针一个char一个字节,那么也就是8个char
        
        long long *pp3 = &buf[1];
        long long *pp4 = &buf[3];    //long long 占八个字节,所以结果是1
    
        short *pp3 = &buf[1];
        short *pp4 = &buf[3];    //short 占2个字节,所以结果是4(本质上就是有几个short)
    
        printf("%d\n", pp4 - pp3);    //不要相加,编译无法通过,没有意义
        return 0;                      
    
    
    }
    
    指针的一些操作

    赋值:int *p = &a;
    求值:int i = *p;
    取指针地址:int **pp = &p; p本身是一个指针
    求差值: p1 -p2 通常用与一个数组内求两个元素的距离
    比较: p1 == p2 通常用来比较两个指针是否指向同一个位置

    13.通过指针求数组中的最大值
    #include<stdio.h>
    int max(int arr[])
    {
        int maxValue = arr[0];
        for (int i = 0; i < 10; i++)
        {
            if (maxValue < arr[i])
            {
                maxValue = arr[i];
            }    
        }
        printf("%d\n", maxValue);
    }
    
    int max2(int *s)
    {
        int maxValue = *s;
        for (int i = 0; i < 10; i++)
        {
            if (maxValue < *(s+i))
            {
                maxValue = *(s+i);
            }
        }
        printf("%d\n", maxValue);
    }
    int main() 
    {
        int arr[100] = {1,34,23,45,767,232,12,23,34};
        max(arr);
        max2(arr);
        return 0;
    }
    
    14.通过指针将数组逆置
    #include<stdio.h>
    
    int reverse(int arr[])
    {
        int start = 0;
        int end = 9;
        while (start < end)
        {
            int temp = arr[start];
            arr[start] = arr[end];
            arr[end] = temp;
            start++;
            end--;
        }
        int i;
        for (i = 0; i <= 9; i++)
        {
            printf("%d\n",arr[i]);
        }
    }
    
    int reverse2(int *arr)
    {
        int *start = &arr[0];
        int *end = &arr[9];
        while (start < end)
        {
            int temp = *start;
            *start = *end;
            *end = temp;
            start++;
            end--;
        }
        int i;
        for (i = 0; i <= 9; i++)
        {
            printf("%d\n", arr[i]);
        }
    }
    int main() 
    {
        int arr[100] = {1,34,23,45,767,232,12,23,34,56};
        reverse2(arr);
        return 0;
    }
    
    15.在不对数组排序的条件下,求数组中第二大元素
    #include<stdio.h>
    
    void s_max(int arr[])
    {
        int max;
        int s_max;
        if (arr[0] > arr[1])
        {
            max = arr[0];
            s_max = arr[1];
        }
        else
        {
            max = arr[1];
            s_max = arr[0];
        }
        int i;
        for (i = 2; i < 10; i++)
        {
            if (max < arr[i])
            {
                s_max = max;
                max = arr[i];
            }
            else if(max > arr[i] && arr[i] > s_max)
            {
                s_max = arr[i];
            }
        }
        printf("%d\n", s_max);
    }
    void s_max2(int *s)
    {
        int max;
        int s_max;
        if (*s > *(s+1))
        {
            max = *s;
            s_max = *(s + 1);
        }
        else
        {
            max = *(s + 1);
            s_max = *s;
        }
        int i;
        for (i = 2; i < 10; i++)
        {
            if (max < *(s+i))
            {
                s_max = max;
                max = *(s + i);
            }
            else if (max > *(s + i) && *(s + i) > s_max)
            {
                s_max = *(s + i);
            }
        }
        printf("%d\n", s_max);
    }
    int main() 
    {
        int arr[100] = {1,34,23,45,767,232,12,23,34,56};
        s_max(arr);
        printf("---------------------------------------\n");
        s_max2(arr);
        return 0;
    }
    
    16.指针实现冒泡排序
    #include<stdio.h>
    
    void bubble(int arr[])
    {
        for (int i = 0; i < 7; i++)
        {
            for (int j = 0; j < 7-i-1; j++)
            {
                if (arr[j] > arr[j + 1])
                {
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
    
        int i;
        for (i = 0; i <7; i++)
        {
            printf("%d\n", arr[i]);
        }
    }
    
    void bubble2(int *s)
    {
        for (int i = 0; i < 7; i++)
        {
            for (int j = 0; j < 7 - i - 1; j++)
            {
                if (*(s+j) > *(s+j+1))    //s指向数组中第一个元素的位置,s+j就是指向数组中第j个元素的地址,*(s+j)就是第j个元素的值
                {
                    int temp = *(s+j);
                    *(s + j) = *(s + j+1);
                    *(s + j + 1) = temp;
                }
            }
        }
    
        int i;
        for (i = 0; i <7; i++)
        {
            printf("%d\n", *(s+i));  //注意括号的位置
        }
    }
    int main() 
    {
        int arr[100] = {1,34,23,45,767,232,12,23,34,56};
        bubble(arr);
        printf("---------------------------------------");
        bubble2(arr);
        return 0;
    }
    
    17.指针实现将英文逆置
    #include<stdio.h>
    
    int reverse(char arr[])
    {
        char *start = &arr[0];
        char *end = &arr[strlen(arr) - 1];
    
        while (start < end)
        {
            char *temp = *start;    
            *start = *end;
            *end = temp;
            start++;
            end--;
        }
    
        printf("%s\n", arr); 
        
    }
    
    int main() 
    {
        char arr[100] = "hello world";
        reverse(arr);
        return 0;
    }
    
    18.指针实现汉字逆置
    #include<stdio.h>
    
    int reverse(char arr[])
    {
        short *start = &arr[0];                  //short刚好占两个字节,在vs中表示汉字正合适
        short *end = &arr[strlen(arr) - 2];  //vs中汉字是gbk编码方式,一个汉字占2个字节
                                      //qt中是utf-8编码方式,每个汉字占3个字节,strlen得到的是字节数,假如有四个汉字,strlen结果在vs中就是8,因此这里减的是2
        while (start < end)
        {
            short *temp = *start;    
            
            *start = *end;
            *end = temp;
            start++;
            end--;
        }
    
        printf("%s\n", arr); 
        
    }
    
    int main() 
    {
        char arr[100] = "你好世界";
        reverse(arr);
        return 0;
    }
    
    19.多级指针
    多级指针图解.png
    #include <stdio.h>
    #include <string.h>
    
    int main() {
        int a = 10;
        int *p = &a;
        int **q = &p;
        int ***t = &q;
        int ****m = &t;
    
        printf("*m = %d\n", *m);
        printf("t = %d\n", t);
    
        printf("**m = %d\n", **m);
        printf("q= %d\n",q);
    
        printf("***m = %d\n", ***m);
        printf("p= %d\n", p);
    
        printf("****m = %d\n", ****m);
        printf("a= %d\n", a);
        system("pause");
    }
    
    打印
    *m = 12385392
    t = 12385392
    **m = 12385404
    q= 12385404
    ***m = 12385416
    p= 12385416
    ****m = 10
    a= 10
    请按任意键继续. . .
    

    从上边的图解中可以看到,首先定义了一个变量a,给他赋值为10,然后定义了一个一级指针p指向他,二级指针q指向p,三级指针t指向q,四级指针m指向t,我们知道,*号用在定义指针的时候表示一种类型,例如int *p = &a 定义一种一级指针类型指向a,在使用的时候表示操作所指向的内存,m指向t所指向的内存,那么*m就是操作m所指向的内存,就是t指向的内存,*m ->t 。那么**m就可以看作*(*m) -> *t,*t同理就是操作t所指向的内存,就是q,那么**m -> q,以此类推

    简单应用:英汉词典

    #define _CRT_SECURE_NO_WARNINGS
    #include<string.h>
    #include<stdio.h>
    #define MAX 3
    
    struct dict {
        char *key;
        char *content;
    };
    
    void get_dict(struct dict **tmp) {
        //在堆区分配MAX个结构体空间
        //把一级指针p取地址传入到tmp,这步操作等价于struct dict **tmp = &p;
        //tmp是个中间变量,我们是要将p指向申请到的空间,*tmp表示的就是
        //指针变量p,所以*tmp = xxx就相当于p = xxx
        *tmp = (struct dict *)malloc(MAX * sizeof(struct dict));
        if (tmp == NULL) {
            return;
        }
    
        //上边我们申请了MAX个结构体空间,接下来要在这个空间中申请局部的
        //空间来存储每个结构体中的变脸key content
    
        //第一个结构体放第一个单词(用.实现)
        //(*tmp)[0]表示第一个结构体变量,strlen("a") + 1表示求字母a的长度,+1是因为后边需要\0
    
        //key申请空间
        (*tmp)[0].key = (char *)malloc(strlen("a") + 1);
        //存值
        strcpy((*tmp)[0].key,"a");
    
        //content申请空间
        (*tmp)[0].content = (char *)malloc(strlen("字母A")+1);
        //存值
        strcpy((*tmp)[0].content,"字母A");
    
        //第二个结构体存放第二个单词(用->实现)
        (*tmp + 1)->key = (char *)malloc(strlen("b") + 1);
        strcpy((*tmp + 1)->key, "b");
    
        (*tmp + 1)->content = (char *)malloc(strlen("字母B") + 1);
        strcpy((*tmp + 1)->content, "字母B");
    
        //第三个结构体存放第三个单词
        (*tmp + 2)->key = (char *)malloc(strlen("c") + 1);
        strcpy((*tmp + 2)->key, "c");
    
        (*tmp + 2)->content = (char*)malloc(strlen("字母C") + 1);
        strcpy((*tmp + 2)->content, "字母C");
    }
    
    //把p传递到方法中,相当于操作struct dict *tmp = p
    //此时等于把p指向的内容赋值给了tmp,tmp指针同样也指向
    //p所指向的空间了,那么就可以通过tmp取读取空间中的内容
    void print_dict(struct dict *tmp) {
        int i = 0;
        for (; i < MAX; i++) {
            printf("[%s]%s\n", (tmp+i)->key, (tmp + i)->content);
        }
    }
    void free_dice(struct dict *tmp) {
        //先释放成员变量,再释放结构体指针变量
        int i = 0;
        for (i = 0; i < MAX; i++) {
            if ((tmp + i)->key != NULL) {
                free((tmp + i)->key);
                (tmp + i)->key = NULL;
            }
            if ((tmp + i)->content != NULL) {
                free((tmp + i)->content);
                (tmp + i)->content = NULL;
            }
        }
    }
    
    int search_dict(struct dict *tmp, char *key, char *content) {
        int i = 0;
        for (i = 0; i < MAX; i++) {
            if (strcmp(tmp[i].key,key) == 0)
            {
                strcpy(content, tmp[i].content);
                return 1;
            }
        }
        return 0;
    }
    int main(int argc, char *argv[]) {
        //一个结构体存储一个单词
        struct dict *p = NULL;
    
        //给单词p分配空间,然后设置存储内容
        get_dict(&p);
    
        //遍历单词,上边分配空间时将p的地址传入,是为了修改p
        //指向的内容,而下边是为了读取,不需要修改,所以直接传递p
        print_dict(p);
    
        //查询单词解释
        char key[1024];
        char content[1024];
    
        while (1) {
            printf("输入需要查询的单词:");
            //注意这里使用fgets存在的问题,fgets接收到字符串保存到key
            //中后,会在字符串末尾加上一个换行符\n,所以直接这样进行
            //strcmp(key,"break")等比较的操作是行不通的,因为他多了个换行符
            //除非这样比较strcmp(key,"break\n")
            //fgets(key, sizeof(key), stdin);
            scanf("%s", key);
            if (strcmp(key,"break")==0)
            {
                break;
            }
            int flag = search_dict(p,key, content);
            if (flag == 0)
            {
                printf("查询不到次单词%s\n", key);
            }
            else {
                printf("%s 的解释为:%s\n",key, content);
            }
        }
    
        //释放空间
        void free_dice(p);
        system("pause");
    }
    
    二维数组和指针的关系
    二维数组与指针.png

    相关文章

      网友评论

        本文标题:06.C(指针)

        本文链接:https://www.haomeiwen.com/subject/mvnicftx.html