Abort trap 6问题源于调用extra_info()方法,它多次使用strncat()。删除此功能不会在运行时产生错误。
据我所知:
中止陷阱:6是由于使用指向不存在的内存位置Abort trap: 6 in C Program的无效索引引起的。当需要释放可变内存时也可能发生这种情况。要避免这种情况,您可以在每次重复使用时使用多个变量或释放单个变量。但我感觉解决方案要简单得多。
#include <stdio.h>
#include <string.h>
char line[1001]; // The line supports up to a 1000 characters
char lines[11][1001]; // An array of lines (up to 10 lines where each line is a 1000 characters max)
char info[100]; // Holds extra info provided by user
char * extra_info(
char string_1[],
char string_2[],
char string_3[],
char string_4[],
char string_5[]
);
int main(){
int
i, // Line number
j; // Length of the line
char result[100], text[100];
FILE *file;
strcpy(text, "String No."); // The default text
file = fopen("test.txt", "w+"); // Open the file for reading and writing
for(i = 0; i < 10; i++){ // Loop to create a line.
if(i != 9){ // If the line is NOT at the 10th string
sprintf(result, "%s%d, ", text, i); // Format the text and store it in result
}
else{
sprintf(result, "%s%d ", text, i); // Format the text and store it in result
}
extra_info(
"st",
"nd",
"rd",
"th",
"th"
);
strncat(line, info, 100); // Append the extra info at the end of each line
printf("%s", result); // Display the result variable to the screen
strncat(line, result, 15); // Concatenate all strings in one line
}
strncat(line, "\n\n", 2); // Add a new-line character at the end of each line
for(j = 0; j < 10; j++){ // Now loop to change the line
strcpy(lines[i], line); // Copy the line of text into each line of the array
fputs(lines[i], file); // Put each line into the file
}
fclose(file);
}
char * extra_info( // Append user defined and predefined info at the end of a line
char string_1[],
char string_2[],
char string_3[],
char string_4[],
char string_5[]
){
char text[100]; // A variable to hold the text
/* Append a default text into each strings
and concatenate them into one line */
sprintf(text, " 1%s", string_1);
strncat(line, text, 100);
sprintf(text, ", 2%s", string_2);
strncat(line, text, 100);
sprintf(text, ", 3%s", string_3);
strncat(line, text, 100);
sprintf(text, ", 4%s", string_4);
strncat(line, text, 100);
sprintf(text, ", 5%s.", string_5);
strncat(line, text, 100);
strcpy(info, line); // Copies the line into the info global variable
return line;
}
这段代码很好地使用GCC编译,但我偶然发现代码工作正常的情况,但是由于这个错误可能会破坏某些功能。这与以这种方式多次调用strncat()有关,这使我认为会出现内存分配问题,但在尝试其他示例后,解决方案看起来要简单得多。任何有关这方面的帮助将不胜感激。提前致谢。
我在2018年3月编写了随附的代码,以满足自己在strncat()
发生的问题,以便在我提交答案之前删除另一个问题。这只是重新定位代码。
strncat()
功能(正如我在comment中所说的)邪恶和卑鄙。它也与strncpy()
界面不一致 - 并且与你在其他任何地方遇到的任何东西都不同。读完之后,你会决定(幸运的话)你永远不应该使用strncat()
。
strncat()
C标准定义strncat()
(和POSIX同意 - strncat()
)
C11 §7.24.3.2 The
strncat
function概要
#include <string.h> char *strncat(char * restrict s1, const char * restrict s2, size_t n);
描述
strncat
函数从n
指向的数组到s2
指向的字符串的末尾附加的s1
字符(空字符和后面跟随的字符不会附加)不超过s2
。s1
的初始字符会覆盖strncat
末尾的空字符。终止空字符始终附加到结果.309)如果在重叠的对象之间进行复制,则行为未定义。返回
s1
函数返回s1
的值。309)因此,可以在
strlen(s1)+n+1
指向的数组中结束的最大字符数是strncat()
。
脚注标识了与char *source = …;
char target[100] = "";
strncat(target, source, sizeof(target));
最大的陷阱 - 您无法安全使用:
strncat()
这与在C代码中采用数组大小参数1的大多数其他函数所发生的情况相反。
要安全地使用target
,你应该知道:
sizeof(target)
strlen(target)
- 或者,对于动态分配的空间,分配的长度source
- 你必须知道目标字符串中已有的长度strlen(source)
strncat(target, source, sizeof(target) - strlen(target) - 1);
- 如果您担心源字符串是否被截断;如果你不在乎,就不需要了有了这些信息,您可以使用:
strlen(target)
但是,这样做会有点愚蠢;如果你知道strncat()
,你可以避免让strncat(target + strlen(target), source, sizeof(target) - strlen(target) - 1);
再次找到它:
strncat()
请注意,与strncpy()
不同,size_t t_size = sizeof(target);
size_t t_length = strlen(target);
strncpy(target + t_length, source, t_size - t_length - 1);
target[t_size - 1] = '\0';
保证无效终止。这意味着您可以使用:
strncat()
如果源字符串太长而无法附加到目标,则可以保证得到相同的结果。
说明strncat()
方面的多个程序。请注意,在macOS上,<string.h>
中有一个__builtin___strncat_chk
的宏定义,它调用了一个不同的函数 - strncat()
- 它验证了-Wmissing-prototypes -Wstrict-prototypes
的用法。对于命令行的紧凑性,我删除了我通常使用的两个警告编译器选项 - strncat19.c
- 但这不会影响任何编译。
strncat()
这证明了#include <stdio.h>
#include <string.h>
int main(void)
{
char spare1[16] = "abc";
char buffer[16] = "";
char spare2[16] = "xyz";
strncat(buffer, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", sizeof(buffer) - 1);
printf("%zu: [%s]\n", strlen(buffer), buffer);
printf("spare1 [%s]\n", spare1);
printf("spare2 [%s]\n", spare2);
return 0;
}
的一个安全用途:
clang
它编译得很干净(Apple的Apple LLVM version 10.0.0 (clang-1000.11.45.5)
来自XCode 10.1($ gcc -O3 -g -std=c11 -Wall -Wextra -Werror strncat19.c -o strncat19
$ ./strncat19
15: [ABCDEFGHIJKLMNO]
spare1 [abc]
spare2 [xyz]
$
)和GCC 8.2.0,即使设置了严格的警告:
strncat29.c
strncat19.c
这与sizeof(buffer)
类似,但(a)允许您指定要在命令行上复制的字符串,并且(b)错误地使用sizeof(buffer) - 1
而不是#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
const char *data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (argc == 2)
data = argv[1];
char spare1[16] = "abc";
char buffer[16] = "";
char spare2[16] = "xyz";
strncat(buffer, data, sizeof(buffer));
printf("%zu: [%s]\n", strlen(buffer), buffer);
printf("spare1 [%s]\n", spare1);
printf("spare2 [%s]\n", spare2);
return 0;
}
作为长度。
$ clang -O3 -g -std=c11 -Wall -Wextra -Werror strncat29.c -o strncat29
strncat29.c:12:27: error: the value of the size argument in 'strncat' is too large, might lead to a buffer
overflow [-Werror,-Wstrncat-size]
strncat(buffer, data, sizeof(buffer));
^~~~~~~~~~~~~~
strncat29.c:12:27: note: change the argument to be the free space in the destination buffer minus the terminating null byte
strncat(buffer, data, sizeof(buffer));
^~~~~~~~~~~~~~
sizeof(buffer) - strlen(buffer) - 1
1 error generated.
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror strncat29.c -o strncat29
In file included from /usr/include/string.h:190,
from strncat29.c:2:
strncat29.c: In function ‘main’:
strncat29.c:12:5: error: ‘__builtin___strncat_chk’ specified bound 16 equals destination size [-Werror=stringop-overflow=]
strncat(buffer, data, sizeof(buffer));
^~~~~~~
cc1: all warnings being treated as errors
$
此代码无法使用严格的警告选项进行编译:
-Werror
即使没有请求警告,GCC也会发出警告,但由于缺少$ gcc -o strncat29 strncat29.c
In file included from /usr/include/string.h:190,
from strncat29.c:2:
strncat29.c: In function ‘main’:
strncat29.c:12:5: warning: ‘__builtin___strncat_chk’ specified bound 16 equals destination size [-Wstringop-overflow=]
strncat(buffer, data, sizeof(buffer));
^~~~~~~
$ ./strncat29
Abort trap: 6
$ ./strncat29 ZYXWVUTSRQPONMK
15: [ZYXWVUTSRQPONMK]
spare1 [abc]
spare2 [xyz]
$ ./strncat29 ZYXWVUTSRQPONMKL
Abort trap: 6
$
选项,它会产生一个可执行文件:
__builtin__strncat_chk
这就是strncat97.c
的功能。
strncat()
此代码还采用可选的字符串参数;它还要注意命令行上是否有另一个参数,如果是,它会直接调用#include <stdio.h>
#include <string.h>
/*
** Demonstrating that strncat() should not be given sizeof(buffer) as
** the size, even if the string is empty to start with. The use of
** (strncat) inhibits the macro expansion on macOS; the code behaves
** differently when the __strncat_chk function (on High Sierra or
** earlier - it's __builtin__strncat_chk on Mojave) is called instead.
** You get an abort 6 (but no other useful message) when the buffer
** length is too long.
*/
int main(int argc, char **argv)
{
const char *data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (argc >= 2)
data = argv[1];
char spare1[16] = "abc";
char buffer[16] = "";
char spare2[16] = "xyz";
size_t len = (argc == 2) ? sizeof(buffer) : sizeof(buffer) - 1;
if (argc < 3)
strncat(buffer, data, len);
else
(strncat)(buffer, data, len);
printf("buffer %2zu: [%s]\n", strlen(buffer), buffer);
printf("spare1 %2zu: [%s]\n", strlen(spare1), spare1);
printf("spare2 %2zu: [%s]\n", strlen(spare2), spare2);
return 0;
}
函数,而不是让宏首先检查它:
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror strncat97.c -o strncat97
strncat97.c: In function ‘main’:
strncat97.c:26:9: error: ‘strncat’ output truncated copying 15 bytes from a string of length 26 [-Werror=stringop-truncation]
(strncat)(buffer, data, len);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
$ clang -O3 -g -std=c11 -Wall -Wextra -Werror strncat97.c -o strncat97
$
现在编译器会产生不同的结果:
$ ./strncat97
0x7ffee7506420: buffer 15: [ABCDEFGHIJKLMNO]
0x7ffee7506430: spare1 3: [abc]
0x7ffee7506410: spare2 3: [xyz]
$ ./strncat97 ABCDEFGHIJKLMNOP
Abort trap: 6
$ ./strncat97 ABCDEFGHIJKLMNO
0x7ffeea141410: buffer 15: [ABCDEFGHIJKLMNO]
0x7ffeea141420: spare1 3: [abc]
0x7ffeea141400: spare2 3: [xyz]
$
这证明了使用多个编译器的优势 - 不同的编译器有时会检测到不同的问题。这段代码很乱,试图使用不同数量的选项来做多件事。它足以表明:
strncat37.c
getopt()
这是上述节目的全能,全舞蹈版本,通过SOQ进行选项处理。它还使用我的错误报告例程;它们的代码可以在GitHub上的stderr.c
(Stack Overflow Questions)存储库中找到stderr.h
子目录中的文件src/libsoq和#include "stderr.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
/*
** Demonstrating that strncat() should not be given sizeof(buffer) as
** the size, even if the string is empty to start with. The use of
** (strncat) inhibits the macro expansion on macOS; the code behaves
** differently when the __strncat_chk function (on High Sierra or
** earlier - it's __builtin__strncat_chk on Mojave) is called instead.
** You get an abort 6 (but no other useful message) when the buffer
** length is too long.
*/
static const char optstr[] = "fhlmsV";
static const char usestr[] = "[-fhlmsV] [string]";
static const char hlpstr[] =
" -f Function is called directly\n"
" -h Print this help message and exit\n"
" -l Long buffer length -- sizeof(buffer)\n"
" -m Macro cover for the function is used (default)\n"
" -s Short buffer length -- sizeof(buffer)-1 (default)\n"
" -V Print version information and exit\n"
;
int main(int argc, char **argv)
{
err_setarg0(argv[0]);
int f_flag = 0;
int l_flag = 0;
int opt;
while ((opt = getopt(argc, argv, optstr)) != -1)
{
switch (opt)
{
case 'f':
f_flag = 1;
break;
case 'h':
err_help(usestr, hlpstr);
/*NOTREACHED*/
case 'l':
l_flag = 1;
break;
case 'm':
f_flag = 0;
break;
case 's':
l_flag = 0;
break;
case 'V':
err_version(err_getarg0(), &"@(#)$Revision$ ($Date$)"[4]);
/*NOTREACHED*/
default:
err_usage(usestr);
/*NOTREACHED*/
}
}
if (optind < argc - 1)
err_usage(usestr);
const char *data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (optind != argc)
data = argv[optind];
char spare1[16] = "abc";
char buffer[16] = "";
char spare2[16] = "xyz";
size_t len = l_flag ? sizeof(buffer) : sizeof(buffer) - 1;
printf("Specified length: %zu\n", len);
printf("Copied string: [%s]\n", data);
printf("Copied %s\n", f_flag ? "using strncat() function directly"
: "using strncat() macro");
if (f_flag)
(strncat)(buffer, data, len);
else
strncat(buffer, data, len);
printf("%p: buffer %2zu: [%s]\n", (void *)buffer, strlen(buffer), buffer);
printf("%p: spare1 %2zu: [%s]\n", (void *)spare1, strlen(spare1), spare1);
printf("%p: spare2 %2zu: [%s]\n", (void *)spare2, strlen(spare2), spare2);
return 0;
}
。
-Werror
和以前一样,Clang和GCC对代码的可接受性有不同的看法(而$ clang -O3 -g -I./inc -std=c11 -Wall -Wextra -Werror strncat37.c -o strncat37 -L./lib -lsoq
$ gcc -O3 -g -I./inc -std=c11 -Wall -Wextra -Werror strncat37.c -o strncat37 -L./lib -lsoq
strncat37.c: In function ‘main’:
strncat37.c:80:9: error: ‘strncat’ output may be truncated copying between 15 and 16 bytes from a string of length 26 [-Werror=stringop-truncation]
(strncat)(buffer, data, len);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
$
意味着来自GCC的警告被视为错误):
$ ./strncat37 -h
Usage: strncat37 [-fhlmsV] [string]
-f Function is called directly
-h Print this help message and exit
-l Long buffer length -- sizeof(buffer)
-m Macro cover for the function is used (default)
-s Short buffer length -- sizeof(buffer)-1 (default)
-V Print version information and exit
$ ./strncat37
Specified length: 15
Copied string: [ABCDEFGHIJKLMNOPQRSTUVWXYZ]
Copied using strncat() macro
0x7ffedff4e400: buffer 15: [ABCDEFGHIJKLMNO]
0x7ffedff4e410: spare1 3: [abc]
0x7ffedff4e3f0: spare2 3: [xyz]
$ ./strncat37 -m -s
Specified length: 15
Copied string: [ABCDEFGHIJKLMNOPQRSTUVWXYZ]
Copied using strncat() macro
0x7ffeeaf043f0: buffer 15: [ABCDEFGHIJKLMNO]
0x7ffeeaf04400: spare1 3: [abc]
0x7ffeeaf043e0: spare2 3: [xyz]
$ ./strncat37 -m -l
Specified length: 16
Copied string: [ABCDEFGHIJKLMNOPQRSTUVWXYZ]
Copied using strncat() macro
Abort trap: 6
$ ./strncat37 -f -s
Specified length: 15
Copied string: [ABCDEFGHIJKLMNOPQRSTUVWXYZ]
Copied using strncat() function directly
0x7ffeef0913f0: buffer 15: [ABCDEFGHIJKLMNO]
0x7ffeef091400: spare1 3: [abc]
0x7ffeef0913e0: spare2 3: [xyz]
$ ./strncat37 -f -l
Specified length: 16
Copied string: [ABCDEFGHIJKLMNOPQRSTUVWXYZ]
Copied using strncat() function directly
0x7ffeed8d33f0: buffer 16: [ABCDEFGHIJKLMNOP]
0x7ffeed8d3400: spare1 0: []
0x7ffeed8d33e0: spare2 3: [xyz]
$
运行时:
-m -l
默认行为也是正确的行为;程序不会崩溃,也不会产生意想不到的副作用。当使用宏运行并且指定的长度太长(-f -l
)时,程序崩溃。当使用函数运行并且长度太长(spare1
)时,程序会在buffer
结束后添加null来覆盖数组scanf()
的第一个字节,并显示16个字节的数据而不是15个字节。
1当你使用%31s
或类似的时候,sizeof(string) - 1
有一个例外;指定的数字是可以存储在字符串中的非空字符数;它会在读取其他31个字符后添加一个空字节。因此,可以安全使用的最大尺寸是strncatXX.c
。
您可以在SOQ子目录中的GitHub上的src/so-5405-4423(Stack Overflow Questions)存储库中找到int main(){
的代码。
从问题中获取代码并将int main(void){
更改为-Werror
,因为我的默认编译选项会产生错误(如果我不使用main()
则会发出警告)非原型return 0;
,并在main()
末尾添加$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes so-5405-4423-v1.c -o so-5405-4423-v1
In file included from /opt/gcc/v8.2.0/lib/gcc/x86_64-apple-darwin17.7.0/8.2.0/include-fixed/stdio.h:425,
from so-5405-4423-v1.c:1:
so-5405-4423-v1.c: In function ‘main’:
so-5405-4423-v1.c:32:29: error: ‘%d’ directive writing between 1 and 2 bytes into a region of size between 1 and 100 [-Werror=format-overflow=]
sprintf(result, "%s%d, ", text, i); // Format the text and store it in result
^~~~~~~~
so-5405-4423-v1.c:32:29: note: directive argument in the range [0, 10]
so-5405-4423-v1.c:32:13: note: ‘__builtin___sprintf_chk’ output between 4 and 104 bytes into a destination of size 100
sprintf(result, "%s%d, ", text, i); // Format the text and store it in result
^~~~~~~
so-5405-4423-v1.c:37:29: error: ‘ ’ directive writing 1 byte into a region of size between 0 and 99 [-Werror=format-overflow=]
sprintf(result, "%s%d ", text, i); // Format the text and store it in result
^~~~~~~
so-5405-4423-v1.c:37:13: note: ‘__builtin___sprintf_chk’ output between 3 and 102 bytes into a destination of size 100
sprintf(result, "%s%d ", text, i); // Format the text and store it in result
^~~~~~~
cc1: all warnings being treated as errors
$
,这是什么在运行macOS 10.14.2 Mojave的Mac上,左边给出了我在GCC 8.2.0上编译的这些错误:
text
编译器指出", "
是一个可以包含0到99个字符的字符串,因此理论上它可以在与数字和" "
(或"String No."
进行一次迭代)连接时导致溢出。它被初始化为text
这一事实意味着没有溢出风险,但你可以通过使用较短的长度来减轻20
- 比如说100
而不是-Werror
。
我承认这个在GCC中相对较新的警告并不总是像所有那样有用(这是代码正常的情况,但警告仍然出现)。我通常会解决这个问题,如果只是因为它目前显示我的默认选项而且代码没有使用clang
的任何警告进行编译,而且如果没有这种级别的保护,我还没准备好。我不使用-Weverything
的-Wall
选项raw;它产生的警告肯定适得其反(至少是AFAIAC)。但是,我反驳了对我不起作用的“一切”选项。如果-Wextra
或for(j = 0; j < 10; j++){ // Now loop to change the line
strcpy(lines[i], line); // Copy the line of text into each line of the array
fputs(lines[i], file); // Put each line into the file
}
选项太痛苦,出于某种原因,我会反击它,但要小心翼翼。我会检查疼痛程度,并旨在处理任何症状。
你也有循环:
i
不幸的是,当这个循环运行时,10
等于lines
,它超出了数组j
的界限。这可能导致崩溃。据推测,该指数应该是i
而不是so-5405-4423-v2.c
。
这是您的代码的检测版本(#include <stdio.h>
#include <string.h>
char line[1001];
char lines[11][1001];
char info[100];
char *extra_info(char string_1[], char string_2[], char string_3[],
char string_4[], char string_5[]);
int main(void)
{
char result[100], text[20];
const char filename[] = "test.txt";
FILE *file;
strcpy(text, "String No.");
file = fopen(filename, "w+");
if (file == NULL)
{
fprintf(stderr, "Failed to open file '%s' for writing/update\n", filename);
return 1;
}
for (int i = 0; i < 10; i++)
{
if (i != 9)
sprintf(result, "%s%d, ", text, i);
else
sprintf(result, "%s%d ", text, i);
fprintf(stderr, "Iteration %d:\n", i);
fprintf(stderr, "1 result (%4zu): [%s]\n", strlen(result), result);
fprintf(stderr, "1 line (%4zu): [%s]\n", strlen(line), line);
extra_info("st", "nd", "rd", "th", "th");
fprintf(stderr, "2 line (%4zu): [%s]\n", strlen(line), line);
fprintf(stderr, "1 info (%4zu): [%s]\n", strlen(info), info);
strncat(line, info, 100);
fprintf(stderr, "3 line (%4zu): [%s]\n", strlen(line), line);
printf("%s", result);
strncat(line, result, 15);
fprintf(stderr, "3 line (%4zu): [%s]\n", strlen(line), line);
}
fprintf(stderr, "4 line (%4zu): [%s]\n", strlen(line), line);
strncat(line, "\n\n", 2);
for (int j = 0; j < 10; j++)
{
strcpy(lines[j], line);
fputs(lines[j], file);
}
fclose(file);
return 0;
}
char *extra_info(char string_1[], char string_2[], char string_3[],
char string_4[], char string_5[])
{
char text[100];
sprintf(text, " 1%s", string_1);
fprintf(stderr, "EI 1: add (%zu) [%s] to (%zu) [%s]\n", strlen(string_1), string_1, strlen(line), line);
strncat(line, text, 100);
sprintf(text, ", 2%s", string_2);
fprintf(stderr, "EI 2: add (%zu) [%s] to (%zu) [%s]\n", strlen(string_2), string_2, strlen(line), line);
strncat(line, text, 100);
sprintf(text, ", 3%s", string_3);
fprintf(stderr, "EI 3: add (%zu) [%s] to (%zu) [%s]\n", strlen(string_3), string_3, strlen(line), line);
strncat(line, text, 100);
sprintf(text, ", 4%s", string_4);
fprintf(stderr, "EI 4: add (%zu) [%s] to (%zu) [%s]\n", strlen(string_4), string_4, strlen(line), line);
strncat(line, text, 100);
sprintf(text, ", 5%s.", string_5);
fprintf(stderr, "EI 5: add (%zu) [%s] to (%zu) [%s]\n", strlen(string_5), string_5, strlen(line), line);
strncat(line, text, 100);
fprintf(stderr, "EI 6: copy (%zu) [%s] to info\n", strlen(line), line);
strcpy(info, line);
return line;
}
):
Iteration 0:
1 result ( 13): [String No.0, ]
1 line ( 0): []
EI 1: add (2) [st] to (0) []
EI 2: add (2) [nd] to (4) [ 1st]
EI 3: add (2) [rd] to (9) [ 1st, 2nd]
EI 4: add (2) [th] to (14) [ 1st, 2nd, 3rd]
EI 5: add (2) [th] to (19) [ 1st, 2nd, 3rd, 4th]
EI 6: copy (25) [ 1st, 2nd, 3rd, 4th, 5th.] to info
2 line ( 25): [ 1st, 2nd, 3rd, 4th, 5th.]
1 info ( 25): [ 1st, 2nd, 3rd, 4th, 5th.]
3 line ( 50): [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.]
3 line ( 63): [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, ]
Iteration 1:
1 result ( 13): [String No.1, ]
1 line ( 63): [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, ]
EI 1: add (2) [st] to (63) [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, ]
EI 2: add (2) [nd] to (67) [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st]
EI 3: add (2) [rd] to (72) [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd]
EI 4: add (2) [th] to (77) [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd]
EI 5: add (2) [th] to (82) [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th]
EI 6: copy (88) [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th.] to info
2 line ( 88): [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th.]
1 info ( 88): [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th.]
3 line ( 176): [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th.]
3 line ( 189): [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th.String No.1, ]
Iteration 2:
1 result ( 13): [String No.2, ]
1 line ( 189): [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th.String No.1, ]
EI 1: add (2) [st] to (189) [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th.String No.1, ]
EI 2: add (2) [nd] to (193) [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th.String No.1, 1st]
EI 3: add (2) [rd] to (198) [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th.String No.1, 1st, 2nd]
EI 4: add (2) [th] to (203) [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th.String No.1, 1st, 2nd, 3rd]
EI 5: add (2) [th] to (208) [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th.String No.1, 1st, 2nd, 3rd, 4th]
EI 6: copy (214) [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th.String No.1, 1st, 2nd, 3rd, 4th, 5th.] to info
String No.0, String No.1, Abort trap: 6
运行时,它产生的输出类似于:
line
当您观察到214个字节从info
(大到足以容纳该字符串)复制到lldb
(不是 - 它只有100个字节长)时,随之而来的崩溃并不是很令人惊讶。尚不完全清楚所期望的行为是什么。
在我的Mac上,__strcpy_chk
调试器报告了extra_info()
中的崩溃; AFAICT,它位于frame #6: 0x00007fff681bbe84 libsystem_c.dylib`__strcpy_chk + 83
frame #7: 0x00000001000017cc so-5405-4423-v2`extra_info(string_1=<unavailable>, string_2=<unavailable>, string_3="rd", string_4="th", string_5="th") at so-5405-4423-v2.c:86
末尾突出显示的行中:
strncat()
因此,虽然显然不是strncat()
导致崩溃,但使用strncat()
的方式显然不正确 - IMO,它是不正确的,但视图可能不同。我仍然支持我的基本结论:不要使用#include <stdio.h>
#include <string.h>
char line[1001]; // The line supports up to a 1000 characters
char lines[11][1001]; // An array of lines (up to 10 lines where each line is a 1000 characters max)
char info[100]; // Holds extra info provided by user
char * extra_info(
char string_1[],
char string_2[],
char string_3[],
char string_4[],
char string_5[]
);
int main(){
int
i, // Line number
j; // Length of the line
char result[100], text[100];
FILE *file;
strcpy(text, "String No."); // The default text
file = fopen("test.txt", "w+"); // Open the file for reading and writing
for(i = 0; i < 10; i++){ // Loop to create a line.
if(i != 9){ // If the line is NOT at the 10th string
sprintf(result, "%s%d, ", text, i); // Format the text and store it in result
}
else{
sprintf(result, "%s%d ", text, i); // Format the text and store it in result
}
strncat(line, info, 100); // Append the extra info at the end of each line
strncat(line, result, 15); // Concatenate all strings in one line
}
extra_info(
"st",
"nd",
"rd",
"th",
"th"
);
strncat(line, "\n\n", 2); // Add a new-line character at the end of each line
for(j = 0; j < 10; j++){ // Now loop to change the line
strcpy(lines[i], line); // Copy the line of text into each line of the array
fputs(lines[i], file); // Put each line into the file
}
fclose(file);
}
char * extra_info( // Append user defined and predefined info at the end of a line
char string_1[],
char string_2[],
char string_3[],
char string_4[],
char string_5[]
){
char text[100]; // A variable to hold the text
/* Append a default text into each strings
and concatenate them into one line */
sprintf(text, " 1%s", string_1);
strncat(line, text, 100);
sprintf(text, ", 2%s", string_2);
strncat(line, text, 100);
sprintf(text, ", 3%s", string_3);
strncat(line, text, 100);
sprintf(text, ", 4%s", string_4);
strncat(line, text, 100);
sprintf(text, ", 5%s.", string_5);
strncat(line, text, 100);
return line;
}
。
解决方案很简单,因为我感觉到,在C中没有任何邪恶,恶毒或愤世嫉俗。第一个strcpy()没有必要发生,第二个extra_info()被错放了它的目的,第三个即使我要使用strcpy()参数也要被交换。因此错误中止陷阱6:
qazxswpoi