C++学习------cinttypes头文件的源码学习02---函数定义

时间:2022-11-19 14:59:51


函数定义

257  __BEGIN_DECLS
258 intmax_t imaxabs(intmax_t __i) __attribute_const__ __INTRODUCED_IN(19);
259 imaxdiv_t imaxdiv(intmax_t __numerator, intmax_t __denominator) __attribute_const__ __INTRODUCED_IN(19);
260 intmax_t strtoimax(const char* __s, char** __end_ptr, int __base);
261 uintmax_t strtoumax(const char* __s, char** __end_ptr, int __base);
262 intmax_t wcstoimax(const wchar_t* __s, wchar_t** __end_ptr, int __base) __INTRODUCED_IN(21);
263 uintmax_t wcstoumax(const wchar_t* __s, wchar_t** __end_ptr, int __base) __INTRODUCED_IN(21);
264 __END_DECLS

imaxabs---返回绝对值

实现方法非常简单,判断是否大于0,大于0返回原值,小于0返回相反数。

//http://www.aospxref.com/android-12.0.0_r3/xref/bionic/libc/upstream-openbsd/lib/libc/stdlib/imaxabs.c
34 intmax_t
35 imaxabs(intmax_t j)
36 {
37 return (j < 0 ? -j : j);
38 }

imaxdiv---返回除法结果(有商有余数)

实现方法:使用整数除法'/'求商,模运算'%'求余数 注意:因为这里是有符号整数的除法,所以要考虑出现负数的情况,如果被除数大于等于0,计算后的余数小于0,那么我们要将商值加1,从余数中减去除数,即最后要保证输出的余数是正数。最后返回对应的结构体,如下:

被除数

除数


余数

>=0

>0

>=0

>=0

>=0

<0

<=0

>=0

<0

>0

<=0

<=0

<0

<0

>=0

>=0

//http://www.aospxref.com/android-12.0.0_r3/xref/bionic/libc/upstream-openbsd/lib/libc/stdlib/imaxdiv.c
36 imaxdiv_t
37 imaxdiv(intmax_t num, intmax_t denom)
38 {
39 imaxdiv_t r;
40
41 /* see div.c for comments */
42
43 r.quot = num / denom;
44 r.rem = num % denom;
45 if (num >= 0 && r.rem < 0) {
46 r.quot++;
47 r.rem -= denom;
48 }
49 return (r);
50 }

strtoimax---输入str返回对应的int值,等价于intmax_t的strtol函数

这里调用了一个模板函数来实现,模板参数为类型,该类型的最小值,最大值

//http://www.aospxref.com/android-12.0.0_r3/xref/bionic/libc/bionic/strtol.cpp
174 intmax_t strtoimax(const char* s, char** end, int base) {
175 return StrToI<intmax_t, INTMAX_MIN, INTMAX_MAX>(s, end, base);
176 }

详细分析一下模板函数

36  template <typename T, T Min, T Max> T StrToI(const char* nptr, char** endptr, int base) {

入参:const char* nptr为待转换的str,char** endptr为最后转换结束指向的字符指针,int base为进制

37    // Ensure that base is between 2 and 36 inclusive, or the special value of 0.
38 if (base < 0 || base == 1 || base > 36) {
39 if (endptr != nullptr) *endptr = const_cast<char*>(nptr);
40 errno = EINVAL;
41 return 0;
42 }
  1. 检查输入的进制是否有效,如果小于0或者等于1或者大于36,视为异常的进制,此时将endptr赋值为输入nptr首字符(如果外部输入了不为空的指针记录这个值的情况下);
44    // Skip white space and pick up leading +/- sign if any.
45 // If base is 0, allow 0x for hex and 0 for octal, else
46 // assume decimal; if base is already 16, allow 0x.
47 const char* s = nptr;
48 int c;
49 do {
50 c = *s++;
51 } while (isspace(c));
  1. 跳过前面的空格符号,直到遇到非空格符;
52    int neg;
53 if (c == '-') {
54 neg = 1;
55 c = *s++;
56 } else {
57 neg = 0;
58 if (c == '+') c = *s++;
59 }
  1. 开始检测符号,检测到'-'标记neg值然后记录指针s调到下一个,检测到'+'也同理;
60    if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X') && isxdigit(s[1])) {
61 c = s[1];
62 s += 2;
63 base = 16;
64 }
  1. 开始检测16进制,如果是0进制(默认值)或者16进制,而且当前字符(c指向的)是'0'和下一个(s指向的),是'x'或'X',而且再下一个字符(s[1])是16进制里面的字符,那么就跳过两个检测(即字符'0x'),标识为16进制;
65    if (base == 0) base = (c == '0') ? 8 : 10;
  1. 如果上一部检测16进制不是,那么如果当前的字符为'0',那就设定为8进制,否则,一律认为是10进制;
67    // We always work in the negative space because the most negative value has a
68 // larger magnitude than the most positive value.
69 T cutoff = Min / base;
70 int cutlim = -(Min % base);
  1. 因为负数表示比整数多一个数,如-128到127,我们在负数域上面进行计算,使用最小值除以进制数,得到商值cutoff和余数的负数cutlim
71    // Non-zero if any digits consumed; negative to indicate overflow/underflow.
72 int any = 0;
73 T acc = 0;
74 for (; ; c = *s++) {
75 if (isdigit(c)) {
76 c -= '0';
77 } else if (isalpha(c)) {
78 c -= isupper(c) ? 'A' - 10 : 'a' - 10;
79 } else {
80 break;
81 }
82 if (c >= base) break;
83 if (any < 0) continue;
84 if (acc < cutoff || (acc == cutoff && c > cutlim)) {
85 any = -1;
86 acc = Min;
87 errno = ERANGE;
88 } else {
89 any = 1;
90 acc *= base;
91 acc -= c;
92 }
93 }

7.使用any记录,acc记录最终的结果,开始遍历这一串字符(已经除去了正负号,进制表示);如果是数字那么c就减去字符'0'得到具体的数值,如果是字母,那就判断是大写还是小写,相应的减去('A'-10)或('a'-10)得到了具体的数值,其它符号的话就停止循环遍历,识别结束。

每次循环拿到的数值c其实就是数字最高位上面的表示,如果c>=base,说明这个字符表示超过了可以表示的进制,也直接结束;

如果any值小于0,说明在上一次的判断中,acc值已经小于了cutoff,即乘以对应的进制之后一定会突破最小值,或者(acc == cutoff && c > cutlim),本次操作完之后也会出现突破最小值的情况,所以直接将acc置为最小值,置错位标志为超出限制。

如果any值大于0,说明上一次乘法可以继续累乘。如128十进制,acc的处理如下:

  • acc = 0;
  • 检测c = 1; acc = acc*10 = 0; acc = acc - c = 0 - 1 = -1;
  • 检测c = 2; acc = acc*10 = -10; acc = acc - c = -12;
  • 检测c = 8; acc = acc10 = -120; acc = acc - c = -128; 不同于我们正常的(((110)+2)10)+8 = 128,这里使用的是(((-110)-2)*10)-8 = -128
94    if (endptr != nullptr) *endptr = const_cast<char*>(any ? s - 1 : nptr);
95 if (!neg) {
96 if (acc == Min) {
97 errno = ERANGE;
98 acc = Max;
99 } else {
100 acc = -acc;
101 }
102 }
103 return acc;
104 }

8.最后就是检测对应的结果:如果需要保存最后的指针,先check any值(是否越界),越界的话直接返回nptr指针,否则返回s的前一个(因为s是被移到下一个字符之后检测了才发现失效的,所以最后一个字符是上一个); 然后检测符号,如果是负数,那么就要处理好越界的情况,置为Max,然后赋值errno,对应的取反即可,最后返回对应值就好。

strtoumax---等价于uintmax_t的strtoul

使用无符号的模板函数进行处理,流程基本与strtoimax一致,只是这里使用的是(((1*10)+2)*10)+8 = 128这样的方式,而且没有最小值,只有最大值,可以参照代码进行相应的分析。注意:这个函数你买了也是区分了+/-符号的,也就是说输入字符串‘-128’最后在模板函数里面最后的acc取反也是可以得到acc等于-128的,但是因为返回参数为uintmax_t,那就会将-128(补码表示1000 0000)解释为unsigned类型的数据也即是128(源码表示1000 0000)

197  uintmax_t strtoumax(const char* s, char** end, int base) {
198 return StrToU<uintmax_t, UINTMAX_MAX>(s, end, base);
199 }

模板函数

106  template <typename T, T Max> T StrToU(const char* nptr, char** endptr, int base) {
107 if (base < 0 || base == 1 || base > 36) {
108 if (endptr != nullptr) *endptr = const_cast<char*>(nptr);
109 errno = EINVAL;
110 return 0;
111 }
112
113 const char* s = nptr;
114 int c;
115 do {
116 c = *s++;
117 } while (isspace(c));
118 int neg;
119 if (c == '-') {
120 neg = 1;
121 c = *s++;
122 } else {
123 neg = 0;
124 if (c == '+') c = *s++;
125 }
126 if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X') && isxdigit(s[1])) {
127 c = s[1];
128 s += 2;
129 base = 16;
130 }
131 if (base == 0) base = (c == '0') ? 8 : 10;
132
133 T cutoff = Max / static_cast<T>(base);
134 int cutlim = Max % static_cast<T>(base);
135 T acc = 0;
136 int any = 0;
137 for (; ; c = *s++) {
138 if (isdigit(c)) {
139 c -= '0';
140 } else if (isalpha(c)) {
141 c -= isupper(c) ? 'A' - 10 : 'a' - 10;
142 } else {
143 break;
144 }
145 if (c >= base) break;
146 if (any < 0) continue;
147 if (acc > cutoff || (acc == cutoff && c > cutlim)) {
148 any = -1;
149 acc = Max;
150 errno = ERANGE;
151 } else {
152 any = 1;
153 acc *= base;
154 acc += c;
155 }
156 }
157 if (neg && any > 0) acc = -acc;
158 if (endptr != nullptr) *endptr = const_cast<char*>(any ? s - 1 : nptr);
159 return acc;
160 }

wcstoimax---等价于intmax_t的wcstol

与上面的类似,只是输入为w_char,实现方案类似,不再赘述

wcstoumax---等价于uintmax_t的wcstoul

与上面的类似,只是输入为w_char,实现方案类似,不再赘述