zoukankan      html  css  js  c++  java
  • C语言学习(7)

    一、C语言指针

    1. 一维数组和指针的关系

      第一种: int类型指针和int类型数组之间的关系

      第二种: char类型指针和char类型数组之间的关系

      规则: C语言规定,数组名出现在表达式中,代表该数组首元素的地址(数组名就是个指针,指向首元素的指针)

          C语言规定,数组名取地址出现在表达式中,代表指向整个数组的一个指针,称作数组指针

    #include <stdio.h>
    
    int main()
    {
        //int a[10];
        char a[22];
        //doube a[8];
        printf("a的地址:%p
    ", a);
        printf("a+1的地址:%p
    ", a + 1);
        printf("&a的地址:%p
    ", &a);       //int (*)[10]
        printf("&a+1的地址:%p
    ", &a + 1); //增加40字节
        printf("&a[0]的地址:%p
    ", &a[0]);
        printf("&a[0]+1的地址:%p
    ", &a[0] + 1);
    }

      总结: 一维数组和指针之间的关系有如下几种常见写法

      写法一: char buf[10]="hello";

           char *p=buf;

          *(p+i); //i -->0到strlen(buf)

      写法二: char buf[10]="hello";

           char *p=buf;

           p[i]; //i -->0到strlen(buf)

    #include <stdio.h>
    /*
        %s -->要求后面是字符串的起始位置(指针)
        %c -->要求是普通变量的名字即可(不要求是指针)
        %d -->要求是普通变量的名字即可(不要求是指针)
    */
    int main()
    {
        char buf[10] = "hello";
        int otherbuf[10] = {78, 56};
        char *p = buf; //&buf[0]
        printf("%s
    ", buf);
        printf("%s
    ", &buf[0]);
        printf("%s
    ", p); //%s就是通过首地址,自动把整个字符串完整输出
        printf("%s
    ", &buf[2]);
    
        printf("%c
    ", buf[0]);
        printf("%c
    ", *p);
        int *q = otherbuf; //&otherbuf[0]
    }

    2. 野指针

      目前为止,C语言保持多个相同类型的数据有如下几种方法:

      方法一:数组

      方法二:指针

      定义了指针,但是这个指针没有明确的指向,是不能使用的(很危险,容易产生段错误)

      #include <stdlib.h>

      void *malloc(size_t size); //给指针分配size个字节的内存空间,不会自动把内存空间清零

      char *p=malloc(20); //给指针p分配20字节的内存空间

      int *p=malloc(20); //给指针p分配20字节的内存空间

      void free(void *ptr); //把之前操作系统分配给你内存空间释放(把内存还给操作系统)

      free(p);

      void *calloc(size_t nmemb, size_t size); //分配内存空间同时会自动把内存空间清零

      char *p=calloc(5,10); //申请分配5块内存空间,每一块的大小是10个字节,总共分配5*10=50字节的空间

      int *p=calloc(10,4); //10块内存空间,每一块大小是4字节

      void *realloc(void *ptr, size_t size); //重新分配内存空间,改变之前分配的内存空间的大小

      realloc(p,100); //重新给p申请100个字节的存储空间

    注意:重新分配的内存首地址可能跟原来的地址一样,也可能不一样

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    int main()
    {
        int i;
        //用法一:char类型指针指向单个字符的地址
        char a = '@';
        char *p = &a; //定义指针p指向a的首地址
    
        //用法二:char类型指针指向一个字符串的首地址(实际开发中用的最多)
        //第一种: 定义指针q,q指向字符串常量hello的起始地址
        //char *q="hello";
        //printf("q指向的字符串是:%s
    ",q);
        //printf("q指向的是字符h在内存中的地址:%c
    ",*q);
    
        //第二种: 定义指针q,指向char数组的首地址
        //char buf[10]="world";
        //char *q=buf;  //数组名buf就是个指针,等价于 &buf[0]
        //char *q=&buf[0];
        //printf("q指向的字符串是:%s
    ",q);
        //printf("q指向的是字符串的起始地址:%c
    ",*q);
        //传统做法,遍历数组通过下标来实现
        //有了指针之后,通过指针来遍历访问数组成员了
        //写法一:
        //for(i=0; i<strlen(buf); i++)
        //{
        //printf("%c
    ",*(q+i));  //利用了指针的加法
        //}
        //写法二:
        //while((*q)!='')
        //{
        //printf("*q is:%c
    ",*q);
        //q++;
        //}
    
        //第三种: 定义指针,没有分配内存空间,就直接使用指针
        //char *q; //q指向哪里不清楚,随机的,是个野指针
        char *q = malloc(20); //分配20字节的空间给指针q
        printf("q指向的地址是:%p
    ", q);
    
        printf("请输入一个字符串!
    ");
        scanf("%s", q); //很危险,万一q后面的地址非法,就完蛋了
        printf("你输入的字符串是:%s
    ", q);
        //释放刚才申请的内存空间
        free(q);
        return 0;
    }
    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
        int i;
        int *p = malloc(16); //16个字节空间,不会自动清零
        *p = 56;
        *(p + 1) = 96;
    
        int *q = calloc(4, 4); //4块内存空间,每一块4字节,会自动清零
        for (i = 0; i < 4; i++)
        {
            printf("*(p+%d) is:%d
    ", i, *(p + i));
            printf("*(q+%d) is:%d
    ", i, *(q + i));
        }
    
        //发现p指向的16字节空间不够用,想重新分配多一点
        p = realloc(p, 160);
        printf("原本存放在p里面的内容也复制过来:%d
    ", *p);
        printf("原本存放在p里面的内容也复制过来:%d
    ", *(p + 1));
    
        //发现q指向的16字节空间多了,想重新分配少一点
        q = realloc(q, 4);
    
        //释放
        free(p);
        free(q);
    }

    3. 指针的加法和减法运算

      指针的加法运算:(指针加上一个整数)

      两个指针直接相加 --》没有任何实际意义,面试题也不会这么写

      char buf[10];

      char *p=buf; //指向首地址

      p+1; // 初学者有错误的认识,把地址当成是数字+1

      总结:指针做加法运算,加的是类型的大小

      指针的减法运算:(指针减去一个整数)

      两个指针直接相减---》只能是在数组里面两个指针相减有意义

      计算公式: (p-q)结果是: (p-q)/指针类型的大小

      总结:指针做减法运算,减的是类型的大小

    #include <stdio.h>
    /*
        指针的加法运算,加的是指针类型的大小
    */
    int main()
    {
        int a = 9;
        double b = 78;
        char c = 'u';
    
        int *p1 = &a;
        double *p2 = &b;
        char *p3 = &c;
    
        printf("p1保存的地址:%p
    ", p1);   //int -->4字节
        printf("p1+1的地址:%p
    ", p1 + 1); //+4个字节
    
        printf("p2保存的地址:%p
    ", p2);   //double -->8字节
        printf("p2+1的地址:%p
    ", p2 + 2); //+16字节
    
        printf("p3保存的地址:%p
    ", p3);   //char -->1字节
        printf("p3+1的地址:%p
    ", p3 + 1); //+1字节
    }

    4. 空指针和void类型的指针

      空指针: 在C语言中用NULL表示,在系统中NULL就是0x0000 0000地址

      用法一:判断函数的返回值(返回指针),是否正确

      void *malloc(size_t size);

      char *p=malloc(10); //p可以存放10个字符

      if(p==NULL) //说明malloc申请10个字节空间失败了

      else //说明申请成功

      int *q=malloc(20); //q可以存放5个整数

      用法二:定义一个指针的时候,先让它指向NULL,后面改变指向

      int *p=NULL; //p是个野指针,p由系统随机指向

      p=&a;

      scanf("%d",p)

      void类型的指针: C语言表示通用类型指针

      void类型的指针可以转换C语言其它类型的指针

      void *malloc(size_t size); //void *通用性比较强

      char *p=malloc();

      int *p=malloc();

      double *p=malloc();

    5. 一维数组跟指针有关的几种写法

      int a[5];

      a //指针 int *

      &a //指针,指向整个数组的一个指针(称作数组指针) int (*)[5]

      &a[0] //指针 int *

      a[0] //普通变量,不是指针

    6. 数组指针和指针数组

    数组指针:中心词是指针

      定义: int a[5]; ---》类型 int [5]

      int (*p)[5];

      p=&a;

      char b[9]; --》 类型 char [9]

      char (*p)[9];

      p=&b;

      指针的一般定义:

      公式: 类型 *名字

      int *p;

      char *p;

      数组指针也符合这个公式

      int a[100];

      int (*p)[100]=a; //左右两边指针类型不一致

      数组指针 int类型指针

      int (*p)[100]=&a; //左右两边指针类型一致

      数组指针 数组指针

    #include <stdio.h>
    
    int main()
    {
        /*     int (*p)[5];  //数组指针
        int *q[5];    //指针数组 */
        //最显著的区别--》数组指针,指针名字跟*加了圆括号
    
        //定义int*类型的指针数组
        int n1 = 48;
        int n2 = 89;
        int n3 = 96;
        int *a[5] = {&n1, &n2, &n3};
        //  a[0] --》&n1
    
        printf("n1的地址:%p
    ", a[0]);
        printf("n1的值:%d
    ", *(a[0]));
    
        //定义char*类型的指针数组(存放多个字符串的首地址)
        char buf1[10] = "china";
        char buf2[10] = "hello";
        char buf3[10] = "world";
    
        char *b[3] = {buf1, buf2, buf3};
        printf("%s
    ", b[0]);
    }

    指针数组:中心词是数组

      指针数组:本质上还是数组,只不过这个数组里面存放的全部都是相同类型的指针

      前面学习的数组,里面存放的都是相同类型的普通变量

      数组的一般定义:

      公式: 类型 数组名[数组元素个数]

      int a[10];

      int * b[10]; // 定义了一个最多可以存放10个int *的数组

      char *c[10]; //定义了一个最多可以存放10个char*的数组

    作业:

    1.

    char *p;

    int *p;

    double *p sizeof(p) sizeof(*p) //看看大小是多少,为什么??

    32位的ubuntu系统 ---》全部都是4个字节

    64位的ubuntu系统 ---》全部都是8个字节

    原因: 在64位系统上所有的地址长度都是8字节,而指针里面存放的就是其他变量的首地址,所以指针大小全部都一样

    答:在32位系统计算机中则为4!
        在64位系统计算机中则为8!
    因为sizeof求得是所占的Byte数;
    而p在该系统中内存随机分配,但是在不同系统中所占的位数不一样
    所以说,sizeof(p) p是地址,地址位数在不同的系统中位数一样
    若是sizeof(*p) *p解引用了对应的类型,所以所占的位数也不一样
    char是1位字节
    int是4位字节
    double是8位字节
    View Code
    2.

    char buf[10]=''gecchina";

    char *p=&buf[3];

    p=p+2;

    *p='#';

    printf("buf is:%s ",buf);

    答:最后输出为buf is:gecch#na
    因为一开始*p的地址指向的是该数组的3位上,对应的字符为c,
    后经过指针的加法运算,加多了2位,此时*p的地址指向为
    该数组的5位上,对应的字符为i,经过解引用赋值,
    将该字符替换了#,最后输出为gecch#na
    View Code
    3.

    char buf[5][10]={"hello","world","china"};

    char *p=&buf[1][3]; //world中l的地址

    char *q=&buf[2][1]; //china中h的地址

    printf("q-p is:%d ",q-p);

    总结: 指针相减表示它们之间间隔了多少个数据成员

    答:最后输出为8。
    *p的指针指向该数组的1,3 上,该字符为l
    *q的指针指向该数组的2,1上,该字符为h
    假如该数组的首地址为0,这个数组总共有50位,即0-49;
    则*p指的地址是该数组的12位;
    *q指的地址是该数组的20位;
    输出做了个地址间的减法,则为20-12=8
    View Code
  • 相关阅读:
    ubuntu 安装 Java 开发环境
    mtd-utils 的 使用
    容器技术与虚拟化技术
    Shell之作业控制
    Shell常用语句及结构
    Shell常用命令之read
    Shell之函数
    文件的copy
    类中调用初始化方法
    父类中的方法被覆盖以及子类调用父类覆盖的方法
  • 原文地址:https://www.cnblogs.com/xiaojian98/p/12449457.html
Copyright © 2011-2022 走看看