【问题标题】:Using sizeof to append a character to the end of a string form user使用 sizeof 将字符附加到字符串形式用户的末尾
【发布时间】:2020-12-19 10:58:32
【问题描述】:

我正在尝试使用以下代码将右括号附加到从用户那里获取的字符串的末尾

int main( void )
{
    char charArray[ 20 ];

    fgets( charArray, 20, stdin );

    charArray[ sizeof( charArray ) / sizeof( charArray[ 0 ] ) ] = ')';

    printf( "%s", charArray );
}

但如果我输入:4+5,输出只是:4+5 而不是:4+5)

我什至尝试了以下变体,以防万一

int main( void )
{
    char charArray[ 20 ];
    int n;

    fgets( charArray, 20, stdin );

    n = sizeof( charArray ) / sizeof( charArray[ 0 ] );

    charArray[ n ] = ')';

    printf( "%s", charArray );
}

但它不起作用。所以我通过这样做消除了对 sizeof 的需要

int main( void )
{
    char charArray[ 20 ];
    int i = 0;

    do{
        scanf( "%c", &charArray[ i ] );
        ++i;
    } while( charArray[ i - 1 ] != '\n' );

    charArray[ i - 1 ] = ')';

    printf( "%s", charArray );
}

但是我将来可能需要 sizeof ,所以我想知道我做错了什么。而且 fgets 和 sizeof 的使用似乎比 scanf 更直接和简洁,并且使用神秘的 [ i - 1 ] 下标来做。 请告诉我 sizeof 方法:我的代码有什么问题?

【问题讨论】:

  • charArray[ sizeof( charArray ) / sizeof( charArray[ 0 ] ) ] = ')'; 根据定义写入超出数组范围。
  • 你在操作字符串,strlen的使用呢?

标签: arrays c string character sizeof


【解决方案1】:

sizeof( char ) 始终等于1。所以这个表达式

sizeof( charArray ) / sizeof( charArray[ 0 ] )

等价于表达式

sizeof( charArray )

因为sizeof( charArray[ 0 ] )sizeof( char ),因此它产生了在此声明的情况下整个数组大小的值

char charArray[ 20 ];

等于20

表示例如这个语句

charArray[ sizeof( charArray ) / sizeof( charArray[ 0 ] ) ] = ')';

尝试写入数组外的内存。

函数fgets 追加换行符'\n',前提是用户输入的字符串小于指定的字符数组大小。

所以你需要用换行符替换字符')'

您可以通过以下方式使用标准 C 函数 strcspn 轻松完成此操作

size_t n = strcspn( charArray, "\n" );
if ( n + 1 < sizeof( charArray ) ) charArray[n] = ')';

在 if 语句的条件下,使用表达式n + 1 来考虑数组不包含换行符'\n'(用户输入的字符数超过指定大小)的情况。在这种情况下,n 将产生小于数组大小的终止零字符 '\0' 的位置。

但我们不能用它代替字符')',否则数组将不包含字符串。所以这个条件n + 1 &lt; sizeof( charArray )保证字符串包含换行符'\n'

【讨论】:

  • 在我的教科书中有一整章是关于使用 fgets、strcspn 和许多其他函数进行字符串操作的。我坚持我无法全部记住它们,所以我决定粗略地阅读这一章以跳到下一章,以便在我需要处理字符串的任何时候都可以使用这本书作为参考。这确实是一个糟糕的决定。感谢您向我展示了 strcspn 的价值。
【解决方案2】:

表达式sizeof(charArray) 是 20,与您存储在其中的字符串没有任何关系(当然,除非该字符串大于 20 个字节)。

如果要获取字符串末尾\0的索引,则需要使用strlen而不是sizeof

例如,以下是一种安全的方法:

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

int main( void ) {
    // Allow extra space for ')' and use actual size for input.

    char charArray[21];
    if (fgets(charArray, sizeof(charArray) - 1, stdin) == NULL) {
        fprintf(stderr, "Some problem occurred on input\n");
        return 1;
    }

    // Ensure terminated with newline.

    size_t len = strlen(charArray);
    if (len < 1 || charArray[len - 1] != '\n') {
        fprintf(stderr, "Input is not terminated with newline\n");
        return 1;
    }

    // Just replace newline with ')' character.

    charArray[len - 1] = ')';
    printf("%s\n", charArray);
}

顺便说一下,如果您正在寻找标准 C 中的防弹用户输入功能,建议您查看this one。我已经使用了 很多 年没有问题。

【讨论】:

    【解决方案3】:

    你在这里做错的是不知道数组和字符串之间的区别。你已经声明了一个大小为 20 的数组。这个大小永远不会改变。

    您正在寻找的是字符串的长度,您可以通过strlen(charArray) 得到它。

    字符串是以\0 字符结尾的可打印字符序列。字符串的长度是终止符之前的字符数。

    此代码将打印5, 20:

    char arr[20] = "Hello";
    printf("%zu, %zu\n", strlen(arr), sizeof(arr));
    

    注意这将打印3:

    char arr[20] = "Hello";
    printf("%zu\n", strlen(&arr[2]));
    

    还请注意,您可以执行以下操作:

    char arr[20] = "Hello\0World!\n";
    printf("%zu, %zu\n", 
           strlen(&arr[0]), // Hello
           strlen(arr[strlen(&arr[0]) + 1]) // World!\n
    );
    

    这将打印5, 7。第二个长度为 7,因为换行符也很重要。另请注意,在初始化数组时,始终会附加 '\0',除非数组的大小太小。这将声明一个没有终止符的字符串:

    char no_terminator[5] = "Hello";
    

    避免这种情况。这可能会给您带来很多问题。

    所以在你的情况下,你可以这样做:

    fgets( charArray, 20, stdin );
    
    // Standard way of removing newline from a string. It's a very useful
    // trick since fgets saves the newline in the string.
    charArray[strcspn(charArray, "\n")] = '\0';
    
    size_t len = strlen(charArray);
    charArray[len] = ')';
    charArray[len+1] = '\0';
    

    但要小心,让len+1 永远不会超过 20。如果超过了,那么您正在调用未定义的行为,这可能会导致调试变得一团糟。此外,检查fgets 是否返回NULL 是明智的。如果是这种情况,您应该处理错误。

    如果你经常处理这种输入,我可以推荐这个包装器:

    void my_readline(char *str, int n)
    {
        if(fgets(str, n, stdin) == NULL) {
            perror("Error reading string");
            exit(EXIT_FAILURE);
        }
        str[strcspn(charArray, "\n")] = '\0';
    }
    

    当然,您可以以任何您想要的方式自定义错误处理。

    【讨论】:

      【解决方案4】:

      首先,char 的大小总是 1,它在规范中定义。

      其次,数组的大小就是数组本身的大小,以“字节”为单位。

      意思是

      sizeof( charArray ) / sizeof( charArray[ 0 ] )
      

      等于

      20 / 1
      

      等于20

      也就是说,您将总是写入数组的第 21 个元素,这超出了界限并导致未定义的行为。

      如果您想将字符附加到字符串,请使用strcat 将字符附加为字符串(确保不会超出范围):

      // Check to make sure there's space in the array, the -1 is for the terminator
      if (strlen(charArray) < (sizeof charArray - 1))
      {
          strcat(charArray, ")");
      }
      

      或使用strlen 获取字符串空终止符的位置并用您的字符覆盖它(但请记住添加一个新的空终止符,并进行检查,以免超出范围):

      // Get the index of the current null-terminator in the string
      size_t terminator_index = strlen(charArray);
      
      // Check to make sure there's space in the array, the -1 is for the terminator
      if (terminator_index < (sizeof charArray - 1))
      {
          // Can append new character
          charArray[terminator_index] = ')';
      
          // Add terminator
          charArray[terminator_index + 1] = '\0';
      }
      

      【讨论】:

      • 非常感谢您的解释和打开的可能性。所以看起来即使是 do while 方法也是错误的,因为我覆盖了空终止符。我认为终结符固定在数组的最后一个下标中。我也在几分钟内收到了 3 个答案,我要谢谢大家!这个网站是程序员的金矿。
      • 其实做的很好,我正在覆盖新行
      猜你喜欢
      • 2014-03-06
      • 2022-12-11
      • 2017-09-29
      • 2020-08-19
      • 1970-01-01
      • 1970-01-01
      • 2023-03-12
      • 2021-12-16
      • 2015-09-21
      相关资源
      最近更新 更多