C语言中的指针(上)

news2024/11/16 7:34:39

目录

一、基本概念

1.变量的存储空间

2.定义指针

3.引用与解引用

二、指针的算术运算、类型以及通用指针

1.指针的算数运算

2.指针类型以及通用型指针

三、指向指针的指针(pointers to pointers)

四、函数传值以及传引用

1.局部变量

2.从存储地址出修改局部变量

五、指针和数组以及数组作为函数参数

1.指针与数组

2.数组作为函数参数

六、指针和字符数组

1.创建一个字符数组

2.字符数组作为函数参数

3.字符数组的存储方式


一、基本概念

1.变量的存储空间

       通常我们所说的内存是计算机的随机内存即RAM,并且不同的数据类型分配到的存储空间的大小也各不相同;整数类型数据 int 分配到的存储空间大小为 4bytes,字符类型的数据 char 分配到的存储空间为 1bytes,浮点数 float 分配到的存储空间为 4bytes。

2.定义指针

      指针的本质就是一个整数类型的变量,用来存放被指向数据的地址,即 int *p = &a; 代表的是创建一个指针 p 用来存储变量 a 的地址,其中 & 代表的是取得数据 a 的地址。

#include<stdio.h>

int main()
{
    int a = 3; 
    int *p = &a;
    printf("%d\n", p); 

    return 0;

}

运行上面的代码得到的结果为:

即数据 a 的存储地址为 e21ff898。

3.引用与解引用

       在定义指针时使用到的 * 表示的是引用,即得到被指向数据的地址,而在指针的前面使用 * 表示的是解引用,用来表示被指向数据的值。

#include<stdio.h>

int main()
{
    int a = 3; 
    int *p = &a;
    printf("%d\n", p);
    printf("%d\n", *p);  //输出p代表的地址的值,即a的值,为3

    return 0;
}

运行上面的代码,前一行打印语句的输出为 a 的地址,由于第二行打印是对指针 p 进行反引用,所以第二行打印语句的输出为 a 的值 3。

二、指针的算术运算、类型以及通用指针

1.指针的算数运算

       指针代表的是被指向数据的地址,因此指针的值加一或者减一,并不会使得被指向数据的值产生相应的加一或者减一的效果,而且指针加一或者减一,则指向的地址进行反引用得到的数据将会是乱码,而不是一个确切的值。

#include<stdio.h>

int main()
{
    int a = 3; 
    int *p = &a;
    
    printf("Address of a is : %d.\n", p);
    printf("size of integer is: %d bytes.\n", sizeof(int));
    printf("value of (p + 1) is : %d.\n", p + 1); //如果p = 2004,则 p+1 = 2008;这是因为整型的地址占用为4个字节
    printf("value at address of p is : %d.\n", *p);
    printf("value at address of (p + 1) is : %d.\n", *(p + 1)); //得到的是一个垃圾值

    return 0;
}

运行上面的代码,得到的结果如下:

从上面的结果中可以看出,由于整型数据的存储大小为4个字节,因此对指针 p 进行加一的操作得到的数据是在原来的 p 的值上加了4,而不是单纯的加一操作。并且对 p+1 进行解引用得到的数值将会是一个垃圾值。

2.指针类型以及通用型指针

       指针是强类型的(这是因为指针不止用来存储地址,也可以用它来解引用那些地址所对应的内容),也就是说不同的数据类型需要相对应类型的指针来进行指向;比如一个整型变量,指针指向的是第一个字节的位置,则计算机读取时向后再数三个字节得到该变量的值,若是使用其它类型的指针,则无法正确完成指向;虽然 float 类型的数据也分配有四个字节,但它和整型的数据并不相同,因此利用整型的指针并没有办法正确存储 float 类型的数据。

#include<stdio.h>

int main()
{
    int a = 3; 
    int *p = &a;
    
    float *p0 = &a;
    printf("While int pointer is used, address of a is : %d, value of a is ; %d;\n", p, *p);//172 = 1010 1100
    printf("While float pointer is used, address of a is : %d, value of a is ; %d;\n\n", p0, *p0); //可以看出两种类型的指针对应的地址是一样的,但值不一样

    return 0;
}

运行上面的代码,得到的结果如下:

可以从结果中看出,对于整数类型的数据,使用浮点数类型的指针会得到错误的指向结果。

       void 类型的指针,也被称为通用型指针,由于通用指针没有使用到任何类型的定义,因此不能对其进行反解。

#include<stdio.h>

int main()
{
    int a = 3; 
    int *p = &a;
    
    void *p0 = p; //通用指针没有使用到任何类型的定义,因此不能对其进行反解
    printf("While int pointer is used, address of a is : %d, value of a is ; %d;\n", p, *p);//172 = 1010 1100
    printf("While void pointer is used, address of a is : %d, value of a is ; %d;\n\n", p0, *p0); //可以看出两种类型的指针对应的地址是一样的,但值不一样

    return 0;
}

运行上面的代码,则会发现程序报错:

通用型指针,不会指向任何类型的地址,故无法进行解引用。

三、指向指针的指针(pointers to pointers)

       指针是用来表示被指向数据的地址的,故指针的本质是一个整型的变量,所以指向指针的指针的数据类型也是整型。

        当定义了一个指针 int *p,则可以用 int **q 来存储指针 p 的地址,也可以使用 int ***r来存储指针 q 的地址,以此类推即可。

#include<stdio.h>

int main()
{

    int a = 3;
    int *p = &a;

    printf("Address of data a is : %d.\n", p);
    int **P = &p;
    int ***q = &P;
    printf("Address of pointer p is : %d.\n", P);
    printf("Value of p(Address of a) is : %d.\n", *P);
    printf("Value of a is : %d.\n", *(*P));
    printf("Address of P is : %d.\n", q);
    printf("Value of P(address of p) is : %d.\n", *q);
    printf("Value of p(address of a) is : %d.\n", *(*q));
    printf("Value of data a is : %d.\n", ***q);
    ***q = 10;
    printf("a = %d\n", a);
    **q = *p +2;
    printf("%d\n", **q);
    
    return 0;
}

运行上述代码得到的结果为:

在上面的代码中使用 p 来作为指针指向数据 a 的地址,P 作为指针来指向指针 p 的地址,q 作为指针来指向指针 P 的地址;通过解引用可以利用指针来改变被指向数据的值。

四、函数传值以及传引用

1.局部变量

       当我在一个函数中声明一个变量,那么该变量称为局部变量,也就是说只能在该函数中对该变量进行修改以及处理。

#include<stdio.h>

int Increment(int a)
{
    a = a + 1;  
}

int main()
{
    int a = 3;

    Increment(a);
    printf("a = %d.\n", a);
    return 0;
}

运行上面的代码,若不考虑自定义函数 Increment() 中的局部变量的影响,则可以推测输出的 a 的值应该为 4。但通过运行代码得到的结果如下:

这说明在自定义函数中 a 是一个局部变量,和主函数中的 a 并不是同一个变量,也就是说在自定义函数中的变量存储的地址和主函数中的变量存储的地址是不一样的,所以我们需要从指针的层面对它们进行修改。

2.从存储地址出修改局部变量

由于局部变量和主函数中的变量的存储地址不相同,因此可以使用指针从变量的地址处进行处理。

#include<stdio.h>

void Increment(int *p)
    {
        *p = (*p) + 1;
    }


int main()
{
    int a = 3;
    int *p = &a;

    Increment(&a);
    printf("a = %d.\n", a);
    return 0;
}

运行上面的代码,可以实现利用自定义函数对主函数中的变量进行修改。

        在自定义函数中的参数被称为形参,主函数中的参数称为实参,自定义函数的调用过程中实现了将实参转化到形参中进行处理的目的,这就是函数的调用。利用指针以及解引用进行处理的时候能够实现从参数的存储地址处修改参数而不是从参数的值处修改参数,从而达到处理数据的目的,这种方法被称为传引用。

五、指针和数组以及数组作为函数参数

1.指针与数组

       定义一个数组后,使用指针指向该数组时,指针存储的是数组中的第一个元素的地址。数组名本身也可以用来代表一个地址,在使用指针时甚至可以不用取地址符,即可以写为 int *p = &A[i]或者 int *p = A + i。

#include<stdio.h>

int main()
{
    int b[] = {0, 1, 2, 3, 4};

    int *P = &b;       //得到的是数组中的第一个元素的地址,在此处甚至可以不用对数组b取地址
    int *P0 = &b[0];   //得到第一个元素的地址,此处也可以写成: int *P0 = b + 1;后面的写法类推
    int *P1 = &b[1];   //得到第二个元素的地址,与第一个元素的地址相差4个字节
    int *P2 = &b[2];   //与前一个元素的地址相差4个字节
    int *P3 = &b[3];   //与前一个元素的地址相差4个字节
    int *P4 = &b[4];   //与前一个元素的地址相差4个字节

    printf("Address of P is : %d.\n", P);
    printf("Address of P0 is : %d.\n", P0);
    printf("Address of P1 is : %d.\n", P1);
    printf("Address of P2 is : %d.\n", P2);
    printf("Address of P3 is : %d.\n", P3);
    printf("Address of P4 is : %d.\n", P4);


    //利用该循环使用两种不同的方法输出数组及地址
    for(int j = 0; j < 5; j++)
    {
        printf("%d:\n", j + 1);
        printf("Address  = %d\n", &b[j]);  //直接取数组元素地址即可
        printf("Value = %d\n", b[j]);      //打印数组元素
        printf("Address = %d\n", b + j);   //数组名可以直接代表元素地址
        printf("Value = %d\n", *(b + j));  //对数组名解引用得到数组元素的值
    }

    P0 = P0 + 1;
    printf("%d\n", *P0);  //输出的值为b[1]的值,即输出1
 
    return 0;
}

运行上面的代码得到的输出为:

       从运行结果中可以看出,数组名可以直接作为地址进行指针指向,且 *p = A + i 和 *p = A[i] 的运行效果是一样的。

2.数组作为函数参数

       当数组作为一个参数时,编译器不会分配整个数组长度的空间用来进行存储,而是只分配出一个同名的指针的存储空间,即将 A 转化成为了 *A,并没有拷贝变量的值,而是直接拷贝了变量的地址,也就是说函数在传递变量的过程就是一个引用的过程。并且不同的操作系统中指针分配到的存储空间是不一样的,64位操作系统中占用了8个字节,32位操作系统中占用了4个字节。

尝试编写函数来计算数组的大小:

int SumofElements(int A[]) 
{
    int i, sum = 0;
    int size = sizeof(A)/sizeof(A[0]); //数组整体字节数目比上一个元素的字节数目
    printf("SOE - Size of A = %d, size of A[0] = %d.\n",sizeof(A), sizeof(A[0]));
    for(i = 0; i < size; i++)
    {
        sum += A[i];
    }
    return sum;
}

       上面这个函数尝试以数组作为函数形参,并在函数中使用 for 循环来计算数组中的各个元素之和,但运行该自定义函数后发现计算结果是错误的。

#include<stdio.h>

int SumofElements(int A[]) 
{
    int i, sum = 0;
    int size = sizeof(A)/sizeof(A[0]); //数组整体字节数目比上一个元素的字节数目
    printf("SOE - Size of A = %d, size of A[0] = %d.\n",sizeof(A), sizeof(A[0]));
    for(i = 0; i < size; i++)
    {
        sum += A[i];
    }
    return sum;
}

int main()
{
     //数组作为函数参数
    int A[] = {1, 2, 3, 4, 5};
    int size = sizeof(A)/sizeof(A[0]); //数组整体字节数目比上一个元素的字节数目
    int total = SumofElements(A);
    printf("Main - Size of A = %d, size of A[0] = %d.\n",sizeof(A), sizeof(A[0]));
    printf("Sum of elements = %d.\n", total);
    return 0;
}

       从运行的结果中可以看出,该函数在计算数组中的元素之和时发生了错误,这是因为在作为函数参数时数组名被默认为是一个指针,而不是整个数组。

对自定义函数进行改写:

#include<stdio.h>

int SumofElements(int A[], int size) 
{
    int i, sum = 0;
    for(i = 0; i < size; i++)
    {
        sum += A[i];
    }
    return sum;
}

int main()
{
    int A[] = {1, 2, 3, 4, 5};
    int size = sizeof(A)/sizeof(A[0]); //数组整体字节数目比上一个元素的字节数目
    int total = SumofElements(A, size);
    printf("Main - Size of A = %d, size of A[0] = %d.\n",sizeof(A), sizeof(A[0]));
    printf("Sum of elements = %d.\n", total);

    return 0;
}

运行得到的结果为:

可以看出该计算结果是正确的。

六、指针和字符数组

1.创建一个字符数组

       在 C 语言中,创建一个字符数组需要的长度一般要比存储在该数组中的元素的个数多一,比如创建一个字符数组其中包含 john 这个字符串,那么可以将该字符数组的长度设置为5或者大于5即可,这是因为在字符数组中存储字符串之后,计算机并不知道字符串中的最后一个字母在哪一位上,故需要在最后一个元素的后面加上一个 NULL('\0')元素,用来指明字符串的结束,也就是说字符数组是以 '\0' 结尾的。

#include<stdio.h>

int main()
{
      char C[4];
      C[0] = 'j';
      C[1] = 'o';
      C[2] = 'h';
      C[3] = 'n';
      
      printf("%s", C);
      return 0;
}

运行上面的代码得到的结果为:

       可以看出,由于没有将字符数组的长度设置为大于字符串长度的值,打印出来的字符串没有一个合适的结尾。因此对代码进行修改:

#include<stdio.h>

int main()
{
      char C[4];
      C[0] = 'j';
      C[1] = 'o';
      C[2] = ‘h';
      C[3] = 'n';
      C[4] = '\0';
      
      printf("%s", C);
      return 0;
}

得到正确的结果为:

       但是无论字符数组的长度设置为多大, 函数 sizeof() 都不会考虑字符串末尾添加的 NULL 元素,也就是说使用该函数得到的数组的长度均为其中的字符串的长度。

       如果不想在定义字符数组的时候添加末尾的 NULL 元素,可以在定义字符数组的时候直接编写好其中需要增加的字符串,这样得到的字符数组进行打印不会出现错误的值。

#include<stdio.h>

int main()
{
    char C[] = "john";
    printf("%s", C);
    return 0; 
}

2.字符数组作为函数参数

       当字符数组作为函数参数时,效果和一般的数组是一样的,也就是说字符数组在函数中也是被传引用,函数中传递的只是该数组的基地址(首个元素的地址),并不会将所有元素的地址在函数中进行传递。

#include<stdio.h>

int print(char *C)
{
    int i = 0;
    while(C[i] != '\0')    //此处注意是单引号
        {
             printf("%c", C[i]); 
             i++;
        }
    printf("\n");
}

int main()
{   
    char C[20] = "HELLO"
    char* A;
    A = C;
    print(A); 
    return 0;
}

 运行上面的代码得到的结果为:

 也可以将上面代码中的自定义函数进行修改,将其中的打印方式修改如下:

printf("%c", *(C + i));

效果和前面的是一样的,因为上面的修改是对数组指针进行了解引用。

还可以对自定义函数进行如下的修改:

int print(char *C)
{
    while(*C != '\0')
        { 
            printf("%c", *C);
            C++;
        }
    printf("\n");
}

得到的结果和前面仍然是一样的。

3.字符数组的存储方式

#include<stdio.h>

int print(char *C)
{
    while(*C != '\0')
        {
            printf("%c", *C);
            C++;
        }
    printf("\n");
}


int main()
{
    char C[20] = "HELLO";

    print(C);
    return 0;
}

上面的这部分代码和之前的一样,运行的时候得到的结果如下:
 而且这部分代码可以在自定义函数中对字符串进行修改:

#include<stdio.h>

int print(char *C)
{
    C[0] = 'A';
    while(*C != '\0')
        {
            printf("%c", *C);
            C++;
        }
    printf("\n");
}


int main()
{
    char C[20] = "HELLO";

    //char *C = "HELLO";
    print(C);
    return 0;
}

 得到的运行结果为:

从结果中可以看出,在自定义函数中实现了对字符串的修改。

主函数中的字符串也可以使用其他的定义方式:

#include<stdio.h>

int print(char *C)
{
    while(*C != '\0')
        {
            printf("%c", *C);
            C++;
        }
    printf("\n");
}


int main()
{

    char *C = "HELLO";
    print(C);
    return 0;
}

运行得到的结果为:

 尝试在自定义函数中对字符串进行修改:

#include<stdio.h>

int print(char *C)
{
    C[0] = 'A';
    while(*C != '\0')
        {
            printf("%c", *C);
            C++;
        }
    printf("\n");
}


int main()
{

    char *C = "HELLO";
    print(C);
    return 0;
}

运行上述代码会发现程序无法输出:

也就是说使用下面的定义字符串的方式时,无法在自定义函数中对字符串进行修改。

这是因为,当使用上面的方式定义字符串时,是直接将整个数组存储在一起,因此可以对其中的一两个元素进行修改;但当使用下面的方式定义字符串时,时将字符串的地址作为变量进行传输,没办法在自定义函数中对其中的某个元素进行修改。

到这里算是总结完指针中的部分内容了,还有一大堆需要学习的东西,真的是非常的棘手。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1231842.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CleanMyMac X4.16免费版mac电脑一键清理电脑垃圾工具

但是&#xff0c;我最近发现随着使用时间的增加&#xff0c;一些奇奇怪怪的文件开始占据有限的磁盘空间&#xff0c;存储空间变得越来越小&#xff0c;系统占用空间越来越大&#xff0c;越来越多的无效文件开始影响我电脑的运行速度。 Mac的文件管理方式和Windows不太一样&…

竞赛 题目:基于深度学习的人脸表情识别 - 卷积神经网络 竞赛项目 代码

文章目录 0 简介1 项目说明2 数据集介绍&#xff1a;3 思路分析及代码实现3.1 数据可视化3.2 数据分离3.3 数据可视化3.4 在pytorch下创建数据集3.4.1 创建data-label对照表3.4.2 重写Dataset类3.4.3 数据集的使用 4 网络模型搭建4.1 训练模型4.2 模型的保存与加载 5 相关源码6…

Linux进程通信——IPC、管道、FIFO的引入

进程间的通信——IPC 进程间通信 (IPC&#xff0c;InterProcess Communication) 是指在不同进程之间传播或交换信息。 IPC的方式通常有管道 (包括无名管道和命名管道) 、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。 …

怎么在echarts图上左右滑动切换数据区间

说在前面 不管前端还是后端&#xff0c;大家或多或少都了解使用过echarts图表吧&#xff0c;很多时候我们只是需要展示指定区间的数据&#xff0c;但有时我们希望在图表上能够轻松地切换数据的展示区间&#xff0c;以便更清晰地观察特定时间段或区域的变化。在本文中&#xff0…

[机缘参悟-119] :反者道之动与阴阳太极

目录 一、阴阳对立、二元对立的规律 1.1 二元对立 1.2 矛盾的对立与统一 二、阴阳互转、阴阳变化、变化无常 》无序变化和有序趋势的规律 三、阴阳合一、佛魔一体、善恶同源 四、看到积极的一面 五、反者道之动 5.1 概述 5.2 "否极泰来" 5.3 “乐极生悲”…

grafana面板介绍

grafana 快速使用 背景 随着公司业务的不断发展&#xff0c;紧接来的是业务种类的增加、服务器数量的增长、网络环境的越发复杂以及发布更加频繁&#xff0c;从而不可避免地带来了线上事故的增多&#xff0c;因此需要对服务器到应用的全方位监控&#xff0c;提前预警&#xf…

面试题c/c++ --STL 算法与数据结构

1.6 STL 模板 模板底层实现&#xff1a;编译器会对函数模板进行两次编译&#xff0c; 在声明的地方对模板代码本身进行编译&#xff0c; 在调用的地方对参数替换后的代码进行编译。 模板传参分析 模板重载 vector 是动态空间&#xff0c; 随着元素的加入&#xff0c; 它的内…

人工智能给我们的生活带来了巨大的影响?

1. 人工智能从哪些方面给我们带来了影响&#xff1f; 人工智能出现&#xff0c;极大地影响了人类的生活&#xff0c;下面是人工智能所影响的领域&#xff1a; 1. 日常生活 智能家居: AI驱动的设备&#xff0c;如智能扬声器、灯光、恒温器&#xff0c;正在改变我们与家居环境的…

.NET 8.0 AOT 教程 和使用 和 .NET ORM 操作

NET AOT编译是一种.NET运行时的编译方式&#xff0c;它与传统的JIT编译方式不同。在传统的JIT编译中&#xff0c;.NET应用程序的代码在运行时才会被编译成本地机器码&#xff0c;而在AOT编译中&#xff0c;代码在运行之前就被提前编译成本地机器码。这样可以在代码运行的时候不…

文件上传漏洞(CVE-2022-23043)

简介 CVE-2022-23043是一个与Zenario CMS 9.2文件上传漏洞相关的安全漏洞。该漏洞被定义为文件的不加限制上传&#xff0c;攻击者可以利用这个漏洞上传webshell以执行任意命令。利用这个漏洞的攻击者暂无特定情况。要利用此漏洞&#xff0c;攻击者首先需要访问Zenario CMS的管…

Java实现拼图小游戏

1、了解拼图游戏基本功能&#xff1a; 拼图游戏内容由若干小图像块组成的&#xff0c;通过鼠标点击图像块上下左右移动&#xff0c;完成图像的拼凑。 2、拼图游戏交互界面设计与开发&#xff1a; 通过创建窗体类、菜单、中间面板和左右面板完成设计拼图的交互界面 &#xff…

vue和uni-app的递归组件排坑

有这样一个数组数据&#xff0c;实际可能有很多级。 tree: [{id: 1,name: 1,children: [{ id: 2, name: 1-1, children: [{id: 7, name: 1-1-1,children: []}]},{ id: 3, name: 1-2 }]},{id: 4,name: 2,children: [{ id: 5, name: 2-1 },{ id: 6, name: 2-2 }]} ]要渲染为下面…

原理Redis-ZipList

ZipList 1) ZipList的组成2) ZipList的连锁更新问题3) 总结 1) ZipList的组成 ZipList 是一种特殊的“双端链表” &#xff0c;由一系列特殊编码的连续内存块组成。可以在任意一端进行压入/弹出操作, 并且该操作的时间复杂度为 O(1)。 ZipListEntry: ZipList 中的Entry并不像…

ClickHouse的 MaterializeMySQL引擎

1 概述 MySQL 的用户群体很大&#xff0c;为了能够增强数据的实时性&#xff0c;很多解决方案会利用 binlog 将数据写入到 ClickHouse。为了能够监听 binlog 事件&#xff0c;我们需要用到类似 canal 这样的第三方中间件&#xff0c;这无疑增加了系统的复杂度。 ClickHouse 20.…

数据结构--字符串的模式匹配

案例导入概念 朴素&#xff08;暴力&#xff09;模式匹配算法 定位操作&#xff1a; 计算时间复杂度 总结

如何使用Docker部署Apache+Superset数据平台并远程访问?

大数据可视化BI分析工具Apache Superset实现公网远程访问 文章目录 大数据可视化BI分析工具Apache Superset实现公网远程访问前言1. 使用Docker部署Apache Superset1.1 第一步安装docker 、docker compose1.2 克隆superset代码到本地并使用docker compose启动 2. 安装cpolar内网…

go语言学习-go环境安装

1、安装Go 1.1 下载安装 go官网 找对应电脑的版本进行安装即可。 点击安装包&#xff0c;直接下一步下一步即可&#xff0c;安装目录可以自行设置一下。 1.2 验证 windows通过cmd验证。 linux或者mac可以通过自带终端执行测试。 2、配置环境变量 2.1 windows 找到系统…

基于安卓android微信小程序美容理发店预约系统app

项目介绍 为美容院设计一个系统以减少员工的工作量就成为了想法的初始状态。紧接着对美容院进行进一步的调查发现我的想法已然落后。基本上每个美容院都以有了自己的信息系统&#xff0c;并且做的已经较完善了。 在这时我突然想到&#xff0c;现在关注美容养生的人越来越多&am…

leetcode数据结构与算法刷题(三)

目录 第一题 交叉链表 思想&#xff1a; 注意点 第一步先求两个链表的长度 第二步 让长的先走&#xff0c;当长短一样时一起走。 犯错点 第二题 判断是有环 思想&#xff1a; 注意 错误分享 第三题&#xff08;重点面试题&#xff09; 思路&#xff1a; 这题面试问题&a…

复杂数据统计与R语言程序设计实验一

1.下载并安装R语言软件&#xff0c;熟悉基本操作的命令及操作界面&#xff0c;掌握软件的使用方法&#xff08;提供学号加姓名的截图&#xff09;。 2.下载并安装Rstudio&#xff0c; &#xff08;提供运行代码及运行结果的截图&#xff09;。 3.下载并安装R包DT&#xff0c;…