0

我正在开始一个将用 python 编写的解释器转换为 C 的个人项目。这纯粹是为了学习目的。

我遇到的第一件事是尝试转换以下内容:

if __name__ == "__main__":
    if not argv[-1].endswith('.py'):
        ...

到目前为止,我已经对该endswith方法进行了以下转换

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

bool endswith(char* str, char* substr)
{
    // case1: one of the strings is empty
    if (!str || !substr) return false;

    char* start_of_substring = strstr(str, substr);

    // case2: not in substring
    if (!start_of_substring) return false;

    size_t length_of_string    = strlen(str);
    size_t length_of_substring = strlen(substr);
    size_t index_of_match      = start_of_substring - str;

    // case2: check if at end
    return (length_of_string == length_of_substring + index_of_match);

}

int main(int argc, char* argv[])
{
    char *last_arg = argv[argc-1];
    if (endswith(last_arg, ".py")) {
        // ...
    } 

}

这看起来是否涵盖了 中的所有情况endswith,还是我错过了一些边缘情况?如果是这样,如何改进?最后,这不是批评,而是编写 C 应用程序的真正问题:编写 C 需要比在 python 中执行相同操作多 5-10 倍的代码是否普遍(或者因为我是初学者所以更多并且不知道如何正确地做事?)

及相关:https ://codereview.stackexchange.com/questions/54722/determine-if-one-string-occurs-at-the-end-of-another/54724

4

3 回答 3

4

对于初学者,该函数应声明为

bool endswith(const char* str, const char* substr);

因为传递给函数的字符串都没有在函数内更改。

其次,这个 if 语句

if (!str || !substr) return false;

您正在检查是否至少一个指针是空指针的位置对于字符串函数是多余的。

所有标准字符串函数都遵循通用约定,即如果用户传递空指针,则函数行为未定义。也就是说,传递非空指针是函数用户的责任。

第三,如果调用strstr

char* start_of_substring = strstr(str, substr);

将返回一个非空指针,这并不意味着第一个字符串以第二个字符串结尾或不以第二个子字符串结尾。例如,第一个字符串可以包含第二个字符串的多个副本。在这种情况下,您的函数将返回 false。

该函数可以如下所示,如下面的 tje 演示程序所示。

特别是假设任何字符串都以空字符串结尾。

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

bool endswith( const char *s1, const char *s2 )
{
    size_t n1 = strlen( s1 );
    size_t n2 = strlen( s2 );
    
    return ( n2 == 0 ) || ( !( n1 < n2 ) && memcmp( s1 + n1 - n2, s2, n2 ) == 0 );
}

int main(void) 
{
    const char *s1 = "Hello World!";
    const char *s2 = "World!";
    
    printf( "\"%s\" ends with \"%s\" is %s.\n", 
            s1, s2, endswith( s1, s2 ) ? "true" : "false" );
            
    return 0;
}

程序输出为

"Hello World!" ends with "World!" is true.
于 2021-04-08T20:12:02.400 回答
3

这看起来像是覆盖了所有情况,还是我错过了一些边缘情况?

您至少错过了子字符串出现两次或更多次的情况,即最后出现的一次。

我不会用strstr()这个。相反,我会根据两个字符串的相对长度来确定要在主字符串中查找的位置,然后使用strcmp(). 例子:

bool endswith(char* str, char* substr) {
    if (!str || !substr) return false;

    size_t length_of_string    = strlen(str);
    size_t length_of_substring = strlen(substr);

    if (length_of_substring > length_of_string) return false;

    return (strcmp(str + length_of_string - length_of_substring, substr) == 0);
}

关于该return语句:str + length_of_string - length_of_substring等价于&str[length_of_string - length_of_substring]-- 即指向尾部子字符串的第一个字符的指针,其长度与substr. 该strcmp函数比较两个 C 字符串,返回一个小于、等于或大于零的整数,具体取决于第一个参数按字典顺序是小于、等于还是大于第二个。特别是,strcmp()当它的参数相等时返回 0,并且这个函数返回正是这样一个测试的结果。

编写 C 语言需要比在 python 中执行相同操作多 5-10 倍的代码,这很常见吗?

Python 是一种比 C 更高级的语言,因此用于任务的 C 代码通常比用于相同任务的 Python 代码更长。此外,C 块是明确分隔的,这使得 C 代码比 Python 代码长一点。不过,我不确定 5-10 倍是一个好的估计值,而且我认为在这种情况下,您是在将苹果与橙子进行比较。类似于您的 Python 代码的代码很简单

int main(int argc, char* argv[]) {
    if (endswith(argv[argc-1], ".py")) {
        // ...
    } 
}

C 没有内置endswith()函数是另一回事。

于 2021-04-08T19:48:09.360 回答
1

最后,这不是批评,而是编写 C 应用程序时的一个真正问题:编写 C 需要比在 python 中执行相同操作多 5-10 倍的代码是否普遍?

听起来有点多,但这取决于你做什么。是的,通常 C 代码更长。部分原因是语言本身,部分原因是它有一个庞大的库,用于存储您必须在 C 中从头开始实现的各种内容。您看到函数了argv[-1].endswith('.py')吗?好吧,有人为此编写了代码。你只是没有看到它。

但是有一些特性有时可以使 C 中的代码更短。例如,在 Python 中,赋值是语句。在 C 中,它们是表达式。这意味着在 C 中,您可以执行以下操作:

if(c = foo()) { // Assign c to the return value of foo 
                // and then evaluate it as a Boolean

您还可以使用逗号运算符,如下所示:

if((c == foo(), ++c) > 4) {

通常,这样的构造是一个坏主意。特别是如果它们很复杂。但至少它是 C 代码有时可以更短的示例。

于 2021-04-08T20:14:07.130 回答