2

I think I know the answer to my own question but I would like to have confirmation that I understand this perfectly.

I wrote a function that returns a string. I pass a char* as a parameter, and the function modifies the pointer.

It works fine and here is the code:

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

void get_file_name(char* file_name_out)
{
    char file_name[12+1];
    char dir_name[50+12+1];

    strcpy(file_name, "name.xml");
    strcpy(dir_name, "/home/user/foo/bar/");
    strcat(dir_name, file_name);

    strcpy(file_name_out, dir_name); // Clarity - equivalent to a return
}

int main()
{
    char file_name[100];

    get_file_name(file_name);
    printf(file_name);

    return 0;
}

But if I replace char file_name[100]; by char *filename; or char *filename = "";, I get a segmentation fault in strcpy().

I am not sure why ?

My function takes a char* as a parameter and so does strcpy().

As far as I understand, char *filename = ""; creates a read-only string. strcpy() is then trying to write into a read-only variable, which is not allowed so the error makes sense.

But what happens when I write char *filename; ? My guess is that enough space to fit a pointer to a char is allocated on the stack, so I could write only one single character where my file_name_out points. A call to strcpy() would try to write at least 2, hence the error.

It would explain why the following code compiles and yields the expected output:

void foo(char* a, char* b)
{
    *a = *b;
}

int main()
{
    char a = 'A', b = 'B';
    printf("a = %c, b = %c\n", a, b);
    foo(&a, &b);
    printf("a = %c, b = %c\n", a, b);
    return 0;
}

On the other hand, if I use char file_name[100];, I allocate enough room on the stack for 100 characters, so strcpy() can happily write into file_name_out.

Am I right ?

Community
  • 1
  • 1
KevinG
  • 882
  • 3
  • 16
  • 33
  • 4
    "But if I replace char file_name[100]; by char *filename; or char *filename = "";, I get a segmentation fault in strcpy()." - char *filename has no memory allocated; it's just a pointer – Mitch Wheat May 18 '15 at 10:26
  • Take a look memory allocation, at `malloc` function and how to use it. – LPs May 18 '15 at 10:28
  • 1
    "My guess is that enough space to fit a pointer to a char is allocated on the stack, so I could write only one single character" - first part right, second part *wrong*. A pointer's *value* is the address it holds.. Declaring one without initialization leaves the pointer *indeterminate*. It holds no known-address to a valid memory region. Dereferencing it invokes undefined behavior (reading or writing makes no difference). – WhozCraig May 18 '15 at 10:29
  • Ah, yes, I edited the question. So this would be the right way of saying it: I can write one single character _where my variable points_ if I have defined where it points. – KevinG May 18 '15 at 10:48

4 Answers4

4

As far as I understand, char *filename = ""; creates a read-only string. strcpy() is then trying to write into a read-only variable, which is not allowed so the error makes sense.

Yes, that's right. It is inherently different from declaring a character array. Initializing a character pointer to a string literal makes it read-only; attempting to change the contents of the string leads to UB.

But what happens when I write char *filename; ? My guess is that enough space to fit a pointer to a char is allocated on the stack, so I could write only one single character into my file_name_out variable.

You allocate enough space to store a pointer to a character, and that's it. You can't write to *filename, not even a single character, because you didn't allocate space to store the contents pointed to by *filename. If you want to change the contents pointed to by filename, first you must initialize it to point to somewhere valid.

Filipe Gonçalves
  • 20,783
  • 6
  • 53
  • 70
2

I think the issue here is that

char string[100];

allocates memory to string - which you can access using string as pointer but

char * string;

does not allocate any memory to string so you get a seg fault.

to get memory you could use

string = calloc(100,sizeo(char));

for example, but you would need to remember at the end to free the memory with

free(string);

or you could get a memory leak.

another memory allocation route is with malloc

So in summary

char string[100];

is equivalent to

char * string;
string = calloc(100,sizeo(char));
...
free(string);

although strictly speaking calloc initializes all elements to zero, whereas in the string[100] decalaration the array elements are undefined unless you use string[100]={} if you use malloc instead to grad the memory the contents are undefined.

Another point made by @PaulRooney is that char string[100] gives memory allocation on the stack whereas calloc uses the heap. For more information about the heap and stack see this question and answers...

Community
  • 1
  • 1
tom
  • 1,303
  • 10
  • 17
  • Might also be significant to note the difference between the `char string[100];` and `calloc` allocation is that one is on the stack, whilst the other is on the heap. – Paul Rooney May 18 '15 at 10:58
  • thanks @PaulRooney - will put edit in... – tom May 18 '15 at 14:22
0

char file_name[100]; creates a contiguous array of 100 chars. In this case file_name is a pointer of type (char*) which points to the first element in the array.

char* file_name; creates a pointer. However, it is not initialized to a particular memory address. Further, this expression does not allocate memory.

911
  • 908
  • 8
  • 16
  • 1
    *In this case file_name is a pointer of type (char\*)*. Not really. `file_name` is of type `char [100]`. Arrays and pointers are not always interchangeable. You can say that `file_name` will *decay* into a pointer to the first element in most situations. But it is *not* a pointer to char. If `file_name` was a `char *`, then `&file_name` would be a `char **`, whereas in fact it is a `char (*)[100]`. – Filipe Gonçalves May 18 '15 at 10:32
0
char *filename; 

Allocate nothing. Its just a pointer pointing to an unspecified location (the value is whatever was in that memory previously). Using this pointer will never work as it probably points outside the memory range your program is allowed to use.

char *filename = "";

Points to a piece of the programs data segment. As you already said it's read only and so attempting to change it leads to the segfault.

In your final example you are dealing with single char values, not strings of char values and your function foo treats them as such. So there is no issue with the length of buffers the char* values point to.

Paul Rooney
  • 20,879
  • 9
  • 40
  • 61