【问题标题】:grammatical difficulties, unsuspected output语法困难,意外输出
【发布时间】:2020-12-09 19:37:37
【问题描述】:

你能告诉我为什么运行这两个代码我有不同的输出吗?

    void UART_OutString(unsigned char buffer[]){
    int i;
    while(buffer[i]){
        UART_OutChar(buffer[i]);
        i++;
    }
}

    void UART_OutString(unsigned char buffer[]){
  int i = 0;
  while(buffer[i]){
     UART_OutChar(buffer[i++]);
  }
}

问候,杰纳迪

【问题讨论】:

  • int i; 在第一个代码中未初始化,首先...
  • 默认不等于0吗?同样在调试器中我看到'int i'有一个值= 0x0
  • 在编译器中启用警告? (局部变量默认不初始化)。
  • @Gendozz 你很幸运,当时它恰好为零,但不,默认情况下它没有初始化。
  • 在第一部分中,您不会将 i 初始化为 0

标签: c grammar


【解决方案1】:

您没有在第一种情况下初始化 i 变量,所以这是一个无聊的拼写错误,您的编译器应该警告您...


话虽如此,我们可以应用 KISS principle 并以最易读的方式重写整个代码,即 for 循环,从本质上讲,它很难忘记初始化循环迭代器:

void UART_OutString(const char* buf[]){
  for(int i=0; buf[i]!='\0'; i++){
     UART_OutChar(buffer[i]);
  }
}

事实证明,最易读的方式往往也是最快的方式。

(但是,int 在某些低端系统上可能效率低下,所以如果您只使用长度为 255 或更短的字符串,uint8_t i 将是更好的选择。嵌入式系统不应该使用@987654326 @ 并且总是 stdint.h 类型。)

【讨论】:

    【解决方案2】:

    为了它的价值,我将其实现为

    void UART_OutChar(unsigned char c);
    
    void UART_OutString(unsigned char buffer[]){
        for(unsigned char *p = buffer; *p; p++) {
            UART_OutChar(*p);
        }
    }
    

    完全避免单独的计数器变量。

    【讨论】:

    • 您能解释一下为什么最好不要预订柜台吗?
    • @Gendozz 有一个计数器,您需要在每次迭代中递增计数器。并且您还需要每次将计数器添加到指针值。如果去掉计数器,只需要增加指针值即可。
    • 这不一定更好。 UART 表示这是一个嵌入式系统。让我们假设一个具有 8 位索引寄存器但 16 位地址总线的 8 位微控制器。然后,此代码将强制执行 16 位算术而不是 8 位,从而导致代码慢得多。
    【解决方案3】:

    初始化局部变量总是一个好主意,尤其是在 C 语言中,你应该假设没有为你做任何事情(因为通常是这种情况)。受监管的语言不允许您这样做是有原因的。

    我相信读取未分配的变量会导致未指定的行为(实际上,C 不知道那里没有任何东西,只会抓取任何东西),这意味着它是完全不可预测的。

    这也可能导致各种问题,因为你随后用它索引一个数组,C 不会阻止你索引一个数组越界,所以如果随机的 i 值 C 碰巧抓取大于数组,那么您将在 buffer[i] 返回的内容中遇到未定义的行为。这可能特别令人讨厌,因为它可能导致任何类型的内存读取/分段错误都会使您的程序崩溃,具体取决于它决定读取的内容。

    因此未分配 i = 随机行为,然后您可以通过使用该 i 值来索引您的数组获得更多随机行为。

    我相信这是一个坏主意的所有原因。在 C 语言中,特别重要的是要注意这样的事情,因为它通常允许您编译和运行代码。

    初始化 i 和使用@AKX 答案中的解决方案都是很好的解决方案,尽管我认为这会更多地回答您关于为什么它们返回不同的问题。真正的答案是第一种方法完全随机返回

    【讨论】:

    • 在大多数系统上,读取未初始化的局部变量(没有获取其地址)会导致 unspecified 行为。然而,使用这样的变量作为数组索引几乎可以肯定是 undefined 行为,因为越界访问。
    • 谢谢 Lundin 我将编辑我的答案以在每个实例中使用正确的术语。虽然如果有技术上的差异,我希望你能解释一下?
    • 关于未初始化变量的具体情况见this。关于未定义行为与未指定行为,请参阅What is undefined behavior and how does it work?。 TL;DR 是:未定义的行为意味着任何疯狂和疯狂的事情都可能发生,例如代码被优化出来。而未指定的行为是未记录的和编译器/系统特定的行为,如果依赖它可能会产生意想不到的结果,但不会像未定义的行为那样导致编译器完全崩溃。
    • 干杯,哦,我很少依赖我的 C 代码不完全和完全香蕉反正哈哈
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多