在进行K& R练习时,我遇到了一种我无法理解的行为,而且我在SOF上找不到任何解决方案:
程序(将base 10 ints转换为base n 字符串)与我的额外printf()
语句一起运行正常。一旦我开始删除或评论它们,就会开始意外行为
完整的代码:
#include <stdio.h>
#include <string.h>
#include <assert.h>
#define MAXLINE 1000
char numtochar(unsigned n);
void reverse(char s[]);
void itob(int n, char s[], unsigned b);
int main() {
// test numtochar
assert(numtochar(1) == '1');
assert(numtochar(11) == 'b');
assert(numtochar(61) == 'Z');
assert(numtochar(62) == '+');
// test reverse
char t[] = "TestiNg";
reverse(t);
assert(!strcmp(t, "gNitseT"));
// test itob
printf("if this is commented out, it will segfault\n");
char s[MAXLINE];
itob(10, s, 10);
printf("%s\n", s);
assert(strcmp(s, "10") == 0);
itob(11, s, 2);
printf("%s\n", s);
assert(strcmp(s, "1011") == 0);
itob(100, s, 8);
printf("%s\n", s);
assert(strcmp(s, "144") == 0);
itob(1337, s, 32);
printf("%s\n", s);
assert(strcmp(s, "19p") == 0);
itob(127, s, 64);
printf("%s\n", s);
assert(strcmp(s, "1/") == 0);
return 0;
}
/* This numbering is not standard base-64, but will be consistent so long
* as base <= 64
* 0..63 => 0..9a..zA..Z+/
*/
char numtochar(unsigned n) {
assert(n < 64);
if (n < 10)
return ('0' + n);
else if (n >= 10 && n < 36)
return ('a' + (n - 10));
else if (n >= 36 && n < 62)
return ('A' + (n - 36));
else if (n == 62)
return '+';
else if (n == 63)
return '/';
}
void reverse(char s[]) {
int c, i, j;
for (i=0, j=strlen(s)-1; i < j; i++, j--) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
return;
}
void itob(int n, char s[], unsigned b) {
assert(b <= 64);
int c, i, sign;
if ((sign = n) < 0)
n = -n;
do {
s[i++] = numtochar(n % b);
} while ((n /= b) != 0);
if (sign < 0)
s[i++] = '-';
s[i] = '\0';
reverse(s);
return;
}
如下所示,如果我运行所有printf语句,它将按预期运行。如果我注释掉第一个语句,它将运行一次但不会再运行。
user@laptop:~/git/ansi_c/ch3 $ make ex05.o # no printf statements commented
gcc -o ex05.o ex05.c
user@laptop:~/git/ansi_c/ch3 $ ./ex05.o
if this is commented out, it will segfault
10
1011
144
19p
1/
user@laptop:~/git/ansi_c/ch3 $ make ex05.o # comment out first printf statement
gcc -o ex05.o ex05.c
user@laptop:~/git/ansi_c/ch3 $ ./ex05.o
10
1011
144
19p
1/
user@laptop:~/git/ansi_c/ch3 $ make ex05.o # resave after no changes
gcc -o ex05.o ex05.c
user@laptop:~/git/ansi_c/ch3 $ ./ex05.o
Segmentation fault (core dumped)
但是,如果我取消注释第一个printf语句并注释掉第二个(printf("%s\n", s);
),则itob的结果不再传递断言语句。
user@laptop:~/git/ansi_c/ch3 $ make ex05.o # uncomment first printf, comment second
gcc -o ex05.o ex05.c
user@laptop:~/git/ansi_c/ch3 $ ./ex05.o
if this is commented out, it will segfault
101101
ex05.o: ex05.c:33: main: Assertion `strcmp(s, "1011") == 0' failed.
Aborted (core dumped)
gcc版本是7.2.1
如果我删除所有printf语句,它也会段错误。作为C的新手,如果确实存在这个问题,我不确定在哪里可以解释内存,因为我看到的所有类似问题都围绕着使用malloc。
答案 0 :(得分:4)
在C中学习的一件重要事情是,如果不使用值初始化变量然后访问它,则会导致未定义的行为。以此功能为例......
Query query = mFirestore.collection("rootcollection")
.orderBy("timestamp", Query.Direction.DESCENDING)
.whereEqualTo("month", 3);
void Main()
{
var filePath = Path.GetTempPath() + "output.html"; //or output.xls but with ms-excel warning
var iEnumerbleValue = Enumerable.Range(1, 500).Select(e => new { a = 1, b = e });
File.WriteAllText(filePath, CreateHtml(iEnumerbleValue).ToString());
Process.Start("EXCEL.EXE", filePath);
}
HDoc CreateHtml<T>(IEnumerable<T> coll)
{
var fields = typeof(T).GetProperties();
return H.Doc(
H.head(H.style()),
H.body(
H.table(
H.tbody(
H.tr(fields.Select(f => H.th(f.Name))),
from item in coll
select H.tr(
fields.Select(f => H.td(f.GetValue(item)))
)
)
)
)
);
}
一开始有什么价值?它可能是0,但实际上它是当时在内存中发生的任何随机值。这就是为什么取消评论代码会改变事物的原因,因为它会影响void itob(int n, char s[], unsigned b) {
assert(b <= 64);
int c, i, sign;
if ((sign = n) < 0)
n = -n;
do {
s[i++] = numtochar(n % b);
} while ((n /= b) != 0);
if (sign < 0)
s[i++] = '-';
s[i] = '\0';
reverse(s);
return;
}
的价值。
只需将其更改为以值0开头即可解决您的问题。同时删除i
,因为它未使用
i
一个好用的工具是c
- 它会告诉你内存损坏和泄漏的位置。
答案 1 :(得分:0)
在发布的代码上运行gdb
会导致:
注意:``untitled2`是我给你的程序的名字。
gdb untitled2
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from untitled2...done.
(gdb) br main
Breakpoint 1 at 0x4008e1: file untitled2.c, line 12.
(gdb) r
Starting program: /home/rkwill/Documents/forum/untitled2
Breakpoint 1, main () at untitled2.c:12
12 {
(gdb) c
Continuing.
if this is commented out, it will segfault
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400d2a in itob (n=10, s=0x7fffffffda80 "\264\332\377\377\377\177",
b=10) at untitled2.c:103
103 s[i++] = numtochar(n % b);
(gdb)
位于此代码块中:
do
{
s[i++] = numtochar(n % b);
} while ((n /= b) != 0);
主要问题是使用局部变量i
而不进行初始化(因此它包含垃圾在变量位置的堆栈中。)
换句话说,程序包含一些未定义的行为,这就是导致seg错误的原因。