簡體   English   中英

沒有任何庫的 C 字符串到 int

[英]C string to int without any libraries

我正在嘗試編寫我的第一個內核模塊,所以我無法包含 atoi、strtol 等庫。如果沒有這些內置函數,如何將字符串轉換為 int? 我試過了:

int num;
num = string[0] - '0';

這適用於第一個字符,但如果我刪除[0]以嘗試轉換完整的字符串,它會給我一個警告:賦值從指針中生成整數而沒有強制轉換。 那我該怎么辦?

在為 int 函數創建自己的字符串時,請確保檢查並防止溢出。 例如:

/* an atoi replacement performing the conversion in a single
   pass and incorporating 'err' to indicate a failed conversion.
   passing NULL as error causes it to be ignored */
int strtoi (const char *s, unsigned char *err)
{
    char *p = (char *)s;
    int nmax = (1ULL << 31) - 1;    /* INT_MAX  */
    int nmin = -nmax - 1;           /* INT_MIN  */
    long long sum = 0;
    char sign = *p;

    if (*p == '-' || *p == '+') p++;

    while (*p >= '0' && *p <= '9') {
        sum = sum * 10 - (*p - '0');
        if (sum < nmin || (sign != '-' && -sum > nmax)) goto error;
        p++;
    }

    if (sign != '-') sum = -sum;

    return (int)sum;

error:

    fprintf (stderr, "strtoi() error: invalid conversion for type int.\n");
    if (err) *err = 1;
    return 0;
}

您無法刪除 [0]。 這意味着您從指針string中減去 '0' ,這是沒有意義的。 您仍然需要取消引用它: num = string[i] - '0';

此函數跳過前導和尾隨空格,處理一個可選的+ / -符號,並在無效輸入時返回 0,

// Convert standard null-terminated string to an integer
// - Skips leading whitespaces.
// - Skips trailing whitespaces.
// - Allows for one, optional +/- sign at the front.
// - Returns zero if any non-+/-, non-numeric, non-space character is encountered.
// - Returns zero if digits are separated by spaces (eg "123  45")
// - Range is checked against Overflow/Underflow (INT_MAX / INT_MIN), and returns 0.
int StrToInt(const char* s)
{
    int minInt = 1 << (sizeof(int)*CHAR_BIT-1);
    int maxInt = -(minInt+1);
    char* w;

    do { // Skip any leading whitespace
        for(w=" \t\n\v\f\r"; *w && *s != *w; ++w) ;
        if (*s == *w) ++s; else break;
    } while(*s);

    int sign = 1;
    if ('-' == *s) sign = -1; 
    if ('+' == *s || '-' == *s) ++s;

    long long i=0;
    while('0' <= *s && *s <= '9')
    {
        i = 10*i + *s++ - '0';
        if (sign*i < minInt || maxInt < sign*i)
        {
            i = 0;
            break;
        }
    }

    while (*s) // Skip any trailing whitespace
    {
        for(w=" \t\n\v\f\r"; *w && *s != *w; ++w) ;
        if (*w && *s == *w) ++s; else break;
    }

    return (int)(!*s*sign*i);
}
  1. 字符串是一個字符array ,由地址(又名pointer )表示。

  2. pointer的值可能類似於0xa1de2bdf 這個值告訴我數組的開始在哪里。

  3. 您不能用character類型減去pointer類型(例如 0xa1de2bdf - 'b' 沒有真正意義)。

要將字符串轉換為數字,您可以嘗試以下操作:

//Find the length of the string
int len = 0;
while (str[len] != '\0') { 
   len++;
}

//Loop through the string
int num = 0, i = 0, digit;
for (i=0; i<len; i++) {

   //Extract the digit
   digit = ing[i] - '0';

   //Multiply the digit with its correct position (ones, tens, hundreds, etc.)
   num += digit * pow(10, (len-1)-i);
}

當然,如果您不允許使用math.h庫,您可以編寫自己的pow(a,b)函數,該函數為您提供a^b的值。

int mypowfunc(int a, int b) {
   int i=0, ans=1;
   //multiply the value a for b number of times
   for (i=0; i<b; i++) {
      ans *= a;
   }   
   return ans;
}

我以一種易於理解的方式編寫了上面的代碼。 它假定您的字符串在最后一個有用字符的后面有一個空字符 ('\0')(這是一種很好的做法)。

此外,您可能想要檢查該字符串實際上是一個只有數字的有效字符串(例如,'0'、'1'、'2' 等)。 您可以通過在遍歷字符串時包含if... else..語句來做到這一點。

在現代內核中,您想使用kstrto*

http://lxr.free-electrons.com/source/include/linux/kernel.h#L274

274 /**
275  * kstrtoul - convert a string to an unsigned long
276  * @s: The start of the string. The string must be null-terminated, and may also
277  *  include a single newline before its terminating null. The first character
278  *  may also be a plus sign, but not a minus sign.
279  * @base: The number base to use. The maximum supported base is 16. If base is
280  *  given as 0, then the base of the string is automatically detected with the
281  *  conventional semantics - If it begins with 0x the number will be parsed as a
282  *  hexadecimal (case insensitive), if it otherwise begins with 0, it will be
283  *  parsed as an octal number. Otherwise it will be parsed as a decimal.
284  * @res: Where to write the result of the conversion on success.
285  *
286  * Returns 0 on success, -ERANGE on overflow and -EINVAL on parsing error.
287  * Used as a replacement for the obsolete simple_strtoull. Return code must
288  * be checked.
289 */

“無法包含庫”-> 不清楚是否允許代碼訪問INT_MAXINT_MIN 如果不使用語言提供的宏,如INT_MAXINT_MIN ,就無法以完全可移植的方式確定最小/最大有符號整數。

使用INT_MAXINT_MIN可用。 否則我們可以猜測char寬度是 8。我們可以猜測沒有填充位。 我們可以猜測整數是 2 的補碼。 有了這些合理的假設,最小值和最大值定義如下。

注意:移入符號位是未定義的行為(UB),所以不要這樣做。


讓我們添加另一個限制:制定一個適用於從signed charintmax_t的任何有符號整數的解決方案。 這不允許代碼使用更寬的類型,因為可能沒有更寬的類型。


typedef int Austin_int;

#define Austin_INT_MAXMID ( ((Austin_int)1) << (sizeof(Austin_int)*8 - 2) )
#define Austin_INT_MAX (Austin_INT_MAXMID - 1 + Austin_INT_MAXMID)
#define Austin_INT_MIN (-Austin_INT_MAX - 1)

int Austin_isspace(int ch) {
  const char *ws = " \t\n\r\f\v";
  while (*ws) {
    if (*ws == ch) return 1;
    ws++;
  }
  return 0;
}

// *endptr points to where parsing stopped
// *errorptr indicates overflow
Austin_int Austin_strtoi(const char *s, char **endptr, int *errorptr) {
  int error = 0;
  while (Austin_isspace(*s)) {
    s++;
  }

  char sign = *s;
  if (*s == '-' || *s == '+') {
    s++;
  }

  Austin_int sum = 0;
  while (*s >= '0' && *s <= '9') {
    int ch = *s - '0';
    if (sum <= Austin_INT_MIN / 10 &&
        (sum < Austin_INT_MIN / 10 || -ch < Austin_INT_MIN % 10)) {
      sum = Austin_INT_MIN;
      error = 1;
    } else {
      sum = sum * 10 - ch;
    }
    s++;
  }

  if (sign != '-') {
    if (sum < -Austin_INT_MAX) {
      sum = Austin_INT_MAX;
      error = 1;
    } else {
      sum = -sum;
    }
  }

  if (endptr) {
    *endptr = (char *) s;
  }
  if (errorptr) {
    *errorptr = error;
  }
  return sum;
}

以上取決於Austin_INT_MIN Austin_INT_MIN % 10部分中的 C99 或更高版本。

這是我能想到的最干凈、最安全的方法

int str_to_int(const char * str, size_t n, int * int_value) {
    int i;
    int cvalue;
    int value_muliplier = 1;
    int res_value = 0;
    int neg = 1; // -1 for negative and 1 for whole.
    size_t str_len; // String length.
    int end_at = 0; // Where loop should end.

    if (str == NULL || int_value == NULL || n <= 0)
        return -1;

    // Get string length
    str_len = strnlen(str, n);

    if (str_len <= 0)
        return -1;

    // Is negative.
    if (str[0] == '-') {
        neg = -1;
        end_at = 1; // If negative 0 item in 'str' is skipped.
    }

    // Do the math.
    for (i = str_len - 1; i >= end_at; i--) {
        cvalue = char_to_int(str[i]);

        // Character not a number.
        if (cvalue == -1)
            return -1;

        // Do the same math that is down below.
        res_value += cvalue * value_muliplier;
        value_muliplier *= 10;
    }

    /*
     * "436"
     * res_value = (6 * 1) + (3 * 10) + (4 * 100)
    */

    *int_value = (res_value * neg);
    return 0;
}

int char_to_int(char c) {
    int cvalue = (int)c;

    // Not a number.
    // 48 to 57 is 0 to 9 in ascii.
    if (cvalue < 48 || cvalue > 57)
        return -1;

    return cvalue - 48; // 48 is the value of zero in ascii.
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM