为什么C中的此数组会不断为其分配垃圾值?

时间:2018-08-11 14:21:19

标签: c arrays numbers multiplication

我一直在尝试这个普遍的问题,我必须在C中将两个非常大的数字相乘(不使用stdio.h以外的任何库)。我知道我的算法和代码是正确的,因为它适用于空运行。

但是,这里的ans数组会不断分配自身的垃圾值,对此我找不到任何原因。

输入格式为number1 * number2

以下是我的代码:

#include <stdio.h>

int main(){

char str[200];
gets(str);

int length = 0;
for (int i = 0; ; i++){

    if(str[i]=='\0'){
        break;
    }

    length++;
}


int s1[101], s2[101];
int l1 = 0, l2 = 0, temp = 0;

for(int i = 0; i<length; i++){
    l1++;
    if(str[i]=='*'){
        l1-=2;
        temp = i;
        break;
    }
}

l2 = length-l1;
l2-=3;


for(int i = 0; i<l1; i++){
    s1[i] = str[i]-'0';
}
for(int i = l1+3; i<length; i++){
    s2[i] = str[i]-'0';
}


int a[100],b[100];
int ans[200];
int i,j,tmp;

for(int k = 0; i<200; i++){
    ans[k] = 0;
}

 j = l1 - 1;  
 i = 0;       

while (i < j){
  tmp = s1[i];
  s1[i] = s1[j];
  s1[j] = tmp;
  i++;             
  j--;         
}


j = l2 - 1;  
 i = 0;       

while (i < j){
  tmp = s2[i];
  s2[i] = s2[j];
  s2[j] = tmp;
  i++;             
  j--;         
}

for(i = 0;i < l2;i++){
    for(j = 0;j < l1;j++){
        ans[i+j] += s2[i]*s1[j];
        printf(" %d", ans[i+j]);
    }
}
for(i = 0;i < l1+l2;i++)
{
    tmp = ans[i]/10;
    ans[i] = ans[i]%10;
    ans[i+1] = ans[i+1] + tmp;
}
for(i = l1+l2; i>= 0;i--)
{
    if(ans[i] > 0)
        break;
}
//printf("Product : ");
for(;i >= 0;i--)
{
   //printf("%d",ans[i]);
}
return 0;
}

有人可以帮我解决这个问题,并告诉我为什么会出现此垃圾值,以及如何避免此错误吗?

1 个答案:

答案 0 :(得分:3)

红鲱鱼

原始代码使用了int ans[200] = { 0 };,但根据一些注释,将其更改为:

int ans[200];
for (int k = 0; i < 200; i++){
    ans[k] = 0;
}

该循环显然是错误的;索引k应该被测试并增加,而不是i

但是,原始代码是正确的-根据需要将数组的所有元素初始化为0。很抱歉,您从一些评论员那里得到了“蠢货”。

诊断

我认为您的主要问题是将第二个数字转换为数字字符串。

for (int i = l1 + 3; i < length; i++){
    s2[i] = str[i] - '0';
}

您需要在s2 .. 0范围内分配给l2中的索引,但是您要从l1+3开始索引,而该索引将从{{ 1}}持有垃圾。在下面的解决方案中,我采取了一种懒惰(但有效)的方法:我在循环之前创建了s2,并使用了int z = 0;

(现在,我返回并重读kiner_shahcomment,我看到该评论也诊断出了这一缺陷-但我承认直到理解完该评论后,我才明白我的意思。发布此答案。)

处方

我添加了一些断言,以确保事情按预期进行。我添加了广泛的调试打印。我还将s2[z++] = str[i] - '0';重命名为l1,并将len1重命名为l2;这两个字符的名称很容易与len211混淆。我创建了一个打印功能来显示转换后的数字。我本可以/应该创建一个函数来封装转换过程。

即使我键入12作为数字,当一个简单的测试为第二个数字打印零时,也很容易发现主要问题。巧合的是,它为零。关键是它不是预期的123

请注意,打印结果必须处理特殊情况0。将字符串转换为一系列十进制数字的代码可以发现所有数字是否均为零(简单地将转换后的数字相加;如果结果为零,则所有数字均为零),然后在特殊情况下进行计算(零乘以除以零)。

123

示例测试运行

计算98 x 123

#include <assert.h>
#include <stdio.h>

/* Print digits - ensure each digit is visible with parentheses */
static void print_number(const char *tag, int len, int data[len])
{
    printf("Number %s: [", tag);
    for (int i = 0; i < len; i++)
        printf("(%d)", data[i]);
    printf("]\n");
}

int main(void)
{
    char str[200];
    if (fgets(str, sizeof(str), stdin) == NULL)
        return 1;

    int length;
    for (length = 0; str[length] != '\0' && str[length] != '\n'; length++)
        ;
    str[length] = '\0';     /* Zap newline */

    printf("Calculation: [%s]\n", str);                 // Debug

    int s1[101], s2[101];
    int len1 = 0, len2 = 0;

    /* This is fragile - it depends on the user getting the format right! */
    for (int i = 0; i < length; i++)
    {
        len1++;
        if (str[i] == '*')
        {
            len1 -= 2;
            break;
        }
    }
    assert(str[len1+0] == ' ');                         // Debug
    assert(str[len1+1] == '*');                         // Debug
    assert(str[len1+2] == ' ');                         // Debug

    len2 = length - len1;
    len2 -= 3;

    printf("Number 1: [%.*s]\n", len1, str);            // Debug
    printf("Number 2: [%.*s]\n", len2, &str[len1 + 3]); // Debug

    for (int i = 0; i < len1; i++)
    {
        assert(str[i] >= '0' && str[i] <= '9');         // Debug
        s1[i] = str[i] - '0';
    }
    print_number("1A", len1, s1);                       // Debug
    int z = 0;
    for (int i = len1 + 3; i < length; i++)
    {
        assert(str[i] >= '0' && str[i] <= '9');         // Debug
        s2[z++] = str[i] - '0';
    }
    print_number("2A", len2, s2);                       // Debug

    int ans[200] = { 0 };
    int i, j, tmp;

    /* Reverse digits of first number */
    /* Need a function for this! */
    j = len1 - 1;
    i = 0;
    while (i < j)
    {
        tmp = s1[i];
        s1[i] = s1[j];
        s1[j] = tmp;
        i++;
        j--;
    }

    /* Reverse digits of second number */
    j = len2 - 1;
    i = 0;
    while (i < j)
    {
        tmp = s2[i];
        s2[i] = s2[j];
        s2[j] = tmp;
        i++;
        j--;
    }

    /* Raw multiplication - deal with carries later */
    for (i = 0; i < len2; i++)
    {
        for (j = 0; j < len1; j++)
        {
            ans[i + j] += s2[i] * s1[j];
            printf("[%d+%d] = %d\n", i, j, ans[i + j]);
        }
    }

    /* Deal with carries */
    for (i = 0; i < len1 + len2; i++)
    {
        int old1 = ans[i];                              // Debug
        int old2 = ans[i+1];                            // Debug
        tmp = ans[i] / 10;
        ans[i] = ans[i] % 10;
        ans[i + 1] = ans[i + 1] + tmp;
        printf("Fixup %d: old (%d)(%d) new (%d)(%d)\n", // Debug
               i, old1, old2, ans[i], ans[i+1]);        // Debug
    }

    /* Find most significant digit */
    for (i = len1 + len2; i >= 0; i--)
    {
        if (ans[i] > 0)
            break;
    }
    printf("Significant digits = %d\n", i + 1);         // Debug

    /* Print digits in reverse order */
    j = i;      // Save starting point in j             // Debug
    if (i == -1)                                        // Debug
        putchar('0');                                   // Debug
    else                                                // Debug
    {                                                   // Debug
        printf("Product : ");                           // Debug
        for ( ; i >= 0; i--)                            // Debug
        {                                               // Debug
            printf("(%d)", ans[i]);                     // Debug
        }                                               // Debug
    }                                                   // Debug
    putchar('\n');                                      // Debug
    i = j;      // Recover starting point from j        // Debug

    printf("Product : ");
    if (i == -1)
        putchar('0');
    else
    {
        for ( ; i >= 0; i--)
        {
            printf("%d", ans[i]);
        }
    }
    putchar('\n');

    return 0;
}

计算987654321 x 123456789012

Calculation: [98 * 123]
Number 1: [98]
Number 2: [123]
Number 1A: [(9)(8)]
Number 2A: [(1)(2)(3)]
[0+0] = 24
[0+1] = 27
[1+0] = 43
[1+1] = 18
[2+0] = 26
[2+1] = 9
Fixup 0: old (24)(43) new (4)(45)
Fixup 1: old (45)(26) new (5)(30)
Fixup 2: old (30)(9) new (0)(12)
Fixup 3: old (12)(0) new (2)(1)
Fixup 4: old (1)(0) new (1)(0)
Significant digits = 5
Product : (1)(2)(0)(5)(4)
Product : 12054
Calculation: [987654321 * 123456789012]
Number 1: [987654321]
Number 2: [123456789012]
Number 1A: [(9)(8)(7)(6)(5)(4)(3)(2)(1)]
Number 2A: [(1)(2)(3)(4)(5)(6)(7)(8)(9)(0)(1)(2)]
[0+0] = 2
[0+1] = 4
[0+2] = 6
[0+3] = 8
[0+4] = 10
[0+5] = 12
[0+6] = 14
[0+7] = 16
[0+8] = 18
[1+0] = 5
[1+1] = 8
[1+2] = 11
[1+3] = 14
[1+4] = 17
[1+5] = 20
[1+6] = 23
[1+7] = 26
[1+8] = 9
[2+0] = 8
[2+1] = 11
[2+2] = 14
[2+3] = 17
[2+4] = 20
[2+5] = 23
[2+6] = 26
[2+7] = 9
[2+8] = 0
[3+0] = 20
[3+1] = 32
[3+2] = 44
[3+3] = 56
[3+4] = 68
[3+5] = 80
[3+6] = 72
[3+7] = 72
[3+8] = 81
[4+0] = 40
[4+1] = 60
[4+2] = 80
[4+3] = 100
[4+4] = 120
[4+5] = 120
[4+6] = 128
[4+7] = 145
[4+8] = 72
[5+0] = 67
[5+1] = 94
[5+2] = 121
[5+3] = 148
[5+4] = 155
[5+5] = 170
[5+6] = 194
[5+7] = 128
[5+8] = 63
[6+0] = 100
[6+1] = 133
[6+2] = 166
[6+3] = 179
[6+4] = 200
[6+5] = 230
[6+6] = 170
[6+7] = 111
[6+8] = 54
[7+0] = 138
[7+1] = 176
[7+2] = 194
[7+3] = 220
[7+4] = 255
[7+5] = 200
[7+6] = 146
[7+7] = 94
[7+8] = 45
[8+0] = 180
[8+1] = 202
[8+2] = 232
[8+3] = 271
[8+4] = 220
[8+5] = 170
[8+6] = 122
[8+7] = 77
[8+8] = 36
[9+0] = 205
[9+1] = 238
[9+2] = 280
[9+3] = 232
[9+4] = 185
[9+5] = 140
[9+6] = 98
[9+7] = 60
[9+8] = 27
[10+0] = 240
[10+1] = 284
[10+2] = 238
[10+3] = 193
[10+4] = 150
[10+5] = 110
[10+6] = 74
[10+7] = 43
[10+8] = 18
[11+0] = 285
[11+1] = 240
[11+2] = 196
[11+3] = 154
[11+4] = 115
[11+5] = 80
[11+6] = 50
[11+7] = 26
[11+8] = 9
Fixup 0: old (2)(5) new (2)(5)
Fixup 1: old (5)(8) new (5)(8)
Fixup 2: old (8)(20) new (8)(20)
Fixup 3: old (20)(40) new (0)(42)
Fixup 4: old (42)(67) new (2)(71)
Fixup 5: old (71)(100) new (1)(107)
Fixup 6: old (107)(138) new (7)(148)
Fixup 7: old (148)(180) new (8)(194)
Fixup 8: old (194)(205) new (4)(224)
Fixup 9: old (224)(240) new (4)(262)
Fixup 10: old (262)(285) new (2)(311)
Fixup 11: old (311)(240) new (1)(271)
Fixup 12: old (271)(196) new (1)(223)
Fixup 13: old (223)(154) new (3)(176)
Fixup 14: old (176)(115) new (6)(132)
Fixup 15: old (132)(80) new (2)(93)
Fixup 16: old (93)(50) new (3)(59)
Fixup 17: old (59)(26) new (9)(31)
Fixup 18: old (31)(9) new (1)(12)
Fixup 19: old (12)(0) new (2)(1)
Fixup 20: old (1)(0) new (1)(0)
Significant digits = 21
Product : (1)(2)(1)(9)(3)(2)(6)(3)(1)(1)(2)(4)(4)(8)(7)(1)(2)(0)(8)(5)(2)
Product : 121932631124487120852

计算000 x 0000

$ bc <<< '987654321 * 123456789012'
121932631124487120852
$

关于简化调试的注意事项

这应该可以帮助您前进;有很多可以/应该做的改进。这将保留大多数原始代码。它还显示了如何添加代码(断言和打印操作)以查看发生了什么。当我将其视为早期版本代码的输出时:

Calculation: [000 * 0000]
Number 1: [000]
Number 2: [0000]
Number 1A: [(0)(0)(0)]
Number 2A: [(0)(0)(0)(0)]
[0+0] = 0
[0+1] = 0
[0+2] = 0
[1+0] = 0
[1+1] = 0
[1+2] = 0
[2+0] = 0
[2+1] = 0
[2+2] = 0
[3+0] = 0
[3+1] = 0
[3+2] = 0
Fixup 0: old (0)(0) new (0)(0)
Fixup 1: old (0)(0) new (0)(0)
Fixup 2: old (0)(0) new (0)(0)
Fixup 3: old (0)(0) new (0)(0)
Fixup 4: old (0)(0) new (0)(0)
Fixup 5: old (0)(0) new (0)(0)
Fixup 6: old (0)(0) new (0)(0)
Significant digits = 0
0
Product : 0

很容易看出问题出在哪里(第二个转换循环),从那里很容易诊断出主要问题。调试时,打印输入以确保程序符合您的期望。打印中间结果以检查您是否达到了预期。仔细打印最终结果-确保获得想要的东西。 Calculation: [98 * 123] Number 1: [98] Number 2: [123] Number 1A: [(9)(8) Number 2A: [(0)(0)(0) [0+0] = 0 [0+1] = 0 [1+0] = 0 [1+1] = 0 [2+0] = 0 [2+1] = 0 Fixup 0: old (0)(0) new (0)(0) Fixup 1: old (0)(0) new (0)(0) Fixup 2: old (0)(0) new (0)(0) Fixup 3: old (0)(0) new (0)(0) Fixup 4: old (0)(0) new (0)(0) Significant digits = -1 Product : 的使用说明了“小心”;在打印数字字符串时,很容易发现(%d)是错误的,而仅(19)可能是对还是错,这取决于。

我还将向您介绍C #define macro for debug printing,以获取有关如何使用条件编译来处理调试打印(如果它仍将是长期程序的有用部分)的信息。您可以在我的GitHub上的SOQ(堆栈溢出问题)存储库中找到相关代码,分别位于src/libsoq子目录中的文件19debug.c中。