摘自:
程序员编程艺术:第二章、字符串是否包含及匹配/查找/转换/拷贝问题
5.3、字符串转换为整数
题目:输入一个表示整数的字符串,把该字符串转换成整数并输出。
例如输入字符串"345",则输出整数345。
分析:此题看起来,比较简单,每扫描到一个字符,我们把在之前得到的数字乘以10再加上当前字符表示的数字。这个思路用循环不难实现。然其背后却隐藏着不少陷阱,正如zhedahht 所说,有以下几点需要你注意:
1、由于整数可能不仅仅之含有数字,还有可能以'+'或者'-'开头,表示整数的正负。如果第一个字符是'+'号,则不需要做任何操作;如果第一个字符是'-'号,则表明这个整数是个负数,在最后的时候我们要把得到的数值变成负数。
2、如果使用的是指针的话,在使用指针之前,我们要做的第一件是判断这个指针是不是为空。如果试着去访问空指针,将不可避免地导致程序崩溃(此第2点在下面的程序不需注意,因为没有用到指针)。
3、输入的字符串中可能含有不是数字的字符。
每当碰到这些非法的字符,我们就没有必要再继续转换。
4、溢出问题。由于输入的数字是以字符串的形式输入,因此有可能输入一个很大的数字转换之后会超过能够表示的最大的整数而溢出。
总结以上四点,代码可以如下编写:
- //字符串转换为整数
- //copyright@ yansha
- #include <iostream>
- #include <string>
- using namespace std;
- int str_2_int(string str)
- {
- if (str.size() == 0)
- exit(0);
- int pos = 0;
- int sym = 1;
- // 处理符号
- if (str[pos] == '+')
- pos++;
- else if (str[pos] == '-')
- {
- pos++;
- sym = -1;
- }
- int num = 0;
- // 逐位处理
- while (pos < str.length())
- {
- // 处理数字以外的字符
- if (str[pos] < '0' || str[pos] > '9')
- exit(0);
- num = num * 10 + (str[pos] - '0');
- // 处理溢出
- if (num < 0)
- exit(0);
- pos++;
- }
- num *= sym;
- return num;
- }
- int main()
- {
- string str = "-3450";
- int num = str_2_int(str);
- cout << num << endl;
- return 0;
- }
@helloword:这个的实现非常不好,当输入字符串参数为非法时,不是抛出异常不是返回error code,而是直接exit了。直接把进程给终止了,想必现实应用中的实现都不会这样。建议您改改,不然拿到面试官那,会被人喷死的。ok,听从他的建议,借用zhedahht的代码了:
- //http://zhedahht.blog.163.com/blog/static/25411174200731139971/
- enum Status {kValid = 0, kInvalid};
- int g_nStatus = kValid;
- int StrToInt(const char* str)
- {
- g_nStatus = kInvalid;
- long long num = 0;
- if(str != NULL)
- {
- const char* digit = str;
- // the first char in the string maybe '+' or '-'
- bool minus = false;
- if(*digit == '+')
- digit ++;
- else if(*digit == '-')
- {
- digit ++;
- minus = true;
- }
- // the remaining chars in the string
- while(*digit != '/0')
- {
- if(*digit >= '0' && *digit <= '9')
- {
- num = num * 10 + (*digit - '0');
- // overflow
- if(num > std::numeric_limits<int>::max())
- {
- num = 0;
- break;
- }
- digit ++;
- }
- // if the char is not a digit, invalid input
- else
- {
- num = 0;
- break;
- }
- }
- if(*digit == '/0')
- {
- g_nStatus = kValid;
- if(minus)
- num = 0 - num;
- }
- }
- return static_cast<int>(num);
- }
updated:yansha看到了上述helloword的所说的后,修改如下:
- #include <iostream>
- #include <string>
- #include <assert.h>
- using namespace std;
- int str_2_int(string str)
- {
- assert(str.size() > 0);
- int pos = 0;
- int sym = 1;
- // 处理符号
- if (str[pos] == '+')
- pos++;
- else if (str[pos] == '-')
- {
- pos++;
- sym = -1;
- }
- int num = 0;
- // 逐位处理
- while (pos < str.length())
- {
- // 处理数字以外的字符
- assert(str[pos] >= '0');
- assert(str[pos] <= '9');
- num = num * 10 + (str[pos] - '0');
- // 处理溢出
- assert(num >= 0);
- pos++;
- }
- num *= sym;
- return num;
- }
- int main()
- {
- string str = "-1024";
- int num = str_2_int(str);
- cout << num << endl;
- return 0;
- }
5.4、字符串拷贝
题目描述:
要求实现库函数strcpy,
原型声明:extern char *strcpy(char *dest,char *src);
功能:把src所指由NULL结束的字符串复制到dest所指的数组中。
说明:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。
返回指向dest的指针。
分析:如果编写一个标准strcpy函数的总分值为10,下面给出几个不同得分的答案:
- //2分
- void strcpy( char *strDest, char *strSrc )
- {
- while( (*strDest++ = * strSrc++) != '/0' );
- }
- //4分
- void strcpy( char *strDest, const char *strSrc )
- {
- //将源字符串加const,表明其为输入参数,加2分
- while( (*strDest++ = * strSrc++) != '/0' );
- }
- //7分
- void strcpy(char *strDest, const char *strSrc)
- {
- //对源地址和目的地址加非0断言,加3分
- assert( (strDest != NULL) && (strSrc != NULL) );
- while( (*strDest++ = * strSrc++) != '/0' );
- }
- //10分
- //为了实现链式操作,将目的地址返回,加3分!
- char * strcpy( char *strDest, const char *strSrc )
- {
- assert( (strDest != NULL) && (strSrc != NULL) );
- char *address = strDest;
- while( (*strDest++ = * strSrc++) != '/0' );
- return address;
- }
勘误:上述字符串拷贝的写法有点问题,正如本文评论下留言的读者paranoid2006所述:字符串拷贝的问题,虽然限定源字符串为const,但是不能防止src和dest有overlap,层叠的问题。也就是在拷贝src到dest的中途会导致dest覆盖部分src的字符串。
当dest+strlen(src)大于src或者src+strlen(src)大于dest均有可能导致覆盖。