Why can't you change characters using pointers? - c

recently I got a copy of the C Programming Language Book.
On page 95 there is an implementation of the strcpy function.
void strcopy(char *s, char *t) {
while(*s++ = *t++)
;
}
int main(int argc, char *argv[]) {
char *hello = "hello";
char *world = "world";
strcopy(hello, world);
return 0;
}
i tried to implement it myself, but after (successfully) compiling with
gcc -o c_lang c_programming_lang_exercises.c
and trying running it with
./c_lang
I get the following error message:
zsh: bus error
I tried it with different implementations, but somehow this does not work for me.
Maybe someone has an idea why?
Thank you for your help :)
Edit:
For my own understanding, please correct me if im wrong.
In the function call "strcopy(hello, world);" i pass the value of the pointers itself here, so i call the function with something like this: strcopy(0x123a4, 0x234859b). Now C doesnt sees this as variable, and therefore its value get copied and passed to the function.
In the strcopy function itself i cant dereference it, because i just get the value of the pointers.
What i should do is to pass the address, the pointers refere to, so that i can dereference the values in the strcopy function and access the right memory address.
The other mistake was probably in the main function i declared char *hello = "hello"that is as far as I understand it, called a string constant and is therefore not modifiable.
So, if my assumptions are correct, the following code should work:
void strcopy(char *s, char *t) {
//code goes here...
}
int main(int stringc, char *argv[]) {
char hello[] = "hello";
char world[] = "world";
strcopy(&hello, &world);
return 0;
}
And it works, but i get the following compiler warning:
incompatible pointer types passing 'char (*)[6]' to parameter of type 'char **' [-Wincompatible-pointer-types]
strcopy(&hello, &world);

C makes a really bad concession when it comes to types, and this has to do with historical reasons.
In main(), you declare your strings as:
char *hello = "hello";
What you should do is turn the warning levels waaaay up and recompile. The problem is that "hello" is a string literal — it cannot be modified. It should be declared as:
const char *hello = "hello";
(Turning warnings up will tell you this.)
Consequently, you are trying to copy characters into read-only memory, which, on modern processors and with modern compilers, is not permitted — it produces an access violation. Zsh complains for you.
Instead, make sure you have write access to a local array that is copied from read-only memory:
char hello[] = "hello";
Yep, that makes a local, mutable array that is six characters long (five for the word “hello” plus one for the null-terminator) and automatically initialized from the read-only memory.
This is one of those subtle sticking points to declaring things in C that confuses beginners regularly:
const char * s = "Hello world!"; — pointer to ROM
char s[] = "Hello world!"; — local, mutable array initialized from ROM
That’s it!

Related

C - printing a char* through strcpy is acting contradictory?

First post here, I've used this site for years but this dilemma is really annoying. So when I run this C code through the VS2015 Developer Command prompt with the C compiler:
#include <stdio.h>
#include <string.h>
int main(){
char* ptr = NULL;
const char* pt2 = "hello";
strcpy(&ptr, pt2);
printf("%s",&ptr);
return 0;
}
I get these warnings:
midterm_c.c(35): warning C4047: 'function': 'char *' differs in levels of indirection from 'char **'
midterm_c.c(35): warning C4024: 'strcpy': different types for formal and actual parameter 1
midterm_c.c(36): warning C4477: 'printf' : format string '%s' requires an argument of type 'char *', but variadic argument 1 has type 'char **'
Yet when I run it, it prints "hello", which shouldn't happen because it doesn't make sense to use the reference operators on the ptr variable. But when I run this code without them:
char* ptr = NULL;
const char* ptr2 = "hello";
strcpy(ptr, ptr2);
printf("%s",ptr);
return 0;
I get no warnings, successfully compiles, yet when I run it the program crashes even though this should work properly. I'm getting really frustrated because this doesn't make any sense to me and one of the reasons why I'm really hating C right now. If anyone has any idea how this is happening I'd really appreciate it. Thank you.
In your working code, you basically just treated part of your stack as a string buffer; it's flagrantly illegal by the standard, but it happens to work because you don't read from anything you stomp on after the stomping.
You're correct that passing a char** to strcpy makes no sense, but what ends up happening is that it writes to the char* variable itself (and some neighboring memory if it's a 32 bit program), treating it as an array of char (after all, an eight byte pointer has enough room for a seven character string plus NUL terminator). Effectively, your code is incorrect code that still happens to behave like correct code, since this:
char* ptr = NULL;
const char* pt2 = "hello";
strcpy(&ptr, pt2);
printf("%s",&ptr);
compiles to code that behaves almost exactly like this (aside from the warnings):
char ptr[sizeof(char*)]; // Your char* behaves like an array
const char* pt2 = "hello";
strcpy(ptr, pt2);
printf("%s", ptr);
When you don't take the address of ptr, you pass a NULL pointer to strcpy, which crashes immediately (because you can't write to a NULL pointer's target on almost all systems).
Of course, the correct solution would be to make ptr point to allocated memory, either global, automatic, or dynamically allocated memory. For example, using a stack buffer, this would be legal:
char buf[8];
char *ptr = buf; // = &buf[0]; would be equivalent
const char* pt2 = "hello";
strcpy(ptr, pt2);
printf("%s", ptr);
although it would be rather pointless, since ptr could just be replaced with buf everywhere and ptr removed:
char buf[8];
const char* pt2 = "hello";
strcpy(buf, pt2);
printf("%s", buf);
First of all, you don't know how to use strcpy. The first parameter needs to point to space available to copy into, it doesn't allocate space for you.
Your first example probably only "works" because it is copying into stack-space. This is undefined behavior and will cause all kinds of problems.
Your second example (the more correct one) will generally cause a segfault because you are trying to copy into (or read from) a null address.
You're writing to random memory, which is undefined behavior. This means that according to the C standard, the compiler is allowed to generate code that does literally anything, including but not limited to:
Printing "hello".
Printing random gibberish.
Crashing.
Corrupting some other variable in your app.
Ripping a hole in the fabric of space-time, causing an invasion by evil space lemurs from the fifth dimension that ruin the market for Kopi Luwak coffee by causing a glut in the supply.
Tl;dr: Don't do that.
You need to allocate memory for ptr variable (malloc). Then, a usefull function is strdup().
It take a string, allocate the good memory's space for a new string and copy the content of the source in it before returning the new string.
You can do:
ptr = strdup(pt2);
The documentation: http://manpagesfr.free.fr/man/man3/strdup.3.html

Where does const string store? in stack or .data? [duplicate]

This question already has answers here:
How are string literals compiled in C?
(2 answers)
"Life-time" of a string literal in C
(9 answers)
Closed 8 years ago.
I have written a simple c code which shows below. In this code snippet I want to verify where the const string abcd stores. I first guess that it should be stored in .data section for read-only. After a test in Debian, however, things is different from what I initial guessed. By checking the assembly code which generated by gcc, I find it is placed in the stack frame of function p. But when I try it later in OSX, the string is stored in .data section again. Now I am confused by this. Is there any standard for the storing of const string?
#include<stdio.h>
char *p()
{
char p[] = "abcd";
return p;
}
int main()
{
char *pp = p();
printf("%s\n",pp);
return 0;
}
UPDATE: rici's answer awaken me. In OSX, the initial literal is stored in .data and then moved into function's stack frame later. Thus, it becomes a local variable for this function. However, gcc in Debian handle this situation is different from OSX. In Debian, gcc directly stored literal in stack instead of moving it from .data. I'm sorry for my carelessness.
in your case, it's located in stack. and returning the pointer to main will cause undefined behavior. but, if you have static char p[] = "abcd"; or char *p = "abcd"; they(the data) are located in .data.
There is a huge difference between:
const char s[] = "abcd";
and
const char* t = "abcd";
The first of these declares s to be an array object initialized from the string "abcd". s will have an address distinct from that of any other object in the program. The character string itself might be a compile-time artifact; the initialization is a copy so the character string does not need to be present at runtime if the compiler can find some other way of performing the initialization (such as a store immediate operation).
The second declaration declares t to be a pointer to a string constant. The string constant now must be present at runtime, because expressions like t+1, which are pointers inside the string, are valid. The language standard does not guarantee that every occurrence of string literals in the program is unique, nor does it guarantee that all occurrence are merged (although good compilers will try to do the second.) It does, however, guarantee that they have static lifetime.
Consequently, this is undefined behaviour, because the lifetime of the array s ends when the function returns:
const char *gimme_a_string() {
const char s[] = "abcd";
return s;
}
However, this is fine:
const char *gimme_a_string() {
const char *s = "abcd";
return s;
}
Also:
const char s[] = "abcd";
const char t[] = "abcd";
printf("%d\n", s == t);
is guaranteed to print 0, while
const char* s = "abcd";
const char* t = "abcd";
printf("%d\n", s == t);
might print either 0 or 1, depending on the implementation. (As written, it will almost certainly print 1. However, if the two declarations are in separate compilation units and lto is not enable, it is likely to print 0.)
Since the array form is initialized with a copy the non-const version is fine:
char s[] = "abcd";
s[3] = 'C';
But the char pointer version must be a const to avoid undefined behaviour.
// Will produce a warning on most compilers with compile option -Wall or equivalent
char* s = "abcd";
// *** UNDEFINED BEHAVIOUR *** Can cause random program breakage
s[3] = 'C';
Technically, the non-const declaration of s is legal (which is why the compiler only warns) because it is the attempt to modify the constant which is UB. But you should always heed compiler warnings; it is better to think of the declaration / initialization as wrong, because it is.

Why do I get "Incompatible types when assigning" error?

I'm having C code which seems to have similar pointer assignments but shows different behaviours while compiling .
My structure declaration and definition is below,
typedef struct {
int a;
char b[20];
}
TestStruct;
TestStruct t1;
Why does the below code gives "error: incompatible types when assigning to type ‘char[20]’ from type ‘char *’"
t1.b = "Hello World";
but the code below compiles successfully,
char *charPtr = t1.b;
charPtr = "Hello World";
Note: I'm using GCC compiler v4.6.3
Ths strings cannot be assigned to arrays in C, unless as part of an initialization.
The right way to do something like that is by means of the function strcpy() of ths <string.h> standard header.
strcpy(t1.b, "Hello world");
It is not true that the array t1.b is a pointer to char.
Actually, it has type array of 20 elements of type char.
In an expression, the array normally decays to a pointer to char.
However, the array has a fixed address in memory. It is not an lvalue, its address cannot be changed by an assignment.
The opposite assignment is valid:
charPtr = "Hello World";
The address of the string "Hello world" is assigned to charPtr.
However, your sentences have not the intended effect:
char *charPtr = t1.b;
charPtr = "Hello World";
The effect is that charPtr becomes equal to the address of t1.b.
Then, this value is discarded in the second sentence and replaced by the address of the array "Hello world".
More details: Be carefull in handling strings. A string literal like "Hello world" is an array stored (in general) in only-read memory. If you try to modify it, you can obtain unexpected results.
In particular, this happens with the assignment charPtr = "Hello world".
The string can be read, but not changed.
To change or manipulate a string, it has to be copied (with strcpy()) to an array or to an allocated portion of memory.
You can't directly assign a string literal to a char array. Use strcpy() or strlcpy().
In the second example, the array decays into a pointer, and then you change that pointer. Note that in this example, t1.b remains unchanged.
When you define:
typedef struct {
int a;
char b[20];
}TestStruct;
b is a constant pointer pointing to char. Hence t1.b = "Hello World"; is a compiling error since it change the value of t1.b
Solutions:
1) Use strcpy:
strcpy(t1.b, "Hello World");
2) Tricky type-casting way
char** pb = (char**)&t1.b;
*pb = "Hello World";
Although these 2 methods bring the same purpose (setting t1.b to "Hello World" string), the underlying idea is very different.
You cannot assign const char* to char[] as it violates constancy.

string array initialisation

This is a continuation of another question I have.
Consider the following code:
char *hi = "hello";
char *array1[3] =
{
hi,
"world",
"there."
};
It doesn't compile to my surprise (apparently I don't know C syntax as well as I thought) and generates the following error:
error: initializer element is not constant
If I change the char* into char[] it compiles fine:
char hi[] = "hello";
char *array1[3] =
{
hi,
"world",
"there."
};
Can somebody explain to me why?
In the first example (char *hi = "hello";), you are creating a non-const pointer which is initialized to point to the static const string "hello". This pointer could, in theory, point at anything you like.
In the second example (char hi[] = "hello";) you are specifically defining an array, not a pointer, so the address it references is non-modifiable. Note that an array can be thought of as a non-modifiable pointer to a specific block of memory.
Your first example actually compiles without issue in C++ (my compiler, at least).

How to change a character in a string using pointers?

im having troubles with this code
int main() {
char *My_St = "abcdef";
*(My_St+1)='+';
printf("%s\n",My_St);
return 0;
}
i built this code and has no errors, but when i try to run it, it throws a segmentation fault, could someone tell what's wrong
You can't because you are trying to modify const data.
change it to:
char My_St[] = "abcdef";
Then you will be able to change it.
Think about what you were doing, you were declaring a pointer that pointed to "abcdef". It IS a pointer, not an array of chars. "abcdef" lives in the farm, I mean, in the .text area of your program and that is immutable.
When you do it the way I've shown, you are telling the compiler: i'm declaring this array, that will have as many chars as are needed to accommodate "abcdef" and also, as you are there, copy "abcdef" to it.
You provided a hint to the compiler by declaring My_St with type char *. Assigning a string literal to this pointer essentially makes it a const char * because a string literal cannot be modified, meaning the memory location is read-only. Writing to that read-only memory location is what is producing your segfault. Change it from char *My_St to char My_St[] to get it working.
char *My_St refers to constant memory, most likely. You will need to dynamically allocate your string and then fill it (using strcpy).
char *str = malloc(7);
strcpy(str, "abcdef");
Or
char *str = strdup("abcdef");
And then it is safe to modify str.
The basics are correct, however your character string is (behind the scenes) constant and can't be modified. You'd have to define a array of chars (e.g. char[20]), copy the string into it and then modify the character.
To be 100% correct you'd have to write const char *My_St = "abcdef"; which makes it clearer that you can't do what you're trying to do.

Resources