的时候太匆忙,居然还找错地方了。见到面试官的时候已经迟到了。到了后头一直晕。。。结果可想而知。
当然,给自己找了主观上的借口,还是得客观的分析下为啥那个程序没有完整的写出来。头晕的我,当时
精力不太集中,一边想着程序的大体框架,一边又不断地考虑各种错误检查和处理,又考虑着题目中一些没有
明确说明的情况。结果什么都没做好:错误处理考虑到了很多情况,但不完备,并且零散地分部在if语句中,
其实想清楚后是可以分好类的。至于程序框架部分,写了个七七八八,但还是没写好。
事后,分析和总结了这次失利的情况。内功有待增强,临场发挥很欠缺,特别不该在匆忙,欠准备中做事情。
当然,针对这类写程序的问题:(1)优先写出大体程序框架 (2)考虑错误检查及处理,针对题目中不明晰的
地方请教面试官。毕竟,错误检查及处理未做好,顶多就算考虑不完备;要是程序的大体框架,主体逻辑都没
写好,那就给别人“写不出程序”的印象了。
好了,说了那么多废话,咱们回到正题,来看看面试题,并找找解决思路吧。题目是用英文描述的,我也
记不得具体描述了。需要考虑2~32进制;出错时将end_ptr指向第一次发现的非法字符的位置;还需要考虑
溢出情况;从描述上来看,合法的输入数据的格式为:
[若干空格符,制表符] [正负号] [标明进制的字符] [数字字母串] [字符串结束符]
题目描述:实现my_strtoi函数,完成字符串到整型数值的转换。
函数定义:
int my_strtoi(const char *src_str,char **end_ptr,int base);
函数说明:
这个函数会将参数src_str字符串根据参数base来转换成整型数。
参数base范围从2至36,或0。参数base代表采用的进制方式,如base值为10则采用10进制,若base值
为16则采用16进制等。当base值为0时则是采用10进制做转换,但遇到如’0x’前置字符则会使用16进制做
转换、遇到’0’前置字符而不是’0x’的时候会使用8进制做转换。
一开始strtoi()会扫描参数src_str字符串,跳过前面的空格字符,直到遇上数字或正负符号才开始做转换,
再遇到非数字或字符串结束时('\0')结束转换,并将结果返回。若参数end_ptr不为NULL,则会将遇到不合条件
而终止的src_str中的字符指针由end_ptr返回;若参数end_ptr为NULL,则会不返回非法字符串。
OK,按照我们上面总结的,先来“画出”程序的主题逻辑。
(1)通过while循环,跳过前导空格符或者制表符。需要注意的是指针自增要放到while循环里,而不是在循环
条件里,因为后者会导致指针多移动了一个位置,指向空格,制表符后的第二个非空格字符;要是输入字符串
只有空格怎么办?那么此时指向的将是'\0'后面的那个字符。也就是指针越界,访问了“非法内存”。
(2)发现了" + "或者" - "时,记录下数值的符号类型,将指针往后移一位;没发现的情况,也是当做”正值“
处理,但指针不向后移动。
(3)处理数字或者字母。如何将它们转int呢?对于扫描”123“字符串,转换为整型数,有两种方式:从左往右
扫描,也就是从”最高位“。扫描到1,记录下来,当扫描到2时,怎么办?1 * 10 + 2 = 12 。那接下来扫描到3
怎么办? 12 * 10 + 3 = 123 。 如果是从右往左扫描呢? 那就相当于从”最低位“计入。扫描到3,计入。接着扫描
到2,就是2 * 10 + 3 = 23 。 再扫描到了1,那么就是1* 10 * 10 + 23 = 123 。[扫描数字字符] - '0' + 10 就是该数字
字符所表示的数值。
在字符串格式已经给定,我们可以通过类似strlen这样的函数快速求得字符串长度的情况下,两种方法都
可行。但如果追求更高效,还是从左到右扫描,毕竟求字符串长度,也是要耗时的。这里只是提供给大家这
两种思路。我们的程序采用从左到右扫描输入字符串。
那如果是字母呢? 大小写同等视之。如果是'a',我们按照数值10来对待;如果是'Z',我们按照35对待。
也就说,单个字母和数字所能表示最大数值35。现在知道为什么我们所能支持的最大进制为36了吧?
[扫描字母] - 'a' + 10 (小写)或者[扫描字母] - 'A' + 10 (大写)就是该字母所表示的数值。
(4)扫描到字符串结束符'\0',非法字符或者数值溢出时,扫描循环结束,返回结果。
接下来就是考虑错误情况及题目未明晰部分了。
(1)输入字符串为NULL。
(2)输入字符串中仅包含'\0',或者仅包含在空格,制表符。
(3)扫描过程中遇到非法字符。可以是在扫描前导空格,制表符过程中;也可以是在要扫描”数值符号“字符;
也可以是在扫描后面的数字,字母字符。
(4)扫描到的数值溢出,也就是"小于” INT_MIN(如-2147483648),或者“大于”INT_MAX(如2147483647),
此时也要结束处理。注意,此处的“小于”,“大于”的比较,是不能通过int类型的数值比较进行的,因为整型的
两个数相加,结果会溢出,直接数值比较,肯定会得出错误的结果。
但我们知道unsigned int是能表示比int更广的数值范围的,是否可以通过直接将扫描的数值的正值(累加
而来)与此符号下的最大数值表示 (-INT_MIN或者INT_MAX)进行比较呢?看上去似乎比较可行,但是此处得
小心了。unsigned int 比int能多表示的范围并大不了多少。当base > 2 时,就可能出现上面所说的溢出问题。
比如:base为10,对21474836469进行扫描时,我们发现2147483646 < INT_MAX (2147483647)。此时并
没有溢出,于是继续扫描后面的字符9,此时如果进行 2147483646 x 10 + 9 与 2147483647 的比较,显然比较
结果会因为数值溢出而无效。那怎么办呢?
比较简单的一个办法是:(overflow - (*p - 'A' + 10)) / base < sum 。 看见没? 压根不给你因为累加而出现
溢出的机会。 上面表示了在不越界的前提下,如果计入当前扫描的字符,前面那些位所能组成的最大数值。
需要厘清的问题点基本都厘清了,但还有少许不明晰的,也是题目中未指明的。
(1)传入NULL指针,返回什么?
(2)扫描到非法字符时,返回什么?非法字符串由end_ptr指向,但是否需要返回当前已经扫描到的数值呢?
(3)扫描过程中,发现溢出时,是返回当前已经扫描到的数值,还是该符号对应的,能表示的最大或最小值
呢?比如,负数的最小是INT_MIN(如-2147483648),正数最大是INT_MAX(如2147483647)。
(4)扫描数字,字母过程中,如果遇到空格或者制表符该如何处理? 如123 456 34 ,此时是返回12345634,
还是想scanf读入字符串那样返回123,一次扫描结束,并提示后面的“ 456 34 ”为非法字符串?
鉴于题目中并未明确说明,我也未从面试官那里获悉这些。所以这里我们可以“自由处理”,当然,也可以
参照strtol函数的处理结果。废话说了那么多,该上点实在的代码了。
- #include <stdio.h>
- #include <ctype.h>
- #include <limits.h>
- #include <string.h>
- #define E_NULL_POINTER -1
- #define E_INV_CHAR -2
- #define E_OUT_RANGE -3
- #define NO_ERROR 0
- #define MAX_INPUT_STR_LEN 256
- #define MIN_BASE 2
- #define MAX_BASE 36
- int my_strtoi(const char *string,char **endptr,int base)
- {
- char *p = string;
- int sum = 0;
- int sign = 1;
- unsigned int overflow = 0;
- if(NULL == p)
- {
- printf("\ninput string is NULL , can't be converted to int \n");
- return E_NULL_POINTER;
- }
- while(isspace(*p))
- {
- p++;
- }
- if(*p == '-')
- {
- sign = -1;
- p++;
- }
- else if(*p == '+')
- {
- p++;
- }
- if(-1 == sign)
- {
- overflow = -INT_MIN;
- }
- else
- {
- overflow = INT_MAX;
- }
- if((base < MIN_BASE && base != 0) || (base > MAX_BASE))
- {
- base = 10;
- }
- else if(base == 0)
- {
- base = 10;
- if(*p == '0')
- {
- base = 8;
- p++;
- if(*p == 'x' || *p == 'X')
- {
- base = 16;
- p++;
- }
- }
- }
- printf("\ninput string is %s , base = %d \n",string,base);
- /*
- For the "if sentence " below :
- if *p is found to be '\0', that indicates that there is no valid character.
- otherwise, it indicates that the first invalid character is found.
- We may just handle the case that *p == '\0', because the other cases will be
- handle in the following while loop as well.
- */
- if(!isalnum(*p))
- {
- *endptr = p;
- return E_INV_CHAR;
- }
- while(*p != '\0')
- {
- if(isdigit(*p))
- {
- if( (*p - '0') > base -1)
- {
- *endptr = p;
- return E_INV_CHAR;
- }
- else
- {
- if((overflow - (*p - '0')) / base < sum)
- {
- *endptr = p;
- return E_OUT_RANGE;
- }
- else
- {
- sum = sum * base + (*p - '0');
- }
- }
- }
- else if(isupper(*p))
- {
- if((*p - 'A' + 10) > (base -1))
- {
- *endptr = p;
- return E_INV_CHAR;
- }
- else
- {
- if((overflow - (*p - 'A' + 10)) / base < sum)
- {
- *endptr = p;
- return E_OUT_RANGE;
- }
- else
- {
- sum = sum * base + (*p - 'A' + 10);
- }
- }
- }
- else if(islower(*p))
- {
- if((*p - 'a' + 10) > (base -1))
- {
- *endptr = p;
- return E_INV_CHAR;
- }
- else
- {
- if((overflow - (*p - 'a' + 10)) / base < sum)
- {
- *endptr = p;
- return E_OUT_RANGE;
- }
- else
- {
- sum = sum * base + (*p - 'a' + 10);
- }
- }
- }
- else
- {
- *endptr = p;
- return E_INV_CHAR;
- }
- p++;
- }
- return (sum * sign);
- }
- void strtoi_ret_check(int ret, char **ppc_invalid)
- {
- if(E_NULL_POINTER == ret)
- {
- printf("Error : invalid argument, input string can not be NULL \n");
- }
- else if (E_INV_CHAR == ret)
- {
- if(**ppc_invalid == '\0')
- {
- printf("Error : No valid char ( digit or letter) is found \n");
- }
- else
- {
- printf("Error : Invalid char: %s is found during conversion \n",*ppc_invalid);
- }
- }
- else if(E_OUT_RANGE == ret)
- {
- printf("Error : Overflow is found at %s , input string is too long \n",*ppc_invalid);
- }
- else
- {
- // printf("Input string %s is converted to int : %d \n");
- printf("Input string is converted to int : %d \n",ret);
- }
- }
- int main(void)
- {
- // your code goes here
- int ret;
- char *temp = NULL;
- char **end_ptr = &temp;
- char test_str[MAX_INPUT_STR_LEN] = {0};
- int len;
- printf("INT_MIN = %d , INT_MAX = %d \n", INT_MIN,INT_MAX);
- ret = my_strtoi(NULL,&temp,0);
- strtoi_ret_check(ret,end_ptr);
- ret = my_strtoi(" ",end_ptr,0);
- strtoi_ret_check(ret,end_ptr);
- ret = my_strtoi(" -0123",end_ptr,0);
- strtoi_ret_check(ret,end_ptr);
- ret = my_strtoi(" -123",end_ptr,16);
- strtoi_ret_check(ret,end_ptr);
- ret = my_strtoi(" 0X123",end_ptr,16);
- strtoi_ret_check(ret,end_ptr);
- ret = my_strtoi(" 012389",end_ptr,0);
- strtoi_ret_check(ret,end_ptr);
- ret = my_strtoi(" 2ab",end_ptr,16);
- strtoi_ret_check(ret,end_ptr);
- ret = my_strtoi(" 24 ",end_ptr,16);
- strtoi_ret_check(ret,end_ptr);
- ret = my_strtoi(" 46",end_ptr,36);
- strtoi_ret_check(ret,end_ptr);
- ret = my_strtoi(" 234",end_ptr,38);
- strtoi_ret_check(ret,end_ptr);
- ret = my_strtoi(" 1234567890123",end_ptr,38);
- strtoi_ret_check(ret,end_ptr);
- ret = my_strtoi(" aaaaaaaa123456 ",end_ptr,16);
- strtoi_ret_check(ret,end_ptr);
- while(1)
- {
- printf("\nplease input the string you want to convert to integer : \n");
- scanf("%s",test_str);
- /*
- fgets(test_str,MAX_INPUT_STR_LEN + 1,stdin);
- len = strlen(test_str);
- if(len > 0)
- {
- test_str[len - 1] = '\0';
- }
- */
- if(strcmp(test_str,"exit") == 0)
- {
- break;
- }
- ret = my_strtoi(test_str,end_ptr,0);
- strtoi_ret_check(ret,end_ptr);
- }
- printf("process finishes \n");
- getchar();
- return 0;
- }
#include <stdio.h>
#include <ctype.h>
#include <limits.h>
#include <string.h>
#define E_NULL_POINTER -1
#define E_INV_CHAR -2
#define E_OUT_RANGE -3
#define NO_ERROR 0
#define MAX_INPUT_STR_LEN 256
#define MIN_BASE 2
#define MAX_BASE 36
int my_strtoi(const char *string,char **endptr,int base)
{
char *p = string;
int sum = 0;
int sign = 1;
unsigned int overflow = 0;
if(NULL == p)
{
printf("\ninput string is NULL , can't be converted to int \n");
return E_NULL_POINTER;
}
while(isspace(*p))
{
p++;
}
if(*p == '-')
{
sign = -1;
p++;
}
else if(*p == '+')
{
p++;
}
if(-1 == sign)
{
overflow = -INT_MIN;
}
else
{
overflow = INT_MAX;
}
if((base < MIN_BASE && base != 0) || (base > MAX_BASE))
{
base = 10;
}
else if(base == 0)
{
base = 10;
if(*p == '0')
{
base = 8;
p++;
if(*p == 'x' || *p == 'X')
{
base = 16;
p++;
}
}
}
printf("\ninput string is %s , base = %d \n",string,base);
/*
For the "if sentence " below :
if *p is found to be '\0', that indicates that there is no valid character.
otherwise, it indicates that the first invalid character is found.
We may just handle the case that *p == '\0', because the other cases will be
handle in the following while loop as well.
*/
if(!isalnum(*p))
{
*endptr = p;
return E_INV_CHAR;
}
while(*p != '\0')
{
if(isdigit(*p))
{
if( (*p - '0') > base -1)
{
*endptr = p;
return E_INV_CHAR;
}
else
{
if((overflow - (*p - '0')) / base < sum)
{
*endptr = p;
return E_OUT_RANGE;
}
else
{
sum = sum * base + (*p - '0');
}
}
}
else if(isupper(*p))
{
if((*p - 'A' + 10) > (base -1))
{
*endptr = p;
return E_INV_CHAR;
}
else
{
if((overflow - (*p - 'A' + 10)) / base < sum)
{
*endptr = p;
return E_OUT_RANGE;
}
else
{
sum = sum * base + (*p - 'A' + 10);
}
}
}
else if(islower(*p))
{
if((*p - 'a' + 10) > (base -1))
{
*endptr = p;
return E_INV_CHAR;
}
else
{
if((overflow - (*p - 'a' + 10)) / base < sum)
{
*endptr = p;
return E_OUT_RANGE;
}
else
{
sum = sum * base + (*p - 'a' + 10);
}
}
}
else
{
*endptr = p;
return E_INV_CHAR;
}
p++;
}
return (sum * sign);
}
void strtoi_ret_check(int ret, char **ppc_invalid)
{
if(E_NULL_POINTER == ret)
{
printf("Error : invalid argument, input string can not be NULL \n");
}
else if (E_INV_CHAR == ret)
{
if(**ppc_invalid == '\0')
{
printf("Error : No valid char ( digit or letter) is found \n");
}
else
{
printf("Error : Invalid char: %s is found during conversion \n",*ppc_invalid);
}
}
else if(E_OUT_RANGE == ret)
{
printf("Error : Overflow is found at %s , input string is too long \n",*ppc_invalid);
}
else
{
// printf("Input string %s is converted to int : %d \n");
printf("Input string is converted to int : %d \n",ret);
}
}
int main(void)
{
// your code goes here
int ret;
char *temp = NULL;
char **end_ptr = &temp;
char test_str[MAX_INPUT_STR_LEN] = {0};
int len;
printf("INT_MIN = %d , INT_MAX = %d \n", INT_MIN,INT_MAX);
ret = my_strtoi(NULL,&temp,0);
strtoi_ret_check(ret,end_ptr);
ret = my_strtoi(" ",end_ptr,0);
strtoi_ret_check(ret,end_ptr);
ret = my_strtoi(" -0123",end_ptr,0);
strtoi_ret_check(ret,end_ptr);
ret = my_strtoi(" -123",end_ptr,16);
strtoi_ret_check(ret,end_ptr);
ret = my_strtoi(" 0X123",end_ptr,16);
strtoi_ret_check(ret,end_ptr);
ret = my_strtoi(" 012389",end_ptr,0);
strtoi_ret_check(ret,end_ptr);
ret = my_strtoi(" 2ab",end_ptr,16);
strtoi_ret_check(ret,end_ptr);
ret = my_strtoi(" 24 ",end_ptr,16);
strtoi_ret_check(ret,end_ptr);
ret = my_strtoi(" 46",end_ptr,36);
strtoi_ret_check(ret,end_ptr);
ret = my_strtoi(" 234",end_ptr,38);
strtoi_ret_check(ret,end_ptr);
ret = my_strtoi(" 1234567890123",end_ptr,38);
strtoi_ret_check(ret,end_ptr);
ret = my_strtoi(" aaaaaaaa123456 ",end_ptr,16);
strtoi_ret_check(ret,end_ptr);
while(1)
{
printf("\nplease input the string you want to convert to integer : \n");
scanf("%s",test_str);
/*
fgets(test_str,MAX_INPUT_STR_LEN + 1,stdin);
len = strlen(test_str);
if(len > 0)
{
test_str[len - 1] = '\0';
}
*/
if(strcmp(test_str,"exit") == 0)
{
break;
}
ret = my_strtoi(test_str,end_ptr,0);
strtoi_ret_check(ret,end_ptr);
}
printf("process finishes \n");
getchar();
return 0;
}
程序在DEV-C++ 5.6.2的运行结果为:
- INT_MIN = -2147483648 , INT_MAX = 2147483647
- input string is NULL , can't be converted to int
- Error : invalid argument, input string can not be NULL
- input string is , base = 10
- Error : No valid char ( digit or letter) is found
- input string is -0123 , base = 8
- Input string is converted to int : -83
- input string is -123 , base = 16
- Input string is converted to int : -291
- input string is 0X123 , base = 16
- Error : Invalid char: X123 is found during conversion
- input string is 012389 , base = 8
- Error : Invalid char: 89 is found during conversion
- input string is 2ab , base = 16
- Input string is converted to int : 683
- input string is 24 , base = 16
- Error : Invalid char: is found during conversion
- input string is 46 , base = 36
- Input string is converted to int : 150
- input string is 234 , base = 10
- Input string is converted to int : 234
- input string is 1234567890123 , base = 10
- Error : Overflow is found at 123 , input string is too long
- input string is aaaaaaaa123456 , base = 16
- Error : Overflow is found at a123456 , input string is too lon
- please input the string you want to convert to integer :
- 123 456 a
- input string is 123 , base = 10
- Input string is converted to int : 123
- please input the string you want to convert to integer :
- input string is 456 , base = 10
- Input string is converted to int : 456
- please input the string you want to convert to integer :
- input string is a , base = 10
- Error : Invalid char: a is found during conversion
- please input the string you want to convert to integer :
- 2147483646 2147483648
- input string is 2147483646 , base = 10
- Input string is converted to int : 2147483646
- please input the string you want to convert to integer :
- input string is 2147483648 , base = 10
- Error : Overflow is found at 8 , input string is too long
- please input the string you want to convert to integer :
- exit
- process finishes
- --------------------------------
- Process exited with return value 0
- Press any key to continue . . .
INT_MIN = -2147483648 , INT_MAX = 2147483647
input string is NULL , can't be converted to int
Error : invalid argument, input string can not be NULL
input string is , base = 10
Error : No valid char ( digit or letter) is found
input string is -0123 , base = 8
Input string is converted to int : -83
input string is -123 , base = 16
Input string is converted to int : -291
input string is 0X123 , base = 16
Error : Invalid char: X123 is found during conversion
input string is 012389 , base = 8
Error : Invalid char: 89 is found during conversion
input string is 2ab , base = 16
Input string is converted to int : 683
input string is 24 , base = 16
Error : Invalid char: is found during conversion
input string is 46 , base = 36
Input string is converted to int : 150
input string is 234 , base = 10
Input string is converted to int : 234
input string is 1234567890123 , base = 10
Error : Overflow is found at 123 , input string is too long
input string is aaaaaaaa123456 , base = 16
Error : Overflow is found at a123456 , input string is too lon
please input the string you want to convert to integer :
123 456 a
input string is 123 , base = 10
Input string is converted to int : 123
please input the string you want to convert to integer :
input string is 456 , base = 10
Input string is converted to int : 456
please input the string you want to convert to integer :
input string is a , base = 10
Error : Invalid char: a is found during conversion
please input the string you want to convert to integer :
2147483646 2147483648
input string is 2147483646 , base = 10
Input string is converted to int : 2147483646
please input the string you want to convert to integer :
input string is 2147483648 , base = 10
Error : Overflow is found at 8 , input string is too long
please input the string you want to convert to integer :
exit
process finishes
--------------------------------
Process exited with return value 0
Press any key to continue . . .
(1)传入NULL指针,返回什么? (此处我们定义了E_NULL_POINTER专门处理这种情况)
(2)扫描到非法字符时,返回什么?非法字符串由end_ptr指向,但是否需要返回当前已经扫描到的数值呢?
(此处定义了E_INV_CHAR来对应这种情况,并不返回已经扫描到的数值)
(3)扫描过程中,发现溢出时,是返回当前已经扫描到的数值,还是该符号对应的,能表示的最大或最小值
呢?比如,负数的最小是INT_MIN(如-2147483648),正数最大是INT_MAX(如2147483647)。
(此处定义了E_OUT_RANGE来对应这种情况,并不返回已经扫描到的数值,也不返回INT_MAX或INT_MIN)
(4)扫描数字,字母过程中,如果遇到空格或者制表符该如何处理? 如123 456 34 ,此时是返回12345634,
还是想scanf读入字符串那样返回123,一次扫描结束,并提示后面的“ 456 34 ”为非法字符串?
(此处定义了E_INV_CHAR来对应这种情况,并不返回已经扫描到的数值)
需要说明下程序中需要注意的几点:
(1)对传入NULL指针或者只包含空格,制表符字符串指针的特殊情况的处理。
(2)程序对溢出的判断方式,及比较对象采用的是绝对值(比较时针对绝对值)
(3)对base的判断和计算。注意,此代码仅处理base为0,数字,字母串以0或者0x,0X开头的情况,并不能
处理base为8,数字,字母串以0; 或者base为16,字母串以0或者0x,0X开头的情况。请特别注意!!
(4)base进制下,可以表达的最大数值为base - 1 。从左到右扫描字符串转为字符串的“计算公式”。
(5)将扫描到的字符如何转为数值。例如对于数字字符,[扫描数字字符] - '0' + 10 才能转换为数值。
(6)指针的使用。要是用错了,很容易出现指针的越界访问,或者访问空指针这样的情况。请看main中测试
程序代码部分,如果是用初始化为NULL的temp,需要传入&temp,因为my_strtoi中会根据错误情况修改指针
指向;如果是传入char **,请记得将其先指向一个char *的指针,不然my_strtoi中修改end_ptr,用到*end_ptr
时,引用的就是非法内存了。
(7)上面的main程序测试代码中,我们看到用的是scanf("%s",test_str);读入输入字符串。当然,带来的问题
是明显的,如果我们输入123 456 ab 会被当做3个字符串。我们会看到代码中也有给出使用fgets的例子,但
已经被注释掉了,因为fgets读入的时候会在字符串末尾加入一个换行符'\n',所以需要单独处理下。
上面贴出的程序有哪些问题呢?
(1)设定的几种返回值,明显会与输入串为-1,-2这样的情况冲突。
(2)效率有待提高。比如可以用下register变量。
(3)使用了ctype.h里面判断字符类型的一系列函数,这个理论情况下是没有的,得自己实现。
(4)判断溢出,每次都要做那么“复杂”的运算,低效。这种判断方法有待改进。
如果自己大脑堵塞,找不到办法去优化,改进怎么办? Google,百度找别人写的程序,如果可以,找glibc
中函数的实现。毕竟glibc可是那么多大拿编写和维护的。说道这里,不得不开启本文的兄弟篇——