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

问题描述 投票:-1回答:2

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()有关,这使我认为会出现内存分配问题,但在尝试其他示例后,解决方案看起来要简单得多。任何有关这方面的帮助将不胜感激。提前致谢。

c
2个回答
2
投票

我在2018年3月编写了随附的代码,以满足自己在strncat()发生的问题,以便在我提交答案之前删除另一个问题。这只是重新定位代码。

strncat()功能(正如我在comment中所说的)邪恶和卑鄙。它也与strncpy()界面不一致 - 并且与你在其他任何地方遇到的任何东西都不同。读完之后,你会决定(幸运的话)你永远不应该使用strncat()

TL;DR — Never use 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字符(空字符和后面跟随的字符不会附加)不超过s2s1的初始字符会覆盖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(){的代码。


Analysis of Code from Question

从问题中获取代码并将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)。但是,我反驳了对我不起作用的“一切”选项。如果-Wextrafor(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; }


-1
投票

解决方案很简单,因为我感觉到,在C中没有任何邪恶,恶毒或愤世嫉俗。第一个strcpy()没有必要发生,第二个extra_info()被错放了它的目的,第三个即使我要使用strcpy()参数也要被交换。因此错误中止陷阱6:

qazxswpoi
© www.soinside.com 2019 - 2024. All rights reserved.