精华内容
下载资源
问答
  • Scala原生 执照 Scala Native是根据Apache许可发行
  • 汇编语言的五大技巧

    2021-06-11 11:54:18
    汇编语言是古老的编程语言,在所有的语言中,它与原生机器语言最为接近。它能直接访问计算机硬件,要求用户了解计算机架构和操作系统。学习汇编最大的用处就是可以帮助我们更好地理解高级语言,因此还是很有必要的...

    (此文来自乐字节)

    编程是门艺术,大多数开发者实际工作中只是开发App,正常来说是不会接触到汇编的,主要有两大原因,一来编译语言不容易学习,二来,日常生活中比较少用到。

    汇编语言是最古老的编程语言,在所有的语言中,它与原生机器语言最为接近。它能直接访问计算机硬件,要求用户了解计算机架构和操作系统。学习汇编最大的用处就是可以帮助我们更好地理解高级语言,因此还是很有必要的,

    本文使用RISC-V 为例来向大家展示,来如何使用编写语言设计程序逻辑,并最终将程序逻辑转换为汇编语言的程序。

    用合适的语言设计逻辑

    这是最难的一步,许多学生想直接编写完整的功能模块的软件包。但是,如果你不喜欢汇编,那么这是一种注定要失败的方法,相反,为了把逻辑从语言中分离出来,我们必须用我们能理解的语言来写。

    在这里插入图片描述

    如果一个学生不懂C语言或一些低级语言,那么我建议他们用伪代码来写。太高级的语言编译困难,而太低级的语言又会讲逻辑设计困难,所以,推荐使用C/C++或其他类似的语言。

    在翻译时,有些编辑器可以把它们并排放在一起,这是很有帮助的。因为在大脑中保留一份指令列表是很困难的,特别是当你在编译一个复杂的程序时。

    一步一个脚印

    许多学生试图从头到尾编写整个程序,而中间没有测试任何内容。如果是初学者,我建议用增量式编程,关键是在完成一部分逻辑时进行测试。这可以像完成一个for循环就进行测试。

    在这里插入图片描述

    测试的一种方法是将C/C++程序与汇编程序连接在一起,你可以通过在C++中创建函数程序集的原型并在两者之间切换来实现这一点。你需要确保两者是不同的,否则链接会出错,按照一般的做法通常会在C函数前面加上一个“c”来区分。我们可以调用Show来运行汇编语言编写的函数:

    extern “C” { // Turn off name mangling void show(int *x); }

    extern " C “将告诉c++函数遵循C的"调用约定”。我们真正关心的是关闭名称修改,这样我们就可以创建一个名为“show”的标签,并拥有我们的函数。

    了解汇编语言的功能定位

    正如我乐字节同事常说的那样:“认清自己的角色”。知道C/ C++为我们做了什么和程序集没有为我们做什么是很重要的。。例如,4 + 3 * 4将自动将运算排序为先执行乘法,再执行加法。然而,在汇编中,我们必须先选择乘法指令,然后再选择加法指令。

    知道如何调用函数

    大多数ISA架构都会附带调用约定手册,比如ARM和RISC-V。这些只是为在所有语言中调用函数制定了一些基本规则。不过幸运的是RISC-V寄存器的 “ABI” 命名规则,有助于程序员理解它们的含义。比如:

    整数参数在寄存器 A0-A7 中,浮点参数在寄存器 FA0-FA7 中

    通过对堆栈指针的 sub 操作去分配函数堆栈。在调用完成后使用 add 操作进行销毁 堆栈大小必须以 8 的整数倍形式分配

    所有参数和临时寄存器必须在函数调用后,被视为销毁态

    在函数调用之后,已保存寄存器才能被显式保存。如果使用了任何已保存的寄存器,则必须在函数返回之前还原它们的原始值

    通过 a0 寄存器做为返回值,将数据返回给调用方。

    .global main

    main:

    addi sp, sp, -8

    sd ra, 0(sp)

    la a0, test_solve

    call solve

    mv a0, zero

    ld ra, 0(sp)

    addi sp, sp, 8

    ret
    你可以从上面的代码中看到,我们首先分配我们的堆栈框架,保存所有需要保存的寄存器,执行,然后在返回之前撤消的所有寄存器。

    文档

    用C或其他语言编写汇编代码会让你为每一行C代码编写多行汇编代码。如果你试图调试程序,这可能会让你有些难度,所以,我总是写C代码作为汇编的注释,然后把它拆开,并展示我做它的每一步。

    你可以从上面的代码中看到,我有原始的C代码(第一个注释),然后对每个片段进行内联注释。这样的方式使我们能够保证程序可以正确地执行每一步。

    PS:自学视频阿里P8强烈推荐的8个Java项目实战大合集B站

    展开全文
  • x86汇编语言简介

    千次阅读 2020-03-07 21:30:44
    汇编语言是最古老编程语言,它是所有语言最接近原生机器语言的。它拥有直接访问计算机硬件特权。 汇编器和链接器 汇编器是一种工具程序,用于将汇编语言符号转换成机器语言的“0101”二进制。链接器同样也是...

    汇编语言是最古老的编程语言,它是所有语言中最接近原生机器语言的。它拥有直接访问计算机硬件的特权。

    汇编器和链接器

    汇编器是一种工具程序,用于将汇编语言符号转换成机器语言的“0101”二进制。链接器同样也是工具程序,通过链接器把汇编器生成的单个文件组成一个可执行程序。

    汇编语言与高级语言的区别

    首先我们要知道机器语言是一种数字语言,处理器被设计成可以专门计算这些数字语言的结构。但是要我们人去识别这些数字是很难的,所以需要一些好记的符号代表这些数字语言的含义。机器语言和汇编语言是一一对应的:每条汇编指令对应一条机器语言指令。

    高级语言和汇编语言是一对多的关系,比如我们常见的一条赋值语句就需要要多条汇编语言来组成实现。

    汇编语言是不可移植的

    一种语言可以在不同的的计算机操作系统中编译运行,我们就称为这种语言是可移植的。我们最常见的C++和Java等高级语言就是可移植的,只要满足一定的条件它们几乎可以在任何计算机下运行。

    汇编语言却是不可移植的,因为这种语言是专门为一种处理器架构所设计的。目前广为人知的不同种类的汇编语言,每一种都是基于一种处理器系列。汇编语言指令会直接与该计算机体系结构进行匹配,或者在执行使用一种被称为伪代码解释器的处理器内置程序来进行转换。

    学习汇编语言可以干什么

    1)编写嵌入式程序。嵌入式程序是指一些存放在专用设备中小容量存储器内的端程序,例如:电话、汽车燃油和点火系统、空调控制系统、安全系统、数据采集一起、显卡、声卡、硬盘驱动器、调制解调器和打印机。

    2)处理仿真和硬件监控的实施应用程序要求精确定时和响应。高级语言不会让程序员对编译器生成的机器代码进行精确控制。汇编语言则允许程序员精确指定程序的可执行代码。

    3)电脑游戏要求软件在减少代码大小和加快执行速度方面进行高度优化。因为汇编语言允许直接访问计算机硬件。所以可以为了提高游戏速度进行手工优化。

    4)有助于形成对计算机硬件、操作系统和应用程序之间交互的全面理解。使用汇编语言可以很方便的检验从计算机体系结构和操作系统资料中获得的理论知识。

    5)一些高级语言对其数据表示进行了抽象,使得它们在执行底层任务时不是很方便。这种情况下,程序员可以调用汇编语言编写的子程序完成它们的任务。

    6)硬件制造商为销售的设备创建设备驱动程序。设备驱动程序是一种把用户操作系统指令转换为对硬件细节的具体引用的程序。谁让驱动需要与硬件打交道,而汇编又有直接访问硬件的权力呢?

    展开全文
  • C大概是大多数程序猿用到的最接近底层的语言了,C语言原生的速度就很高了,BUT别忘了C的手动内存管理,它会让你将性能发挥到极致。 // 单行注释以//开始。(仅适用于C99或更新的版本) /* 多行注释是这个样子...

    源代码下载:learnc.c
    C语言在今天仍然是高性能计算的主要选择。
    C大概是大多数程序猿用到的最接近底层的语言了,C语言原生的速度就很高了,BUT别忘了C的手动内存管理,它会让你将性能发挥到极致。


    // 单行注释以//开始。(仅适用于C99或更新的版本)
    
    /*
    多行注释是这个样子的。(c89也适合)
    */
    
    //常数:#define 关键词
    #define DAYS_IN_YEAR 365
    
    //以枚举的方式定义常数
    enum days{SUN = 1, MON, TUE, WED, THU, FRI, SAT};
    //MON自动被定义为2,TUE被定义为3,以此类推。
    
    //用#include来导入头文件
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    
    //<尖括号>间的文件名是c标准库的头文件。
    //标准库意外的头文件,适用双引号代替尖括号。
    #include"my_header.h"
    
    //函数的签名可以事先在.h文件中定义,也可以直接在.c文件的头部定义。
    void function_1();
    int function_2(void);
    
    //如果函数出现在main()之后,那么必须在main()之前先声明一个函数原型。
    int add_two_ints(int x1, intx2); //函数原型
    //虽然“int add_two_ints(int, int);”也是有效的(不需要名称),BUT建议名称参数在原型更容易检查。
    
    //程序的入口函数调用,主要返回整数类型
    Int main(void){
       //程序
    }
    
    //用于运行程序的命令行参数传递给主函数
    //argc的参数的数量,程序计数为1
    //argv数组的字符数组,它包含参数本身
    //argv[0]=程序的名字,argv[1]=第一个参数,等等。
    int main(int argc, char**argv)
    {
    //打印输出使用printf,"打印格式"
    //%d是一个整数,\n是一个换行符
    printf("%d\n",0); //打印输出0
    }
    

    **类型**
    
    //在使用变量之前我们必须先声明它们。
    //变量在声明是需要指明其类型,而类型能够告诉系统这个变量所占用的空间
    
    //int型变量一般占用4个字节
    int x_int = 0;
    
    //short型变量一般占用2个字节
    short x_short = 0;
    
    //char型变量会占用1个字节
    char x_char = 0;
    char y_char = 'y';  //字符变量的字面值需要使用单引号
    
    //long型(长整型)需要4个字节到8个字节;而long long则至少需要8个字节(64位)
    long x_long = 0;
    long long y_long = 0;
    
    // float一般是用32位表示的浮点数字
    float x_float = 0.0;
    
    // double一般是用64位表示的浮点数字
    double x_double = 0.0;
    
    // 整数类型也可以有无符号的类型表示。这样这些变量就无法表示负数
    // 但是无符号整数所能表示的范围就可以比原来的整数大一些
    
    unsigned short ux_short;
    unsigned int ux_int;
    unsigned long long ux_long_long;
    
    // 单引号内的字符是机器的字符集中的整数。
    '0' // 在ASCII字符集中是48
    'A' // 在ASCII字符集中是65
    
    //sizeof(T)返回变量T的大小
    //sizeof(obj)返回表达式(变量,文字)的大小
    printf("%zu\n", sizeof(int)); // => 4 (大多数的机器字长为4)
    
    // 如果‘sizeof’的参数是一个表达式,那么这个参数不会被演算(VLA例外,见下文)
    // 它产生的值是编译期的常数
    int a = 1;
    // size_t是一个无符号整型,表示对象的尺寸,至少2个字节
    size_t size = sizeof(a++); // a++ 不会被演算
    printf("sizeof(a++) = %zu where a = %d\n", size, a);
    // 打印 "sizeof(a++) = 4 where a = 1" (在32位架构上)
    
    // 数组必须要被初始化为具体的长度
    char my_char_array[20]; // 这个数组占据 1 * 20 = 20 个字节
    int my_int_array[20]; // 这个数组占据 4 * 20 = 80 个字节
                          // (这里我们假设字长为4)
    
    // 可以用下面的方法把数组初始化为0:
    char my_array[20] = {0};
    
    // 索引数组和C语言类似 
    my_array[0]; // => 0
    
    // 数组是可变的,其实就是内存的映射!
    my_array[1] = 2;
    printf("%d\n", my_array[1]); // => 2
    
    // 在C99 (C11中是可选特性),变长数组(VLA)也可以声明长度。
    // 其长度不用是编译期常量。
    printf("Enter the array size: "); // 询问用户数组长度
    char buf[0x100];
    fgets(buf, sizeof buf, stdin);
    
    // stroul 将字符串解析为无符号整数
    size_t size = strtoul(buf, NULL, 10);
    int var_length_array[size]; // 声明VLA
    printf("sizeof array = %zu\n", sizeof var_length_array);
    
    // 上述程序可能的输出为:
    // > Enter the array size: 10
    // > sizeof array = 40
    
    // 字符串就是以 NUL (0x00) 这个字符结尾的字符数组,
    // NUL可以用'\0'来表示.
    // (在字符串字面量中我们不必输入这个字符,编译器会自动添加的)
    char a_string[20] = "This is a string";
    printf("%s\n", a_string); // %s 可以对字符串进行格式化
    /*
    也许你会注意到 a_string 实际上只有16个字节长.
    第17个字节是一个空字符(NUL) 
    而第18, 19 和 20 个字符的值是未定义。
    */
    printf("%d\n", a_string[16]); // => 0
    //  byte #17值为0(18,19,20同样为0)
    
    // 多维数组
    int multi_array[2][5] = {
            {1, 2, 3, 4, 5},
            {6, 7, 8, 9, 0}
        }
    // 获取元素
    int array_int = multi_array[0][2]; // => 3
    

    **操作符**
    
    // 多个变量声明的简写
    int i1 = 1, i2 = 2;
    float f1 = 1.0, f2 = 2.0;
    
    int a, b, c;
    a = b = c = 0;
    
    
    // 算数运算直截了当
    i1 + i2; // => 3
    i2 - i1; // => 1
    i2 * i1; // => 2
    i1 / i2; // => 0 (0.5,但会被化整为 0)
    
    f1 / f2; // => 0.5, 也许会有很小的误差
    // 浮点数和浮点数运算都是近似值
    
    // 取余运算
    11 % 3; // => 2
    
    // 你多半会觉得比较操作符很熟悉, 不过C中没有布尔类型
    // 而是用整形替代
    // (C99中有_Bool或bool。)
    // 0为假, 其他均为真. (比较操作符的返回值总是返回0或1)
    3 == 2; // => 0 (false)
    3 != 2; // => 1 (true)
    3 > 2; // => 1
    3 < 2; // => 0
    2 <= 2; // => 1
    2 >= 2; // => 1
    
    // C不是Python —— 连续比较不合法
    int a = 1;
    // 错误
    int between_0_and_2 = 0 < a < 2;
    // 正确
    int between_0_and_2 = 0 < a && a < 2;
    
    // 逻辑运算符适用于整数
    !3; // => 0 (非)
    !0; // => 1
    1 && 1; // => 1 (且)
    0 && 1; // => 0
    0 || 1; // => 1 (或)
    0 || 0; // => 0
    
    // 条件表达式 ( ? : )
    int a = 5;
    int b = 10;
    int z;
    z = (a > b) ? a : b; //  10 “若a > b返回a,否则返回b。”
    
    // 增、减
    char *s = "iLoveC"
    int j = 0;
    s[j++]; // "i" 返回s的第j项,然后增加j的值。
    j = 0;
    s[++j]; // => "L"  增加j的值,然后返回s的第j项。
    // j-- 和 --j 同理
    
    // 位运算
    ~0x0F;        // => 0xF0 (取反)
    0x0F & 0xF0;  // => 0x00 (和)
    0x0F | 0xF0;  // => 0xFF (或)
    0x04 ^ 0x0F;  // => 0x0B (异或)
    0x01 << 1;    // => 0x02 (左移1位)
    0x02 >> 1;    // => 0x01 (右移1位)
    
    // 对有符号整数进行移位操作要小心 —— 以下未定义:
    // 有符号整数位移至符号位 int a = 1 << 32
    // 左移位一个负数 int a = -1 << 2
    // 移位超过或等于该类型数值的长度
    // int a = 1 << 32; // 假定int32位
    

    **控制结构**
    
    if (0) {
      printf("I am never run\n");
    } else if (0) {
      printf("I am also never run\n");
    } else {
      printf("I print\n");
    }
    
    // While循环
    int ii = 0;
    while (ii < 10) { // 任何非0的值均为真
        printf("%d, ", ii++); // ii++ 在取值过后自增
    } // =>  打印 "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
    
    printf("\n");
    
    int kk = 0;
    do {
        printf("%d, ", kk);
    } while (++kk < 10); // ++kk 先自增,再被取值
    // => 打印 "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
    
    printf("\n");
    
    // For 循环
    int jj;
    for (jj=0; jj < 10; jj++) {
        printf("%d, ", jj);
    } // => 打印 "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
    
    printf("\n");
    
    // *******注意*******:
    // 循环和函数必须有主体部分,如果不需要主体部分:
    int i;
        for (i = 0; i <= 5; i++) {
        ; // 使用分号表达主体(null语句)
    }
    //或者
    for(i = 0; i <= 5; i++);
    
    //多重分支:switch()
    switch(a){
    case 0: //标签需要是整数常量表达式(如枚举体)
     printf("hey, 'a' equals 0!\n");
     break;  //如果没有break,将继续往下执行
    case 1:
     printf("hun, 'a' equals 1!\n");
     break;
    ...
    default:
     //假设'a'不匹配任何标签
     fputs("error!\n", stderr);
     exit(-1);
     break;
    }
    
    //goto
     typedef enum { false, true } bool;
     // c99之前C没有bool类型
     bool disaster = false;
     int i, j;
      for(i=0;i<100;++i)
      for(j=0;j<100;++j)
     {
        if((i + j) >= 150)
            disaster = true;
        if(disaster)
            goto error;
      }
      error :
      printf("Error occurred at i = %d & j = %d.\n", i, j);
    

    **类型转换**
    
    // 在C中每个变量都有类型,你可以将变量的类型进行转换
    // (有一定限制)
    
    // 在C中每个变量都有类型,你可以将变量的类型进行转换
    // (有一定限制)
    
    int x_hex = 0x01; // 可以用16进制字面量赋值
    
    // 在类型转换时,数字本身的值会被保留下来
    printf("%d\n", x_hex); // => 打印 1
    printf("%d\n", (short) x_hex); // => 打印 1
    printf("%d\n", (char) x_hex); // => 打印 1
    
    // 类型转换时可能会造成溢出,而且不会抛出警告
    printf("%d\n", (char) 257); // => 1 (char的最大值为255,假定char为8位长)
    
    // 使用<limits.h>提供的CHAR_MAX、SCHAR_MAX和UCHAR_MAX宏可以确定`char`、`signed_char`和`unisigned char`的最大值。
    
    // 整数型和浮点型可以互相转换
    printf("%f\n", (float)100); // %f 格式化单精度浮点
    printf("%lf\n", (double)100); // %lf 格式化双精度浮点
    printf("%d\n", (char)100.0);
    

    **指针**
    
    // 指针变量是用来储存内存地址的变量
    // 指针变量的声明也会告诉它所指向的数据的类型
    
    int x = 0;
    printf("%p\n", &x); // 用 & 来获取变量的地址
    // (%p 格式化一个类型为 void *的指针)
    // => 打印某个内存地址
    
    // 指针类型在声明中以*开头
    int* px, not_a_pointer; // px是一个指向int型的指针
    px = &x; // 把x的地址保存到px中
    printf("%p\n", (void *)px); // => 输出内存中的某个地址
    printf("%zu, %zu\n", sizeof(px), sizeof(not_a_pointer));
    // => 在64位系统上打印“8, 4”。
    
    // 要得到某个指针指向的内容的值,可以在指针前加一个*来取得(取消引用)
    // 注意: 是的,这可能让人困惑,'*'在用来声明一个指针的同时取消引用它。
    printf("%d\n", *px); // => 输出 0, 即x的值
    
    // 你也可以改变指针所指向的值
    // 此时你需要取消引用上添加括号,因为++比*的优先级更高
    (*px)++; // 把px所指向的值增加1
    printf("%d\n", *px); // => 输出 1
    printf("%d\n", x); // => 输出 1
    
    // 数组是分配一系列连续空间的常用方式
    int x_array[20];
    int xx;
    for (xx=0; xx<20; xx++) {
        x_array[xx] = 20 - xx;
    }   // 初始化 x_array 为 20, 19, 18,... 2, 1
    
    // 声明一个整型的指针,并初始化为指向x_array
    int* x_ptr = x_array;
    // x_ptr现在指向了数组的第一个元素(即整数20). 
    // 这是因为数组通常衰减为指向它们的第一个元素的指针。
    // 例如,当一个数组被传递给一个函数或者绑定到一个指针时,
    //它衰减为(隐式转化为)一个指针。
    // 例外: 当数组是`&`操作符的参数:
    int arr[10];
    int (*ptr_to_arr)[10] = &arr; // &arr的类型不是`int *`!
                                  // 它的类型是指向数组的指针(数组由10个int组成)
    // 或者当数组是字符串字面量(初始化字符数组)
    char arr[] = "foobarbazquirk";
    // 或者当它是`sizeof`或`alignof`操作符的参数时:
    int arr[10];
    int *ptr = arr; // 等价于 int *ptr = &arr[0];
    printf("%zu, %zu\n", sizeof arr, sizeof ptr); // 应该会输出"40, 4"或"40, 8"
    
    // 指针的增减多少是依据它本身的类型而定的
    // (这被称为指针算术)
    printf("%d\n", *(x_ptr + 1)); // => 打印 19
    printf("%d\n", x_array[1]); // => 打印 19
    
    // 你也可以通过标准库函数malloc来实现动态分配
    // 这个函数接受一个代表容量的参数,参数类型为`size_t`
    // 系统一般会从堆区分配指定容量字节大小的空间
    // (在一些系统,例如嵌入式系统中这点不一定成立)
    int *my_ptr = malloc(sizeof(*my_ptr) * 20);
    for (xx=0; xx<20; xx++) {
        *(my_ptr + xx) = 20 - xx; // my_ptr[xx] = 20-xx
    } // 初始化内存为 20, 19, 18, 17... 2, 1 (类型为int)
    
    // 对未分配的内存进行取消引用会产生未定义的结果
    printf("%d\n", *(my_ptr + 21));  // => 谁知道会输出什么
    
    // malloc分配的区域需要手动释放
    // 否则没人能够再次使用这块内存,直到程序结束为止
    free(my_ptr);
    
    
    // 字符串通常是字符数组,但是经常用字符指针表示
    // (它是指向数组的第一个元素的指针)
    // 一个优良的实践是使用`const char *`来引用一个字符串字面量,
    // 因为字符串字面量不应当被修改(即"foo"[0] = 'a'犯了大忌)
    const char* my_str = "This is my very own string";
    printf("%c\n", *my_str); // => 'T'
    
    // 如果字符串是数组,(多半是用字符串字面量初始化的)
    // 情况就不一样了,字符串位于可写的内存中
    char foo[] = "foo";
    foo[0] = 'a'; // 这是合法的,foo现在包含"aoo"
    
    function_1();
    } // main函数结束
    

    **函数**
    
    // 函数声明语法:
    // <返回值类型> <函数名称>(<参数>)
    
    int add_two_ints(int x1, int x2){
        return x1 + x2; // 用return来返回一个值
    }
    
    /*
    函数是按值传递的。当调用一个函数的时候,传递给函数的参数
    是原有值的拷贝(数组除外)。你在函数内对参数所进行的操作
    不会改变该参数原有的值。
    
    但是你可以通过指针来传递引用,这样函数就可以更改值
    
    例子:字符串本身翻转
    */
    
    // 类型为void的函数没有返回值
    void str_reverse(char *str_in){
        char tmp;
        int ii = 0;
        size_t len = strlen(str_in); // `strlen()`` 是C标准库函数
        for(ii = 0; ii < len / 2; ii++){
            tmp = str_in[ii];
            str_in[ii] = str_in[len - ii - 1]; // 从倒数第ii个开始
            str_in[len - ii - 1] = tmp;
        }
    }
    
    /*
    char c[] = "This is a test.";
    str_reverse(c);
    printf("%s\n", c); // => ".tset a si sihT"
    */
    
    // 如果引用函数之外的变量,必须使用extern关键字
    int i = 0;
    void testFunc() {
        extern int i; // 使用外部变量 i
    }
    
    // 使用static确保external变量为源文件私有
    static int i = 0; // 其他使用 testFunc()的文件无法访问变量i
    void testFunc() {
        extern int i;
    }
    //**你同样可以声明函数为static**
    

    **用户自定义类型和结构**
    
    // Typedefs可以创建类型别名
    typedef int my_type;
    my_type my_type_var = 0;
    
    // struct是数据的集合,成员依序分配,按照编写的顺序
    struct rectangle {
        int width;
        int height;
    };
    
    // 一般而言,以下断言不成立:
    // sizeof(struct rectangle) == sizeof(int) + sizeof(int)
    //这是因为structure成员之间可能存在潜在的间隙(为了对齐)
    
    void function_1(){
    
        struct rectangle my_rec;
    
        // 通过 . 来访问结构中的数据
        my_rec.width = 10;
        my_rec.height = 20;
    
        // 你也可以声明指向结构体的指针
        struct rectangle *my_rec_ptr = &my_rec;
    
        // 通过取消引用来改变结构体的成员...
        (*my_rec_ptr).width = 30;
    
        // ... 或者用 -> 操作符作为简写提高可读性
        my_rec_ptr->height = 10; // Same as (*my_rec_ptr).height = 10;
    }
    
    // 你也可以用typedef来给一个结构体起一个别名
    typedef struct rectangle rect;
    
    int area(rect r){
        return r.width * r.height;
    }
    
    // 如果struct较大,你可以通过指针传递,避免复制整个struct。
    int area(const rect *r)
    {
        return r->width * r->height;
    }
    

    **函数指针**
    
    /*
    在运行时,函数本身也被存放到某块内存区域当中
    函数指针就像其他指针一样(不过是存储一个内存地址) 但却可以被用来直接调用函数,并且可以四处传递回调函数
    但是,定义的语法初看令人有些迷惑
    
    例子:通过指针调用str_reverse
    */
    void str_reverse_through_pointer(char *str_in) {
        // 定义一个函数指针 f. 
        void (*f)(char *); // 签名一定要与目标函数相同
        f = &str_reverse; // 将函数的地址在运行时赋给指针
        (*f)(str_in); // 通过指针调用函数
        或 f(str_in); // 等价于这种调用方式
    }
    
    /*
    只要函数签名是正确的,任何时候都能将任何函数赋给某个函数指针
    为了可读性和简洁性,函数指针经常和typedef搭配使用:
    */
    
    typedef void (*my_fnp_type)(char *);
    
    // 实际声明函数指针会这么用:
    // ...
    // my_fnp_type f; 
    
    // 特殊字符
    '\a' // bell
    '\n' // 换行
    '\t' // tab
    '\v' // vertical tab
    '\f' // formfeed
    '\r' // 回车
    '\b' // 退格
    '\0' // null,通常置于字符串的最后。
         //   hello\n\0\. 按照惯例,\0用于标记字符串的末尾。
    '\\' // 反斜杠
    '\?' // 问号
    '\'' // 单引号
    '\"' // 双引号
    '\xhh' // 十六进制数字. 例子: '\xb' = vertical tab
    '\ooo' // 八进制数字. 例子: '\013' = vertical tab
    
    // 打印格式:
    "%d"    // 整数
    "%3d"   // 3位以上整数 (右对齐文本)
    "%s"    // 字符串
    "%f"    // float
    "%ld"   // long
    "%3.2f" // 左3位以上、右2位以上十进制浮
    "%7.4s" // (字符串同样适用)
    "%c"    // 字母
    "%p"    // 指针
    "%x"    // 十六进制
    "%o"    // 八进制
    "%%"    // 打印 %
    

    **演算优先级**
    //-------------------------------------------------------------------------------
    //        操作符                                                         组合          
    //-------------------------------------------------------------------------------
    // ()  [ ]  ->  .                                                     从左到右      
    // !  ~  ++  --  +  =  *(type)sizeof                                  从右到左      
    // *  /   %                                                           从左到右       
    // <  <=  >  >=                                                       从左到右      
    // ==  !=                                                             从左到右      
    //  =  +=  -=  *=  /=  %=  &=  ^=  |=  <<=  >>=                       从右到左    
    

    原文

    展开全文
  • A、混合开发中,最接近原生开发框架; B、性能强大,流畅; C、优秀路由设计; D、优秀动画设计; E、简单易学,Dart语言更具优势; F、跨多种平台,减少开发成本;支持插件,可以访问原生系统调用。 2、...

    1、Flutter优点
    A、混合开发中,最接近原生开发的框架;
    B、性能强大,流畅;
    C、优秀的路由设计;
    D、优秀的动画设计;
    E、简单易学,Dart语言更具优势;
    F、跨多种平台,减少开发成本;支持插件,可以访问原生系统的调用。

    在这里插入图片描述

    2、Flutter缺点
    A、脱离不开原生,开发人员需要具备原生(Android、iOS)基础开发能力;
    B、适配问题,开发工具版本升级后,修改量大;
    C、原生集成第三方SDK后,兼容性适配是个令人头痛的问题;
    D、代码可读性较差,对代码质量和管理要求较高;
    E、Widget的类型难以选择,糟糕的UI控件API;
    F、Flutter packages和Dart packages上第三方sdk繁杂,适配性差,不可乱用;
    G、目前几乎没有第三方开发者平台开发Flutter能力的SDK,需要原生去集成;
    H、打包后,apk/ipa要大很多。

    准备工作

    0.首先你需要准备一个梯子,

    1.一个安卓模拟器

    1.1 如果你又准备使用模拟器又曾经装过windows版的docker 请务必关了 Hyper-V ,还不明白你就你就点击这个

    1.2 如果你关了 Hyper-V 还是蓝屏的话,请打开 Windows Defender — 设备安全性 — 关闭内存完整性

    2.如果你使用真机开发,请打开你的手机 -> 设置 -> 点击版本号 -> 开发者模式

    获取Flutter SDK

    ​ 点击这里跳转到Flutter官网下载SDK,笔者选择了Stable channel (Windows) 也就是 稳定版 ,作为初学者也没有必要使用Beta版本 点击最新的稳定版下载就OK了。要提一句的是:下载的Flutter开发环境的是免安装的,我将其解压到了 D:\flutter下,它最后的地址是D:\flutter\flutter。

    配置环境变量

    在环境变量中的Path中添加路径你的路径\flutter\bin
    如果你梯子猛,你不需要 读3和4
    去用户变量增加PUB_HOSTED_URL 地址为https://pub.flutter-io.cn
    增加变量FLUTTER_STORAGE_BASE_URL地址为https://storage.flutter-io.cn

    展开全文
  • 浅谈PHPCI框架(一)

    2017-02-24 23:57:00
    作为前端开发人员,掌握一门后端语言是必不可少,PHPCI框架是一个快速开发框架,基于MVC,比较接近原生PHP,在原有PHP代码上封装了许多类,易上手,容易扩展,适用于小项目,并且CI文档及案例及其丰富,我用了5天时间...
  • 使用MUI框架构建App请求http接口实例

    万次阅读 热门讨论 2016-01-18 00:57:52
    最近在看DcloudMUI框架,MUI封装了android、ios等多个平台原生接口,达到开发一个项目,采取不同打包方式,生产多种平台app。无可厚非,对于从事java web开发工程师,带来...但是Dcloud官方声称MUI是最接近原生
  • 汇编语言是古老的编程语言,在所有的语言中,它与原生机器语言最为接近。它能直接访问计算机硬件,要求用户了解计算机架构和操作系统。用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号...
  • 但是Dcloud官方声称MUI是最接近原生App体验前端框架,并且在一些低端机上表现也比较理想,想必也做了不少优化处理,另一方面,随着手机市场激烈竞争,手机配置及价格都越来越让人满意,性能问题就愈发可以...
  • flutter入门

    2021-03-11 23:44:28
    A、混合开发中,最接近原生开发框架; B、性能强大,流畅; C、优秀路由设计; D、优秀动画设计; E、简单易学,Dart语言更具优势; F、跨多种平台,减少开发成本;支持插件,可以访问原生系统调用。 2、...
  • Flutter篇 flutter框架优缺点

    千次阅读 2020-12-01 17:21:04
    A、混合开发中,最接近原生开发框架; B、性能强大,流畅; C、优秀路由设计; D、优秀动画设计; E、简单易学,Dart语言更具优势; F、跨多种平台,减少开发成本;支持插件,可以访问原生系统调用。 2、...
  • C 简明教程

    2018-06-07 22:11:00
    C语言是本人接触和学习的第一门编程语言,最早的时候是在大一的C语言...C大概是大多数程序员用到的最接近底层的语言了,C语言原生的速度就很高了,但是别忘了C的手动内存管理,它会让你将性能发挥到极致。 // 单...
  • Less 是一门 CSS 预处理语言,它扩充了 CSS 语言,增加了诸如变量、混合(mixin)、函数等功能,让 CSS 更易维护、方便制作主题、扩充。 Less 可以运行在 Node、...众多CSS预处理器中Less语法最接近原生CSS,因此相对
  • 现在web开发应该是很火. 随着前端技术成熟, 手机性能提升. webApp 已经越来越接近原生体验. ...学习任何一门语言官方资料永远都是重要,也是必须要了解 Vue官网 官网提供了教程和各种学习实例....
  • 枸杞由来和技术栈

    2021-01-02 15:11:32
    我认为 React Native 没有取代原生语言的能力,单靠 React Native 本身想构建一个稍大型 App 是不现实。 如果没有原生语言开发者,对于枸杞和系统资源有交互这样中小型项目,React Native ...
  • 万维网核心语言、标准通用标记语言一个应用超文本标记语言(HTML)第五次重大修改 Android 一种基于Linux自由及开放源代码操作系统,主要使用于移动设备,如智能手机和平板电脑,由Google公司和开放...
  • 虽然大多数的语言可以既可被编译(Compiled)又可被解译(interpreted),但大多数只在一种情况下能够良好运行。在一些编程系统中,程序要经过几个阶段的编译,一般而言,后阶段的编译往往更接近机器语言。这种常用...
  • 通过原生的JS去解码FLV数据,再通过Media Source Extensions API作为桥梁将组装好的MP4格式数据传给原生HTML5 Video标签 。h5实现的播放器的流程如下: <p><img alt="2" src=...
  • javascript小结

    2019-09-14 22:18:42
    首先,Javascript技术体系主要分为核心语言定义,内置和原生对象,浏览器对象模型BOM以及文档对象模型,最后还有各个浏览器事件处理模型; 在对象中访问节点一般使用getElementById();ge...
  • MongoDB

    2019-07-17 20:26:00
    初识MongoDB MongoDB 是一个基于分布式文件存储数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展高性能数据存储解决...操作简单方便易懂 更加接近程序员操作 原生语法ORM 因为是非关系型数据库,没有...

空空如也

空空如也

1 2 3
收藏数 60
精华内容 24
关键字:

最接近原生的语言