【问题标题】:Different results when running the function multiple times - C多次运行该函数时的不同结果 - C
【发布时间】:2017-12-17 07:01:42
【问题描述】:

我编写了一个函数,当我运行它时它运行良好,但是当我用不同的输入多次运行它时出现问题。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#define MAX_SIZE 20

int SumStr(char *str) {
  int i = 0, j = 0, num = 0, tempnum = 0;
  char temp[MAX_SIZE];
  while (*(str + i) != 0) { //While not NULL - Checked
    while (((*(str + i)) >= 48) && ((*(str + i)) <= 57)) { //while str[i] is [0-9]
        *(temp + j) = *(str + i);
        ++j; ++i;
    }
    if (j != 0) {
        tempnum = atoi(temp);
        num = tempnum + num;
        tempnum = 0;
        j = 0;
    }
    ++i;
}
return num;
}

void Test3(char *arr, int sum)
{

int tempSum = SumStr(arr);
if (tempSum != sum)
{
    printf("Your Output is %d, Expected: %d (-3)\n", tempSum, sum);
}
}

void main() {

Test3("ax3b5mt11f", 19);
Test3("5$5$5", 15);
Test3("1234", 1234);
Test3("1$0!100", 101);
Test3("1$1!1", 3);
}

该函数的目的是对字符串中的所有数字求和。

当我用这个 main 运行一次函数时(例如),它运行良好;

void main() {
Test3("1$0!100", 101);
}

Output: num=101

但是当 main 用不同的输入多次运行函数时,输出完全错误。


这个main的输出;

void main() {
Test3("ax3b5mt11f", 19);
Test3("5$5$5", 15);
Test3("1234", 1234);
Test3("1$0!100", 101);
Test3("1$1!1", 3);
}

是;

Your Output is 6871, Expected: 15
Your Output is 6718, Expected: 1234
Your Output is 5024, Expected: 101

【问题讨论】:

  • 除非你的任务是尽可能写出最模糊的代码,否则((*(str + i)) &gt;= 48) &amp;&amp; ((*(str + i)) &lt;= 57) 真的应该是(s[i] &gt;= '0' &amp;&amp; s[i] &lt;= '9')
  • 或者,比明确的范围更好,使用来自&lt;ctype.h&gt;isdigit()
  • 实际上,我正在使用指针进行测试,这就是这段代码写成这样的原因。此外,我不能使用 ctype.h,我只能使用我在代码中已经提到的库。谢谢!
  • 不,这个任务不会强迫你写出这种可憎的东西。您可以随时添加一个临时的char c = *(str + i);。无论您的任务标准如何,与幻数的比较都是不可原谅的。
  • @StoryTeller 我完全同意这段代码很糟糕。感谢 cmets!

标签: c function memory-leaks


【解决方案1】:

一个问题是在将temp-string 传递给atoi 之前,您没有使用'\0'-字符终止它。 你可以写:

temp[j] = '\0';  // or: *(temp + j) = '\0';
if (j != 0) { ...

请注意,将未以'\0' 正确终止的字符序列传递给需要(终止的)字符串的函数会产生未定义的行为;这可能就是你当时观察到的。

其次,如果输入字符串的最后一个字符是数字,则将i 增加两次,从而错过输入的字符串终止字符。这再次导致未定义的行为。为了克服这个问题,您可以在增加 i 之前检查您是否位于字符串的末尾:

    if (*(str + i) != 0) {
      ++i;
    }

【讨论】:

  • 是的,这是个问题,但大多数时候对atoi 来说并不重要。有了这个修复,我仍然有 3 次失误。
  • 我在将它传递给 atoi 之前尝试将 NULL temp 设为 NULL,但仍然有 3 次未命中。
  • @LiorGingihashvili 正确终止修复 this 问题的相关性有点没有意义。不这样做是完全错误的,如果在调用 any 函数(标准库或其他)之前没有终止该字符串,则会调用未定义的行为。即使它没有“修复”这个问题,它也应该被修复,因为它 (a) 消除了未定义的行为,并且 (b) 消除了 is 的可能性i> 导致您的问题。
  • @Jean-François Fabre:对;两者都导致UB,所以我只是从问题中删除了“主要”。
  • @StephanLechner 这真的帮助我理解并解决了这个问题,非常感谢。也谢谢大家的建议!
【解决方案2】:

除了不终止 temp[j] 之外,您还可以通过在内部循环和外部循环中增加 i 来跳过 str 中的终止字符。 当您跳过终止时,它会开始处理下一个字符串 [或取决于您的编译器/操作系统,任何旧废话]。

顺便说一句,while (isdigit(str[i])) 不仅比你的更具可读性、可移植性和高效性,而且还不需要注释。

【讨论】:

  • isdigit 确实更具可读性;它也是从ctype.h 定义的,OP 表示他们不能使用。
【解决方案3】:

我要祝贺Lior Gingihashvili 使用问题中显示的测试代码。也就是说,IMO,一种出色的工作方式。

此代码不会尝试修复原始代码 — accepted answer 已经完成了。此代码显示了解决问题的完全不同的方式。它使用一组不同的库函数——strcspn()strtol()——并且它避免了对源字符串的任何复制(因此它不会遇到空终止字符串的问题)。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static
int SumStr(const char *str)
{
    int sum = 0;
    str += strcspn(str, "0123456789");
    while (*str >= '0' && *str <= '9')
    {
        /*
        ** Since *str is a digit, the value will not be negative and
        ** strtol() won't fail unless the value is too big for a long.
        ** Since we can't include <limits.h> (or <assert.h>, or
        ** <errno.h>), I plan to ignore overflow.
        */
        char *eon;
        long val = strtol(str, &eon, 10);
        sum += (int)val;
        str = eon + strcspn(eon, "0123456789");
    }
    return sum;
}

static
void Test3(char *arr, int sum)
{
    int tempSum = SumStr(arr);
    const char *pass_fail = (tempSum == sum) ? "**PASS**" : "!!FAIL!!";
    printf("%s Output: %5d, Expected: %5d, Data (%s)\n",
           pass_fail, tempSum, sum, arr);
}

int main(void)
{
    Test3("ax3b5mt11f", 19);
    Test3("5$5$5", 15);
    Test3("1234", 1234);
    Test3("1$0!100", 101);
    Test3("1$1!1", 3);
    Test3("x$.!-_+", 0);
    Test3("xxx1000y2345z3456=23412!", 30213);
    Test3("-0+0-0+0-", 0);
    return 0;
}

运行时,代码产生:

**PASS** Output:    19, Expected:    19, Data (ax3b5mt11f)
**PASS** Output:    15, Expected:    15, Data (5$5$5)
**PASS** Output:  1234, Expected:  1234, Data (1234)
**PASS** Output:   101, Expected:   101, Data (1$0!100)
**PASS** Output:     3, Expected:     3, Data (1$1!1)
**PASS** Output:     0, Expected:     0, Data (x$.!-_+)
**PASS** Output: 30213, Expected: 30213, Data (xxx1000y2345z3456=23412!)
**PASS** Output:     0, Expected:     0, Data (-0+0-0+0-)

(是的:当我在代码中遇到错误时,我确实得到了!!FAIL!! 行。)

【讨论】:

  • 我注意到代码安全地为空字符串生成零——其中第一个字节是空字节。
【解决方案4】:

正确运行的程序:

//Explanations in the commemnts

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#define MAX_SIZE 20

int SumStr(char *str) {
  int i = 0, j = 0, num = 0, tempnum = 0;
  char temp[MAX_SIZE];

  while (*(str + i) != 0) {
  //While not NULL - Checked

    while (  (( *(str + i)) >= '0') && ((*(str + i)) <= '9')  ) // more readable 
        { //while str[i] is [0-9]
            *(temp + j) = *(str + i);
            ++j;
            ++i;
        }

        if (j != 0) {

            *(temp + j) = 0; // termination of the string needed for atoi

            tempnum = atoi(temp);
            num = tempnum + num;

            tempnum = 0;
            j = 0;
        }

        // new:
        if (*(str + i) != 0) { 
            ++i;
        }
        else break; // detection of the end of string
    }


    return num;
}

void Test3(const char *arr, int sum)
{

    int tempSum = SumStr(arr);
    if (tempSum != sum)
    {
        printf("Your Output is %d, Expected: %d (-3)\n", tempSum, sum);
    }
}

void main() {

    Test3("ax3b5mt11f", 19);
    Test3("5$5$5", 15);
    Test3("1234", 1234);
    Test3("1$0!100", 101);
    Test3("1$1!1", 3);
    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-12
    • 2021-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-07
    相关资源
    最近更新 更多