I am going through a book and I tried running this example but I receive a segmentation fault - gdb says it's when it sets argv[0] = filename;
this code is copied/pasted straight from book's downloadable code samples.
#include <unistd.h>
int main() {
char filename[] = "/bin/sh\x00";
char **argv, **envp; // arrays that contain char pointers
argv[0] = filename; // only argument is filename - segmentation fault here
argv[1] = 0; // null terminate the argument array
envp[0] = 0; // null terminate the environment array
execve(filename, argv, envp);
}
Edit: The book is Hacking: The Art of Exploitation by Jon Erickson, which has VERY good reviews. This specific example is used as the first tutorial on converting C into machine code in the shellcode section, specifically it is exec_shell.c and can be downloaded from http://nostarch.com/hacking2/htm . I imagine some context around the use of this code was necessary in order to avoid some of the negative comments below, sorry for leaving details out, and thanks for the help.
It obviously isn’t a very good book. The problem is that neither argv nor envp are initialized, so when you write to argv[0], you’re trying to overwrite some random location in memory.
Try something like this:
#include <unistd.h>
int main() {
char *filename = "/bin/sh";
char *argv[2], *envp[1];
argv[0] = filename;
argv[1] = 0;
envp[0] = 0;
execve(filename, argv, envp);
}
This alternative initializes argv and envp on the stack, with enough space to contain two pointers and one pointer respectively.
In the code above, I’ve made one additional change to repair an additional common (but, in this case, harmless) misunderstanding. The \x00 that was at the end of "/bin/sh\x00" is redundant, since in C static strings are implicitly null-terminated. "/bin/sh\x00" is a string terminated by two nulls.
Alternatively, as pointed out by caf, here is a more compact example with exactly equivalent meaning:
#include <unistd.h>
int main() {
char *filename = "/bin/sh";
char *argv[2] = { filename, 0 };
char *envp[1] = { 0 };
execve(filename, argv, envp);
}
You never allocate the "arrays of pointers" meant to go in argv and envp! What book is it, that omits such crucial steps?!
Either add argv = malloc(2 * sizeof(char*)) (and similarly for envp) before you start assigning to argv[0] and friends, or change argv's and envp's declarations to be arrays of pointers rather than pointers to pointers (the latter's quite a feasible approach, in this specific case, since you do know exactly how many pointers you need in each at the time you're writing the code -- dynamic allocation is therefore somewhat supererogatory;-).
char **argv
argv is pointing to a memory location which you are not allowed to access/write to. It is something that is better known as a wild pointer.
Looks like you need to get a better book! In this code argv is a pointer with no storage allocated to it and pointing at random memory (or probably NULL). When you dereference it with argv[0] = ..., your code ends up trying to write to random memory. Your variable declaration should be something more like:
char *argv[3], *envp[1];
I have no idea where did you get this book, but it obviously sucks. argv is an uninitialized pointer, it holds a random address. Hence accessing it will most probably lead to the access violation.
Before using such multi-level pointers, I recommend reading up on dynamic memmory allocation in C.
Whenever you use pointers, you must also think whether you need to allocate space for the data that the pointers are going to point to (as also the pointers themselves, for multi-level pointers).
For example,
char **bar;
here, bar allocates space for 1 pointer-to-pointer, ie. enough space to store one address. This is not very useful without any additional data allocation.
In reality, you should be doing:
char **bar = calloc( 2 , sizeof(char *) );
here, bar allocates space for 1 pointer-to-pointer, ie. again, space to store one address as bar, AND 2 consecutive locations for storing 2 more pointers, namely bar[0] & bar1.
char bar[0]= calloc( 10 , sizeof(char) );
here, bar[0] allocates space for storing a string of size 10 - 1 (for \0 at end).
Now, if you do a string copy:
strcpy(bar[0],"Hello!");
the final memory map comes to: (addresses in circles, contents in blocks)
Many of the people here are on the right track, but missing some of the numerous problems here.
#include <unistd.h>
int main() {
char filename[] = "/bin/sh\x00";
char **argv, **envp; // arrays that contain char pointers
argv[0] = filename; // only argument is filename - segmentation fault here
argv[1] = 0; // null terminate the argument array
envp[0] = 0; // null terminate the environment array
execve(filename, argv, envp);
}
The problems here are:
1. The pointer array of character strings is never initialized. Pointers take up space too, and thus an array of pointers needs to use malloc in c.
2. Each character pointer in your pointer array needs its own malloc statement before use.
Here is the working code, with printouts to show you what is going on:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
unsigned int i=0;
char filename[] = "/bin/sh\x00";
char **argv; // arrays that contain char pointers
argv=(char **)malloc(sizeof(char*));
argv[0]=(char *)malloc(strlen(filename)*sizeof(char));
strcpy(argv[0],filename);
printf("Arg 0 is %u chars long...\n",strlen(argv[0]));
printf("Arg 0 is ");
while (argv[0][i] != '\0') {
printf("%c",argv[0][i]);
i++;
}
printf("!\n");
free(argv[0]);
}
Related
I came across this in someones code... I don't know if it is correct (because even tho it looks wrong, it works). Could someone clarify whether this is correct or not, why so, and why it works anyway ?
In short, we want to store all the arguments (given as command-line), concatenated, in 1 string.
Note: each string has at least 1 character.
Snippet:
int main(int argc, char **argv) {
// Declaring a pointer to a string
char *desintation_string;
// Allocating enough memory to store all arguments (given as command-line) concatenated
destination_string = malloc((argc) * sizeof(char)); /* <————— is this correct ? does
it indeed allocate
enough memory to fit
all the arguments
concatenated ? */
. . .
}
The question is:
Does this line "destination_string = malloc((argc) * sizeof(char));" allocate enough memory to do so ?
Can someone explain exactly what this is doing ? Cuz I read it as: it is allocating (argc * 1 Byte). Yet, when you run it and copy the arguments to it, it works, could someone explain that too ?
No. Say your arguments are "foo" "bar". This makes argc = 2. With malloc((argc) * sizeof(char)) you are allocating memory for only 2 chars in this case.
argv is a 2D array (hence argv**). You need to check length of each argument fist in order to allocate memory for them.
What malloc((argc) * sizeof(char)) does:
argc is the number of arguments you pass. sizeof(char) returns the number of bytes needs to be allocated for a char variable. So you get malloc(<number of bytes needed to store argc number of char variables>). malloc() allocates that number of bytes in the heap.
Does this line "destination_string = malloc((argc) * sizeof(char));"
allocate enough memory to do so ?
No. You need to allocate enough memory. for example here
#define BUFSIZE YOUR_EXPECTED_SIZE
destination_string = malloc((BUFSIZE) * sizeof(char));
for example command line: a.out foo bar
snprintf(foo, 1024, "%s - %s\n", argv[1], argv[2]);
Your code will work for the only a short type of arguments (i.e. -x). But for the long type of arguments (i.e. --list), it will be failed.
This is how you can do it.
int main(int argc, char **argv) {
// Declaring a pointer to a string.
char *desintation_string;
int Arg_Size = 0;
// Allocating enough memory to store all arguments concatenated.
// argv[0] is path not argument given in command line
for (int i=1, i <= argc, i++)
Arg_Size += sizeof(argv[i]);
destination_string = malloc(Arg_Size);
. . .
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I have this code:
char** SplitToWords(char* str);
int main()
{
char** wordarr;
char str[] = "This is a sentence";
wordarr = SplitToWords(str);
return 0;
}
After the main comes the function implementation.
I am not sure the following does what I want it to do (i.e. receive an array of strings from a function):
wordarr = SplitToWords(str);
I somehow managed to convince the compiler that it's ok, but I assume it just does something else.
If it does, how do I find out the length of the array (the number of strings in it).
Thanks
I'll try to quickly visit all aspects you might not yet fully understand:
A string in C is described as a contiguous sequence of chars, ending with a char of value 0 (as a literal: '\0'). It is not a first class object, therefore hasn't its own type. So what you use to hold a string is an array of char. Therefore, taking your question by the word, "receive an array of strings from a function" is not possible.
An array is a contiguous sequence of objects of the same type. In C, the identifier of an array doesn't have a value itself; when it's evaluated, it decays as a pointer to the array's first element instead. This is especially important when passing arrays to functions or returning them from functions -- you can't actually pass the array, you always pass a pointer
e.g. you could write:
char x[] = "foo"; // initialize a char array from a string literal
char *xp = x; // here, x evaluates as a pointer to the first element of the array
You already use pointer types for your function's argument and return value, I just think it's quite important to understand what happens entirely.
You write char** SplitToWords(char* str); and ask whether this returns an "array of strings" -- well, sort of, as you should understand after reading 1. and 2. -- What it does is returning a pointer to char *. This pointer could be a pointer to the first element of an array. So in this case, it would return a pointer to an array of char * pointers. Each of these pointers could itself be a pointer to an array of chars, therefore point to a string. But what's very important is to understand you never return an array, you always return a pointer to it. It's so important because:
You might get the idea to do something like this:
char** SplitToWords(char* str)
{
char *words[16];
// code to fill `words` with pointers to the actual words
return words; // WRONG!
}
Here, because you're not returning the array words but a pointer to it (see point 2), you return a pointer to an object that no longer exists. words is in the scope of your function and has automatic storage duration, that means it only lives as long as the execution is inside of the function. One solution would be to declare words with the static storage class specifier. This way, it lives for the entire execution time of the program. But be aware that this also means there's only a single instance ever, it's always the same object. This will be a major headache for threaded programs, for example. The other way around is to dynamically allocate words using malloc(). But then, the caller of the function must free() it later.
As for your second question, how to let the caller know the number of words -- it's in the comments already, but just for completeness, a typical approach to solve this is to append another entry that is a NULL pointer. So the caller can iterate over the pointers until it finds NULL.
Regarding your comment, of course you can create the array outside the function and pass a pointer to the function, so the function only fills it. This is a common idiom in C (e.g. think about fgets(), which takes a pointer to the char array that's filled with a string by the function).
Functions working this way will need an additional size_t parameter, so they know the size of the array they should fill through the pointer, otherwise you'd have the risk of buffer overflows (this is why gets() was finally removed from the C standard). If you decide that the caller provides the storage, your function should have this prototype:
// returns the number of words found, up to `nwords`
size_t SplitToTwords(char **words, size_t nwords, char *str);
It should be called e.g. like this:
char *words[16];
size_t nwords = SplitToWords(words, 16, "the quick brown fox"); // returns 4
Remember that the strings holding the words themselves need storage as well. You can either manipulate the bytes in str to insert a '\0' after each word, overwriting the first whitespace character (this is what strtok() does) or you can copy the words to new strings, but then you would have to malloc() each of them again and the caller has to free() them later.
Yes, you could solve it by using a function with return value char **. However, there's no way to find out how many words there are afterwards.
You can solve this by allocating one more element for the return pointer and set it to NULL. Then you can get the number of words with this code:
wordarr = SplitToWords(str);
char **ptr=wordarr;
int noWords=0;
while(!*(ptr+noWords))
noWords++;
But if you want to return multiple data in C, you either need to define a return struct or using return arguments. In this case, it could look like this for the first option:
typedef struct wordList {
char **wordarr;
int noWords;
}
wordList SplitToWords(char* str);
And the second:
char** SplitToWords(char* str, int *noWords);
or
void SplitToWords(char* str, char*** wordarr, int *noWords);
Note that there's three *. That's because we want it to be a pointer to char **
#include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAXSTRINGS 5000
int main(int argc, char *argv[]) {
char *stringTable[MAXSTRINGS];
char sentence[] = "This is a sentence";
char *token = NULL;
int i = 0;
while ((token = strtok(token == NULL ? sentence : NULL, " ")) != NULL)
{
printf("%s\n\r", token);
stringTable[i] = (char *)malloc(strlen(token) + 1); //have no "plain" C compiler - VS C++ used so cast needed :)
strcpy(stringTable[i++], token);
}
stringTable[i] = NULL; // if you need to iterate through later
printf("%d tokens found\n\r", i);
for (int y = 0; y < i; y++)
free(stringTable[y]);
}
This question already has answers here:
Difference between char[] and char * in C [duplicate]
(3 answers)
Closed 7 years ago.
I think I know the answer to my own question but I would like to have confirmation that I understand this perfectly.
I wrote a function that returns a string. I pass a char* as a parameter, and the function modifies the pointer.
It works fine and here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void get_file_name(char* file_name_out)
{
char file_name[12+1];
char dir_name[50+12+1];
strcpy(file_name, "name.xml");
strcpy(dir_name, "/home/user/foo/bar/");
strcat(dir_name, file_name);
strcpy(file_name_out, dir_name); // Clarity - equivalent to a return
}
int main()
{
char file_name[100];
get_file_name(file_name);
printf(file_name);
return 0;
}
But if I replace char file_name[100]; by char *filename; or char *filename = "";, I get a segmentation fault in strcpy().
I am not sure why ?
My function takes a char* as a parameter and so does strcpy().
As far as I understand, char *filename = ""; creates a read-only string. strcpy() is then trying to write into a read-only variable, which is not allowed so the error makes sense.
But what happens when I write char *filename; ? My guess is that enough space to fit a pointer to a char is allocated on the stack, so I could write only one single character where my file_name_out points. A call to strcpy() would try to write at least 2, hence the error.
It would explain why the following code compiles and yields the expected output:
void foo(char* a, char* b)
{
*a = *b;
}
int main()
{
char a = 'A', b = 'B';
printf("a = %c, b = %c\n", a, b);
foo(&a, &b);
printf("a = %c, b = %c\n", a, b);
return 0;
}
On the other hand, if I use char file_name[100];, I allocate enough room on the stack for 100 characters, so strcpy() can happily write into file_name_out.
Am I right ?
As far as I understand, char *filename = ""; creates a read-only
string. strcpy() is then trying to write into a read-only variable,
which is not allowed so the error makes sense.
Yes, that's right. It is inherently different from declaring a character array. Initializing a character pointer to a string literal makes it read-only; attempting to change the contents of the string leads to UB.
But what happens when I write char *filename; ? My guess is that
enough space to fit a pointer to a char is allocated on the stack, so
I could write only one single character into my file_name_out
variable.
You allocate enough space to store a pointer to a character, and that's it. You can't write to *filename, not even a single character, because you didn't allocate space to store the contents pointed to by *filename. If you want to change the contents pointed to by filename, first you must initialize it to point to somewhere valid.
I think the issue here is that
char string[100];
allocates memory to string - which you can access using string as pointer
but
char * string;
does not allocate any memory to string so you get a seg fault.
to get memory you could use
string = calloc(100,sizeo(char));
for example, but you would need to remember at the end to free the memory with
free(string);
or you could get a memory leak.
another memory allocation route is with malloc
So in summary
char string[100];
is equivalent to
char * string;
string = calloc(100,sizeo(char));
...
free(string);
although strictly speaking calloc initializes all elements to zero, whereas in the string[100] decalaration the array elements are undefined unless you use
string[100]={}
if you use malloc instead to grad the memory the contents are undefined.
Another point made by #PaulRooney is that char string[100] gives memory allocation on the stack whereas calloc uses the heap. For more information about the heap and stack see this question and answers...
char file_name[100]; creates a contiguous array of 100 chars. In this case file_name is a pointer of type (char*) which points to the first element in the array.
char* file_name; creates a pointer. However, it is not initialized to a particular memory address. Further, this expression does not allocate memory.
char *filename;
Allocate nothing. Its just a pointer pointing to an unspecified location (the value is whatever was in that memory previously). Using this pointer will never work as it probably points outside the memory range your program is allowed to use.
char *filename = "";
Points to a piece of the programs data segment. As you already said it's read only and so attempting to change it leads to the segfault.
In your final example you are dealing with single char values, not strings of char values and your function foo treats them as such. So there is no issue with the length of buffers the char* values point to.
I'm reading K&R and I'm almost through the chapter on pointers. I'm not entirely sure if I'm going about using them the right way. I decided to try implementing itoa(n) using pointers. Is there something glaringly wrong about the way I went about doing it? I don't particularly like that I needed to set aside a large array to work as a string buffer in order to do anything, but then again, I'm not sure if that's actually the correct way to go about it in C.
Are there any general guidelines you like to follow when deciding to use pointers in your code? Is there anything I can improve on in the code below? Is there a way I can work with strings without a static string buffer?
/*Source file: String Functions*/
#include <stdio.h>
static char stringBuffer[500];
static char *strPtr = stringBuffer;
/* Algorithm: n % 10^(n+1) / 10^(n) */
char *intToString(int n){
int p = 1;
int i = 0;
while(n/p != 0)
p*=10, i++;
for(;p != 1; p/=10)
*(strPtr++) = ((n % p)/(p/10)) + '0';
*strPtr++ = '\0';
return strPtr - i - 1;
}
int main(){
char *s[3] = {intToString(123), intToString(456), intToString(78910)};
printf("%s\n",s[2]);
int x = stringToInteger(s[2]);
printf("%d\n", x);
return 0;
}
Lastly, can someone clarify for me what the difference between an array and a pointer is? There's a section in K&R that has me very confused about it; "5.5 - Character Pointers and Functions." I'll quote it here:
"There is an important difference between the definitions:
char amessage[] = "now is the time"; /*an array*/
char *pmessage = "now is the time"; /*a pointer*/
amessage is an array, just big enough to hold the sequence of characters and '\0' that
initializes it. Individual characters within the array may be changed but amessage will
always refer to the same storage. On the other hand, pmessage is a pointer, initialized
to point to a string constant; the pointer may subsequently be modified to point
elsewhere, but the result is undefined if you try to modify the string contents."
What does that even mean?
For itoa the length of a resulting string can't be greater than the length of INT_MAX + minus sign - so you'd be safe with a buffer of that length. The length of number string is easy to determine by using log10(number) + 1, so you'd need buffer sized log10(INT_MAX) + 3, with space for minus and terminating \0.
Also, generally it's not a good practice to return pointers to 'black box' buffers from functions. Your best bet here would be to provide a buffer as a pointer argument in intToString, so then you can easily use any type of memory you like (dynamic, allocated on stack, etc.). Here's an example:
char *intToString(int n, char *buffer) {
// ...
char *bufferStart = buffer;
for(;p != 1; p/=10)
*(buffer++) = ((n % p)/(p/10)) + '0';
*buffer++ = '\0';
return bufferStart;
}
Then you can use it as follows:
char *buffer1 = malloc(30);
char buffer2[15];
intToString(10, buffer1); // providing pointer to heap allocated memory as a buffer
intToString(20, &buffer2[0]); // providing pointer to statically allocated memory
what the difference between an array and a pointer is?
The answer is in your quote - a pointer can be modified to be pointing to another memory address. Compare:
int a[] = {1, 2, 3};
int b[] = {4, 5, 6};
int *ptrA = &a[0]; // the ptrA now contains pointer to a's first element
ptrA = &b[0]; // now it's b's first element
a = b; // it won't compile
Also, arrays are generally statically allocated, while pointers are suitable for any allocation mechanism.
Regarding your code:
You are using a single static buffer for every call to intToString: this is bad because the string produced by the first call to it will be overwritten by the next.
Generally, functions that handle strings in C should either return a new buffer from malloc, or they should write into a buffer provided by the caller. Allocating a new buffer is less prone to problems due to running out of buffer space.
You are also using a static pointer for the location to write into the buffer, and it never rewinds, so that's definitely a problem: enough calls to this function, and you will run off the end of the buffer and crash.
You already have an initial loop that calculates the number of digits in the function. So you should then just make a new buffer that big using malloc, making sure to leave space for the \0, write in to that, and return it.
Also, since i is not just a loop index, change it to something more obvious like length:
That is to say: get rid of the global variables, and instead after computing length:
char *s, *result;
// compute length
s = result = malloc(length+1);
if (!s) return NULL; // out of memory
for(;p != 1; p/=10)
*(s++) = ((n % p)/(p/10)) + '0';
*s++ = '\0';
return result;
The caller is responsible for releasing the buffer when they're done with it.
Two other things I'd really recommend while learning about pointers:
Compile with all warnings turned on (-Wall etc) and if you get an error try to understand what caused it; they will have things to teach you about how you're using the language
Run your program under Valgrind or some similar checker, which will make pointer bugs more obvious, rather than causing silent corruption
Regarding your last question:
char amessage[] = "now is the time"; - is an array. Arrays cannot be reassigned to point to something else (unlike pointers), it points to a fixed address in memory. If the array was allocated in a block, it will be cleaned up at the end of the block (meaning you cannot return such an array from a function). You can however fiddle with the data inside the array as much as you like so long as you don't exceed the size of the array.
E.g. this is legal amessage[0] = 'N';
char *pmessage = "now is the time"; - is a pointer. A pointer points to a block in memory, nothing more. "now is the time" is a string literal, meaning it is stored inside the executable in a read only location. You cannot under any circumstances modify the data it is pointing to. You can however reassign the pointer to point to something else.
This is NOT legal -*pmessage = 'N'; - will segfault most likely (note that you can use the array syntax with pointers, *pmessage is equivalent to pmessage[0]).
If you compile it with gcc using the -S flag you can actually see "now is the time" stored in the read only part of the assembly executable.
One other thing to point out is that arrays decay to pointers when passed as arguments to a function. The following two declarations are equivalent:
void foo(char arr[]);
and
void foo(char* arr);
About how to use pointers and the difference between array and pointer, I recommend you read the "expert c programming" (http://www.amazon.com/Expert-Programming-Peter-van-Linden/dp/0131774298/ref=sr_1_1?ie=UTF8&qid=1371439251&sr=8-1&keywords=expert+c+programming).
Better way to return strings from functions is to allocate dynamic memory (using malloc) and fill it with the required string...return this pointer to the calling function and then free it.
Sample code :
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#define MAX_NAME_SIZE 20
char * func1()
{
char * c1= NULL;
c1 = (char*)malloc(sizeof(MAX_NAME_SIZE));
strcpy(c1,"John");
return c1;
}
main()
{
char * c2 = NULL;
c2 = func1();
printf("%s \n",c2);
free(c2);
}
And this works without the static strings.
I've been studying C, and I decided to practice using my knowledge by creating some functions to manipulate strings. I wrote a string reverser function, and a main function that asks for user input, sends it through stringreverse(), and prints the results.
Basically I just want to understand how my function works. When I call it with 'tempstr' as the first param, is that to be understood as the address of the first element in the array? Basically like saying &tempstr[0], right?
I guess answering this question would tell me: Would there be any difference if I assigned a char* pointer to my tempstr array and then sent that to stringreverse() as the first param, versus how I'm doing it now? I want to know whether I'm sending a duplicate of the array tempstr, or a memory address.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char* stringreverse(char* tempstr, char* returnptr);
printf("\nEnter a string:\n\t");
char tempstr[1024];
gets(tempstr);
char *revstr = stringreverse(tempstr, revstr); //Assigns revstr the address of the first character of the reversed string.
printf("\nReversed string:\n"
"\t%s\n", revstr);
main();
return 0;
}
char* stringreverse(char* tempstr, char* returnptr)
{
char revstr[1024] = {0};
int i, j = 0;
for (i = strlen(tempstr) - 1; i >= 0; i--, j++)
{
revstr[j] = tempstr[i]; //string reverse algorithm
}
returnptr = &revstr[0];
return returnptr;
}
Thanks for your time. Any other critiques would be helpful . . only a few weeks into programming :P
EDIT: Thanks to all the answers, I figured it out. Here's my solution for anyone wondering:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void stringreverse(char* s);
int main(void)
{
printf("\nEnter a string:\n\t");
char userinput[1024] = {0}; //Need to learn how to use malloc() xD
gets(userinput);
stringreverse(userinput);
printf("\nReversed string:\n"
"\t%s\n", userinput);
main();
return 0;
}
void stringreverse(char* s)
{
int i, j = 0;
char scopy[1024]; //Update to dynamic buffer
strcpy(scopy, s);
for (i = strlen(s) - 1; i >= 0; i--, j++)
{
*(s + j) = scopy[i];
}
}
First, a detail:
int main()
{
char* stringreverse(char* tempstr, char* returnptr);
That prototype should go outside main(), like this:
char* stringreverse(char* tempstr, char* returnptr);
int main()
{
As to your main question: the variable tempstr is a char*, i.e. the address of a character. If you use C's index notation, like tempstr[i], that's essentially the same as *(tempstr + i). The same is true of revstr, except that in that case you're returning the address of a block of memory that's about to be clobbered when the array it points to goes out of scope. You've got the right idea in passing in the address of some memory into which to write the reversed string, but you're not actually copying the data into the memory pointed to by that block. Also, the line:
returnptr = &revstr[0];
Doesn't do what you think. You can't assign a new pointer to returnptr; if you really want to modify returnptr, you'll need to pass in its address, so the parameter would be specified char** returnptr. But don't do that: instead, create a block in your main() that will receive the reversed string, and pass its address in the returnptr parameter. Then, use that block rather than the temporary one you're using now in stringreverse().
Basically I just want to understand how my function works.
One problem you have is that you are using revstr without initializing it or allocating memory for it. This is undefined behavior since you are writing into memory doesn't belong to you. It may appear to work, but in fact what you have is a bug and can produce unexpected results at any time.
When I call it with 'tempstr' as the first param, is that to be understood as the address of the first element in the array? Basically like saying &tempstr[0], right?
Yes. When arrays are passed as arguments to a function, they are treated as regular pointers, pointing to the first element in the array. There is no difference if you assigned &temp[0] to a char* before passing it to stringreverser, because that's what the compiler is doing for you anyway.
The only time you will see a difference between arrays and pointers being passed to functions is in C++ when you start learning about templates and template specialization. But this question is C, so I just thought I'd throw that out there.
When I call it with 'tempstr' as the first param, is that to be understood as the
address of the first element in the array? Basically like saying &tempstr[0],
right?
char tempstr[1024];
tempstr is an array of characters. When passed tempstr to a function, it decays to a pointer pointing to first element of tempstr. So, its basically same as sending &tempstr[0].
Would there be any difference if I assigned a char* pointer to my tempstr array and then sent that to stringreverse() as the first param, versus how I'm doing it now?
No difference. You might do -
char* pointer = tempstr ; // And can pass pointer
char *revstr = stringreverse(tempstr, revstr);
First right side expression's is evaluavated and the return value is assigned to revstr. But what is revstr that is being passed. Program should allocate memory for it.
char revstr[1024] ;
char *retValue = stringreverse(tempstr, revstr) ;
// ^^^^^^ changed to be different.
Now, when passing tempstr and revstr, they decayed to pointers pointing to their respective first indexes. In that case why this would go wrong -
revstr = stringreverse(tempstr, revstr) ;
Just because arrays are not pointers. char* is different from char[]. Hope it helps !
In response to your question about whether the thing passed to the function is an array or a pointer, the relevant part of the C99 standard (6.3.2.1/3) states:
Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue.
So yes, other than the introduction of another explicit variable, the following two lines are equivalent:
char x[] = "abc"; fn (x);
char x[] = "abc"; char *px = &(x[0]); fn (px);
As to a critique, I'd like to raise the following.
While legal, I find it incongruous to have function prototypes (such as stringreverse) anywhere other than at file level. In fact, I tend to order my functions so that they're not usually necessary, making one less place where you have to change it, should the arguments or return type need to be changed. That would entail, in this case, placing stringreverse before main.
Don't ever use gets in a real program.. It's unprotectable against buffer overflows. At a minimum, use fgets which can be protected, or use a decent input function such as the one found here.
You cannot create a local variable within stringreverse and pass back the address of it. That's undefined behaviour. Once that function returns, that variable is gone and you're most likely pointing to whatever happens to replace it on the stack the next time you call a function.
There's no need to pass in the revstr variable either. If it were a pointer with backing memory (i.e., had space allocated for it), that would be fine but then there would be no need to return it. In that case you would allocate both in the caller:
char tempstr[1024];
char revstr[1024];
stringreverse (tempstr, revstr); // Note no return value needed
// since you're manipulating revstr directly.
You should also try to avoid magic numbers like 1024. Better to have lines like:
#define BUFFSZ 1024
char tempstr[BUFFSZ];
so that you only need to change it in one place if you ever need a new value (that becomes particularly important if you have lots of 1024 numbers with different meanings - global search and replace will be your enemy in that case rather than your friend).
In order to make you function more adaptable, you may want to consider allowing it to handle any length. You can do that by passing both buffers in, or by using malloc to dynamically allocate a buffer for you, something like:
char *reversestring (char *src) {
char *dst = malloc (strlen (src) + 1);
if (dst != NULL) {
// copy characters in reverse order.
}
return dst;
}
This puts the responsibility for freeing that memory on the caller but that's a well-worn way of doing things.
You should probably use one of the two canonical forms for main:
int main (int argc, char *argv[]);
int main (void);
It's also a particularly bad idea to call main from anywhere. While that may look like a nifty way to get an infinite loop, it almost certainly will end up chewing up your stack space :-)
All in all, this is probably the function I'd initially write. It allows the user to populate their own buffer if they want, or to specify they don't have one, in which case one will be created for them:
char *revstr (char *src, char *dst) {
// Cache size in case compiler not smart enough to do so.
// Then create destination buffer if none provided.
size_t sz = strlen (src);
if (dst == NULL) dst = malloc (sz + 1);
// Assuming buffer available, copy string.
if (dst != NULL) {
// Run dst end to start, null terminator first.
dst += sz; *dst = '\0';
// Copy character by character until null terminator in src.
// We end up with dst set to original correct value.
while (*src != '\0')
*--dst = *src++;
}
// Return reversed string (possibly NULL if malloc failed).
return dst;
}
In your stringreverse() function, you are returning the address of a local variable (revstr). This is undefined behaviour and is very bad. Your program may appear to work right now, but it will suddenly fail sometime in the future for reasons that are not obvious.
You have two general choices:
Have stringreverse() allocate memory for the returned string, and leave it up to the caller to free it.
Have the caller preallocate space for the returned string, and tell stringreverse() where it is and how big it is.