0

I have the below function which when called from main, returns a formatted filename (000.jpg) as a string when given an int. (I know we can use sprintf for this)

Initialising char fn[8] = "000.jpg" in main works.

When passed into function getfilename(),
assigning indiv chars e.g. fn[4] = 'p'; works,
but it won't work if I assign fn = "000.gif";
I get a Bus error: 10

What am I doing wrong?

(The rest of the code works fine, and output is correct so long as I don't do the line: fn = "000.gif"; But I want to learn how to be able to manipulate the string when passed across functions)

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

char *getfilename(int counter, char *fn);

int main(void) {
    int counter=14;
    char fn[8]= "000.jpg"; // this is fine

    getfilename(counter, fn);
    printf("%s\n", fn);
}

char *getfilename(int counter, char *fn) {
    fn[7] = '\0';
    fn[4] = 'p';        // this is fine
    fn[5] = 'n';
    fn[6] = 'g';
    //fn = "000.gif";     // this will return Bus error: 10

    // ITOA IMPLEMENTATION FOR 3-DIGIT FILENAME
    int numOfDigits = log10(counter) + 1; 
    for (int i=numOfDigits-1; i>=0; i--, counter/=10) { 
        if (numOfDigits==1) {
            fn[i+2] = (counter % 10) + '0'; 
            fn[0] = fn[1] = '0';
        }
        else if (numOfDigits==2) {
            fn[i+1] = (counter % 10) + '0'; 
            fn[0] = '0';
        }
        else {
            fn[i] = (counter % 10) + '0'; 
        }
    } 
    //////////
    return fn;
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335

2 Answers2

3

Change fn = "000.gif" to strcpy(fn, "000.gif")

And a word of advice. It could be a good thing to change char fn[8]="000.jpg" to char fn[]="000.jpg". What if you use a longer string in the future. Will you remember to increase the size of the array?

On the other hand, if you know for certain that the size will never change, you could make the code a bit more robust with something like this:

struct filename {
    char fn[8];
};

char *getfilename(int counter, struct filename *arg) {
    char *fn = arg->fn;
    // Keep the rest the same
}

int main(void) {
    int counter = 14;
    struct filename f = { "000.jpg" };
    getfilename(counter, &f); // Note the &
    printf("%s\n", f.fn);     

That way, you would get warnings if you're trying to pass a random string to the function. Note that . and -> is basically doing the same thing, but the latter is used with pointers to struct. Also note that - unlike arrays - you don't have to pass structs via pointers. You can choose to pass the directly, just like variables. Same goes for returning and assigning them. I chose pointers in this case for no other reason than that I have to choose one.

Another option that's more convenient, although the syntax may seem a bit scary is this:

char *getfilename(int counter, char (*arg)[8]) {
    char *fn = *arg;

    // Keep the rest of the function

I actually wrote a question about that method right now: How do I force a warning from using an array of wrong size when passed to function?

Both methods have their pros and cons.

klutt
  • 30,332
  • 17
  • 55
  • 95
1

In this statement

fn = "000.gif"; 

you assigned the local pointer fn (function parameters are function local variables) with the address of the first character of the string literal "000.gif". And then you are trying to change the string literal as for example

fn[0] = fn[1] = '0';

Any attempt to change a string literal results in undefined behavior.

From the C Standard (6.4.5 String literals)

7 It is unspecified whether these arrays are distinct provided their elements have the appropriate values. If the program attempts to modify such an array, the behavior is undefined.

What you need is to change the content of the original array fn declared in main instead of changing the pointer fn declared as a parameter of the function getfilename. You can do it the following way

strcpy( fn, "000.gif" );
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • let me see if I follow you: so effectively fn within getfilename() only knows that it's a pointer to that address of first letter '0', and not realise it's meant to encapsulate the [8] array? – Hanafi Haffidz Aug 26 '21 at 09:14
  • Ok yeah I think I understand what you mean now. Therefore, is it correct to assume that strcpy() is the just way to go for string manipulations in C, due to it's rather low-level implementation? – Hanafi Haffidz Aug 26 '21 at 09:24
  • @HanafiHaffidz strcpy copies characters of the string literal "000.gif" in the array pointed to by the pointer fn. – Vlad from Moscow Aug 26 '21 at 09:28