3

I'm relearning C. The following code segment compiles and runs as expected under Tiny C Compiler (TCC). It compiles without complaint under GCC, but it doesn't produce any output under Windows 10.

When compiled and ran under Ubuntu, Tiny C Compiler works just as well, but GCC reports a segmentation fault (core dump). Why is it failing under 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);
}

I'm a retired programmer trying to reconnect with C after decades of development in C# and T-SQL. My experience in C is way out of date since I wrote code under the K&R way back in the 1990s.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Chief Cook
  • 61
  • 5
  • 1
    Modifying string literals causes undefined behavior. They typically reside in write-protected memory. – HolyBlackCat Mar 14 '23 at 22:45
  • 1
    The problem is that your combination of platform/compiler with GCC implements `"Ab12Cd3EfG*h&"` (a [string literal](https://learn.microsoft.com/en-us/cpp/c-language/c-string-literals)) in read-only memory. Hence the crash. Look here for more details: https://softwareengineering.stackexchange.com/questions/294748/why-are-c-string-literals-read-only – paulsm4 Mar 14 '23 at 22:51
  • 2
    *... since I wrote code under K&R ...* Modern C is a lot stricter about a lot of things, and fortunately a *lot* more portable than it was 25-30 years ago. Especially regarding function prototypes - do not try mixing prototypes and K&R-style functions. And beware that optimizing compilers can make hacks that used to work fail in ugly, subtle, and hard-to-diagnose ways. – Andrew Henle Mar 14 '23 at 22:52
  • A good workaround is to a) declare a character array, b) initialize the array with your string literal, and c) pass the array to your function, as MikeCAT suggested below. Another is to use [malloc()](https://man7.org/linux/man-pages/man3/malloc.3.html) or [strdup()](https://man7.org/linux/man-pages/man3/strdup.3.html) and pass the pointer to your function. – paulsm4 Mar 14 '23 at 22:56
  • @Chief Cook, Tip: avoid magic numbers like 96, 123, - 32. Use `if(ch >= 'a' && ch <= 'z') str[i] = (char)(ch - 'a' + 'A');`. Even better research `islower(), tolower()`. – chux - Reinstate Monica Mar 15 '23 at 00:59

1 Answers1

2

Modifying string literals invokes undefined behavior. On some systems, string literals are placed to read-only regions and trying to modify them leads to termination of the program.

Instead of passing string literals to functions that modifies input strings, you should declare arrays with strings and pass them.

int main()
{
    char str1[] = "Ab12Cd3EfG*h&";
    char str2[] = "HiJ12kLm&No";
    void (* func)(char *) = &UpCase;
    func(str1);
    
    func = &LowCase;
    func(str2);
}
MikeCAT
  • 73,922
  • 11
  • 45
  • 70