gpt4 book ai didi

c - 两个 printfs 以不同的方式打印相同的字符串

转载 作者:IT王子 更新时间:2023-10-29 00:31:33 28 4
gpt4 key购买 nike

我正在尝试创建一个处理大整数运算的库。大整数存储在结构中:

typedef struct BigInt BigInt;
struct BigInt
{
uint32_t size;
uint32_t *data;
};

第一个成员是包含数字长度的 uint32_t,第二个成员是指向实际数字数据(存储在二进制补码中)的指针。我写了一个简单的 toHex(BigInt *a) 函数,它分配内存,将大整数的十六进制值打印到字符串,并返回地址。

在我的主循环中,我有以下内容:

int main(int argc, char *argv[])
{
char *ap, *bp;
BigInt *a = fromUInt32(0x7fffffff), *b = fromUInt32(1), *c = fromUInt32(0x80000000);
_add(a, b);
ap = toHex(a);
bp = toHex(c);
printf("%s\n", ap);
printf("%s\n%s\n", ap, bp);
printf("%s\n%s\n", ap, bp);
free(ap);
free(bp);
deleteBigInt(a);
deleteBigInt(b);
deleteBigInt(c);
}

奇怪的是,打印

0000000080000000
0
0000000080000000
0000000080000000
0000000080000000

因此,第二个 printf 语句为 ap 打印的内容与第一个和第三个 printf 语句不同。看起来第一个 printf 语句是正确的,第二个是搞砸了。我已经使用 GDB 单步执行我的代码,在对 toHex 求值后,ap 指向字符串“0000000080000000”,以空指针终止。

我完全不知所措。据我所知,可能性是:
1. 由于一些奇怪的原因,我遇到了未定义的行为。
2. 在_add中我调用了一个用x86汇编代码编写的例程,其中可能有错误(但我通过保留esi、edi、ebx、ebp和esp来遵守GCC的调用约定)。
3. printf 有一个错误,这似乎不太可能。

我也有一个明显的“内存泄漏”(引用是因为对内存泄漏究竟是什么的看法似乎不同)没有释放 toHex 分配的内存,但这应该无关紧要。

我的 toHex 函数是由 Sourav Ghosh 请求的,如下所示:

char numToHex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
char *toHex(BigInt *a)
{
char *result, *ptr;
// allocate enough space for 8 characters for each uint32_t and 1 terminating 0
ptr = result = malloc(a->size * 8 + 1);
// loop over the uint32_t's stored in a->data
// (there are a->size of them)
for (uint32_t i = 0; i < a->size; i++)
// parse 8 blocks of 4 bits
for (uint32_t j = 0; j < 8; j++)
// grab the right bits and convert them to a hex digit
*(ptr++) = numToHex[(a->data[i] >> ((7 - j) * 4)) & 0xf];
// add a terminating zero byte
*ptr = 0;
return result;
}

我在 ~100 lines of C 的程序中隔离了这种奇怪的行为+ ~70 lines of assembly .编译可以用

nasm -f elf -s <AssemblyName>.asm
gcc <CFile>.c <AssemblyName>.o -o <OutputProgram> -m32 -std=c99 -g

代码未注释,适用于想要自己检查行为的人。

编辑:Jan Spurny 和 Matt McNabb 敦促我使用 Valgrind。 Valgrind 说:0x40A5685 大小 1 的无效读取:vfprintf (vfprintf.c:1655) by 0x40AA7FE: printf (printf.c:34) by 0x4075904: (below main) (libc-start.c:260) Address 0x42121af is 1 bytes before a大小为 17 的 block 分配在 0x40299D8:malloc(在/usr/lib/valgrind/vgpreload_memcheck-x86-linux.so 中)由 0x804887D:toHex(weird.c:107)由 0x8048565:main(weird.c:30)

但这没有意义,因为我在 toHex 中将结果设置为 malloc,之后没有任何改变。我现在敢打赌,某些寄存器在汇编函数中被破坏了。 Edit2:检查 GDB 后,我可以看到没有寄存器损坏。我还是一头雾水。

最佳答案

reduce函数有一个错误:

while (i < a->size && !(a->data[i])) i++;
if (a->data[i] & SIGNBIT) i--;

如果i < a->size满足条件,则a->data[i]越界访问,导致未定义的行为。 reduce的另一家分行有同样的问题


_add 中存在错误函数(虽然这不会在您的测试用例中触发):

void *k = realloc(a->data, b->size * 4);
memmove((void *)(a->data + displacement), (void *)a->data, a->size * 4);
// ....other code using `a->data`

realloc 之后, a->data变得不确定,因此使用它会导致未定义的行为。这可以解释您的症状,因为 future 的分配可能会重新使用 a->data 的相同释放 block 。仍然指向。

也许您还想有一条线 a->data = k;之后呢?


要在调试代码时获得良好的帮助,最好执行以下操作:

  • 检查所有*alloc的结果-family 功能并退出如果 NULL被退回。否则你会得到未定义的行为(期望段错误是不可靠的)。
  • 用 C 重写汇编函数。出于多种原因(调试、代码可移植性、优化),这是一个好主意。甚至可能会发现 gcc -O3 生成的代码比您的手写版本更快;这就是编译器擅长的。
  • 检查调用 newAddress 的结果检查它实际上返回了您在测试用例中所期望的结果。

关于c - 两个 printfs 以不同的方式打印相同的字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29076516/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com