C 代码分配的内存多于应有的内存

Sir*_*sis 1 c dynamic-memory-allocation

对于一门课程,我正在用 C 语言进行乘法表样式的练习。该代码功能齐全,但自动测试显示它分配了 416 个字节,而它应该只分配 376 个字节。这是值 a=2,b =9,c=11,d=17。我已经通过 valgrind 运行了代码,并打开了所有可能的额外细节,但它并没有真正帮助。

我尝试将标题行生成写入为主时间表创建的一部分,但它最终使用的内存比我当前的方法更多。

destroyTimes 函数也不应该有问题,因为 valgrind 没有报告任何泄漏。

这是 times.c

#include "times.h"
#include <stdlib.h>
#include <stdio.h>

Times* createTimes(uint a, uint b, uint c, uint d) {
  int i,j;
  int rows=d-c+2;
  int cols=b-a+2;
  
  Times* ret = malloc(sizeof(Times));
  uint** kert = malloc(sizeof(uint*)*rows);

  if (!ret) {
    return NULL;
  }

  ret->a=a;
  ret->b=b;
  ret->c=c;
  ret->d=d;

  kert[0] = malloc(sizeof(uint)*(cols+1)); 
  kert[0][0] = 1;

  for(j=1;j<cols;j++) {
    kert[0][j] = a;
    a++;
  }
  a = ret->a;

  for(i=1;i<rows;i++) {
    kert[i] = malloc(sizeof(uint)*(cols+1));
    for(j=0;j<cols;j++) {
      kert[i][j] = c*kert[0][j];
    }
    c++;
  };

  ret->times = kert;

  return ret;
}

void destroyTimes(Times* kt) {
  uint i;
  uint rows = kt->d - kt->c + 2;
  if (kt == NULL) {
      return;  
  }

  for (i = 0; i < rows; i++) {
      free(kt->times[i]);
  }
  free(kt->times);

  free(kt);
}
Run Code Online (Sandbox Code Playgroud)

那么这里是 times.h

#ifndef TIMES_H
#define TIMES_H

typedef unsigned int uint;

struct Times {
  uint a,b,c,d;
  uint** times;
};

typedef struct Times Times;

Times* createTimes(uint,uint,uint,uint);

void destroyTimes(Times*);

#endif
Run Code Online (Sandbox Code Playgroud)

438*_*427 5

对于一门课程,我正在用 C 语言进行乘法表样式的练习。

好吧,所以你想学习 C。酷……

但请注意,学习 C 语言只是成功了一半。您还需要学习如何使用调试器。如何启动调试会话。如何设置断点。如何单步执行。如何检查变量值。等等...

现在似乎是这样做的好时机。

同时,C 还带有一个非常强大的内置调试器。它被称为printf注1)。

执行时添加小的调试打印输出malloc将帮助您了解正在发生的情况。

例如:

  printf("rows=%d cols=%d\n", rows, cols);

  size_t total_alloc = 0;
  size_t current_alloc = 0;

  // Times* ret = malloc(sizeof(Times));
  current_alloc = sizeof(Times);
  total_alloc += current_alloc;
  printf("Allocating %zu bytes for Times (total = %zu)\n", current_alloc, total_alloc);
  Times* ret = malloc(current_alloc);

  // uint** kert = malloc(sizeof(uint*)*rows);
  current_alloc = sizeof(uint*)*rows;
  total_alloc += current_alloc;
  printf("Allocating %zu bytes for row-pointers (total = %zu)\n", current_alloc, total_alloc);
  uint** kert = malloc(current_alloc);

  // and likewise for all your malloc
Run Code Online (Sandbox Code Playgroud)

完成之后我可以运行你的程序并得到:

rows=8 cols=9
Allocating 24 bytes for Times (total = 24)
Allocating 64 bytes for row-pointers (total = 88)
Allocating 40 bytes for cols data in row 0 (total = 128)
Allocating 40 bytes for cols data in row 1 (total = 168)
Allocating 40 bytes for cols data in row 2 (total = 208)
Allocating 40 bytes for cols data in row 3 (total = 248)
Allocating 40 bytes for cols data in row 4 (total = 288)
Allocating 40 bytes for cols data in row 5 (total = 328)
Allocating 40 bytes for cols data in row 6 (total = 368)
Allocating 40 bytes for cols data in row 7 (total = 408)
Run Code Online (Sandbox Code Playgroud)

(见注2

查看此输出时,很容易发现这些列有一些奇怪的地方。9列怎么需要40字节呢?每列 4.44 字节!没有意义。是时候看看专栏了malloc

  kert[i] = malloc(sizeof(uint)*(cols+1));
                                     ^^
                                oh, why did I add one here ???
                                I don't need 10 columns - only 9
Run Code Online (Sandbox Code Playgroud)

修复代码(即删除这+ 1两个地方)后,程序现在将生成:

rows=8 cols=9
Allocating 24 bytes for Times (total = 24)
Allocating 64 bytes for row-pointers (total = 88)
Allocating 36 bytes for cols data in row 0 (total = 124)
Allocating 36 bytes for cols data in row 1 (total = 160)
Allocating 36 bytes for cols data in row 2 (total = 196)
Allocating 36 bytes for cols data in row 3 (total = 232)
Allocating 36 bytes for cols data in row 4 (total = 268)
Allocating 36 bytes for cols data in row 5 (total = 304)
Allocating 36 bytes for cols data in row 6 (total = 340)
Allocating 36 bytes for cols data in row 7 (total = 376)
Run Code Online (Sandbox Code Playgroud)

教训:一些调试打印对于查找错误非常有用。但展望未来,学习如何使用真正的调试器非常重要。

注 1:不,printf不是调试器。只是开玩笑......printf不能替代真正的调试器,但进行小型调试打印输出实际上对于查找小程序中的错误非常有用。

注 2:在你的原始代码中,我得到了 408 个字节,而你得到了 416 个字节。不确定为什么不同,但也许你的编译器在结构中添加了 8 个字节的填充Times

一点额外

  uint rows = kt->d - kt->c + 2;
  if (kt == NULL) {  <--- Too late... you all ready did the dereference
      return;  
  }
Run Code Online (Sandbox Code Playgroud)

NULL 的检查必须在之前 kt->d