【问题标题】:Substracting char* in C在 C 中减去 char*
【发布时间】:2022-06-11 06:08:16
【问题描述】:

我正在阅读这篇博文:https://nee.lv/2021/02/28/How-I-cut-GTA-Online-loading-times-by-70/

我不知道怎么做:

size_t len = end - str;

如果我是正确的,C 中的字符串表示为数组,而 C 字符串具有指向该数组第一个元素的指针。那么上面的行是在玩数组下标吗?

他们发布了这些行:

size_t strlen_cacher(char *str) {
    static char *start;
    static char *end;
    size_t len;
    const size_t cap = 20000;

    // if we have a "cached" string and current pointer is within it
    if (start && str >= start && str <= end) {
        // calculate the new strlen
        len = end - str;

        // if we're near the end, unload self
        // we don't want to mess something else up
        if (len < cap / 2)
            MH_DisableHook((LPVOID)strlen_addr);

        // super-fast return!
        return len;
    }

    // count the actual length
    // we need at least one measurement of the large JSON
    // or normal strlen for other strings
    len = builtin_strlen(str);

    // if it was the really long string
    // save it's start and end addresses
    if (len > cap) {
        start = str;
        end = str + len;
    }

    // slow, boring return
    return len;
}

【问题讨论】:

  • static char* end; 已经在 0 初始化之后被初始化,static 变量被给出。所以指针算术len = end - str; 是未定义的行为,因为这种算术仅在指针指向同一个对象(或一个元素之外)时才有效。
  • 但指针运算以对象类型为单位工作。同样,数组索引也以对象类型为单位。因此,len = end - str; 如果 end 指向 nul 终止符,则 len 将是字符串的长度。
  • @WeatherVane if (start ...) 确保 startend 在用于“快速”代码之前由“慢”代码初始化。代码不是很好,我同意。
  • @user3386109 是的,我注意到了,但乍一看...
  • 代码中不包含声称的size_t len = char* str - char* end,减法反之。

标签: arrays c pointers char


【解决方案1】:

假设代码是正确的,end 是指向字符串 nul 终止符的指针, char 数组的最后一个元素。

这是简单的指针运算。在您的示例中,由于您将字符串作为参数传递,因此它将衰减为指向其第一个元素的指针,从指向同一数组的任何其他元素的指针中减去指向数组第一个元素的指针将为您提供偏移量在这两个指针之间的元素数量中,char 或任何其他类型都是如此。

如下代码所示:

#include <stdio.h>

int main() {

    char str[] = "hello";

    char *end = str + 5; // will point to the last element of str, the nul byte
                         // in the above expression str will decay 

    printf("%td", end - str); // 5
    // when you pass str to printf, again it decays
}

【讨论】:

    【解决方案2】:

    if (start &amp;&amp; str &gt;= start &amp;&amp; str &lt;= end) {未定义的行为 (UB),除非 startend 位于 str 指向的同一对象内。

    strlen_cacher("Hello");
    strlen_cacher("World"); // UB
    

    在 C 中,比较&gt;, &gt;=, &lt;=, &lt; 无关指针是 UB。


    “他发布了这些行:” --> importing 其他人的代码时要小心。

    【讨论】:

    • 很好地赶上后续电话。
    【解决方案3】:

    此代码尝试通过缓存最后一个大字符串边界并返回从strend的距离(如果str介于两者之间)来加速对大字符串重复调用的strlen函数startend。如果end 指向空终止符,则距离end - str 是字符串的长度。

    这是有风险的,因为:

    • 如果所有指针不指向同一个数组,则测试str &gt;= start &amp;&amp; str &lt;= end 实际上具有未定义的行为。该测试在古老的 16 位分段架构上会失败,其中仅比较了 &lt;&lt;=&gt;&gt;= 的偏移部分。

    • 这种方法假定长字符串在对strlen_cacher 的调用之间不会发生变化。对于引用页面中的游戏加载器,这可能是正确的,但一般情况下并非如此。例如,在getline() 读取的长行上调用strlen,然后在strtok 从它雕刻的每个令牌上调用将返回不正确的结果。补丁在加载阶段后会自行禁用以避免这个陷阱。

    【讨论】:

      猜你喜欢
      • 2020-08-12
      • 1970-01-01
      • 1970-01-01
      • 2022-08-04
      • 1970-01-01
      • 1970-01-01
      • 2021-02-01
      • 2023-02-13
      • 1970-01-01
      相关资源
      最近更新 更多