【问题标题】:Need help debugging C code需要帮助调试 C 代码
【发布时间】:2013-08-12 07:49:09
【问题描述】:

这周刚学了 C。我的任务是从用户那里获取一个大整数输入,将其存储到一个结构整数中,并创建一个函数来将适当的结构整数打印到标准输出中。该程序是这样工作的,但是一旦它给出输出,它就会停止响应。我在编译器中没有得到任何直接错误,也无法找出问题所在。任何其他改进编程风格的建议/技巧也将不胜感激:)

// Header Files Go Here
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// Function Declarations Go Here
struct integer * convert_integer(char * stringInt);
void printer(struct integer * p);

struct integer {
    int * arr;
    int length;
};

// Main Program
int main() {
    char * x;
    x = (char *) malloc(sizeof(char) * 10000);
    printf("Enter a small string\n");
    scanf("%s",x);
    int j = 0;
    struct integer * book1;
    book1 = convert_integer(x);
    printer(book1);
    return 0;
}

// Function Definitions Go Here  
struct integer * convert_integer(char * stringInt) {
    struct integer * x = malloc(sizeof(int) * 100);
    int j = 0;
    while (stringInt[j] != '\0') {
        if (stringInt[j] < 48 || stringInt[j] >= 57) {
            printf("Invalid input. Enter a number ");
            return;
        }
        x->arr[j] = stringInt[j] - 48;
        j++;
    }
    x->length = j;
    printf("\n the length is %d\n", x->length);
    return x;
}

void printer(struct integer * p) {
    int j = 0;
    while (j < p->length) {
        printf("%d", p->arr[j]);
        j++;
    }
}

【问题讨论】:

  • 使用动态数组更好的解决方案:使用堆栈并用链表实现它。原因是,您总是一次只对一个数字感兴趣。
  • @JacobPollack,很公平,我很快就会这样做。但我认为了解我为什么会面临这种错误很重要:)

标签: c debugging


【解决方案1】:

我添加这个答案是因为 NPE 不够清楚。


这个程序有一些错误,但我想说最重要的是函数convert_integer。您正在执行以下操作:

struct integer* x= malloc(sizeof(int) * 100);

...但这是不正确的。考虑到x 的数据类型,您为x 请求了太多字节的内存(这没什么真正 错误),但是您没有为arr 请求内存块。它需要如下:

struct integer *x = malloc( sizeof( struct integer ) );

x->arr = malloc( sizeof( int ) * c );

...其中c 是一些常数(在您的情况下为100?)。确保当你free这个结构时你首先freearr然后free这个结构,否则你会有内存泄漏。

我注意到你没有做的其他事情,总是检查系统调用的结果。您没有检查malloc 是否返回了无效的内存块。

【讨论】:

  • 我曾经认为如果我分配一个结构的内存,我会自动为其中存储的内容分配内存。我现在想起来这没有任何意义。非常感谢您的帮助 :) 我现在将为我的所有 malloc 添加一个测试语句!
  • @AkashRupela,选择一个答案并投票,以便遇到与您相同问题的其他人能够解决它。
  • 是的。我试过了,但我不赞成,因为我是新人,我还没有声誉。我已经收藏了,一旦我有能力这样做,我会投票赞成:)
【解决方案2】:

这里:

struct integer* x= malloc(sizeof(int) * 100);;

您正在为结构(即指针和 int)分配内存,而不是为数组分配内存。

当您尝试分配给x-&gt;arr[j] 时,您就有了undefined behaviour

【讨论】:

  • 不存在struct integer 需要超过n * 100 个字节(其中n 通常为4 或8)的环境,它仅由一个指针和一个整数组成。他分配的内存比需要的多,但这不应该是一个紧迫的问题。
  • @wildplasser,这仍然不是问题。我知道 NPE 正在做什么,他说他还应该 malloc 一块足够大的内存块供 arr 使用。但问题不在于他指定的行,而是 OP 未包含的行(mallocarr 分配内存块)。
  • 我应该如何为 arr 进行 malloc?在结构中还是在主体中?
  • @AkashRupela,你需要做x = malloc( sizeof( struct integer ) ),在它下面做x-&gt;arr = malloc( sizeof( int ) * 100 )
  • 是的。非常感谢你们(y)我越来越了解内存的工作原理:)
【解决方案3】:

您的代码失败了

x->arr[j]=stringInt[j]-48;

原因是你分配内存片的方式:

struct integer* x= malloc(sizeof(int) * 100);

这会分配n*100 字节的切片并将指向该切片的指针存储在x 中。您想要做的是以下内容:

struct integer *x = malloc(sizeof(*x));
x->arr = malloc(sizeof(int)*100);

这分配了存储integer结构x所需的内存,它分配了存储100个整数的内存并将其分配给x-&gt;arr。使用您的代码,x-&gt;arr 未初始化,因此其中包含随机值(更准确地说:这些值是之前存储在该内存位置的值)。

使用完分配的内存后,还应该释放它:

free(x->arr);
free(x);

【讨论】:

    【解决方案4】:

    您需要在顶部添加printer 的原型。验证号码的if 语句应该是

    if(stringInt[j]<48 || stringInt[j]>57)  // > instead of >=
    

    写成这样会更有意义

    if ( stringInt[j] < '0' || stringInt[j] > '9' )
    

    如果你知道isdigit函数,你也可以使用它。

    您正在为包含 10000 个字符的字符串分配内存。这似乎是矫枉过正。您可以为 256 个字符分配内存,这应该足够了。您还应该在执行return 之前使用语句free (x); 释放内存

    您需要在convert_integer 函数内为arr 分配空间。您的分配应该是:

    struct integer* x= malloc(sizeof(integer)); x->arr = malloc ( sizeof(int) * 256 );
    

    【讨论】:

    • 谢谢你:)。我做了 10000 次,因为最终我也想在更大的输入上测试我的程序。这些很有帮助,但并没有真正解决我的问题。
    【解决方案5】:

    由于您也提出了建议,我将展示一个更好的解决方案。

    struct Integer {
        unsigned char *array; /* no point storing sign info on each element */
        size_t capacity; /* how many we have space for */
        size_t count;    /* how many we've actually used */
        char sign;       /* +1 or -1 */
    };
    

    我在这里做了一些改变:

    1. 您的数组中每个元素只能存储一个以 10 为基数的数字,因此 char 足够大
    2. 每个数组元素中存储整数的符号是​​没有用的,所以我使用unsigned char并将符号保留在外部
    3. 使您的数组固定大小是不灵活的,并且不符合大整数的精神。如果您要动态增长它,为 每个 数字做它很快就会变得昂贵,所以我使用 amortized constant time 技巧,每次它都将其容量翻倍填满

    现在,在我们开始使用整数之前,我们需要分配它。现在让我们做最简单的事情,并从零大小开始:

    struct Integer *alloc_integer() {
        struct Integer *i = malloc(sizeof(*i));
        /* this integer is empty until we use it ... */
        i->array = NULL;
        i->capacity = 0;
        i->count = 0;
        i->sign = 1;
        return i;
    }
    

    当然,如果它保持零大小是没有用的,所以我们需要一些方法来使它更大:

    void grow_integer(struct Integer *i, size_t min_size) {
        /* this check is redundant if we're only called from append_integer */
        if (min_size <= i->capacity) return;
        /* jump to the next power of 2 (ie, we usually double the capacity) */
        size_t new_size = i->capacity * 2;
        while (new_size < min_size) new_size *= 2;
    
        unsigned char *new_array = malloc(new_size * sizeof(*new_array));
        /* figure out how to cope if we run out of memory */
    
        memcpy(new_array, i->array, i->count * sizeof(*new_array));
        free(i->array);
        i->array = new_array;
        i->capacity = new_size;
    }
    

    现在我们可以看到如何附加一个整数:

    void append_integer(struct Integer *i, char digit) {
        if (i->count == i->capacity)
            grow_integer(i, i->capacity + 1);
        i->array[ i->count++ ] = (unsigned char)digit;
    }
    

    (注意grow_integerappend_integer 中的检查有点重叠:我可以删除一个,但我还没有决定grow_integer 是否是公共接口)

    最后,读取字符串应该很容易了:

    struct Integer *str_to_Integer(const char *str) {
        struct Integer *i = alloc_integer();
        for (; *str; ++str) {
            if (!isdigit(*str)) {
                free_integer(i);
                return NULL;
            }
            append_integer(i, *str - '0');
        }
        return i;
    }
    

    请注意,我使用isdigit 并且更喜欢'0' 而不是48,因为我认为当编译器可以为您完成时,强迫人们记住 ASCII 代码值没有任何实际好处。

    【讨论】:

      猜你喜欢
      • 2013-03-24
      • 1970-01-01
      • 2013-02-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多