-2

I'm still a newbie to C

char* str = (char*)malloc(512);
str = "hello";
*(str) = 'H'; // Segmentation fault

I get a Segmentation fault on the third line trying to set an character

I know that str is a pointer to the first char and I can printf it

printf("%c\n", *str); // Output: h

Well, Is it in Read-Only memory? I'm not sure why that happened but I can change the whole pointer just like I did

str = "hello";

I can do that

char** str = (char**)malloc(512*sizeof(char*));
*str = "hello";
*(str+1) = "Yo!";
//*(str) = 'H';
printf("%s\n", *(str)); // Prints: hello
printf("%s\n", *(str+1)); // Prints: Yo!

from the last code, I can do this and still the same

str[0] = "hello";
str[1] = "Yo!";

The reason is that it's an array of pointers after all, I'm just changing that pointer like in the first code, with some index

and the reason why *(str+1) = "Yo!"; works, I'm incrementing the address or the 0 index of char* which gives me the address of the next adjacent char*

from the last code, I cannot do things like

  **(str) = 'H';

*(str)[0] = 'H';

str[0][0] = 'H';

except if I have an array of char

char str[5][10]; // allocated 5 strings slots, 10 max characters for each
*(str)[0] = 'H';
*(str[0]+1) = 'e';
str[0][2] = 'l';
str[0][3] = 'l';
*((*str)+4 )= 'o';
printf("%s\n", str[0]); // Prints: Hello

I can allocate 512 char and just use 12, I can allocate 1024 char and still need more, I can go out of memory if I allocated too much

I've to use char* and I need to be able to change the value of an char like I did in that last array

like, let's say I'm building an compiler, text editor...
I need to be able to allocate memory at runtime plus check some characters like ';'

Or an split() function, which splits an char* into an array of pointers

  • Start copying char[s] into char[n][i] // n = 0, ++i, ++s
  • for each ';' iteration in char*, i = 0, ++n
  • return char**

This can be done easily if I'm working with 2D arrays, but they're not, they're pointers...

How can we change the value of a char in this pointer?

Why can we read any char in this pointer and not write to it?

I don't have a full understanding of pointers yet, Sorry if something is wrong with the code, thanks.

  • 2
    Fundamental misconception. Pointers **point** to data, you don't `malloc()` just to *create* the pointer. When you `malloc()` an address is returned to a memory that you can use until you `free()`. If you reassign the **pointer variable** then you lose reference to the original pointer and thus you can't free it. – Iharob Al Asimi Mar 04 '18 at 20:42
  • And ... yes, string literals to which your pointers are pointing are in read only memory so you can't write to them, arrays are not read only of course, and you can write to them within their bounds. – Iharob Al Asimi Mar 04 '18 at 20:44
  • 1
    Further, there is no need to cast the return of `malloc` anyway, it is unnecessary. See: [**Do I cast the result of malloc?**](http://stackoverflow.com/q/605845/995714) – David C. Rankin Mar 04 '18 at 20:44
  • `str = "hello";` overwrites the pointer obtained from `malloc`, and then you try to write to a string literal. – Weather Vane Mar 04 '18 at 20:44
  • You'll have to use strcpy() to copy strings. – machine_1 Mar 04 '18 at 20:46
  • 1
    @IharobAlAsimi: This question is not a duplicate of [String literals: Where do they go?](https://stackoverflow.com/questions/2589949/string-literals-where-do-they-go) because the OP here is not asking where a string literal goes. As their title indicates, they believe they are writing to an allocated array, so they misunderstand the assignment `str = "Hello"`. So, fundamentally, the question is “What are the semantics of `str = "Hello"`?” – Eric Postpischil Mar 04 '18 at 20:52
  • 1
    @EricPostpischil I was trying to mark as duplicate of all the content that is relevant, but honestly it was too hard to find one for the assignment problem. I realize what you say is true, but being a user for a long time and specially only for the [tag:c] tag, I really see the same question over and over, and I wish finding the right duplicates was easier, because IMHO this pollutes SO. – Iharob Al Asimi Mar 04 '18 at 20:54
  • @EricPostpischil Also, even though the question is not exactly the same, the answer does apply. The OP has doubts about it, so there they would find a complete explanation. – Iharob Al Asimi Mar 04 '18 at 21:00

3 Answers3

2

You get a segfault here

char* str = (char*)malloc(512);
str = "hello";
*(str) = 'H'; // Segmentation fault

because you are setting ptr to point to a string literal, the old value (returned by malloc) is gone. And modifying string literals is undefined behaviour and in most systems string literal are in read-only memory anyway, so you cannot modify them. The segfault is the consequence of that.

Note that str = "hello" is an assignment, you are copying the address of where the string literal is stored in memory in the pointer str. str is now pointing to the string literal. But you haven't done a copy of the contents, for that you need to use a function like strcpy or strncpy:

char* str = malloc(512);
if(str == NULL)
{
    // error handling
    // do not continue
}
strcpy(str, "hello"); // copying the content of the string literal
*(str) = 'H'; // now it works

And please note that you don't have to cast malloc. And don't forget to check if malloc returns NULL and to free the memory afterwards.

edit

   char str[5][10]; // allocated 5 strings slots, 10 max characters for each
   *(str)[0] = 'H';
   *(str[0]+1) = 'e';
   str[0][2] = 'l';
   str[0][3] = 'l';
   *((*str)+4 )= 'o';
   printf("%s\n", str[0]); // Prints: Hello

Here you have one problem though, you are not setting the '\0'-terminating byte, the printf yields an undefined behaviour. The correct code should be:

char str[5][10]; // allocated 5 strings slots, 10 max characters for each
*(str)[0] = 'H';
*(str[0]+1) = 'e';
str[0][2] = 'l';
str[0][3] = 'l';
*((*str)+4 )= 'o';
str[0][5] = '\0'; // terminating the string
printf("%s\n", str[0]); // Prints: Hello

edit 2

How can we change the value of an char in a pointer in C?

The problem with your confusion was that you didn't realize that you lost the pointer to the malloced memory block and tryied to modify a string literal instead.

How can you change the value of a char in a pointer?

Like this:

char text[] = "This is a long text";
char *ptr = text;
ptr[0] = 't';

this works, because text has been initialized with the contents of the string literal. ptr is pointing to the start of start of the sequence of characters stored in text, ptr[0] allows you to access the first character in the sequence.

Why can we read any char in an pointer and not write to it?

If you don't have wright permission (for example read-only memory) or the pointer is declared as const, then you cannot obviously write a character through the pointer. But if the pointer points to valid, writeable memory, then you can write a character through the pointer.

Pablo
  • 13,271
  • 4
  • 39
  • 59
  • +1 thanks for pointing using strcpy(), I totally forgot `=` which wastes `malloc` or the memory I allocated to a new address, I should've been more careful, Thanks for your answer! –  Mar 04 '18 at 20:57
  • also yup, I forgot about `'\0'` ty –  Mar 04 '18 at 20:59
  • @KiraSama don't worry about it, sometimes you do some silly mistake and don't realize it and then you wonder why it seems that the world went crazy. It has happened to me as well. – Pablo Mar 04 '18 at 21:07
2

In order to help with some of the misconceptions you have regarding pointers, I put down a few notes (from a pointers perspective). It's not meant to be facetious or patronizing, but as I went along, telling it from a pointers point of view, just made sense. So let me introduce you to our pointer p:

char *p;

Hello, I am an uninitialize pointer to type char. I am just a varaible that holds the address to something else as my value. So you can assign any address you want to me without having to allocate anything first. For example,

p = "Hello";

I now hold the address of the string-literal "Hello" as my value. "Hello" itself resides in the .rodata section of the executable (read-only). So you cannot change anything I point to now. But, if the memory I point to is mutable, then you are free to iterate over the characters I point to and make any changes you like, e.g.,

char buf[] = "Hello";
p = buf;

Now I hold the address of buf as my value. Since buf is stored on the stack and can be freely changed, e.g.

p[4] = '!';

Now the string pointed to by p tells you were not to go.

Since I can hold the address to anything else (and do so without violating the strict aliasing rule (C11 Standard - §6.5 Expressions (p6,7)), I can also hold the beginning address to a dynamically allocated block of memory. However, you must take care when I do. In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.

That means while I point to a dynamically allocated block, you cannot assign another address to me until you either store the address I point to somewhere else, or free the memory I point to. Otherwise, you will have lost the address for the memory I used to point to and it can never be freed again (a memory leak).

I have another important role as a pointer. I provide pointer arithmetic. My type determines the number of bytes involved when I am incremented, decremented or indexed. Since sizeof char is always 1, any pointer arithmetic on a character type only advances or decrements by 1-byte, but if I am a type int, then the increments or decrements move 4-bytes at a time, and so on...

When you use me to allocate storage, always use the size of what I point to in determining the size of the allocation. For instance to allocate 100 chars,

p = malloc (100 * sizeof *p);

It works the same way if I point to a compound type, e.g.

typedef struct {
    char word[100];
    int nvowels;
} list_t;
...

size_t listsize = 100;
list_t *p = malloc (listsize * sizeof *p);

If you use my size for allocation instead of trying to take the size of some type, I will prevent you from ever getting it wrong.

So that's pretty much a day-in-the-life-of a pointer. (multiple levels of indirection work the same)

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
1
char* str = (char*)malloc(512);
str = "hello";
*(str) = 'H'; // Segmentation fault

And then you say:

Well, Is it in Read-Only memory? I'm not sure why that happened but I can change the whole pointer just like I did

Yeah, you literally changed the whole pointer. You now made str point to read-only memory and totally wasted the memory allocated by malloc. Once again, str = "hello"; does not write data into the memory provided by malloc. It just makes str point to something else.

ForceBru
  • 43,482
  • 10
  • 63
  • 98
  • OP may not understand that `str = "hello"` assigns the address of the first character of `"hello"` to `str` rather than assigns “hello” to the contents of the memory currently pointed to by `str`, and this answer does not correct that misunderstanding. – Eric Postpischil Mar 04 '18 at 20:46
  • @EricPostpischil, that's why I made an edit to my answer (and submitted it 4 seconds after you commented). – ForceBru Mar 04 '18 at 20:48