You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何C代码在TCC可正常运行,GCC下却出现运行异常?

问题:GCC编译后运行异常(Windows无输出、Ubuntu段错误),TCC正常

我是一名退休程序员,曾基于K&R标准编写C代码,之后从事了数十年C#和Transact-SQL(T-SQL)开发,现正重新学习C语言。以下代码段在Tiny C Compiler(TCC)中可正常编译运行,但使用GCC编译无报错后,在Windows 10环境运行无输出;在Ubuntu环境中,TCC仍正常工作,GCC编译运行则出现segmentation fault(core dump)。请问为何在GCC下会出现该问题?

#include <stdlib.h>
#include <stdio.h>

void UpCase(char * str);
void LowCase(char * str);

int main()
{
    void (* func)(char *) = &UpCase;
    func("Ab12Cd3EfG*h&");

    func = &LowCase;
    func("HiJ12kLm&No");
}

void UpCase(char * str)
{
    int i = 0;
    int ch = 0;

    for(i=0; str[i] != '\0'; i++)
    {
        ch = (int)str[i] + 0;
        if(ch > 96 && ch < 123)
            str[i] = (char)(ch - 32);
    }

    printf("UpCase: %s\n", str);
}

void LowCase(char * str)
{
    int i = 0;
    int ch = 0;

    for(i=0; str[i] != '\0'; i++)
    {
        ch = (int)str[i] + 0;
        if(ch > 64 && ch < 91)
            str[i] = (char)(ch + 32);
    }

    printf("LowCase: %s\n", str);
}

核心原因:修改字符串字面量触发未定义行为

C语言中,字符串字面量(如"Ab12Cd3EfG*h&")属于只读数据,现代编译器通常会将其放在程序的只读内存段(Linux的.rodata段、Windows的只读数据区)。你的代码中UpCase和LowCase函数直接修改了字符串字面量的内容,这违反了C标准的规定,属于未定义行为——不同编译器、操作系统的处理方式会完全不同:

  • TCC:默认将字符串字面量分配在可写内存区域,所以修改操作不会触发错误,程序能正常运行;
  • GCC:默认严格遵循标准,将字符串字面量放在只读内存中:
    • Windows系统会静默拦截写入操作,导致程序逻辑异常(无输出);
    • Ubuntu系统会直接抛出段错误(Segmentation Fault),因为写入只读内存是非法内存操作。

另外,K&R C时代对字符串字面量的约束相对宽松,但自C99标准开始,明确规定字符串字面量是const char[]类型,修改它们的行为是未定义的。


解决方案

要修复问题,必须避免直接修改字符串字面量,先将字符串内容复制到可写内存区域,比如栈数组或堆内存:

方案1:使用栈上的字符数组(适合短字符串)

#include <stdlib.h>
#include <stdio.h>

void UpCase(char * str);
void LowCase(char * str);

int main()
{
    void (* func)(char *) = &UpCase;
    char str1[] = "Ab12Cd3EfG*h&"; // 把字面量复制到栈上的可写数组
    func(str1);

    func = &LowCase;
    char str2[] = "HiJ12kLm&No";
    func(str2);
}

// 以下UpCase、LowCase函数代码保持不变
void UpCase(char * str)
{
    int i = 0;
    int ch = 0;

    for(i=0; str[i] != '\0'; i++)
    {
        ch = (int)str[i] + 0;
        if(ch > 96 && ch < 123)
            str[i] = (char)(ch - 32);
    }

    printf("UpCase: %s\n", str);
}

void LowCase(char * str)
{
    int i = 0;
    int ch = 0;

    for(i=0; str[i] != '\0'; i++)
    {
        ch = (int)str[i] + 0;
        if(ch > 64 && ch < 91)
            str[i] = (char)(ch + 32);
    }

    printf("LowCase: %s\n", str);
}

方案2:使用堆内存(适合长字符串)

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

void UpCase(char * str);
void LowCase(char * str);

int main()
{
    void (* func)(char *) = &UpCase;
    // 用strdup把字面量复制到堆内存,返回可写指针
    char *str1 = strdup("Ab12Cd3EfG*h&");
    if (str1 == NULL) {
        perror("strdup failed");
        return 1;
    }
    func(str1);
    free(str1); // 用完后必须释放堆内存

    func = &LowCase;
    char *str2 = strdup("HiJ12kLm&No");
    if (str2 == NULL) {
        perror("strdup failed");
        return 1;
    }
    func(str2);
    free(str2);
}

// 以下UpCase、LowCase函数代码保持不变
void UpCase(char * str)
{
    int i = 0;
    int ch = 0;

    for(i=0; str[i] != '\0'; i++)
    {
        ch = (int)str[i] + 0;
        if(ch > 96 && ch < 123)
            str[i] = (char)(ch - 32);
    }

    printf("UpCase: %s\n", str);
}

void LowCase(char * str)
{
    int i = 0;
    int ch = 0;

    for(i=0; str[i] != '\0'; i++)
    {
        ch = (int)str[i] + 0;
        if(ch > 64 && ch < 91)
            str[i] = (char)(ch + 32);
    }

    printf("LowCase: %s\n", str);
}

额外建议

为了从源头避免这类问题,建议根据函数是否修改参数,明确标注指针的const属性:

  • 如果函数不需要修改字符串,参数声明为const char* str;
  • 如果函数需要修改字符串,保持char* str,但调用时必须确保传入的是可写内存(比如栈数组、堆内存)。

内容的提问来源于stack exchange,提问作者Chief Cook

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 07:35:07