对于GCC 13.2,以下代码的输出取决于优化级别:

#include <ctype.h>
#include <stdio.h>

char *SkipAName(char *s) {
  if (('A' <= *s && *s <= 'Z') || ('a' <= *s && *s <= 'z') || *s == '_' ||
      *s == '$') {
    if (*s == '$') {
      s++;
    }
    while (isalnum(*s)) {
      s++;
    }
    if (*s == '_') {
      s++;
    }
  }
  return s;
}

int TestName(char *name) {
  while (*name) {
    name++;
  }
  return 0;
}

int StrICmp(char *s1, char *s2) {
  while (*s1 && tolower(*s1) == tolower(*s2)) {
    s1++;
    s2++;
  }
  return tolower(*s1) - tolower(*s2);
}

int DoTable(char *s) {
  char *name, c;

  do {
    name = s;
    s = SkipAName(s);
    c = *s;
    *s = 0;
    TestName(name);
    *s = c;
    if (*s == '(') {
      break;
    }
    if (*s != ',') {
      printf("Error 1\n");
      return 1;
    }
    *s = 0;

    if (StrICmp(name, "sparse") == 0) {
    } else {
      printf("Error 2\n");
      return 1;
    }
    *s++ = ',';
    while (*s == ',') {
      s++;
    }
  } while (*s);

  printf("OK\n");
  return 0;
}

int main() {
  char buf[] = "sparse,C(1)";
  DoTable(buf);
  return 0;
}
$ gcc-13 -O0 test.c && ./a.out
OK
$ gcc-13 -O1 test.c && ./a.out
OK
$ gcc-13 -O2 test.c && ./a.out
Error 2
$ gcc-13 -O3 test.c && ./a.out
Error 2

代码来自this project,我试图制作一个最小的可重复的示例;这就是为什么这段代码看起来很尴尬.这是this issue.

我想知道这是GCC 13中的一个错误,还是我遇到了一些不确定的行为.有了Compiler Explorer,看起来第52行的*s = 0似乎被优化了.该代码在GCC 12.3版中运行良好.

推荐答案

我想知道这是GCC 13中的一个错误,还是我遇到了一些不确定的行为.

  • isalnum()tolower()的自变量应为char-that have been converted to type 103(在自动转换为int以匹配参数类型之前).例如:isalnum((unsigned char) *s).该问题可能会导致类似的程序显示UB,但您示例中的特定程序提供的输入不会产生该效果.

  • 该代码假定大写拉丁字母被编码为执行字符集中的连续数字范围,小写拉丁字母也是如此.C不能保证一定会这样,如果不是,那么程序就不会按预期工作.然而,在您的测试环境中,这个问题不太可能成为您的问题.

  • SkipAName()似乎也有奇怪的规则,它认为跳过的名字,但这并不是错误的.

  • TestName()没有呼叫者可观察到的效果.但这并不是错误的,如果观察到的错误行为依赖于该函数以及对它的调用出现在程序中,那么这是非常奇怪的.

  • StrICmp()可以从第二个字符串的末尾运行,从而生成UB,但这不会发生在您的特定输入中.

  • DoTable()修改它的输入有点令人讨厌,尤其是它没有始终如一地撤销它的更改,因为它们似乎只是为了服务于它的内部目的.除此之外,这使得使用字符串文字是不安全的.但是您的示例输入不是字符串文字,DoTable()修改它本身并不是错误的.

  • 我不喜欢S用DoTable()‘的参数s.我并不完全反对函数修改它们的参数,但我非常赞成使用描述性名称.我们完全不清楚"s"这个名字是什么意思,它既有足够的描述性,又与它的所有用途一致.但当然,这并不会使代码出错.

Overall,这段代码有一些问题,我总体上不喜欢它,但我没有看到其中有未定义的行为.您在更高的优化级别显示的GCC 13版本的输出是错误的.也就是说,它们反映了GCC 13中的一个错误.

C++相关问答推荐

当包含头文件时,gcc会发出隐式函数声明警告

如何避免重新分配指针数组时,我们从一开始就不知道确切的大小

为什么下面的C代码会进入无限循环?

ATTiny1606定时器TCA 0中断未触发

C语言中字符数组声明中的标准

为什么复合文字(C99)的返回会生成更多的汇编代码?

数据包未从DPDK端口传输到内核端口

使用额外的公共参数自定义printf

SDL 2.0-从数组渲染纹理

正确的TCP/IP数据包 struct

Setenv在c编程中的用法?

为什么我的Hello World EFI程序构建不正确?

Wcstok导致分段故障

运行时错误:在索引数组时加载类型为';char';`的空指针

在Ubuntu上使用库部署C程序的最佳实践

c如何传递对 struct 数组的引用,而不是设置 struct 的副本

";错误:寄存器的使用无效;当使用-masm=intel;在gcc中,但在AT&;T模式

既然我们在 if 中将 int 的值更改为 10,为什么在第二个 fork 后,子进程及其创建的子进程都会打印 33 ?

为什么程序在打印每个数字之前要等待所有输入?

c 函数指针,另一种语法