What is the difference between these access in C? - c

i started learning C today, and i have some questions about accessing a pointer data.
I have this function in C:
typedef struct
{
size_t size;
size_t usedSize;
char *array;
} charList;
void addToCharList(charList *list, char *data)
{
if(list->usedSize == list->size)
{
list->size *= 2;
list->array = realloc(list->array, list->size * sizeof(int));
}
list->array[list->usedSize++] = *data;
printf("1: %d\n", *data);
printf("2: %s\n", data);
printf("3: %p\n", &data);
}
I use it to create a "auto growing" array of chars, it works, but i'm not understanding why i need to attribute the value "*data" to my array. I did some tests, printing the different ways i tried to access the variable "data", and i had this outputs (I tested it with the string "test"):
1: 116
2: test
3: 0x7fff0e0baac0
1: Accessing the pointer (i think it's the pointer) gives me a number, that i don't know what is.
2: Just accessing the variable gives me the actual value of the string.
3: Accessing it using the "&" gets the memory location/address.
When i'm attributing the value of my array, i can only pass the pointer, why is that? Shouldn't i be attributing the actual value? Like in the second access.
And what is this number that gives me when i access the pointer? (First access)

So, in the first printf, you're not actually accessing the pointer. If you have a pointer named myPointer, writing *myPointer will actually give you access to the thing that the pointer is pointing to. It is understandable to be confused about this, as you do use the * operator when declaring a variable to specify that it is a pointer.
char* myCharPtr; // Here, the '*' means that myCharPtr is a pointer.
// Here, the '*' means that you are accessing the value that myCharPtr points to.
printf("%c\n", *myCharPtr);
In the second printf, you're accessing the pointer itself. And in the third printf, you're accessing a pointer to a char pointer. The & operator, when put before a variable, will return a pointer to that variable. So...
char myChar = 'c';
// pointerToMyChar points to myChar.
char* pointerToMyChar = &myChar;
// pointerToPointerToMyChar points to a pointer that is pointing at myChar.
char** pointerToPointerToMyChar = &pointerToMyChar;
When you're trying to store the value in the array, it's forcing you to do *data because the array stores chars, and data is a pointer to a char. So you need to access the char value that the pointer is pointing to. And you do that via *data.
Lastly: the reason that printf("1: %d\n", *data); prints a number is that chars are secretly (or not secretly) numbers. Every character has a corresponding numeric value, behind the scenes. You can see which numerical value corresponds to which character by looking at an ascii table.

Related

This program is giving output "abc" for both p and c, but how?

When pc is assigning cdefg, why it is printing abc. when it goes to fun it is assigning pc= ""cdefg"
void fun(char *pc)
{
pc = malloc(5);
pc = "cdefg";
}
int main()
{
char *p = "abc";
char *c = p;
fun(p);
printf("%s %s\n",p,c);
}
The reason your program does what it does is that the assignment of pc in fun has nothing to do with assigning p in main. The pointer is passed by value; any changes made by fun get discarded.
If you would like to assign a new value inside a function, do one of three things:
Pass a pointer to pointer, or
Allocate a buffer in the caller, and pass it to the function, along with buffer's length, or
Return the pointer from the function, and assign in the caller.
First approach:
void fun(char **ppc) {
*ppc = "cdefg";
}
...
fun(&p); // in main
Second approach:
void fun(char *pc, size_t len) {
if (len >= 6) {
strcpy(pc, "cdefg");
}
}
...
char p[20]; // in main
fun(p, 20);
Third approach:
char *fun() {
return "sdefg";
}
...
char *p = fun(); // in main
Your program has other issues - for example, malloc-ed memory gets leaked by the assignment that follows.
Try this instead. It actually updates the original pointer, rather than assigning to a copy which is then left dangling:
void fun(char **pc)
{
*pc = malloc(6);
strcpy(*pc, "cdefg");
}
int main()
{
char *p = "abc";
char *c = p;
fun(&p);
printf("%s %s\n",p,c);
}
It also fixed 2 other problems. The buffer of size 5 isn't big enough for the string plus the string terminator character, and you also need to copy the string into the buffer - assignment won't work.
When the function fun is called, the value of the pointer p is copied. Thus, only the local pointer pc in fun is changed. If you want to change the value of a pointer, you should take a double pointer as argument.
By the way, you do not have to call malloc(3) because the string "cdefg" is already present in memory (in rodata). The instruction pc = "cdefg"; puts the address of "cdefg" into pc. You will loose the address of the memory allocated by malloc(3), it's a memory leak.
When you allocated the pointer again in caller function, the value of pointer variable changed. In order to take this new value to the calling function, you have to pass the address of the pointer. ie: pass the pointer by reference.
There are two things at play here, passing by value and reassigning instead of copying.
If we start with the simple reassignment, take a closer look at these two lines:
pc = malloc(5);
pc = "cdefg";
The first lines assign to pc, making pc point to whatever memory malloc returned. The second line reassigns pc to point somewhere else. These two lines are basically the same as having an int variable i and doing
i = 1;
i = 2;
The first assignment you do is lost because the you immediately make another assignment. To make the memory returned by malloc contain the string "cdefg" there are two things you need to do: The first is that you need to allocate six characters, to fit the string terminator, and the second thing you need to do is to copy the string into the memory:
pc = malloc(strlen("cdefg") + 1);
strcpy(pc, "cdefg");
The second issue is more complex, and has to do with how arguments are passed in C. In C the arguments are passed by values which means they are copied and the function only have a local copy of the data in those arguments.
When you pass a pointer, like in your code, then the pointer is copied into the variable pc, and when the function returns the variable goes out of scope and all changes you made to the variable (like reassigning it to point somewhere else) are simply lost.
The solution is to pass arguments by reference. This is unfortunately not possible in C, but it can be emulated using pointers, or rather using pointers to variables. To pass a pointer to a variable that is a pointer, the type is a pointer to a pointer to some other type, so the function should take a pointer to a pointer to char:
void fun(char **ppc) { ... }
The variable ppc points to the variable p from the main function.
Now since ppc is pointing to a variable, you need to use the dereference operator to access the original variable:
*ppc = malloc(strlen("cdefg") + 1);
strcpy(*ppc, "cdefg");
To call the function you use the address-of operator & to create a pointer to the variable:
char *p = "abc";
...
fun(&p);
Because
char *p - in main function
and
char *pc - in fun function
are different variables.

Assign the values of an array to a pointer

I am first concatenating a series of elements in an auxiliary char array to then assign the concatenated array to the pointer. The problem comes when assigning this char array to the pointer, where it produces a segmentation fault.
My approach was the following one:
char aux_name [12];
char * name = (char *) malloc(sizeof(char)*13);
int i;
for(i = 0; i < 5; i++){
sprintf(aux_name, "id_%i", i);
*name = (void *) (intptr_t) aux_name; //Conflict line
//Do something with variable name (it is required a pointer)
}
You don't assign a pointer value to an already malloc()-ed pointer, you'll be facing memory-leak there. You have to use strcpy() to achieve what you want.
OTOH, if you don't allocate memory dynamically, then you can assign the pointer like
name = aux_name;
That said,
I am first concatenating a series of elements in an auxiliary char array
Well, you're not. you're simply overwriting the array every time in every iteration. What you need to do is
Collect the return value of sprintf() every time.
next iteration, advance the pointer to buffer by that many locations to concatinate the new input after the previous one.
Note / Suggestion:
do not cast the return value of malloc() and family in C.
sizeof(char) is guranteed to be 1 in c standard. You don't need to use that, simply drop that part.
You can't do that, and you don't really need to, this would work
size_t nonConstanSizeInBytes = 14;
char *name = malloc(nonConstanSizeInBytes);
snprintf(name, 13, "id_%i", i);

Running execvp from 2D array parameter

I'm attempting to run execvp using the data from a char[][] type (aka an array of strings). Now I know that execvp() takes a pointer to a string as its first parameter and then a pointer to an array of strings as its second - in fact I have even used it successfully before as such - however I cannot seem to get the correct combination of pointers & strings to get it to work out below - whatever I try is deemed incompatible!
Any help very grateful :) - I've removed my headers to compact down the code a bit!
struct userinput {
char anyargs[30][30]; //The tokenised command
};
int main() {
struct userinput input = { { { 0 } } }; //I believe is valid to set input to 0's
struct userinput *inPtr = &input; //Pointer to input (direct access will be unavailable)
strcpy(inPtr->anyargs[0], "ls"); //Hard code anyargs to arbitary values
strcpy(inPtr->anyargs[1], "-lh");
char (*arrPointer)[30]; //Pointer to an array of char *
arrPointer = &(inPtr->anyargs[0]);
printf("arrPointer[0]: %s, arrPointer[1]: %s\n", arrPointer[0],
arrPointer[1]);
printf("At exec case; ");
execvp( arrPointer[0], arrPointer);
perror("Command not recognised"); //Prints string then error message from errno
return 0;
}
There is no such thing as char[][] in C. execvp requires an array of pointers to const char. This can be written as either char * const * or char * const [].
You however have an array of 30-characters-long arrays, not an array of pointers. The two types are not compatible, not interchangeable, and not convertible one to another in either direction.
In this line
char (*arrPointer)[30]; //Pointer to an array of char *
you attempt to declare a pointer to an array of char*, incorrectly. What you have declared instead is a pointer to char[30], which is very different from what execvp expects.
The next line
arrPointer = &(inPtr->anyargs[0]);
purports to initialize a pointer to an array of char* with a pointer to char[30], which cannot possibly be correct even if you declare a pointer to an array of char*, because the right hand side of the assignment is not a pointer to an array of char*, it's a pointer to char[30] and no sequence of casts, indices, addresses and dereferences will turn one to the other.
An array of 30 pointers to char is declared like this:
char* arguments[30];
A dynamically-sized array of pointers to char is made like this:
char** arguments = calloc (nargs, sizeof(char*));
You need to use one of those if you want to call execvp.
In either case each pointer in the array of pointers must be initialized to point to an individual NUL-terminated character array (possibly to elements of your char[30][30] array) and the last pointer (one after all the argumenrs we want to pass) must be set to NULL. (I wonder how you expected to find a NULL in a char[30][30]).
The execvp() expects as second argument a char *const argv[]. This means an array of pointers to char. This is different from a char[30][30] which is represented in memory as 30x30 contiguous chars (so no pointer).
To solve this, define your structure
struct userinput {
char *anyargs[30]; //space for 30 char* pointers
};
You could as well define anyargs as char** and initalize if dynamically with (char**)calloc(number_of_args+1,sizeof(char*))
Later, assign directly the pointers:
inPtr->anyargs[0] = "ls"; //Hard code (or use strdup() )
inPtr->anyargs[1] = "-lh";
inPtr->anyargs[2] = NULL; // end of the argument list !!!
char **arrPointer; //Pointer to an array of char *
arrPointer = inPtr->anyargs;
Edit: Caution: "The array of pointers must be terminated by a NULL pointer.".

Am I passing a copy of my char array, or a pointer?

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.

How can I use pointers to display strings in an array?

I am practicing using pointers.
I have a pointer to an array of 3 strings: "dog", "cat", "rat".
I can print the contents using a for loop and using an array.
However, I am having problems printing them using pointer arithmetic. I would like to increment the pointer to the next element in the array. However, all it does is print the dog letters.
Code:
int main(int argc, char **argv)
{
char *str[3] = { "DOG", "CAT", "RAT"};
int i = 0;
/* display using an array element */
for(i = 0; i < 3; i++)
{
printf("str: %s\n", str[i]);
}
/* display using a pointer arthimatic */
while((*str)++)
{
printf("str: %s\n", str);
}
getchar();
return 0;
}
How can I accomplish this?
Edit:
Code:
while(str)
{
printf("str: %s\n", *(str++));
}
However, I get this error message. Doesn't the I-value have to be a variable of some sort?
error C2105: '++' needs l-value
You first have to get a pointer, and you would need a condition when to stop. A last NULL pointer can be used for that. So the code becomes
char *str[] = { "DOG", "CAT", "RAT", NULL };
char **elem_p = str;
/* display using a pointer arthimatic */
while(*elem_p) {
printf("str: %s\n", *elem_p);
elem_p++;
}
What you did was to increment the pointer stored in the array's first element. That pointer will never too soon equal to a null pointer (if at all), So, the loop will not stop until that pointers' internal value overflows or something else happens so that it equals to a null pointer. Then, you pass a pointer to the arrays first element to printf (you pass the array, but the compiler will convert it to a pointer - see below). Printf will interpret the bytes of those pointers as characters and print them, which will result in garbage printed out if it doesn't crash right away.
You want to increment at a higher level instead: Increment not one element (pointer) of the array, but the pointer to the elements itself. Now, you can't do
str++
Because str is an array. It's not a pointer, even though it can be converted to a pointer, which will then point to the first element of it. So, we create a pointer which points to str[0] initially, but increment it all again. Note that we increment it after printing, so that we print out the first element too.
Actually, i think i should explain why you can't do str++. Well, str is an array. An array is an object that occupies some fixed amount of storage. Above, the array occupies 4 times the size of a char pointer, 4 * sizeof(char*). What looks like a pointer at the first glance is a block of elements of the same type. For addressing elements, the compiler can make up a pointer out of an array in expressions, which then can be used to address elements in the array. If you do str++, the compiler will create a pointer for you. But that pointer is temporary, exists only for a short while for the sole purpose of immediately doing something with it, but it can not be changed like being incremented. That is the reason that we create a real pointer variable that we then can increment.
The problem with your code is that you're incrementing the dereferenced pointer str, which gives you a char * to the first element in the array (ie, dog).
You should be incrementing str itself, not what it points to.
However your loop termination check won't work in that case as there is no element in the array at the moment for which str == 0 holds true. In order to get that to work, you'll need to add a fourth element to the array that is 0.
str, being an array, cannot be incremented.
Right now, you're incrementing (*str), which is str[0]: the first element of that array. Since this element is a pointer, you're incrementing the pointer, making it reference the subsequent elements of the string "DOG".
What you need is a pointer that can point to (and thus walk over) str's elements. Since the elements are char*, the pointer to them would be char** - a double pointer.
(*str)++
Increments the value pointed to by s. You will need to increment the pointer itself and print the value of the pointee. Note, that str is not a l-value and you cannot increment it either.
Also, if you increment first, then you are left with only two legal strings that you can print. You invoke UB after that.
Try this:
int main(void)
{
/* we have added a sentinel to mark the end of the array */
char *str[] = { "DOG", "CAT", "RAT", 0 };
/* since str is an array, you cannot use it to loop, have a walker */
char **w = str;
/* display using a pointer arthimatic */
while(*w)
{
printf("str: %s\n", *w++);
}
return 0;
}
Look up operator precedence and binding in C.
while((*str)++)
{ printf("str: %s\n", str); }
This increments *str which is the letter 'd'. You don't want to do that. You want to increment the pointer. This code should do what you want:
while((str)++)
{ printf("str: %s\n", str); }

Resources