1

I am missing something regarding pointers and strings in c. I am trying to simply get and set an element of a character array in c that was created via a pointer. I can easily get each character via pointer arithmetic, but I can not set any of the elements via pointer arithmetic. Please see the example. What am I missing here? Isn't s1 in both examples the same? I am using mingw(gcc) on win10.

Example A) This works, s1 is printed as "abxd"

char *s1;
s1 = (char[]){'a','b','c','d','\0'};
*(s1+2)='x';
printf("%s",s1);

Example B) This does not work, it just crashes.

char *s1;
s1 = "abcd";
*(s1+2)='x';    //this is the problem, can get but can not set
printf("%s",s1);

Edit: based on the comments received regarding example B using static memory and being impossible to edit. So basically this means that I have to use malloc(heap memory, eg. C) or defining the array on stack memory (eg. D) if I want to edit the string, correct?.

Example C) - works

char *s1;
s1 = (char*)malloc((4+1)*sizeof(char));
s1 = strcpy(s1,"abcd");
*(s1+2)='x';  //or s1[2] = 'x'
printf("%s",s1);

Example D) - works

char s1[4];  // would have thought need to be min of s1[5]
s1 = strcpy(s1,"abcd");
*(s1+2)='x';   // or s1[2]='x';
printf("%s",s1);
Nat S
  • 29
  • 1
  • 3
  • 3
    The "array" you're creating is a literal string stored in static memory. Any attempt to modify it will crash. (s1 is just pointing into your app's static memory, which is fine) – Tibrogargan Jul 11 '18 at 02:44
  • @Tibrogargan can we also static field is a string literal pool – 0.sh Jul 11 '18 at 02:45
  • [This](/a/2036125/2487517) may help. – Tibrogargan Jul 11 '18 at 02:48
  • @0.sh parse error before 'is' – Tibrogargan Jul 11 '18 at 02:49
  • `s1 = (char[]){'a','b','c','d','\0'};` (equivalent to `s1 = (char[]){ "abcd" };` is a *compound-literal* (it is a cast of the *string-literal* `"abcd"` to an array of characters which is **modifiable**). `s1 = "abcd";` initializes `s1` to point to a *string-literal* created in **read-only** memory -- it cannot be modified -- and usually results in a `SEGFAULT` if you try. – David C. Rankin Jul 11 '18 at 04:04
  • Example D has a buffer overflow, the `strcpy` function also copies null terminator – M.M Jul 11 '18 at 04:07
  • 2
    Possible duplicate of [Why do I get a segmentation fault when writing to a string initialized with "char \*s" but not "char s\[\]"?](https://stackoverflow.com/questions/164194/why-do-i-get-a-segmentation-fault-when-writing-to-a-string-initialized-with-cha) [How to correctly assign a new string value?](https://stackoverflow.com/a/3131362/7076153) [What is the difference between char s\[\] and char *s?](https://stackoverflow.com/questions/1704407/what-is-the-difference-between-char-s-and-char-s) – Stargateur Jul 11 '18 at 04:15

2 Answers2

2

Let's look at your examples and make sure you know why what is happening is happening. But first, a quick review of pointers to make sure we are on the same page:

A Pointer & Pointer Arithmetic

A pointer is simply a normal variable that holds the address of something else as its value. In other words, a pointer points to the address where something else can be found. Where you normally think of a variable holding an immediate values, such as int a = 5;, a pointer would simply hold the address where 5 is stored in memory, e.g. int *b = &a;. It works the same way regardless what type of object the pointer points to. It is able to work that way because the type of the pointer controls the pointer arithmetic, e.g. with a char * pointer, pointer+1 point to the next byte, for an int * pointer (normal 4-byte integer), pointer+1 will point to an offset 4-bytes after pointer. (so a pointer, is just a pointer.... where arithmetic is automatically handled by the type)

What am I doing in Example A?

Your initialization is key to why Example A works and why Example B crashes. Example A uses a compound literal to initialize s1 so s1 points to the first character 'a' in "abcd" in modifiable memory. The compound-literal was introduced in C99, but gcc provides the compound-literal as an extension to C89 as well. In Example A you use:

s1 = (char[]){'a','b','c','d','\0'};

which is equivalent to

s1 = (char[]){ "abcd" };

The compound literal is (type){ ..initializer.. }, the key part being the (type) which works as a cast of the initializer value to that type. In your example A "abcd" is cast to char[] (a character array) which you can freely modify.

Why does Example B Crash?

On the other hand:

s1 = "abcd";

initializes s1 to a string-literal. A string-literal is created in read-only memory by most Operating Systems (generally in the .rodata section of the executable). See: Why are C string literals read-only? for a historical view. You cannot modify values in read-only memory and attempting to do so generally results in a SEGFAULT (as you have probably found).

You were right in your comment on Example D!

char s1[4];

Creates a character array with space for 4-characters (ASCII). When you call strcpy (s1, "abcd"); you are attempting to copy 1-more character than will fit:

'a','b','c','d','\0'
 1   2   3   4   5

This results in Undefined Behavior and can result in exploitable buffer-overflow. From man 3 strcpy,

If the destination string of a strcpy() is not large enough, then anything might happen. Overflowing fixed-length string buffers is a favorite cracker technique for taking complete control of the machine. Any time a program reads or copies data into a buffer, the program first needs to check that there's enough space. This may be unnecessary if you can show that overflow is impossible, but be careful: programs can get changed over time, in ways that may make the impossible possible.

So just as you allocated (4+1) chars/bytes in Example C, you need at least (4+1) chars/bytes of storage in s1 in Example D.

Remember, every C-library str... function requires a nul-terminated string. When you create a character-array, it is your responsibility to insure that it is nul-terminated to make it a string in C. If it's not nul-terminated, then it is simply an array of characters -- and any time you fail to pass a nul-terminated string to a function expecting one, the function will not know when to stop reading and will happily stray off reading out-of-bounds until it happens to stumble upon a zero-byte, or SEGFAULTS, whichever occurs first.

Look things over and digest them and let me know if you have further questions. (and add a '\n' to your printf format string (e.g. "%s\n") so that a newline is output -- at the very least on your last call to make your program POSIX compliant)

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • "A string-literal is created in read-only memory", it's implemented behavior. – Stargateur Jul 11 '18 at 05:08
  • That is a good point -- though I have not run across an implementation that does not create string literals in read-only memory, I'm sure they exists, but that also makes about 1000 other answers here on SO subject to that same addition and qualification. Updated. – David C. Rankin Jul 11 '18 at 05:11
0

The first example creates the char array in the read/write memory which you can modify. The second one the pointer to the read only char array. When you try to modify the read only memory location, you get the error.

You can also create the array in the read/write memory by char x[] ="1234";

0___________
  • 60,014
  • 4
  • 34
  • 74