im试图解决C语言中的一个问题,其中我们需要颠倒句子中的单词。例如";你好,我的名字叫"至";是我的名字"你好";。我面临的问题是堆栈溢出错误。不完全确定我的代码是否也能完美工作。如果知道我做错了什么,我会很感激的。非常感谢。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int my_strlen(char sentence[]) {
int count = 0;
for (int i = 0 ; sentence[i] != ' ' ; i++) {
count++;
}
return count;
}
void reverse_words(char sentence[]) {
int len = my_strlen(sentence);
int word_start = len;
int word_end = len;
int index = 0;
int i;
char reverse[len + 1];
while (word_start > 0) {
if (sentence[word_start] == ' ') {
i = word_start + 1;
while (i <= word_end) {
reverse[index] = sentence[i];
i++;
index++;
}
reverse[index++] = ' ';
word_end = word_start - 1;
}
word_start--;
}
for (int j = 0 ; j <= word_end ; j++) {
reverse[index] = sentence[j];
index++;
}
reverse[index] = ' ';
for (int j = 0 ; j < index ; j++) {
sentence[index] = reverse[index];
}
}
int main(void) {
char sentence[] = "abcd efgh ijkl mnop qrst uvxy z";
reverse_words(sentence);
for (int i = 0 ; i < sizeof(sentence) / sizeof(sentence[0]) - 1 ; i++) {
printf("%c", sentence[i]);
}
printf("n");
}
只需考虑传递的字符串仅包含一个单词的情况,例如"A"
。
在这种情况下,变量len
将等于1
。
阵列reverse
将只有两个元素
char reverse[len + 1];
现在在这个while循环
while (word_start > 0) {
if (sentence[word_start] == ' ') {
i = word_start + 1;
while (i <= word_end) {
reverse[index] = sentence[i];
i++;
index++;
}
reverse[index++] = ' ';
word_end = word_start - 1;
}
word_start--;
}
if语句的主体将不会获得控件,因为该字符串不包含空格。所以实际上while循环看起来像
while (word_start > 0) {
//...
word_start--;
}
因此,在循环之后word_start
将变为等于0
。
由于以下原因,环路
for (int j = 0 ; j <= word_end ; j++) {
reverse[index] = sentence[j];
index++;
}
你会有reverse[0] = 'A'
,reverse[1] = ' '
,然后在这个声明之后
reverse[index] = ' ';
您将获得reverse[2] = ' '
。也就是说,有人试图在数组之外进行写入。
此外,这个for循环通常使用变量word_end
的无效值,因为该变量可以在前面的while循环中减少。
另一个问题是while循环中的条件
while (i <= word_end) {
在循环的第一次迭代中,可以在结果字符串的中间插入终止零字符' '
。
注意字符串可以包含相邻的空格。在这种情况下,您的方法将是不正确的。
通常,这样的任务是通过以下方式实现的。
首先你需要反转整个字符串,然后从字符串的开头开始反转字符串中的每个单词。不需要使用辅助阵列。使用辅助可变长度数组会使函数不安全。
这里有一个演示程序,其中的函数不使用标准的C字符串函数。
#include <stdio.h>
size_t my_strlen( const char *s )
{
const char *p = s;
while ( *p ) ++p;
return p - s;
}
void reverse_n( char *s, size_t n )
{
for ( size_t i = 0; i < n / 2; i++ )
{
char c = s[i];
s[i] = s[n-i-1];
s[n-i-1] = c;
}
}
char * reverse_words( char *s )
{
size_t n = my_strlen( s );
reverse_n( s, n );
for ( char *p = s; *p; )
{
while ( *p == ' ' || *p == 't' ) ++p;
if ( *p )
{
char *q = p;
while ( *++p && *p != ' ' && *p != 't' );
reverse_n( q, p - q );
}
}
return s;
}
int main( void )
{
char sentence[] = "hello my name is";
puts( sentence );
puts( reverse_words( sentence ) );
}
程序输出为
hello my name is
is name my hello
正如您所看到的,函数reverse_words
不使用辅助可变长度数组。
以下是其中一个原始函数的代码,有三个高度可见的注释,包括两个更改:
void reverse_words(char sentence[]) {
int len = my_strlen(sentence);
int word_start = len;
int word_end = len;
int index = 0;
int i;
char reverse[len + 1];
while (word_start > 0) {
if (sentence[word_start] == ' ') {
i = word_start + 1;
while (i <= word_end) {
reverse[index] = sentence[i];
i++;
index++;
}
reverse[index++] = ' '; // *** Comment #1 below ***
word_end = word_start - 1;
}
word_start--;
}
#if 0 // Comment #2 below
for (int j = 0 ; j <= word_end ; j++) {
#else
for (int j = 0 ; j < word_end ; j++) {
#endif
reverse[index] = sentence[j];
index++;
}
reverse[index] = ' ';
for (j = 0 ; j < index ; j++) {
#if 0 // Comment #3 below
sentence[index] = reverse[index];
#else
sentence[j] = reverse[j];
#endif
}
}
#1-将先前不存在的空间添加到第一个"0"的末尾;单词";copy
#2-修剪最后一个"后面的空间的副本;单词";复制修复了溢出
#3-这永远不会起作用。固定的
固定#3(根据需要(导致另一个观察结果
在处理索引时,在中途代码使用word_end
作为单词的最后字符的索引。但是,最初,word_end
是索引+1。3号修正后,一个非常简单的修正是:
int len = my_strlen(sentence);
int word_start = len;
int word_end = len - 1; // index of last character of last "word"
使代码的其余部分保持原样。
Off by one errors
是常见的错误原因。