问题描述
我正在尝试使用以下代码将右括号附加到从用户获取的字符串的末尾
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,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方法的启发:我的代码有什么问题?
解决方法
首先,char
的大小总是 1
,它在规范中定义。
第二,数组的大小就是数组本身的大小,以“字节”为单位。
那是
sizeof( charArray ) / sizeof( charArray[ 0 ] )
等于
20 / 1
等于20
。
也就是说,您将总是写入数组的第二十一个元素,该元素超出范围并导致未定义的行为。
如果要将字符附加到字符串中,请使用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
获取字符串null终止符的位置并用char覆盖它(但请记住添加一个新的null终止符,并进行检查,以免超出范围) ):
// 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';
}
,
您在这里做的错误是不知道数组和字符串之间的区别。您已经声明了一个大小为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,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';
}
当然,您可以使用任何方式自定义错误处理。
,表达式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。我已经使用很多多年了,没问题。
, 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 < sizeof( charArray )
确保该字符串包含换行符'\n'
。