Segmentation fault while implementing reverse function in C - c

I decided to make a reverse(s) function in C and in the process, I encountered 2 warning messages during compilation.
alexander#debian:~/Dropbox/GitRepo/M2MPL/text$ make reverse
cc reverse.c -o reverse
reverse.c: In function 'reverse':
reverse.c:29:2: warning: return makes integer from pointer without a cast [enabled by default]
reverse.c:29:2: warning: function returns address of local variable [enabled by default]
alexander#debian:~/Dropbox/GitRepo/M2MPL/text$
Since they were warnings I tried to ignore them and run the program, when I input an array of characters Dropbox using scanf("%s", str1);, the result appeared as Segmentation Fault.
alexander#debian:~/Dropbox/GitRepo/M2MPL/text$ ./reverse
Enter a string to reverse: Dropbox
Segmentation fault
alexander#debian:~/Dropbox/GitRepo/M2MPL/text$
Here's my implementation of the reverse(s) function:
/* reverse: returns a reversed string of s */
char reverse(char s[])
{
int i, len=strlen(s);
char result[LIMIT];
for (i = len; i >= 0; --i) {
result[len-i] = s[i];
}
return result;
}
FULL CODE HERE!
I need help to know why I got a Segmentation fault instead of xobporD as a result and if possible, a suggestion to fix this problem.

First of all:
The compiler does not warn you just for fun. You shouldn't be surprised that your program crashes if you ignore compiler warnings.
You return the pointer to an automatic variable that goes out of scope at the end of the function. The pointer returned to the caller is not valid since it points to an object that no longer exists after the reverse function returns.
Another problem is that the first character that you copy is the '\0' so your resulting string is empty. You need to reverse the characters of the string, but still put the '\0' at the end of the string.

The first warning means that the return type indicated by the signature of the reverse function does not match the type of the variable that you are actually returning. The expected type is char, and you are returning a char * (because result decays to such a pointer). So the compiler transforms the pointer into an integer (char), which is usually not what's intended by the programmer.
The second problem is that you are returning the address of a local variable, and that variable is no longer available after the function returns, because of the way automatic storage works. If you want to return a pointer to some data, it must be allocated either globally (declared static inside the function or declared globally), or it must be dynamically allocated.

There are a few flaws in the question code.
It is likely that the return type should be 'char *' rather than 'char'.
Attempting to return local stack memory to the caller.
The result string is likely missing a string termination character.
The following code fix the above. It returns the string in allocated 'heap' memory to the caller. (The caller should free() the memory when it is no longer needed.):
/* reverse: returns a reversed string of s */
char *reverse(char *s)
{
int i, len=strlen(s);
char *result = malloc(len+1);
if(result)
{
result[len] = '\0';
for(i = len; i >= 0; --i)
result[len-i] = s[i];
}
return(result);
}

Related

Returning array declared in function returns address of local variable in C?

I realize that variables declared in a function call are pushed onto the stack and once the function reaches its end the local variables declared onto the stack are popped off and go into lala land.
The thing I don't undertstand is that if I declare a pointer in a function I can return the pointer with no compiler complaint, whereas with an array it does give me a warning.
Here is an example of what I'm referring to:
char * getStringArray();
char * getStringPointer();
int main(){
char * temp1 = getStringPointer();
char * temp2 = getStringArray();
return 0;
}
char * getStringPointer(){
char * retString = "Fred";
return retString;
}
char * getStringArray(){
char retString[5] = {'F', 'r','e','d','\0'};
return retString;
}
During compilation it throws a "returning address of local variable" warning about getStringArray(). What confuses me is that I've been told that referencing an array solely by its name(like retString, no[])refers to its address in memory, acting like a pointer.
Arrays can be handy and there are many times I would like to use an array in a function and return it, is there any quick way around this?
As I referred to in a comment, will this work? I'm guessing malloc allocates to heap but so its fine. I'm still a little unclear of the difference between static data and data on the heap.
char * getStringPointer(){
char * retString = (char *)malloc(sizeof(char)*4+1);
*(retString+0) = 'F';
*(retString+1) = 'r';
*(retString+2) = 'e';
*(retString+3) = 'd';
*(retString+4) = '\0';
return retString;
}
getStringPointer is allocating a pointer on the stack, then returning that pointer, which points to the string 'Fred\0' somewhere in your executable (not on the stack).
getStringArray allocates space for 5 chars on the stack, assigns them 'F' 'r' 'e' 'd' and '\0', then returns a pointer to the address of 'F', which is on the stack (and therefore invalid after the function returns).
There are two ways round it: either you can malloc some space for your array on the heap, or you can make a struct that contains an appropriately sized array and return that. You can return numerical, pointer and struct types from functions, but not arrays.
"Fred" is an unnamed global, so it's not going away, and returning a pointer to it is safe. But getStringArray() is de-allocating everything that pointer points to. That's the difference.
The array is unambiguously a pointer to stack-local memory so the compiler is certain that's what you are leaking, so that's why you get the warning.
Just make the array static and both the warning and the lifetime restriction will vanish.
Now, the pointer could be pointing to something static or in the heap (in your case it actually was static) and so it's not obvious from looking at just that one expression whether you are leaking something stack-local or not. That's why you don't get the warning when you return a pointer, regardless of whether it's a safe return or not. Again, the static storage class will save you. Your string constant happens to be static, so that works fine.
By adding a static modifier to the variable retString and returning back a pointer will resolve the issue.
char * getStringArray(){
static char retString[5] = {'F', 'r','e','d','\0'};
return retString;
}
That will return the appropriate value.
Notice how its still treated as a pointer, because the first element of the array is decayed into a pointer of type T, in this case, char. This will make the compiler happy as it matches the prototype declaration of the function itself as defined at the start of the source.
See ideone sample

De-Referencing void pointer error when infact already cast

Situation
Consider the following source code which aims to print two a's, i.e. output should be "aa":
#include <stdio.h>
int main ()
{
char a = 'a';
void* a_pnt = &a;
void* b_pnt = (char*)a_pnt;
printf("%c", *(char*)a_pnt);
printf("%c", *b_pnt);// why is the compiler saying I am dereferencing a void pointer? It was already cast
return 0;
}
Complication
The printing of the first "a" works but the compiler is giving a compile time error on line 10 (second printing of "a") saying:
Invalid use of void expression
and a warning on the same line saying:
Dereferencing 'void *' pointer
Although b_pnt was indeed declared a void pointer, it was cast to a character pointer in its definition on line 7. My only guess as to why its complaining is something to do with the fact that I can only cast when referencing at the same time. My hunch is based off the fact that the first variable works just fine.
Solution
The solution is declare and define a character variable called 'b' and cast to character pointer upfront before printing it:
#include <stdio.h>
int main ()
{
char a = 'a';
void* a_pnt = &a;
void* b_pnt = a_pnt;
char b = *((char*)b_pnt);
printf("%c", *(char*)a_pnt);
printf("%c", b);// why is the compiler saying I am dereferencing a pointer?
return 0;
}
The question still remains: Why did the initial attempt fail?
I am deliberately starting off with void pointer to illustrate the issue. It could indeed have been avoided entirely ofcourse with the correct pointer type.
Just because you performed a cast when assigning to b_pnt doesn't mean that it's type changed. It's type is still void * and dereferencing it is an error.
You can freely assign any non-function pointer type to a void * without a warning. But the compiler doesn't keep track of what kind of pointer was stored there, so it's still a void * that needs to be casted before it can be dereferenced.

scanf results in seg fault?

gcc compiles perfectly well, but as soon as scanf accepts a string it seg faults. I'm sort of at a loss. Here's the code.
char *line[256];
void *mainThread()
{
while (*line != "quit") {
scanf("%s", *line);
printf("%s", *line);
}
return;
}
Is there something about scanf I'm not understanding here?
First, you are allocating an array of pointers to characters, not an array of char:
char *line[256]; /* allocates 256 pointers to a character -
(pointers are initialized to NULL) */
You need to allocate an array of characters instead:
char line[256]; /* allocates 256 characters */
Second, you need to use strcmp to compare the strings - with !=, you are comparing the pointer (address) stored in line[0] (which is the same as *line) with a pointer to the string literal "quit", and they are always different.
You can use the following sscce as a starting point:
#include <string.h>
#include <stdio.h>
char line[256];
int main()
{
while (strcmp(line, "quit") != 0) {
scanf("%s", line);
printf("%s", line);
}
return 0;
}
Some additional notes:
See #Joachims answer for an explanation of the actual cause of the segmentation fault.
You are declaring your function to return a void* pointer, but you are not returning anything (using return with no argument). You should then simply declare it as void.
Do not use scanf() to read input, since it might read more characters than you have allocated which leads to buffer overflows. Use fgets() instead. See also Disadvantages of scanf.
Always compile with all warnings enabled, and take them serious - e.g. if you are using gcc, compile with -Wall -pedantic.
When declaring global variables, they are initialized to zero. And as you are declaring an array of pointers to char (instead of an array of char as I think you really intended) you have an array of 256 NULL pointers.
Using the dereference operator * on an array is the same as doing e.g. array[0], which means that as argument to both scanf and printf you are passing line[0], which as explained above, is a NULL pointer. Dereferencing NULL pointers, like scanf and printf will do, is a case of undefined behavior, one that almost always leads to a crash.
It is compiling because your program is syntactically correct. However, it has serious semantic errors which show up as program crash due to segfault. The global array line is initialized to zero, as any global variable. Since, line is an array of pointers (not an array of characters which is intended), the zeros are interpreted as NULL, the null pointer. *line is same as line[0] and the string literal "quit" evaluates to a pointer to its first element. Therefore the while condition is the same as
while(NULL != "quit") // always true
Next, scanf("%s", *line); tries to write the input string into the buffer pointed to by line[0] which is NULL - a value which is unequal to the address of any memory location. This will result in segfault and cause the program to crash.
There are other mistakes in your code snippet. Let's take them one by one.
char *line[256];
The above statement defines an array line of 256 pointers to characters, i.e., its type is char *[256]. What you need is an array of characters -
char line[256];
You can't compare array in C. What you can do is compare them element-by-element. For strings, you should use the standard library function strcmp. Also, please note that the %s conversion specifier in the format string of scanf reads a string from stdin and writes it into the buffer pointed to by the next argument. It puts a terminating null byte at the end but it does not check for buffer overrun if you input a string too large for the buffer to hold. This would lead to undefined behaviour and most likely segfault due to illegal memory access. You should guard against buffer overrun by specifying maximum field width in the format string.
void *mainThread();
The above function declaration means that mainThread is function which returns a pointer of void * type and take an unspecified but fixed number and type of arguments because empty parentheses mean no information about the parameter list is provided. You should write void in the parameter list to mean that the function takes no arguments. Also note that the empty return statement in your function would cause undefined behaviour if the return value of the function is used because you are not returning anything and would be using the garbage value in the return address of the function instead. Assuming that you want to return the string, it should be defined as -
char line[256];
char *mainThread(void) {
while(strcmp(line, "quit") != 0) {
scanf("%255s", line); // -1 for the terminating null byte
printf("%s", line);
}
return line;
}

Warning in C: assignment makes integer from pointer without a cast

I keep getting this error when compiling my program. This is just a small part of my code, so if needed I will provide the rest of the code. Any ideas on why this is occuring?
void strip_quotes(char s[]) {
if (s[0]=='"') s=s+1;
if (s[strlen(s)-2]=='"') s[strlen(s)-2]=NULL;
}
You are setting a character of s to NULL. The proper way to add a null character to a string is to use '\0'.
To explain the message, NULL is likely defined as (void*)0, so when you assign it, you are converting void* to char, hence the warning.
As Dave has already correctly pointed out the reason for the compiler error:
s[strlen(s)-2]=NULL; /* = (void*)0 */
There is another bug in the code that won't cause a compiler error:
if (s[0]=='"') s=s+1;
the increment of s will not be visible to the caller, as C passes by value including pointers (see http://c-faq.com/ptrs/passptrinit.html). Options for correcting:
shift the content of the array to the left using memmove() (or some other copy mechanism)
pass the address of the pointer (a char**)
return a pointer to s
Changing the content of s is preferable as it avoids a possible problem if the array was dynamically allocated: only pointers returned by malloc() (or calloc() and realloc()) can be passed to free(). If the value of s is changed then it cannot be free()d via s.
Note that:
void strip_quotes(char s[]) {
is equivalent:
void strip_quotes(char* s) {
incase you were confused as to were pointers are used in the code.
Dave got it, but I'll try to add a bit.
NULL is a pointer of type void*, which can be assigned to any pointer type. If you are setting a pointer to a value that can never be used to represent valid memory, use NULL.
'\0', aka NUL, is ascii value 0 and is used to terminate strings. It is of type char. http://www.december.com/html/spec/ascii.html .
void strip_quotes(char s[]) {
int len = strlen(s);
if(s[len-1] == '"')
s[--len] = '\0';
if(s[0]=='"')
memmove(s, s+1, len);
}

Pointers to pointers in C

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.

Resources