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
Related
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!
I am having trouble writing a string split function with delimiters. I based my function off of the main function featured here: http://www.cplusplus.com/reference/cstring/strtok/.
When I test it via main, I am only able to pass it char[], but not char*. When passing a char*, the program seg faults.
I.e. passing some char str[] through str_split works but not some char* str. Any help would be greatly appreciated.
char** str_split(char* str, const char* delim)
{
char* tmp;
char** t = (char**)malloc(sizeof(char*) * 1024);
char** tokens = t;
tmp = strtok(str, delim);
while(tmp != NULL)
{
*tokens = (char*)malloc(sizeof(char) * strlen(tmp));
*tokens = strdup(tmp);
tokens++;
tmp = strtok(NULL, delim);
}
return t;
}
These two lines gives you two different problems:
*tokens = (char*)malloc(sizeof(char) * strlen(tmp));
*tokens = strdup(tmp);
The first line will allocate strlen(tmp) bytes, but the problem is that strings have an extra character to terminate the string, so you really need to allocate strlen(tmp) + 1 bytes.
The second line overwrites the original pointer you got from malloc, causing a memory leak.
Also, in C you should not cast the return of malloc.
Oh, and another note: sizeof(char) is specified to always return 1, no matter the actual bit-size of the char type.
As for your seg-faulting, I'm guessing you are calling your function with a string literal, like e.g.
some_var = str_split("hello world", " ");
Or possibly
char *string = "hello world";
some_var = str_split(string, " ");
This will cause undefine behavior, because string literals are actually a pointer to a constant array of characters, and strtok modifies the string. Undefined behavior is arguably the most common cause of crashes.
If you enable more warnings when building you would have gotten a warning about this, or maybe you did get a warning but ignored it, or used casting to get rid of it. Warnings from the compilers are often good indicators of you doing something you should not do, hiding it by e.g. casting will only silent the warning but not fix the problem.
There are also a couple of other problems with your code. One is that if there are only one "word"/"token" in the "sentence" you pass in to the function, you waste 4092 or 8184 bytes (depending on 32- or 64-bit platform) in that allocation. You might want to do a separate tokenization loop first (on a temporary copy of the string) first to find out the exact number of "tokens" or "words" in the input.
Doing this counting will also solve the other problem: What if there are more than 1024 tokens/words? Your loop will blissfully write out of bounds in that case.
Both of these cases are extremes, and your standard use-case may be a better fit for your current code, but it's still something to think about.
you may be assigning value to char * at declaration
char *str="abcdef";
or you may not have allocated memory to the string pointed by char * str. In both of these cases strtok() will result in segmentation fault.
When I test it via main, I am only able to pass it char[], but not char*. When passing a char*, the program seg faults.
Going by the above chances are that you are not allocating memory for your char * in main or you are passing a string literal.
when i'm tryin' to use malloc with string pointers to scan, it's givin' a segmentation fault
main(){
char *a;
a = (char)malloc(50);
printf("enter a string\t");
scanf("%s", a);
printf("%s\n", a);
}
a = (char)malloc(50);
Here, you meant to cast its type to char * instead of char.
Note that it's better not to cast the return type of malloc. See Do I cast the result of malloc?
Your main problem, apart from your using scanf is that you're casting a pointer (which is size 4 or 8 on 32 or 64 bit systems respectively) to a char, which is guaranteed to be size 1 as per standard. Do not ignore compiler warnings. You should've seen something along the lines of:
warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
a = (char) malloc(10);
warning: assignment makes pointer from integer without a cast [enabled by default]
a = (char) malloc(50);
Casting a pointer type to char, only to assign it to a pointer variable makes no sense. If you want to make it absolutely clear that you are allocating enough memory to accomodate N chars, then you could write:
a = malloc(50 * sizeof(char));
//or better still
a = malloc(50 *sizeof *a);//50 times the size of whatever type a is pointing to...
But a char is always size 1, like I said before. Anyway: I set about fixing the code you posted:
The following copy-pasted code works just fine, provided you don't enter more than 49 chars:
#include <stdio.h>
#include <stdlib.h>
int main( void )
{
char *a = malloc(50);
if (a == NULL) return EXIT_FAILURE;//<-- no memory available
printf("enter a string\t");
scanf("%s", a);
printf("%s\n", a);
free(a);//<== be tidy, free your memory, though here, it's a bit redundant
return 0;
}
But really, look into alternatives to scanf, and never cast the pointer returned by malloc in C. C++ is a different matter.
Also, check for NULL pointers, to be safe.
Lastly: Don't ignore compiler warnings.
copy-paste & test here
you can provide stdin input at the bottom right hand side of the page, it'll print the string you provided
Following are some basic questions that I have with respect to strings in C.
If string literals are stored in read-only data segment and cannot be changed after initialisation, then what is the difference between the following two initialisations.
char *string = "Hello world";
const char *string = "Hello world";
When we dynamically allocate memory for strings, I see the following allocation is capable enough to hold a string of arbitary length.Though this allocation work, I undersand/beleive that it is always good practice to allocate the actual size of actual string rather than the size of data type.Please guide on proper usage of dynamic allocation for strings.
char *str = (char *)malloc(sizeof(char));
scanf("%s",str);
printf("%s\n",str);
1.what is the difference between the following two initialisations.
The difference is the compilation and runtime checking of the error as others already told about this.
char *string = "Hello world";--->stored in read only data segment and
can't be changed,but if you change the value then compiler won't give
any error it only comes at runtime.
const char *string = "Hello world";--->This is also stored in the read
only data segment with a compile time checking as it is declared as
const so if you are changing the value of string then you will get an
error at compile time ,which is far better than a failure at run time.
2.Please guide on proper usage of dynamic allocation for strings.
char *str = (char *)malloc(sizeof(char));
scanf("%s",str);
printf("%s\n",str);
This code may work some time but not always.The problem comes at run-time when you will get a segmentation fault,as you are accessing the area of memory which is not own by your program.You should always very careful in this dynamic memory allocation as it will leads to very dangerous error at run time.
You should always allocate the amount of memory you need correctly.
The most error comes during the use of string.You should always keep in mind that there is a '\0' character present at last of the string and during the allocation its your responsibility to allocate memory for this.
Hope this helps.
what is the difference between the following two initialisations.
String literals have type char* for legacy reasons. It's good practice to only point to them via const char*, because it's not allowed to modify them.
I see the following allocation is capable enough to hold a string of arbitary length.
Wrong. This allocation only allocates memory for one character. If you tried to write more than one byte into string, you'd have a buffer overflow.
The proper way to dynamically allocate memory is simply:
char *string = malloc(your_desired_max_length);
Explicit cast is redundant here and sizeof(char) is 1 by definition.
Also: Remember that the string terminator (0) has to fit into the string too.
In the first case, you're explicitly casting the char* to a const one, meaning you're disallowing changes, at the compiler level, to the characters behind it. In C, it's actually undefined behaviour (at runtime) to try and modify those characters regardless of their const-ness, but a string literal is a char *, not a const char *.
In the second case, I see two problems.
The first is that you should never cast the return value from malloc since it can mask certain errors (especially on systems where pointers and integers are different sizes). Specifically, unless there is an active malloc prototype in place, a compiler may assume that it returns an int rather than the correct void *.
So, if you forget to include stdlib.h, you may experience some funny behaviour that the compiler couldn't warn you about, because you told it with an explicit cast that you knew what you were doing.
C is perfectly capable of implicit casting between the void * returned from malloc and any other pointer type.
The second problem is that it only allocates space for one character, which will be the terminating null for a string.
It would be better written as:
char *string = malloc (max_str_size + 1);
(and don't ever multiply by sizeof(char), that's a waste of time - it's always 1).
The difference between the two declarations is that the compiler will produce an error (which is much preferable to a runtime failure) if an attempt to modify the string literal is made via the const char* declared pointer. The following code:
const char* s = "hello"; /* 's' is a pointer to 'const char'. */
*s = 'a';
results in the VC2010 emitted the following error:
error C2166: l-value specifies const object
An attempt to modify the string literal made via the char* declared pointer won't be detected until runtime (VC2010 emits no error), the behaviour of which is undefined.
When malloc()ing memory for storing of strings you must remember to allocate one extra char for storing the null terminator as all (or nearly all) C string handling functions require the null terminator. For example, to allocate a buffer for storing "hello":
char* p = malloc(6); /* 5 for "hello" and 1 for null terminator. */
sizeof(char) is guaranteed to be 1 so is unrequired and it is not necessary to cast the return value of malloc(). When p is no longer required remember to free() the allocated memory:
free(p);
Difference between the following two initialisations.
first, char *string = "Hello world";
- "Hello world" stored in stack segment as constant string and its address is assigned to pointer'string' variable.
"Hello world" is constant. And you can't do string[5]='g', and doing this will cause a segmentation fault.
Where as 'string' variable itself is not constant. And you can change its binding:
string= "Some other string"; //this is correct, no segmentation fault
const char *string = "Hello world";
Again "Hello world" stored in stack segment as constant string and its address is assigned to 'string' variable.
And string[5]='g', and this cause segmentation fault.
No use of const keyword here!
Now,
char *string = (char *)malloc(sizeof(char));
Above declaration same as first one but this time you are assignment is dynamic from Heap segment (not from stack)
The code:
char *string = (char *)malloc(sizeof(char));
Will not hold a string of arbitrary length. It will allocate a single character and return a pointer to char character. Note that a pointer to a character and a pointer to what you call a string are the same thing.
To allocate space for a string you must do something like this:
char *data="Hello, world";
char *copy=(char*)malloc(strlen(data)+1);
strcpy(copy,data);
You need to tell malloc exactly how many bytes to allocate. The +1 is for the null terminator that needs to go onto the end.
As for literal string being stored in a read-only segment, this is an implementation issue, although is pretty much always the case. Most C compilers are pretty relaxed about const'ing access to these strings, but attempting to modify them is asking for trouble, so you should always declare them const char * to avoid any issues.
That particular allocation may appear to work as there's probably plenty of space in the program heap, but it doesn't. You can verify it by allocating two "arbitrary" strings with the proposed method and memcpy:ing some long enough string to the corresponding addresses. In the best case you see garbage, in the worst case you'll have segmentation fault or assert from malloc or free.
I'm trying to understand how pointers to pointers work and I came out with this example, and it compiles fine. But, when it is executed, I get a segmentation fault.
PS: I don't want f1() to return char *.
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int f1(char **str_);
int main(int argc, char **argv)
{
char *str = NULL;
f1(&str);
printf("str : %s\n", *str);
str = realloc(str, (size_t) 0);
assert(str == NULL);
return 0;
}
int f1(char **str_)
{
if ((*str_ = realloc(*str_, sizeof(char) * 12)) == NULL)
{
fprintf(stderr,"realloc() failed\n");
exit(3);
}
(*str_) = "hello there";
return 0;
}
What is wrong with the code?
You either need to turn on more warnings in your compiler or get a better compiler. When I compile your code, I get the warnings:
mem.c: In function ‘main’:
mem.c:12: warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘int’
mem.c:12: warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘int’
mem.c: At top level:
mem.c:7: warning: unused parameter ‘argc’
mem.c:7: warning: unused parameter ‘argv’
I'm not quite sure why the warning on line 12 is repeated, but both GCC 4.2 and 4.6.1 on MacOS X 10.7 give it twice. The warnings about argc and argv are a good reason for using int main(void) when you are not using the command line arguments; they aren't a big problem.
The warning (really, an error) about %s and int vs char * is sufficient to account for your crash - it is not, however, the only issue with the code. In fact, there is also:
an incipient memory leak,
an actual leak,
a place where you are (accidentally) relying on undefined behaviour, and
a place where you're relying on implementation-defined behaviour which may not match your expectations.
Your code annotated:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int f1(char **str_);
int main(int argc, char **argv)
{
char *str = NULL;
f1(&str);
printf("str : %s\n", *str);
str = realloc(str, (size_t) 0); /* (3) Undefined behaviour */
assert(str == NULL); /* (4) Implementation-defined behaviour */
return 0;
}
int f1(char **str_)
{
if ((*str_ = realloc(*str_, sizeof(char) * 12)) == NULL) /* (1) Incipient leak */
{
fprintf(stderr,"realloc() failed\n");
exit(3);
}
(*str_) = "hello there"; /* (2) Actual leak */
return 0;
}
Discussing these in the numbered sequence:
The incipient leak arises if the memory reallocation fails. *str is the only place where the previous value of the pointer to the allocated memory is stored, and if realloc() fails, it returns 0 (null pointer) but does not release the old memory, but you no longer have a pointer to the old memory.
The fix:
char *new_mem = realloc(*str, 12);
if (new_mem == 0)
...error handling...
*str = new_mem;
Rule of thumb: do not assign the return value from realloc() to the variable that is its first argument.
The actual leak arises because you assign the pointer to a string constant over the pointer to the newly allocated memory. The simplest fix is to use strcpy(), though you need to add #include <string.h> when you do so. You would also, normally, make sure you allocate just enough space for the string you are going to copy, leading to:
char const hello[] = "hello there";
char *new_mem = realloc(str, sizeof(hello));
//char *new_mem = realloc(str, strlen(hello)+1);
if (new_mem == 0)
...handle error...
*str = new_mem;
strcpy(new_mem, hello);
In this example, I can use sizeof(hello) because the size of a string includes the null terminator, and because the actual array definition is in scope. If the string to be copied was passed into the function as a pointer, then the alternative using strlen(hello)+1 is correct (and using sizeof() is incorrect), even though it requires a runtime computation of the length instead of a compile time computation as shown.
The undefined behaviour arises because of the memory leak at (2). You try to realloc() a string constant, not the pointer returned by realloc(). This leads to undefined behaviour; it could crash (because realloc() tries to write control information into read-only memory) or it might simply mess up the memory system, leading to a crash some time later.
The fix for this is the correction for item (2).
The implementation-defined behaviour arises because the C standard says:
§7.20.3 Memory management functions ¶1: [...] If the size of the space requested is zero, the behavior is implementation defined:
either a null pointer is returned, or the behavior is as if the size were some
nonzero value, except that the returned pointer shall not be used to access an object.
You are asserting that your implementation will choose the first option, but it might choose the second. The fix is to remove the unwarranted assertion.
So, that's a total of 5 problems in the code, only one of which a compiler is likely to help you with.
The first line of main() sets the variable str to NULL and passes a pointer to it to f1.
f1 runs perfectly fine. The outcome of f1 is that the variable str inside of main is now a pointer to a space in memory holding the string (literal) "hello there".
Your next line, the printf, segfaults. Why? Because you are trying to print *str (note the asterisk here!!) as a string (format specifier %s). What is *str when iterpreted as a string? Whatever "address" is denoted by "hell" probably (at least on a 32-bit machine). Doubtful that address is in your process space.
Classic segfault.
Try passing str rather than *str to printf.
That will work, see http://codepad.org/Mh00txen
There are other issues with the code, e.g. the realloc of 12 chars in f1 does nothing except cause a memory leak because you immediately reassign that pointer to point to a string literal, but that is not the cause of your segfault.