gpt4 book ai didi

c - 如何使用strncat()避免在运行时中止陷阱6错误?

转载 作者:行者123 更新时间:2023-11-30 19:03:20 25 4
gpt4 key购买 nike

中止陷阱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()

TL; DR-切勿使用strncat()

C标准定义了strncat()(并且POSIX同意-strncat()


  C11§7.24.3.2strncat函数
  
  概要

#include <string.h>
char *strncat(char * restrict s1, const char * restrict s2, size_t n);

  
  描述
  
   strncat函数将从 n所指向的数组追加到 s2所指向的字符串的末尾,最多追加 s1个字符(不附加空字符和其后的字符)。 s2的初始字符会覆盖 s1末尾的空字符。总是在结果后附加一个终止的空字符。309)如果在重叠的对象之间进行复制,则行为是不确定的。
  
  退货
  
   strncat函数返回 s1的值。
  
  309)因此,在 s1指向的数组中可以出现的最大字符数为 strlen(s1)+n+1


脚注使用 strncat()标识最大的陷阱-您不能安全使用:

char *source = …;

char target[100] = "";

strncat(target, source, sizeof(target));


这与大多数其他函数在C代码中采用数组大小​​参数1的情况相反。

为了安全地使用 strncat(),您应该知道:


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()的各个方面。请注意,在macOS上, strncat()中有一个 <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;
}


它可以干净地编译(使用来自XCode 10.1( clang)的Apple Apple LLVM version 10.0.0 (clang-1000.11.45.5)和GCC 8.2.0,即使设置了严格的警告也可以:

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror strncat19.c -o strncat19
$ ./strncat19
15: [ABCDEFGHIJKLMNO]
spare1 [abc]
spare2 [xyz]
$


strncat29.c

这类似于 strncat19.c,但是(a)允许您指定要在命令行上复制的字符串,并且(b)错误地使用 sizeof(buffer)而不是 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
$


即使没有请求警告,警告也是由GCC发出的,但是由于 -Werror选项不存在,它会生成一个可执行文件:

$ 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()处理选项。它还使用了我的错误报告例程;它们的代码在GitHub上的 SOQ(堆栈溢出问题)存储库中以 src/libsoq子目录中的文件 stderr.cstderr.h形式提供。

#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;
}


和以前一样,Clang和GCC对代码的可接受性有不同的看法(并且 -Werror表示来自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
$


运行时:

$ ./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末尾添加空值,并显示16个字节的数据而不是15个字节。



1使用 scanf()或类似方法时, %31s是一个例外。指定的数字是可以存储在字符串中的非空字符的数目;读取其他31个字符后,它将添加一个空字节。同样,可以安全使用的最大大小为 sizeof(string) - 1

您可以在GitHub上 SOQ子目录中的 src/so-5405-4423(堆栈溢出问题)存储库中找到 strncatXX.c的代码。



问题代码分析

从问题中获取代码并将 int main(){更改为 int main(void){,因为我的默认编译选项会为非原型 -Werror生成错误(如果我不使用 main()则会警告),并添加 return 0;main()末尾,剩下的给我这些错误,这些错误是在运行macOS 10.14.2 Mojave的Mac上使用GCC 8.2.0编译的:

$ 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
$


编译器注意到 text是一个可以包含0到99个字符的字符串,因此从理论上讲,当与数字和 ", "(或 " "进行一次迭代)连接时,它可能导致溢出。将其初始化为 "String No."的事实意味着不存在溢出风险,但是您可以通过为 text使用较短的长度(例如,使用 20而不是 100)来减轻这种情况。

我承认这个警告在GCC中是相对较新的,并不总是那么有用(这是代码正常的情况,但是警告仍然出现)。我通常会解决此问题,仅是因为当前它以我的默认选项显示,并且代码无法使用 -Werror进行任何警告编译,并且如果没有这种级别的保护,我也无法做好准备。我不使用 clang-Weverything选项raw;它会产生绝对适得其反的警告(至少是AFAIAC)。但是,我反对对我不起作用的“所有”选项。如果 -Wall-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


当您观察到214个字节从 line(足够容纳该字符串)复制到 info(不是-而是100字节长)时,随之而来的崩溃并不是很令人惊讶。尚不清楚所需的行为是什么。

在我的Mac上, lldb调试器在 __strcpy_chk中报告崩溃; AFAICT,在 extra_info()末尾突出显示的行中:

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()的方式显然不是正确的-IMO,这是不正确的,但是视图可能有所不同。我仍然支持我的基本结论:请勿使用 strncat()

关于c - 如何使用strncat()避免在运行时中止陷阱6错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54054423/

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