CSAPP(datalab)解析

操作符的解释

右移操作符>>

算术右移(通常用符号 >> 表示)保留符号位。
  • 对于正数,算术右移和逻辑右移的效果相同,即在左边填充0。
  • 对于负数,算术右移会在左边填充1,保持符号位的1不变。
对于正数 5 (二进制表示 0000 0101),右移1位:
0000 0101 >> 1 = 0000 0010

对于负数 -5 (二进制补码表示 1111 1011),右移1位:
1111 1011 >> 1 = 1111 1101

以 x = INT_MIN(即-2147483648)为例
1000 0000 0000 0000 0000 0000 0000 0000 >> 31 = 
1111 1111 1111 1111 1111 1111 1111 1111
逻辑右移(通常用符号 >>> 表示)

(1)不保留符号位,它会在左边填充0。逻辑右移通常用于无符号整数的位移操作。

(2)逻辑右移的关键是无论原数的符号如何,右移时总是用0填充高位。

对于正数 5 (二进制表示 0000 0101),右移1位:
0000 0101 >>> 1 = 0000 0010

对于负数 -5 (二进制补码表示 1111 1011),右移1位:
1111 1011 >>> 1 = 0111 1101

信息的表示和处理

 2)补码(two’s complement) 

        将数据的最高位设置为符号位。当符号位为0表示为正数;当符号位为1时,表示为负数

故以32位int类型为例,

其正数范围为        0x00000001         ~         0x7FFFFFFF

负数范围为           0x80000000        ~        0xFFFFFFFF

取值范围为        -2147483648        ~        2147483647.

故在int类型中,不是所有的负数都存在其相反数。

注意:

(1)取值范围不是对称的,负数的范围比正数的范围大一。

INT_MIN == -2147483648

-INT_MIN == 2147483648 == INT_MAX + 1-INT_MIN范围超过int最大值,造成上溢,

故最后的值还是INT_MIN,即-INT_MIN == INT_MIN

 3) 有符号整数和无符号整数

注意:

(1)数据的位值不会改变,但解释这些位的方式会不同

int signedValue = -1;       // 有符号整数 -1

unsignedValue = (unsigned int)signedValue; // 强制类型转换    4294967295

解释:

  • 在32位系统中,-1 的二进制表示为 0xFFFFFFFF
  • 当我们将这个值转换为无符号整数时,位值保持不变,但解释为无符号整数,所以值变为 4294967295

(2) 避免混用有符号和无符号类型

可能导致隐式类型转换,从而引发非预期的错误。尤其是在比较或运算时,有符号整数可能被转换为无符号整数,导致逻辑错误。

#define KSIZE 1024
char kbuf[KSIZE];

void *memcpy(void *dest, void*src, size_t n);
int copy_from_kernel(void* user_dest, int maxlen)
{
  int len = KSIZE < maxlen ? KSIZE : maxlen;
  memcpy(user_dest, kbuf, len);
  return len;
}

解释: 

  • memcpy(user_dest, kbuf, len) 中的 len 被隐式转换为无符号整数 4294967295
  • 导致 memcpy 尝试复制 4294967295 字节,这将读取未授权的内存区域,造成安全漏洞。

4)IEEE 754 单精度浮点数表示法

单精度浮点数的表示方式如下:

浮点数编码的值有4钟不同的情况
  • 规格化 的。exp != 0 && exp != 255
  • 非规格化 的。exp == 0
  • 特殊情况
    1. 无穷大 。exp == 255 && frac == 0
    2. NaN 。exp == 255 && frac != 0

 浮点数中的阶码并非真正的指数,而是需要减去一个偏移,偏移为offset= 2^n - 1,其中n为阶码位数

 公式表达:浮点数
非规格浮点数

规格化和非规格化浮点数的处理方式:

(1)

当阶码为零时,计算机系统会把尾数解释为 0.M

当阶码非零时,计算机系统会把尾数解释为 1.M 

(2)

当尾数其位数限制时:

  1. 尾数右移一位,以将其重新限制在有效位数范围内。
  2. 阶码增加1,以反映尾数的调整
浮点数与双精度浮点数(float 和 double)

在于浮点数的存储和表示方式不同,导致比较时精度差异。 

实验

isTmax

判断是否为最大补码

//2
/*
 * isTmax - returns 1 if x is the maximum, two's complement number,
 *     and 0 otherwise 
 *   Legal ops: ! ~ & ^ | +
 *   Max ops: 10
 *   Rating: 1
 */

 逻辑: 若为最大补码 !(~x ^ (x + 1))成立,但需排除x = -1 的情况

allOddBits

判断所有奇数位是否都为1

/* 
 * allOddBits - return 1 if all odd-numbered bits in word set to 1
 *   where bits are numbered from 0 (least significant) to 31 (most significant)
 *   Examples allOddBits(0xFFFFFFFD) = 0, allOddBits(0xAAAAAAAA) = 1
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 12
 *   Rating: 2
 */

逻辑:排除偶数位,异或0xAAAAAAAA得到0

negate

返回给定整数的相反数(对应补码)

/* 
 * negate - return -x 
 *   Example: negate(1) = -1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 5
 *   Rating: 2
 */

逻辑: 由原码x得到 补码 ~x +1 

isAsciiDigit

//3
/* 
 * isAsciiDigit - return 1 if 0x30 <= x <= 0x39 (ASCII codes for characters '0' to '9')
 *   Example: isAsciiDigit(0x35) = 1.
 *            isAsciiDigit(0x3a) = 0.
 *            isAsciiDigit(0x05) = 0.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 15
 *   Rating: 3
 */

逻辑: 取x与0x39 0x30差值,判断是否为负(即符号位为1)

conditional

实现三元表达式

/* 
 * conditional - same as x ? y : z 
 *   Example: conditional(2,4,5) = 4
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 16
 *   Rating: 3
 */

 逻辑:!x + ~0

  • 如果 x 为 0,生成掩码 0x00000000。             (!x 为1,相加溢出
  • 如果 x 不为 0,生成掩码 0xFFFFFFFF

isLessOrEqual

/* 
 * isLessOrEqual - if x <= y  then return 1, else return 0 
 *   Example: isLessOrEqual(4,5) = 1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 24
 *   Rating: 3
 */
逻辑:同符,相等 或 X<Y(X - Y 取符号位为 1;异符,X为负

注意:

(1)被减数若为INT_MIN,相反数上溢        (计算结果仍然正确

(2)X - Y符号位为0,故不能用X < Y判断,需要判断相等

howManyBits 

/* howManyBits - 返回用二进制补码表示x所需的最小位数
 *  示例: howManyBits(12) = 5
 *            howManyBits(298) = 10
 *            howManyBits(-5) = 4
 *            howManyBits(0)  = 1
 *            howManyBits(-1) = 1
 *            howManyBits(0x80000000) = 32
 *  合法操作: ! ~ & ^ | + << >>
 *  最大操作数: 90
 *  难度: 4
 */
逻辑:利用二分法,逼近最高位1对应的位数

(1)将负数转换为其按位取反值

(2)检查前16位,若存在1,则至少需要16位,x>>16,否则x>>0

logicalNeg

实现!操作符

//4
/* 
 * logicalNeg - implement the ! operator, using all of 
 *              the legal operators except !
 *   Examples: logicalNeg(3) = 0, logicalNeg(0) = 1
 *   Legal ops: ~ & ^ | + << >>
 *   Max ops: 12
 *   Rating: 4 
 */

逻辑:       

(1)0  |  ~x + 1 相同符号位均为 0 (INT_MAX相同,但符号位1 

(2)右移31位,非零为0xFFFFFFFF(全1),为零为0x00000000(全0)

 单精度浮点数:

floatScale2 :

2*f的位级等效形式

​
/* 
 * floatScale2 - 返回表达式2*f的位级等效形式
 *   浮点参数f。
 *   参数和结果都作为无符号整数传递,但
 *   它们要被解释为单精度浮点值的位级表示。
 *   当参数是NaN时,返回参数
 *   合法操作: 任何整数/无符号操作包括||, &&。也包括if, while
 *   最大操作数: 30
 *   难度: 4
 */

​
注意:

阶码:

(1)阶码达到255(即0x7F800000),则表示结果为无穷大

(2)阶码部分全为 0(非规格化数或0),需要转为规格数

尾数:

(1)规格化数变为无穷大,尾数为0

(2)零和非规格化数,左移尾数一位相当于将数值乘以2。

(3)若左移尾数,可能到24位(esp不为0),成为规格化数

(4)frac <<= 1;左移后赋值,frac << 1左移得到新值但原值未改变

floatFloat2Int:

一个32位的单精度浮点数表示转换为一个32位的整数表示

/* 
 * floatFloat2Int - 返回表达式(int) f的位级等效形式
 *   浮点参数f。
 *   参数作为无符号整数传递,但
 *   它要被解释为单精度浮点值的位级表示。
 *   任何超出范围的值(包括NaN和无穷大)应返回
 *   0x80000000u。
 *   合法操作: 任何整数/无符号操作包括||, &&。也包括if, while
 *   最大操作数: 30
 *   难度: 4
 */
注意:

二进制:

(1)0x80000000u 为无符号整数

阶码        真实指数(阶码值 - 127偏移量):

(1)小于1(exp <= 0),返回0

(2)超出int 32位整数范围(exp大于31),返回无穷大

尾数:

(1)通过 frac |= 0x00800000,我们在尾数中强制设置第24位为1,将其转换为 1.M 的形式

(2)尾数frac(1.M ,       23位二进制数M),通过esp位数左移右移得到结果作为result

floatPower2

返回表达式2.0^x的位级等效形式

/* 
 * floatPower2 - 返回表达式2.0^x的位级等效形式
 *   (2.0的x次方)对于任何32位整数x。
 *
 *   返回的无符号值应该具有与单精度浮点数2.0^x相同的位级表示。
 *   如果结果太小,无法表示为非正规数,则返回0。如果太大,返回+INF。
 * 
 *   合法操作: 任何整数/无符号操作包括||, &&。也包括if, while 
 *   最大操作数: 30 
 *   难度: 4
 */
注意:

(1)+INF(正无穷大).0x7F800000

  • 符号位 S = 0(正数)
  • 阶码 E = 255(全为1,二进制 11111111
  • 尾数 M = 0

(2)x(真实指数) (-126 ~  -149)(非规范化浮点数)不超出尾数范围,返回尾数

测试结果:

Points  Rating  Errors  Points  Ops     Puzzle
1       1       0       2       7       bitXor
1       1       0       2       1       tmin
1       1       0       2       8       isTmax
2       2       0       2       7       allOddBits
2       2       0       2       2       negate
3       3       0       2       11      isAsciiDigit
3       3       0       2       8       conditional
3       3       0       2       5       isLessOrEqual
4       4       0       2       6       logicalNeg
4       4       0       2       36      howManyBits
4       4       0       2       12      floatScale2
4       4       0       2       17      floatFloat2Int
4       4       0       2       9       floatPower2

Score = 62/62 [36/36 Corr + 26/26 Perf] (129 total operators)

/* 
 * CS:APP Data Lab 
 * 
 * <Please put your name and userid here>
 * 
 * bits.c - Source file with your solutions to the Lab.
 *          This is the file you will hand in to your instructor.
 *
 * WARNING: Do not include the <stdio.h> header; it confuses the dlc
 * compiler. You can still use printf for debugging without including
 * <stdio.h>, although you might get a compiler warning. In general,
 * it's not good practice to ignore compiler warnings, but in this
 * case it's OK.  
 */

#if 0
/*
 * Instructions to Students:
 *
 * STEP 1: Read the following instructions carefully.
 */

You will provide your solution to the Data Lab by
editing the collection of functions in this source file.

INTEGER CODING RULES:
 
  Replace the "return" statement in each function with one
  or more lines of C code that implements the function. Your code 
  must conform to the following style:
 
  int Funct(arg1, arg2, ...) {
      /* brief description of how your implementation works */
      int var1 = Expr1;
      ...
      int varM = ExprM;

      varJ = ExprJ;
      ...
      varN = ExprN;
      return ExprR;
  }

  Each "Expr" is an expression using ONLY the following:
  1. Integer constants 0 through 255 (0xFF), inclusive. You are
      not allowed to use big constants such as 0xffffffff.
  2. Function arguments and local variables (no global variables).
  3. Unary integer operations ! ~
  4. Binary integer operations & ^ | + << >>
    
  Some of the problems restrict the set of allowed operators even further.
  Each "Expr" may consist of multiple operators. You are not restricted to
  one operator per line.

  You are expressly forbidden to:
  1. Use any control constructs such as if, do, while, for, switch, etc.
  2. Define or use any macros.
  3. Define any additional functions in this file.
  4. Call any functions.
  5. Use any other operations, such as &&, ||, -, or ?:
  6. Use any form of casting.
  7. Use any data type other than int.  This implies that you
     cannot use arrays, structs, or unions.

 
  You may assume that your machine:
  1. Uses 2s complement, 32-bit representations of integers.
  2. Performs right shifts arithmetically.
  3. Has unpredictable behavior when shifting if the shift amount
     is less than 0 or greater than 31.


EXAMPLES OF ACCEPTABLE CODING STYLE:
  /*
   * pow2plus1 - returns 2^x + 1, where 0 <= x <= 31
   */
  int pow2plus1(int x) {
     /* exploit ability of shifts to compute powers of 2 */
     return (1 << x) + 1;
  }

  /*
   * pow2plus4 - returns 2^x + 4, where 0 <= x <= 31
   */
  int pow2plus4(int x) {
     /* exploit ability of shifts to compute powers of 2 */
     int result = (1 << x);
     result += 4;
     return result;
  }

FLOATING POINT CODING RULES

For the problems that require you to implement floating-point operations,
the coding rules are less strict.  You are allowed to use looping and
conditional control.  You are allowed to use both ints and unsigneds.
You can use arbitrary integer and unsigned constants. You can use any arithmetic,
logical, or comparison operations on int or unsigned data.

You are expressly forbidden to:
  1. Define or use any macros.
  2. Define any additional functions in this file.
  3. Call any functions.
  4. Use any form of casting.
  5. Use any data type other than int or unsigned.  This means that you
     cannot use arrays, structs, or unions.
  6. Use any floating point data types, operations, or constants.


NOTES:
  1. Use the dlc (data lab checker) compiler (described in the handout) to 
     check the legality of your solutions.
  2. Each function has a maximum number of operations (integer, logical,
     or comparison) that you are allowed to use for your implementation
     of the function.  The max operator count is checked by dlc.
     Note that assignment ('=') is not counted; you may use as many of
     these as you want without penalty.
  3. Use the btest test harness to check your functions for correctness.
  4. Use the BDD checker to formally verify your functions
  5. The maximum number of ops for each function is given in the
     header comment for each function. If there are any inconsistencies 
     between the maximum ops in the writeup and in this file, consider
     this file the authoritative source.

/*
 * STEP 2: Modify the following functions according the coding rules.
 * 
 *   IMPORTANT. TO AVOID GRADING SURPRISES:
 *   1. Use the dlc compiler to check that your solutions conform
 *      to the coding rules.
 *   2. Use the BDD checker to formally verify that your solutions produce 
 *      the correct answers.
 */


#endif

//1
/* 
 * bitXor - x^y using only ~ and & 
 *   Example: bitXor(4, 5) = 1
 *   Legal ops: ~ &
 *   Max ops: 14
 *   Rating: 1
 */
int bitXor(int x, int y) {
    int a, b, result;
    a = ~x & ~y;
    b = x & y;
    result = ~a & ~b;
    return result;
}

/* 
 * tmin - return minimum two's complement integer 
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 4
 *   Rating: 1
 */
int tmin(void) {
    int result;
    result = 1 << 31;
    return result;
}

//2
/*
 * isTmax - returns 1 if x is the maximum, two's complement number,
 *     and 0 otherwise 
 *   Legal ops: ! ~ & ^ | +
 *   Max ops: 10
 *   Rating: 1
 */
int isTmax(int x) {
    int a, b, result;
    a = ~x ^ (x + 1);
    b = !!((x + 1) ^ 0);
    result = !(a) & b;
    return result;
}

/* 
 * allOddBits - return 1 if all odd-numbered bits in word set to 1
 *   where bits are numbered from 0 (least significant) to 31 (most significant)
 *   Examples allOddBits(0xFFFFFFFD) = 0, allOddBits(0xAAAAAAAA) = 1
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 12
 *   Rating: 2
 */
int allOddBits(int x) {
    int mask, result;
    mask = (0xAA<<8) | 0xAA;
    mask = (mask << 16) | mask;
    x &= mask;
    result = !(x ^ mask);
    return result;
}

/* 
 * negate - return -x 
 *   Example: negate(1) = -1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 5
 *   Rating: 2
 */
int negate(int x) {
    int result;
    result = ~x + 1;
    return result;
}

//3
/* 
 * isAsciiDigit - return 1 if 0x30 <= x <= 0x39 (ASCII codes for characters '0' to '9')
 *   Example: isAsciiDigit(0x35) = 1.
 *            isAsciiDigit(0x3a) = 0.
 *            isAsciiDigit(0x05) = 0.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 15
 *   Rating: 3
 */
int isAsciiDigit(int x) {
    int lower_bound, upper_bound, is_greater_equal_lower, is_less_equal_upper;
    lower_bound = 0x30;  // 48
    upper_bound = 0x39;  // 57

    is_greater_equal_lower = x + (~lower_bound + 1); // x - 0x30
    is_less_equal_upper = upper_bound + (~x + 1); // 0x39 - x

    return !((is_greater_equal_lower >> 31) | (is_less_equal_upper >> 31));
}

/* 
 * conditional - same as x ? y : z 
 *   Example: conditional(2,4,5) = 4
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 16
 *   Rating: 3
 */
int conditional(int x, int y, int z) {
    int mask, result;
    mask = !x + ~0;
    result = (mask & y) | (~mask & z);
    return result;
}

/* 
 * isLessOrEqual - if x <= y  then return 1, else return 0 
 *   Example: isLessOrEqual(4,5) = 1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 24
 *   Rating: 3
 */
int isLessOrEqual(int x, int y) {
    int flagx, flagy, notZero, flag, less, result;
    
    // 计算 x 的符号位(0 表示正数,1 表示负数)
    flagx = (x >> 31 & 1);
    
    // 计算 y 的符号位(0 表示正数,1 表示负数)
    flagy = (y >> 31 & 1);
    
    // 判断 x 和 y 的符号是否不同(1 表示不同,0 表示相同)
    notZero = !!(flagx ^ flagy);
    
    // 计算 x - y 的符号位,表示 x 是否小于 y(0 表示 x >= y,1 表示 x < y)
    less = ((x + (~y + 1)) >> 31 & 1);
    
    // 计算最终结果
    // (!(x ^ y)) 用于判断 x 是否等于 y,相等则结果为1,不等则结果为0
    // (flag & flagx) 用于处理符号不同的情况,如果符号不同且 x 为负数,则结果为1,否则为0
    // (~flag & less) 用于处理符号相同的情况,如果符号相同且 x < y,则结果为1,否则为0
    result = (!(x ^ y)) | ((notZero & flagx) | (!notZero & less));
    
    return result;
}


//4
/* 
 * logicalNeg - implement the ! operator, using all of 
 *              the legal operators except !
 *   Examples: logicalNeg(3) = 0, logicalNeg(0) = 1
 *   Legal ops: ~ & ^ | + << >>
 *   Max ops: 12
 *   Rating: 4 
 */
int logicalNeg(int x) {
    int result;
    result = ((x | (~x + 1)) >> 31) + 1;
    return result;
}

/* howManyBits - return the minimum number of bits required to represent x in
 *             two's complement
 *  Examples: howManyBits(12) = 5
 *            howManyBits(298) = 10
 *            howManyBits(-5) = 4
 *            howManyBits(0)  = 1
 *            howManyBits(-1) = 1
 *            howManyBits(0x80000000) = 32
 *  Legal ops: ! ~ & ^ | + << >>
 *  Max ops: 90
 *  Rating: 4
 */
int howManyBits(int x) {
    int sign, b16, b8, b4, b2, b1, b0, result;
    
    sign = x >> 31;
    x = (sign & ~x) | (~sign & x);
    
    b16 = !!(x >> 16) << 4;
    x = x >> b16;
    b8 = !!(x >> 8) << 3;
    x = x >> b8;
    b4 = !!(x >> 4) << 2;
    x = x >> b4;
    b2 = !!(x >> 2) << 1;
    x = x >> b2;
    b1 = !!(x >> 1);
    x = x >> b1;
    b0 = x;
    
    result = b16 + b8 + b4 + b2 + b1 + b0 + 1;
    return result;
}

//float
/* 
 * floatScale2 - Return bit-level equivalent of expression 2*f for
 *   floating point argument f.
 *   Both the argument and result are passed as unsigned int's, but
 *   they are to be interpreted as the bit-level representation of
 *   single-precision floating point values.
 *   When argument is NaN, return argument
 *   Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
 *   Max ops: 30
 *   Rating: 4
 */
unsigned floatScale2(unsigned uf) {
    unsigned exp, frac, sign, result;

    exp = (uf >> 23) & 0xFF;
    frac = uf & 0x7FFFFF;
    sign = uf & 0x80000000;

    if (exp == 255) return uf;

    if (exp == 0) {
        frac <<= 1;
        if (frac & 0x800000) {
            exp = 1;
            frac &= 0x7FFFFF;
        }
        result = sign | (exp << 23) | frac;
        return result;
    }

    exp += 1;
    if (exp == 255) return sign | 0x7F800000;

    result = sign | (exp << 23) | frac;
    return result;
}

/* 
 * floatFloat2Int - Return bit-level equivalent of expression (int) f
 *   for floating point argument f.
 *   Argument is passed as unsigned int, but
 *   it is to be interpreted as the bit-level representation of a
 *   single-precision floating point value.
 *   Anything out of range (including NaN and infinity) should return
 *   0x80000000u.
 *   Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
 *   Max ops: 30
 *   Rating: 4
 */
int floatFloat2Int(unsigned uf) {
    unsigned sign = uf >> 31;
    int exponent = ((uf >> 23) & 0xFF) - 127;
    unsigned fraction = uf & 0x7FFFFF;
    
    // Handling special cases
    if (exponent >= 31) {
        return 0x80000000; // Too large to fit in an int, or it's NaN/infinity
    }
    
    if (exponent < 0) {
        return 0; // Less than 1
    }
    
    // Add the implicit leading 1 to the fraction (normalized numbers)
    fraction = fraction | 0x800000;
    
    // Calculate the integer value
    if (exponent > 23) {
        // Shift left if exponent is greater than the fraction bits
        fraction = fraction << (exponent - 23);
    } else {
        // Shift right if exponent is less or equal
        fraction = fraction >> (23 - exponent);
    }
    
    if (sign) {
        return -fraction; // Apply sign
    } else {
        return fraction;
    }
}


/* 
 * floatPower2 - Return bit-level equivalent of the expression 2.0^x
 *   (2.0 raised to the power x) for any 32-bit integer x.
 *
 *   The unsigned value that is returned should have the identical bit
 *   representation as the single-precision floating-point number 2.0^x.
 *   If the result is too small to be represented as a denorm, return
 *   0. If too large, return +INF.
 * 
 *   Legal ops: Any integer/unsigned operations incl. ||, &&. Also if, while 
 *   Max ops: 30 
 *   Rating: 4
 */

unsigned floatPower2(int x) {
    if (x >= 128) {
        // 如果x >= 128,结果太大,返回+INF
        return 0x7F800000;
    } 
    if (x >= -126) {
        // 如果x在正常范围内,计算出指数部分
        return (x + 127) << 23;
    } 
    if (x >= -149) {
        // 如果x在非正常化范围内,计算出尾数部分
        return 1 << (x + 149);
    } 
    // 如果x太小,无法表示,返回0
    return 0;
}



  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值