当我想用它的地址初始化一个二维字符数组时出现段错误
Segfault when I want to initialize a 2d array of chars with its address
我正在处理一个项目,我需要在另一个函数中修改一个二维数组,而不是在我创建它的地方。但是,我在尝试这样做时遇到了段错误,我不明白这个问题。
这是我要执行的操作的代码:
# include <stdlib.h>
# include <stdio.h>
void test(char ***tab)
{
*tab = malloc(sizeof(char *) * 2);
*tab[0] = malloc(sizeof(char) * 5);
*tab[0][0] = 't';
printf("%c\n", *tab[0][0]);
*tab[0][1] = 'e';
printf("%c\n", *tab[0][1]);
*tab[0][2] = 's';
*tab[0][3] = 't';
*tab[0][4] = '[=10=]';
tab[1] = NULL;
}
int main()
{
char **tab1;
char **tab2;
tab1 = malloc(sizeof(char *) * 2);
tab1[0] = malloc(sizeof(char) * 5);
tab1[0][0] = 't';
tab1[0][1] = 'e';
tab1[0][2] = 's';
tab1[0][3] = 't';
tab1[0][4] = '[=10=]';
tab1[1] = NULL;
printf("tab1 %s\n", tab1[0]);
test(&tab2);
printf("%s\n", tab2[0]);
}
我用以下代码编译我的代码:
gcc -fsanitize=address test.c
这是输出
tab1 test
t
AddressSanitizer:DEADLYSIGNAL
=================================================================
==12145==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x0000004c22c8 bp 0x7ffc58e533d0 sp 0x7ffc58e532c0 T0)
==12145==The signal is caused by a READ memory access.
==12145==Hint: address points to the zero page.
#0 0x4c22c7 in test (/home/user42/Bureau/a.out+0x4c22c7)
#1 0x4c2aaa in main (/home/user42/Bureau/a.out+0x4c2aaa)
#2 0x7fc4aebc4b96 in __libc_start_main /build/glibc-2ORdQG/glibc-2.27/csu/../csu/libc-start.c:310
#3 0x41aaa9 in _start (/home/user42/Bureau/a.out+0x41aaa9)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (/home/user42/Bureau/a.out+0x4c22c7) in test
==12145==ABORTING
如您所见,tab1
在我初始化时不会导致任何段错误,但 tab2
在初始化第一个字符后会导致任何段错误。为什么会这样?
看起来像是运算符优先级问题:
# include <stdlib.h>
# include <stdio.h>
void test(char ***tab)
{
*tab = malloc(sizeof(char *) * 2);
(*tab)[0] = malloc(sizeof(char) * 5);
(*tab)[0][0] = 't';
printf("%c\n", (*tab)[0][0]);
(*tab)[0][1] = 'e';
printf("%c\n", (*tab)[0][1]);
(*tab)[0][2] = 's';
(*tab)[0][3] = 't';
(*tab)[0][4] = '[=10=]';
(*tab)[1] = NULL;
}
int main()
{
char **tab1;
char **tab2;
tab1 = malloc(sizeof(char *) * 2);
tab1[0] = malloc(sizeof(char) * 5);
tab1[0][0] = 't';
tab1[0][1] = 'e';
tab1[0][2] = 's';
tab1[0][3] = 't';
tab1[0][4] = '[=10=]';
tab1[1] = NULL;
printf("tab1 %s\n", tab1[0]);
test(&tab2);
printf("%s\n", tab2[0]);
}
您可以使用 -ggdb3
进行编译以在消毒剂堆栈跟踪中获取更多信息,这将帮助您调试此类问题:
$ gcc foo.c -ggdb3
$ valgrind ./a.out
==121594== Memcheck, a memory error detector
==121594== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==121594== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==121594== Command: ./a.out
==121594==
tab1 test
t
==121594== Use of uninitialised value of size 8
==121594== at 0x109220: test (foo.c:10)
==121594== by 0x109348: main (foo.c:32)
==121594==
==121594== Invalid write of size 1
==121594== at 0x109220: test (foo.c:10)
==121594== by 0x109348: main (foo.c:32)
==121594== Address 0x0 is not stack'd, malloc'd or (recently) free'd
Use of uninitialised value of size 8 at 0x109220: test (foo.c:10)
准确告诉你问题出在哪里。
与一样,tab[1]
是导致未定义行为的越界访问。您可以在此处使用 (*tab)[1] = NULL;
——首先取消对 ***
指针的引用以获得 **
,然后将偏移量 1(概念上,第 1 行)设置为 NULL。
顺便说一句,不要忘记在使用后释放你的内存,sizeof(char)
总是1所以你不需要它。
问题是一元运算符*
比[]
运算符关联更松散,所以test
中的大部分地方都需要加括号。以下修复它。
另请注意,tab[1] = NULL;
完全缺少 *
。它需要是 (*tab)[1] = NULL;
(见下文)。这是一个重要的修复,因为没有它,代码会出现未定义的行为:
void test(char ***tab)
{
*tab = malloc(sizeof(char *) * 2);
(*tab)[0] = malloc(sizeof(char) * 5);
(*tab)[0][0] = 't';
printf("%c\n", (*tab)[0][0]);
(*tab)[0][1] = 'e';
printf("%c\n", (*tab)[0][1]);
(*tab)[0][2] = 's';
(*tab)[0][3] = 't';
(*tab)[0][4] = '[=10=]';
(*tab)[1] = NULL;
}
我正在处理一个项目,我需要在另一个函数中修改一个二维数组,而不是在我创建它的地方。但是,我在尝试这样做时遇到了段错误,我不明白这个问题。
这是我要执行的操作的代码:
# include <stdlib.h>
# include <stdio.h>
void test(char ***tab)
{
*tab = malloc(sizeof(char *) * 2);
*tab[0] = malloc(sizeof(char) * 5);
*tab[0][0] = 't';
printf("%c\n", *tab[0][0]);
*tab[0][1] = 'e';
printf("%c\n", *tab[0][1]);
*tab[0][2] = 's';
*tab[0][3] = 't';
*tab[0][4] = '[=10=]';
tab[1] = NULL;
}
int main()
{
char **tab1;
char **tab2;
tab1 = malloc(sizeof(char *) * 2);
tab1[0] = malloc(sizeof(char) * 5);
tab1[0][0] = 't';
tab1[0][1] = 'e';
tab1[0][2] = 's';
tab1[0][3] = 't';
tab1[0][4] = '[=10=]';
tab1[1] = NULL;
printf("tab1 %s\n", tab1[0]);
test(&tab2);
printf("%s\n", tab2[0]);
}
我用以下代码编译我的代码:
gcc -fsanitize=address test.c
这是输出
tab1 test
t
AddressSanitizer:DEADLYSIGNAL
=================================================================
==12145==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x0000004c22c8 bp 0x7ffc58e533d0 sp 0x7ffc58e532c0 T0)
==12145==The signal is caused by a READ memory access.
==12145==Hint: address points to the zero page.
#0 0x4c22c7 in test (/home/user42/Bureau/a.out+0x4c22c7)
#1 0x4c2aaa in main (/home/user42/Bureau/a.out+0x4c2aaa)
#2 0x7fc4aebc4b96 in __libc_start_main /build/glibc-2ORdQG/glibc-2.27/csu/../csu/libc-start.c:310
#3 0x41aaa9 in _start (/home/user42/Bureau/a.out+0x41aaa9)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (/home/user42/Bureau/a.out+0x4c22c7) in test
==12145==ABORTING
如您所见,tab1
在我初始化时不会导致任何段错误,但 tab2
在初始化第一个字符后会导致任何段错误。为什么会这样?
看起来像是运算符优先级问题:
# include <stdlib.h>
# include <stdio.h>
void test(char ***tab)
{
*tab = malloc(sizeof(char *) * 2);
(*tab)[0] = malloc(sizeof(char) * 5);
(*tab)[0][0] = 't';
printf("%c\n", (*tab)[0][0]);
(*tab)[0][1] = 'e';
printf("%c\n", (*tab)[0][1]);
(*tab)[0][2] = 's';
(*tab)[0][3] = 't';
(*tab)[0][4] = '[=10=]';
(*tab)[1] = NULL;
}
int main()
{
char **tab1;
char **tab2;
tab1 = malloc(sizeof(char *) * 2);
tab1[0] = malloc(sizeof(char) * 5);
tab1[0][0] = 't';
tab1[0][1] = 'e';
tab1[0][2] = 's';
tab1[0][3] = 't';
tab1[0][4] = '[=10=]';
tab1[1] = NULL;
printf("tab1 %s\n", tab1[0]);
test(&tab2);
printf("%s\n", tab2[0]);
}
您可以使用 -ggdb3
进行编译以在消毒剂堆栈跟踪中获取更多信息,这将帮助您调试此类问题:
$ gcc foo.c -ggdb3
$ valgrind ./a.out
==121594== Memcheck, a memory error detector
==121594== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==121594== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==121594== Command: ./a.out
==121594==
tab1 test
t
==121594== Use of uninitialised value of size 8
==121594== at 0x109220: test (foo.c:10)
==121594== by 0x109348: main (foo.c:32)
==121594==
==121594== Invalid write of size 1
==121594== at 0x109220: test (foo.c:10)
==121594== by 0x109348: main (foo.c:32)
==121594== Address 0x0 is not stack'd, malloc'd or (recently) free'd
Use of uninitialised value of size 8 at 0x109220: test (foo.c:10)
准确告诉你问题出在哪里。
与tab[1]
是导致未定义行为的越界访问。您可以在此处使用 (*tab)[1] = NULL;
——首先取消对 ***
指针的引用以获得 **
,然后将偏移量 1(概念上,第 1 行)设置为 NULL。
顺便说一句,不要忘记在使用后释放你的内存,sizeof(char)
总是1所以你不需要它。
问题是一元运算符*
比[]
运算符关联更松散,所以test
中的大部分地方都需要加括号。以下修复它。
另请注意,tab[1] = NULL;
完全缺少 *
。它需要是 (*tab)[1] = NULL;
(见下文)。这是一个重要的修复,因为没有它,代码会出现未定义的行为:
void test(char ***tab)
{
*tab = malloc(sizeof(char *) * 2);
(*tab)[0] = malloc(sizeof(char) * 5);
(*tab)[0][0] = 't';
printf("%c\n", (*tab)[0][0]);
(*tab)[0][1] = 'e';
printf("%c\n", (*tab)[0][1]);
(*tab)[0][2] = 's';
(*tab)[0][3] = 't';
(*tab)[0][4] = '[=10=]';
(*tab)[1] = NULL;
}