在 trim 函数中有一个 if-check 不能正常工作,为什么?
in trim function there's an if-check not working properly, why?
编辑:我编辑了我的代码,结果如下:
#include <stdlib.h>
#include <ctype.h>
char *trim(const char *s) {
if (s == NULL) {
return NULL;
}
size_t count_1 = 0;
for (size_t i = 0; s[i] != '[=11=]'; i++) {
count_1++;
}
if (count_1 < 1) {
return NULL;
}
size_t count_2 = 0;
if (isspace(s[0])) {
count_2++;
}
if (isspace(s[count_1 - 1])) {
count_2++;
}
size_t max_length = (count_1 - count_2) + 1u;
if (max_length >= count_1) {
return NULL;
}
char *str = malloc(max_length);
if (!str) {
return NULL;
}
for (size_t i = 0; s[i] != '[=11=]'; i++) {
if (isspace(s[i]) == 0) { // if isspace is false.
str[i] = s[i];
}
}
str[count_1 - count_2] = 0;
return str;
}
int main(void) {
char s[] = " a b ";
char *str;
str = trim(s);
free(str);
return 0;
}
现在,问题来了
for (size_t i = 0; s[i] != '[=12=]'; i++) {
if (isspace(s[i]) == 0) { // if isspace is false.
str[i] = s[i];
}
我有一个缓冲区溢出,即使我已经检查了长度。事实上,如果 count_1
等于零,我有一个缓冲区溢出错误,但我已经排除了这种情况,但问题仍然存在。通过逐行调试,我注意到我有一个未定义的行为。
我想尝试简化此练习的建议解决方案,因此我为同一练习编写了另一个代码。
这是原来的答案:
这是最小的可重现代码:
#include <stdlib.h>
#include <ctype.h>
char *trim(const char *s) {
size_t count_1 = 0;
for (size_t i = 0; s[i] != '[=13=]'; i++) {
count_1++;
}
size_t count_2 = 0;
if (isspace(s[0])) {
count_2++;
}
if (isspace(s[count_1])) {
count_2++;
}
size_t max_length = (count_1 - count_2) + 1u;
if (max_length >= count_1) {
return NULL;
}
char *str = malloc(max_length);
if (!str) {
return NULL;
}
for (size_t i = 0; s[i] != '[=13=]'; i++) {
if (isalpha(s[i]) == 0) { // if isalpha is false.
str[i] = s[i];
}
str[count_1 - count_2] = 0;
}
return str;
}
int main(void) {
char s[] = " a b ";
char *str;
str = trim(s);
free(str);
return 0;
}
这里是我到目前为止所做的详细解释:
- 我统计了字符串
s
的字符数,长度存储在count_1
.
- 我计算了字符串开头和结尾有多少个空格;金额存储在
count_2
.
注意:我选择使用 isspace
函数(在 <ctype.h>
中),因为我尝试输入 ' '
(即空格),但结果不正确,并且这些 if-checks 不会被评估。 (我用调试器逐行说明了这件事)。
- 在
malloc
内存之前我使用了检查条件来避免缓冲区溢出(这类似于我昨天问的问题),这意味着当且仅当 max_length
小于 count_1
。这样做,我没有缓冲区溢出警告。
我想我可以避免解释最后的步骤,因为它们是不言自明的,而且我也认为它们不会导致错误。如果我错了,我会编辑这一点。
问题我不知道如何解决它:
- 通过逐行调试,我注意到当执行流程进入第二个 if-check 时,
if
主体没有被执行。这很奇怪,因为第一个工作正常。
您的代码中存在多个问题:
count_1
是字符串的长度,您应该将其更明确地命名为 len
- 你 return
NULL
如果不需要修剪。这是值得怀疑的。在所有情况下,您可能应该 return 字符串的副本,并且在分配失败的情况下仅 return NULL
。
- 您只测试字符串开头的 1 个 space 个字符。
- 您只测试字符串末尾的 1 个 space 个字符。
- 此外,如果字符串为
" "
,则此 space 可能会被计算两次。
max_length
用词不当:它不是新字符串的长度,而是分配大小,new_size
似乎更合适。
- 在最后一个循环中,您在原始字符串和新字符串中使用相同的索引
i
:这是不正确的。您应该使用单独的索引,以便在跳过初始 space. 后可以复制原始字符串中的字符
str[count_1 - count_2] = 0;
在循环内是多余的:你应该在循环结束后移动这个语句。
- 类型
char
的参数值在传递给 <ctype.h>
中定义的函数和宏时应转换为 (unsigned char)
以避免在 char
类型已签名。这些函数仅针对 unsigned char
类型的值(介于 0
和 UCHAR_MAX
之间)和特殊的负值 EOF
定义。这些值是由 getchar()
和 getc()
编辑的 return。
这是修改后的版本:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
char *trim(const char *s) {
if (s == NULL) {
return NULL;
}
size_t start, end;
for (start = 0; isspace((unsigned char)s[start]); start++) {
continue;
}
for (end = start; s[end] != '[=10=]'; end++) {
continue;
}
while (end > start && isspace((unsigned char)s[end - 1])) {
end--;
}
// if you are allowed to use strndup, you can return the new string this way:
//return strndup(str + start, end - start);
char *new_str = malloc(end - start + 1);
if (new_str) {
size_t j = 0; // index into the new string
for (size_t i = start; i < end; i++) {
new_str[j++] = str[i];
}
new_str[j] = '[=10=]';
}
return new_str;
}
int main(void) {
char s[] = " a b ";
char *str = trim(s);
printf("trim(\"%s\") -> \"%s\"\n", s, str);
free(str);
return 0;
}
编辑:我编辑了我的代码,结果如下:
#include <stdlib.h>
#include <ctype.h>
char *trim(const char *s) {
if (s == NULL) {
return NULL;
}
size_t count_1 = 0;
for (size_t i = 0; s[i] != '[=11=]'; i++) {
count_1++;
}
if (count_1 < 1) {
return NULL;
}
size_t count_2 = 0;
if (isspace(s[0])) {
count_2++;
}
if (isspace(s[count_1 - 1])) {
count_2++;
}
size_t max_length = (count_1 - count_2) + 1u;
if (max_length >= count_1) {
return NULL;
}
char *str = malloc(max_length);
if (!str) {
return NULL;
}
for (size_t i = 0; s[i] != '[=11=]'; i++) {
if (isspace(s[i]) == 0) { // if isspace is false.
str[i] = s[i];
}
}
str[count_1 - count_2] = 0;
return str;
}
int main(void) {
char s[] = " a b ";
char *str;
str = trim(s);
free(str);
return 0;
}
现在,问题来了
for (size_t i = 0; s[i] != '[=12=]'; i++) {
if (isspace(s[i]) == 0) { // if isspace is false.
str[i] = s[i];
}
我有一个缓冲区溢出,即使我已经检查了长度。事实上,如果 count_1
等于零,我有一个缓冲区溢出错误,但我已经排除了这种情况,但问题仍然存在。通过逐行调试,我注意到我有一个未定义的行为。
我想尝试简化此练习的建议解决方案,因此我为同一练习编写了另一个代码。
这是原来的答案:
这是最小的可重现代码:
#include <stdlib.h>
#include <ctype.h>
char *trim(const char *s) {
size_t count_1 = 0;
for (size_t i = 0; s[i] != '[=13=]'; i++) {
count_1++;
}
size_t count_2 = 0;
if (isspace(s[0])) {
count_2++;
}
if (isspace(s[count_1])) {
count_2++;
}
size_t max_length = (count_1 - count_2) + 1u;
if (max_length >= count_1) {
return NULL;
}
char *str = malloc(max_length);
if (!str) {
return NULL;
}
for (size_t i = 0; s[i] != '[=13=]'; i++) {
if (isalpha(s[i]) == 0) { // if isalpha is false.
str[i] = s[i];
}
str[count_1 - count_2] = 0;
}
return str;
}
int main(void) {
char s[] = " a b ";
char *str;
str = trim(s);
free(str);
return 0;
}
这里是我到目前为止所做的详细解释:
- 我统计了字符串
s
的字符数,长度存储在count_1
. - 我计算了字符串开头和结尾有多少个空格;金额存储在
count_2
.
注意:我选择使用 isspace
函数(在 <ctype.h>
中),因为我尝试输入 ' '
(即空格),但结果不正确,并且这些 if-checks 不会被评估。 (我用调试器逐行说明了这件事)。
- 在
malloc
内存之前我使用了检查条件来避免缓冲区溢出(这类似于我昨天问的问题),这意味着当且仅当max_length
小于count_1
。这样做,我没有缓冲区溢出警告。
我想我可以避免解释最后的步骤,因为它们是不言自明的,而且我也认为它们不会导致错误。如果我错了,我会编辑这一点。
问题我不知道如何解决它:
- 通过逐行调试,我注意到当执行流程进入第二个 if-check 时,
if
主体没有被执行。这很奇怪,因为第一个工作正常。
您的代码中存在多个问题:
count_1
是字符串的长度,您应该将其更明确地命名为len
- 你 return
NULL
如果不需要修剪。这是值得怀疑的。在所有情况下,您可能应该 return 字符串的副本,并且在分配失败的情况下仅 returnNULL
。 - 您只测试字符串开头的 1 个 space 个字符。
- 您只测试字符串末尾的 1 个 space 个字符。
- 此外,如果字符串为
" "
,则此 space 可能会被计算两次。 max_length
用词不当:它不是新字符串的长度,而是分配大小,new_size
似乎更合适。- 在最后一个循环中,您在原始字符串和新字符串中使用相同的索引
i
:这是不正确的。您应该使用单独的索引,以便在跳过初始 space. 后可以复制原始字符串中的字符
str[count_1 - count_2] = 0;
在循环内是多余的:你应该在循环结束后移动这个语句。- 类型
char
的参数值在传递给<ctype.h>
中定义的函数和宏时应转换为(unsigned char)
以避免在char
类型已签名。这些函数仅针对unsigned char
类型的值(介于0
和UCHAR_MAX
之间)和特殊的负值EOF
定义。这些值是由getchar()
和getc()
编辑的 return。
这是修改后的版本:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
char *trim(const char *s) {
if (s == NULL) {
return NULL;
}
size_t start, end;
for (start = 0; isspace((unsigned char)s[start]); start++) {
continue;
}
for (end = start; s[end] != '[=10=]'; end++) {
continue;
}
while (end > start && isspace((unsigned char)s[end - 1])) {
end--;
}
// if you are allowed to use strndup, you can return the new string this way:
//return strndup(str + start, end - start);
char *new_str = malloc(end - start + 1);
if (new_str) {
size_t j = 0; // index into the new string
for (size_t i = start; i < end; i++) {
new_str[j++] = str[i];
}
new_str[j] = '[=10=]';
}
return new_str;
}
int main(void) {
char s[] = " a b ";
char *str = trim(s);
printf("trim(\"%s\") -> \"%s\"\n", s, str);
free(str);
return 0;
}