前言

优化一下之前重载的时候 +
就是让hstring + 1234也能实现,不过要对后面的数进行转换,且在不使用三方函数。


正文

正常的一串数字123456
跟char之间的联系,其实就要看ASCII码了。

可以看到字符0的二进制数是48,其它都是在48基础上+
那么转换的思路就明确了。

1
2
int a = 12345;
char str[12]{}; //int 的范围,数了下大概长度10,然后算上符号。

再然后肯定要通过循环,for和while其实都差不多,但是都有一个痛点

1
2
3
4
5
6
while (a!=0){
int i = 0;
str[i] = (char)(a % 10 + 48);
i++;
a %= 10;
}

一开始傻瓜式写法,都不知道自己怎么想的。。。。

  1. int i=0;放在循环里面,每次都是从0开始覆盖,没有意义
  2. 不一定要强转,编译器有隐转
  3. while的条件倒也惨白,里面a%=10,如果最后一位是1,就死循环了。

所以有的时候脑子真的晕乎乎,修改一下

1
2
3
4
5
int i = 0;
while (a){
str[i++] = (char)(a % 10 + 48);
a /= 10;
}

这样就行了。强转之类的不是必要的问题不大啊。


负数的问题
无非就是前面多个-
而且我们这里本身最后还要倒转,所以在循环结束的时候判断一下是否小于0,

1
if (a < 0) str[i++] = '-';

加上这个就完事。

但是运行的时候就会发现毛病。因为负数,取模运算就搞事情了,+48也得不到那个字符。
所以要先给他反转。

然后 a<0这个预计要用两次,而且第二次的时候a已经反转成正数了。

所以可能要提前放在一个布尔变量里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int a = -12345;
char str[12]{};
bool flag = a < 0;
int i = 0;

if (flag) a = -a;
while (a){

str[i++] = a % 10 + 48;
a /= 10;
}

if(flag) str[i++] = '-';

std::cout << str;

跑了一遍也没啥问题,ok。


然后反转一下。

也需要额外定义一个变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int a = -12345;
char str[12]{};
char strA[12]{};
bool flag = a < 0;
int len = 0;

if (flag) a = -a;
while (a){

str[len++] = a % 10 + 48;
a /= 10;
}

if(flag) str[len++] = '-';
for (int i = 0; str[i];i++){
strA[i] = str[len - 1 - i];
}

std::cout << strA;

这个i下面又用到了,所以改了下名字。

也是成功反转了。


不过依照优化而言,他还是有很多地方

1
2
3
4
5
6
7
8
9
10
11
12
13
int a = -1225475;
char str[12]{};
int len = 11;
bool flag = a < 0;

if (flag) a = -1 * a;
while (a){
str[--len] = a % 10 + 48;
a /= 10;
}
if (flag) str[--len] = '-';

std::cout << &str[len];

这种倒写倒是省了一个变量和循环。

抛开if,有些地方优化其实都是数学上的优化,需要逻辑思维比较好的。
if (flag) a = -1 * a;
像这个用负负得正的思想,但其实a的值无外乎 -1 /0 1 ,
flag这个a < 0;人的思维更偏向于 a >= 0;
布尔值在c/c++也就是0 和 1;
那么不使用if,让负数变成正数,就要a*-1,但是正数不变,或者就是*1
a是输入项,关键就在于-1和1怎么来的。
前面的布尔值0 1,要转变成1 -1,让这个值*2 - 1, 当布尔值为1时,1*2-1还是1,当布尔值为0时,0*2 - 1就等于-1了。

1
2
3
bool flag = a >= 0;

a = a*(flag * 2 - 1);

这样第一个if就省掉了。

1
if (flag) str[--len] = '-';

像第二个if就不是很好省略啊。按我个人肯定做不出来了。

套用老师的做法

目前flag就两种情况,0 1
0的时候 len的长度肯定因为没有符号位-1,去放这个符号位
1的时候 len不变,
0的时候 要加上-
1的时候 不需要
0的时候 ‘-‘ * (flag+1)*(1-flag) + str[len] * flag
1的时候 ‘-‘ * (flag+1)*(1-flag) + str[len] * flag
这种操作,就是负数的时候1-0=1 保住前面的’-‘
而正数的时候1-1=0 前面的’-‘就被舍弃了。
转换成代码

1
str[len = len - 1 * (1 - flag)] = '-' * (flag + 1) * (1 - flag) + str[len] * flag;

u1s1,这个逻辑我要理好久。。。。

不过效果是ok的

不过在测试的时候发现了个问题,是我之前测试的时候都没注意的就是a=0的时候
因为while的条件就是a,当a=0的时候,他压根没进去。

所以按照必定先执行一次的惯例,就要使用dowhile。
这样的话a=0的时候就也能够输出了。

1
2
3
4
5
6
7
8
9
10
11
12
int a = 0;
char str[12]{};
int len = 11;
bool flag = a >= 0;

a = a * (flag * 2 - 1);
do{
str[--len] = a % 10 + 48;
} while (a /= 10);
str[len = len - 1 * (1 - flag)] = '-' * (flag + 1) * (1 - flag) + str[len] * flag;

std::cout << &str[len];

后面的处理真的不是我这个二愣子看得明白的。。。


封装到我们的类里面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
hstring &hstring::operator+(int _val){
char str[12]{}; //根据int长度所设
int len = 11; //考虑到符号位
bool flag = _val >= 0; //判断正负数

_val = _val * (flag * 2 - 1); //是否取反
do{
str[--len] = _val % 10 + 48; //int转char
} while (_val /= 10);
str[len = len - 1 * (1 - flag)] = '-' * (flag + 1) * (1 - flag) + str[len] * flag;


//前面用过的拷贝,自己稍微修改一下变量名。
unsigned short hslen = hsLen + 12 - len - 1;
hslen += hsLen - 1;
if (hslen > hsmLen){
char *rstr = c_str;
c_str = new char[len]; //重新分配字符串空间
hsmLen = hslen; //重置缓冲区长度
memcpy(c_str, rstr, hsLen);
delete[] c_str; //清理内存
}
memcpy(this->c_str + hsLen - 1, str+len, 12-len); //注意12是因为上面字符串长度给的12
hsLen = hslen;

return *this;
}

如果要扩充长度记得修改str的长度,还有最后memcpy的时候的长度-len
有效长度就是在12以内含符号。


long 在win下也就是四字节范围跟int差不多
longlong的范围9223372036854775807,反正在这里也就是修改一下str的长度和len的长度。
浮点数先不考虑,因为存在精度缺失的问题。后面的位数算多了不一定是对的。

反正这种重写操作,要么挨个类型写,要么用模板。这里暂时不搞了,先抓紧复习完进度。


结语

数学。。太弱了,在这方面不占优势。纯纯码农了属于是。