• 2019年7月10日星期三(C语言)


    一、数组意义与数组定义?

    1、需要同时定义多个相同类型的变量,则考虑使用数组。

    2、数组定义公式:元素数据类型数组名字[元素个数]

    二、数组初始化

    1)定义同时初始化

       -> 元素数据类型 数组名字[元素个数] = {初始化列表,每一个成员之间使用","分开}

    int A[3] = {100,200,300};  //编译通过

    int A[3] = {100,200};      //编译通过

    第三个没有赋值的成员等于0

    int A[3] = {100,200,300,400};  //编译警告

    警告: warning: excess elements in array initializer

    int A[] = {100,200};          //编译通过   决定了下标等于2

    int A[] = {100,200,300};      //编译通过   决定了下标等于3

    int A[] = {100,200,300,400};  //编译通过   决定了下标等于4

    2)先定义,没有初始化

    int a;  -> 局部变量  -> 随机值

    int a;  -> 全局变量  -> 0

    int A[3];  -> 局部变量  -> 全部元素都是随机值

    int A[3];  -> 全局变量  -> 全部元素都是0

    int A[3] = {100,200}; -> 局部变量 -> 最后一个元素是0

    int A[3] = {100,200}; -> 全局变量 -> 最后一个元素是0

     

    3)先定义,后初始化   -> 一般结合循环来完成!

    int A[3];

    A = {100,200,300};  //编译出错

     

    int A[3];

    A[3] = {100,200,300}; //编译出错

     

      小结论:定义数组时没有整体初始化,则之后都不能整体初始化,只能单个初始化。

     

    int A[3];

    A[0] = 100;

    A[1] = 200;

    A[2] = 300;

     

    三、数组的下标

    int A[3];  -> 在内存空间中连续申请12个字节,使用变量A间接访问这片内存空间,在访问数组中,使用下标来对成员进行访问。

    例如:

    int A[N]  -> 下标范围: 0~N-1  

    记住: 最后一个元素是A[N-1],而不是A[N]

     

    四、研究数组的名字含义

    1、当数组名作用于sizeof()时,数组名代表这个数组的内存空间。

    sizeof() -> 计算内存空间的大小

    例子:

    int main()

    {

        int A[3];

        printf("%d ",sizeof(A)); //12

    }

    2、当数组名不作用于sizeof()时,数组名代表数组首元素的地址。

    int A[3];

    数组名: A

    首元素: A[0]

    首元素的地址: &A[0]

      结论: A = &A[0]

    例子:

    int main()

    {

        int A[3] = {100,200,300};

        printf("A[0] = %d ",A[0]);

        printf("&A[0] = %p ",&A[0]); //0xbfec8cb4

        printf("A = %p ",A);  //0xbfec8cb4

        return 0;

    }

    sizeof(粤嵌)  -> 计算整个粤嵌的大小

    粤嵌          -> 仅仅代表整个粤嵌第一层楼的地址

    五、指针的概念

    1、什么是指针?什么是指针变量?指针干什么用?

    指针    指向是内存上地址,例如: 0xbfec8cb4

    指针变量指向是专门用于存放地址的变量   p

    指针是唯一的地址,所以确定申请的内存空间在哪里。

    例子:

    int a;  -> 在内存申请一片内存空间,使用变量a间接访问这片内存

    &a;     -> 获取a变量的地址  &-> 取址符

     

    2、究竟&a获取到地址,存放在哪里?

    指针是一个地址,地址就应该存在指针变量中。

    3、指针变量如何定义?

    指针变量怎样定义取决于指向的内容的数据类型。

    例如: int    ->   int *p

          char   ->    char *p

          double ->    double *p

    定义步骤:

    1)先写一个 *

    2)在*后面写一个指针变量名  *p

    3)确定指向的内容  int a;

    4)把第3步的内容的变量名去掉  int 

    5)把第4步的结果写在第2步结果前面   int *p 

    结果:int *p   -> 指针变量,该变量指向一个整型数据!

    变量定义公式: 数据类型 + 变量名

    变量名:p

    数据类型: int*

    例子:

    int a=100;    -> 在内存申请一片内存空间,使用变量a间接访问这片内存

    &a;           -> 获取a变量的地址  &-> 取址符

    int *p = &a;  -> 将a变量的地址赋值给指针变量p

    4、已知指针变量的值,如何求出该地址指向的内容是什么?

    取地址: 已知变量值a,求地址值。  &a

    解引用: 已知地址值p,求变量值。  *p

    例子:

    int a = 100;    -> 在内存申请一片内存空间,使用变量a间接访问这片内存

    &a;           -> 获取a变量的地址  &-> 取址符

    int *p = &a;  -> 将a变量的地址赋值给指针变量p

    int b = *p;   -> 解引用出p地址指向的值,再赋值给变量b

    ========================例子============================

    #include <stdio.h>

    int main()

    {

        int a = 100;

        int *p = &a;

        printf("a = %d ",a); //100

        printf("&a = %p ",&a); // 0xbfde90c8

        printf("p = %p ",p);  // 0xbfde90c8

        printf("*p = %d ",*p); //100

        return 0;

    }

    =======================================================

       练习1: char b=‘A’ 能不能赋值给int *p ?  -> 不可以,指针类型不对,只能是char *

     

      char b = 'A';

      char *p = &b;

      printf("*p = %c ",*p); // 'A'

      printf("*p = %d ",*p); // 65

    5、指针的内存空间多大?

    指针是一个地址,在linux系统32位中,地址长度都是4字节,所有指针变量都是4字节。

    sizeof(指针变量名) = 4

    #include <stdio.h>

    int main()

    {

        int a = 100;

        char b = 'A';

        int *pa = &a;

        char *pb = &b;

        printf("%d ",sizeof(a));//4

        printf("%d ",sizeof(b));//1

        printf("%d ",sizeof(pa));//4

        printf("%d ",sizeof(pb));//4

        return 0;

    }

    六、野指针与空指针

    1、什么是野指针?

    定义了一个指针变量之后,但是没有进行初始化,指针变量就会赋值一个随机值,指向一个未知区域。

    int a;  -> 随机值

    a = 5;

    int *p; -> 随机值  -> 这时候p就称之为野指针。

    p = &a;

    2、如何解决野指针?

    1)在定义指针初始化指针指向的区域。

    例子:

    int a;

    int *p = &a;

    2)使用空指针 -> NULL

    NULL其实是一个宏定义,真正NULL是等于0,被定义在一个头文件:

    #if defined(__cplusplus)

    #define NULL 0

    #else

    #define NULL ((void *)0)

    #endif

    例子:

    int *p = NULL;

    3)其实空指针只是安全区域中其中一个地址,安全区域地址范围: 0x00000000~0x08048000,只要指针指向该范围,那么指针就不会指向别的区域了。

    int a;

    int *p = (0x00000000~0x08048000);

    p = &a;

    七、如果访问了安全区域的数据,会出现什么情况?

    例子:

    #include <stdio.h>

    int main(int argc,char *argv[])

    {

        int *p = (int *)0x08047000;

        int a = *p;

        printf("p = %p ",p);

        return 0;

    }

    编译: 通过

    执行: 出现段错误  Segmentation fault (core dumped)  -> 非法内存访问。

    1、如何在程序中寻找段错误?

    段错误不是语法错误,所以在编译时不会提示出错,只有等到运行时才会提示出现段错误,但是段错误不会提示在哪一行,可以通过printf()函数来寻找段错误位置,只要发生段错误,那么程序就会马上结束。

    例子:

      printf("11111! ");

      xxxx;

      printf("22222! ");

      yyyy;

      printf("33333! ");

      zzzz;

    执行:

    11111!

    22222!

    Segmentation fault (core dumped)   -> 说明段错误是出现"yyyy;"

    2、找到段错误之后,怎么处理?

    一般段错误都是与指针指向有关,找到段错误准确那一行就打印对应指针的值。

    八、void* 通用类型的指针?

    1void *指向一个什么类型的数据?是指向一个void型变量吗?

    例子:

    void a;  -> 没有void型变量

    void *p = &a;

    void b = *p;

    void *类型指针代表该指针指向任何类型,而且C语言没有void型变量。

    2)把特定类型赋值给void *代表含义?

    int a;        --> 买了一个商场(int)

    int *p = &a;  --> p装者商场的地址  p的数据类型是 商场*(int*)

    void *pa = p; --> p的地址赋值给pa,pa是void *类型,pa可以指向任何类型的数据

     含义:本身p已知指向int类型,赋值给papa还是指向那片区域,但是那片区域是什么类型的数据,就不知道了!

    3)把void *赋值给特定类型含义?

    int a;        --> 买了一个商场(int)

    int *p = &a;  --> p装者商场的地址  p的数据类型是 商场*(int*)

    void *pa = p; --> p的地址赋值给pa,pa是void *类型,pa可以指向任何类型的数据

    int *pb = pa; --> pa指向未知类型,但是赋值给pb之后,由于pb是int*,所以该地址上数据变量int *

     

    4)解引用

    可以解引用特定的类型,不可以解引用void *,如果需要解引用void *,必须先将void *转换到特定的数据类型,再解引用。

    例子:

    int a = 100;

    int *p = &a;

    printf("%d ",*p); //100

    void *pa = p;

    printf("%d ",*pa); //编译出错

    void *pa = p;

    printf("%d ",*(int *)pa); //100

    void *pa = p;

    int *pb = pa;  结论: 把void *赋值给某种特定的类型,其实就是强转为该种类型。

    printf("%d ",*pb); //100

    九、指针的运算

    例子:指针加法

    int a;

    int *pa = &a;

    pa+1   -> 向上移动1个单位,每一个单位等于多少个字节,就要看这个指向的内容占用多少个字节。

    char b;

    char *pb = &b;

    pb+2   -> 向上移动2个单位,每一个单位等于1字节,一共是2个字节。

    例子:指针减法

    int c;

    int *pc = &c;

    pc-pb //编译出错,不同类型之间指针不可以相减。

    pc-pa //编译通过,可以求出两个指针之间相差单位数。

    十、数组的运算

    1、由于数组的名字就是数组首元素地址,一旦申请空间后,该数组的地址就不能改变,所以数组名字是一个常量

    #include <stdio.h>

    int main()

    {

        int A[3];

        printf("A = %p ",A);  //0xbfe396c4

        A = 0xbfe10104;  -> 数组名是一个常量,不能再赋值别的地址给数组名。

        A[0] = 100;

        A[0] = 200;  -> 数组本身是一个变量,可以随时对成员赋值。

        return 0;

    }

    2、数组名字是首元素地址,那么首元素的地址是什么类型?

    int A[3]  -> 每一个成员都是int类型的  -> 首元素也是int类型  -> 首元素的地址就是int *

    例子:

    int A[3] = {100,200,300};

    A    -> 首元素的地址

    A+1  -> 将首元素的地址往上移动1个单位,由于首元素是int类型,往上移动4个字节。

    A+2  -> 将首元素的地址往上移动2个单位,由于首元素是int类型,往上移动8个字节

    *(A+0)  -> 解引用首元素的地址                 -> 首元素      -> A[0]

    *(A+1)  -> 解引用首元素往上移动1个单位的地址   -> 第二个元素  -> A[1]

    *(A+2)  -> 解引用首元素往上移动2个单位的地址   -> 第三个元素  -> A[2]

      结论:数组下标其实首元素往上移动的单位数

             A[n] = *(A+n)   非常非常非常重要!

    3、加法交换律

        A[n] = *(A+n) = *(n+A) = n[A]

    #include <stdio.h>

    int main()

    {

        int A[3] = {100,200,300};

        printf("A[0] = %d ",A[0]);  //100

        printf("*(A+0) = %d ",*(A+0));  //100

        printf("*(0+A) = %d ",*(0+A));  //100

        printf("0[A] = %d ",0[A]);  //100

        return 0;

    }

    十一、复杂指针定义?

    简单指针  -> 指针指向基本的数据类型,例如: int*   char*  double*

    复杂指针  -> 指针指向非基本数据类型,例如: 指针/数组/函数/结构体

    1、指向指针变量的指针  -> 二级指针?

    int a = 100;

    int *pa = &a;

    int **p = &pa;

    例子:

    #include <stdio.h>

    int main()

    {

        int a = 100;

        int *pa = &a;

        int **p = &pa;

        printf("pa = %p ",pa);

        printf("p = %p ",p);

        printf("%p ",*p);

        printf("%d ",**p);

        return 0;

    }

    练习:

    1、设有定义:int a,*pa=&a;以下scanf语句中能正确为变量a读入数据的是( A )   scanf("%d",&a);  pa = &a

    A) scanf(“%d”,pa);        B) scanf(“%d”,a);

    C) scanf(“%d”,&pa);       D) scanf(“%d”,*pa);

    2、若有以下定义和语句

    #include <stdio.h>

    int a=4,b=3,*p,*q,*w;

    p=&a;q=&b;w=p;q=NULL;

    则以下选项中错误的语句是( A )

    A) *q=0;   B) w=p;   C)*p=a;   D) *p=*w;

    3、有以下程序

    main()

    {  

            int a=7,b=8,*p,*q,*r;

            p=&a;q=&b;

            r=p;p=q;q=r;

            printf(“%d,%d,%d,%d ”,*p,*q,a,b);  

    }

    程序运行以后的输出结果是( C )

    A)8,7,8,7      B) 7,8,7,8  

    C)8,7,7,8      D) 7,8,8,7

    4、程序中对fun函数有如下说明

    void *fun();   

    此说明的含义是( C )

    A) fun函数无返回值

    B) fun函数的返回值可以是任意的数据类型

    C) fun函数的返回值是无值型的指针类型

    D) 指针fun指向一个函数,该函数无返回值

    5、有以下程序

    int *f(int *x,int *y)

    {  

        if(*x<*y)   return x;

            else          return y;

    }

    main()

    {  

        int a=7,b=8,*p,*q,*r;

            p=&a;q=&b;

            r=f(p,q);

            printf(“%d,%d,%d ”,*p,*q,*r);

    执行后输出结果是( B )            

    A) 7,8,8   B) 7,8,7   C) 8,7,7   D) 8,7,8

  • 相关阅读:
    在Docker中启动Nacos-Server
    maven配置阿里云公共仓库
    Centos7动态IP改静态后SSH很慢
    Vue+NodeJS的跨域请求Session不同
    一款非常简洁漂亮方便调用的jQuery前端分页
    springmvc后台接收List参数的几种办法
    net use命令详解(转)
    c#开发windows服务
    c# base64转字符串
    关于web api 验证
  • 原文地址:https://www.cnblogs.com/zjlbk/p/11166223.html
Copyright © 2020-2023  润新知