在C中向空字符串追加字符

yzuktlbb  于 2023-02-18  发布在  其他
关注(0)|答案(2)|浏览(289)

我对C语言比较陌生,所以任何帮助我理解正在发生的事情都是很棒的!!!
我有一个名为Token的结构体,如下所示:

//Token struct
struct Token {
    char type[16];
    char value[1024];
};

我正在尝试读取文件并将从文件读取的字符追加到Token.value中,如下所示:

struct Token newToken;
    char ch;
    ch = fgetc(file);
    strncat(newToken.value, &ch, 1);

这很有效!
我的问题是Token.value以几个我不理解的值开始,在我追加的字符之前。当我将newToken.value的结果打印到控制台时,我得到@�����TheCharactersIWantedToAppend。我可能会想出一个创可贴解决方案来追溯删除或处理这些字符,但如果没有必要,我宁愿不这样做。
在分析字符时,我将它们视为(按索引1-5的顺序):\330, \377, \377, \377, \177。我读到\377在C中是EOF的特殊字符,但在十进制中也是255?这些值是否构成内存地址?我是否通过在strncat中使用&ch将地址添加到newToken.value?如果是,我如何防止它们进入newToken.value

**注意:**如果我使用strncat(newToken.value, ch, 1)而不是strncat(newToken.value, &ch, 1)(ch与&ch),我会得到一个分段错误。

iqxoj9l9

iqxoj9l91#

我将努力巩固评论中已经给出的答案。
这个版本的代码使用了strncat(),和你的一样,但是解决了Nick(我们必须初始化目标)和Dúthomhas(strncat()的第二个参数必须是字符串,而不是指向单个字符的指针)指出的问题(是的,“string”实际上是char[],传递给函数的值是char*;但它必须指向至少两个字符的数组 ,最后一个字符包含'\0'。)
请注意,strncat()strncpy()和所有相关函数都很复杂。它们不会写入超过N个字符。但是strncpy()只会在源字符串的
少于个字符时将最后的'\0'添加到目标字符串;并且strncat()***总是**添加它,即使它源具有正好N个字符或更多(编辑;谢谢,@Clifford)。

#include <stdio.h>
#include <string.h>

int main() {
    FILE* file = stdin; // fopen("test.txt", "r");
    if (file) {
        struct Token {
            char type[16];
            char value[1024];
        };

        struct Token newToken;
        newToken.value[0] = '\0';                       // A '\0' at the first position means "empty"

        int aux;
        char source[2] = "";                            // An empty string has a single char with value '\0', but this syntax fills the entire array with '\0's
        while ((aux = fgetc(file)) != EOF) {
            source[0] = (char)aux;
            strncat(newToken.value, source, 1);         // As we say WRITE AT MOST 1 CHAR, it won't add a final '\0'
        }

        strncat(newToken.value, "", 1);                 // As the source string is empty, it can now add the final '\0'
        printf(newToken.value);
    }
    return 0;
}

另一个版本使用了一个index变量,将每个字符直接写入目标字符串的“当前”位置,而没有使用strncat()。我认为它更简单、更安全,因为它没有混淆单个字符和字符串的语义。

#include <stdio.h>
#include <string.h>

int main() {
    FILE* file = stdin; // fopen("test.txt", "r");
    if (file) {
        struct Token {
            int index = 0;
            char type[16];
            char value[1024];        // Max size is 1023 chars + '\0'
        };

        struct Token newToken;
        newToken.value[0] = '\0';    // A '\0' at the first position means "empty". This is not really necessary anymore

        int aux;
        while ((aux = fgetc(file)) != EOF)
            // Index will stop BEFORE 1024-1 (value[1022] will be the last "real" char, leaving space for a final '\0')
            if (newToken.index < sizeof newToken.value -1)
                newToken.value[newToken.index++] = (char)aux;

        newToken.value[newToken.index++] = '\0';
        printf(newToken.value);
    }
    return 0;
}

Editedfgetc()返回一个int,我们应该在将其转换为char之前检查EOF *(谢谢,@chqrlie)。

wrrgggsh

wrrgggsh2#

您正在追加未初始化的字符串,因此可以包含 anything。字符串的结尾由NUL(0)字符指示,在您的示例中,6个字节后碰巧有一个,但value数组中不需要任何,因此代码存在严重缺陷,并将导致不确定性行为。
您需要将newToken示例初始化为空字符串。例如:

struct Token newToken = { "", "" } ;

或者零初始化整个结构:

struct Token newToken = { 0 } ;

关键是C没有显式的初始化器就不会初始化非静态对象。
此外,使用strncat()的效率非常低,而且执行时间不确定,这取决于目标字符串的长度(请参见https://www.joelonsoftware.com/2001/12/11/back-to-basics/)。
在这种情况下,最好维护添加的字符数的计数,并将字符和终止符直接写入数组。例如:

size_t index ;
int ch = 0 ;

do
{
    ch = fgetc(file);
    if( ch != EOF ) 
    {
        newToken.value[index] = (char)ch ;
        index++ ;
        newToken.value[index] = '\0' ;
    }
} while( ch != EOF && 
         index < size of(newToken.value) - 1 ) ;

相关问题