I'm trying to learn C now, I'm coming from Java and there is some stuff that is new to me.
I want to print a string and send an int and a string(char array) to another method. But I keep getting some errors that I don't know how to fix.
Would really appreciate if someone could take their time and explain to me what's wrong in my code. I'm quite disoriented at the moment with these pointers. When to use %s and %c when printing etc...
Code:
#include <stdio.h>
void main()
{
int k = 10;
char string;
char *sptr;
string = "hello!";
int *ptr;
sptr = &string;
ptr = &k;
printf("%s \n", &sptr);
printf("Sending pointer.\n");
sendptr(ptr, sptr);
}
And the errors.
test.c: In function ‘main’:
test.c:8:9: warning: assignment makes integer from pointer without a cast
test.c:15:2: warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘char **’
tezt.c: In function ‘sendptr’:
tezt.c:8:8: error: incompatible types when assigning to type ‘char[6]’ from type ‘char’
Thanks for your time! :)
First functions solved.
Second function i get this..
tezt.c: In function ‘sendptr’:
tezt.c:5:2: error: invalid initializer
#include <stdio.h>
void sendptr(int *test, char *fname)
{
char fnamn[] = &fname;
int pt;
pt = *test;
printf("%p \n", test);
printf("%d \n", pt);
printf("%s \n", fnamn);
}
char string;
string = "hello!";
First problem: you're declaring string as a single char, not as an array. Also, you can only initialize the array to a string literal in a single statement.
char string[] = "hello!";
Second problem: sptr is a pointer-to-char, so it has to point to the first element of your string. Either of these will do:
char *sptr = string;
char *sptr = &string[0];
Then, when printing the string, just pass sptr directly.
printf("%s \n", sptr);
EDIT for your next question.
char fnamn[] = &fname;
You're trying to assign a char** (pointer to pointer to char) to an array. That just won't work. If you want to copy the string pointed to by fname into fnamn then you need to use a function such as strncpy.
char fnamn[MAX_STRING_SIZE];
strncpy(fnamn, fname, MAX_STRING_SIZE);
Having said that, if you just want to print the string, then print fname directly without copying it into your array first.
Here's a corrected version of the program with some annotation:
#include <stdio.h>
int main(void) // int and (void) for standard mains.
{
int k = 10;
char *string; // a C string is a char array, you need a pointer to point to it
char *sptr;
int *ptr;
string = "hello!";
sptr = string;
ptr = &k;
printf("%s \n", sptr); // no &. The %s format expects a char*.
printf("Sending pointer.\n");
// sendptr(ptr, sptr); // don't know what this function is, ignoring
return 0;
}
In C language, the & operator means you want to use the address of the variable (ie & = "the address of the variable").
int an_integer=2; // an_integer is a memory part where you want to store 2 ;)
printf("%d", &an_integer); // here you will print the address of the memory part where an_integer is stored (not 2, more something like 2510849).
The * operator in a declaration of variable means that you want to have a pointer to a memory part, when using it in the code, it means the "the value contained at the address of"
int an_integer=2;
int *ptr_integer; // you declare a pointer to an integer
ptr_integer = &an_integer; // here you set the pointer ptr_integer to the address of an_integer
printf("%d", *ptr_integer); // here you print the value contained at the memory address stored in the ptr_integer
The [] operator means you want to store an array of something. In C, an array can be seen as a pointer to a memory space.
int an_integer[2]; // you declare an array of 2 integers
int *ptr_integer; // you declare a pointer to an integer
ptr_integer = (int *)an_integer; // here you set the value of the pointer to the address of the array, you have to cast it into an (int *) to avoid compilation warnings.
For a start, I would suggest changing:
char string;
to:
char *string;
It's pretty clear that you want the string variable to be a string rather than a single character.
In addition, you probably want to change the two lines:
sptr = &string;
printf("%s \n", &sptr);
to:
sptr = string;
printf("%s \n", sptr);
but you could equally well just pass string itself to printf.
As for the sendptr(ptr, sptr);, we can't help that much without knowing more details about it.
To fix your second function (from your edit), change:
char fnamn[] = &fname;
to:
char *fnamn = fname;
or just use fname directly. You don't have to make a copy of the pointer and the former is for things like:
char fnamn[] = "I am a string literal";
I thought it might be helpful to adding something about the difference between a char array and a pointer to a string.
In function1 below, the local variable stringPtr is a pointer to memory which contains the string "hello!". The memory containing this string will be located in a read-only section of the program. The compiler decides where to place the string "hello!" and ensures that your local variable is initialised with this memory address.
You can modify the pointer stringPtr and change it to point somewhere else. But you cannot modify the memory it points at.
Also, it is perfectly valid to use the array access notation stringPtr[2] even though it is a pointer.
In function2 the compiler will set aside 9 bytes of space on the stack for the local variable stringArray and it will ensure that this array is initialised with the string "Goodbye!". As this memory is on the stack you can modify the contents of the array.
#include <stdio.h>
void function1(void)
{
char *stringPtr = "hello!";
printf("The first char is %c\n", stringPtr[0]);
printf("The next char is %c\n", *(stringPtr+1));
// This would cause a segmentation fault, stringPtr points to read-only memory
// stringPtr[0] = 'H';
}
void function2(void)
{
char stringArray[] = "Goodbye!";
printf("The first char is %c\n", stringArray[0]);
}
int main(void)
{
function1();
function2();
return 0;
}
First of all, the return type for main should be int, not void. void main() is only well-defined if your compiler documentation explicitly lists it as a legal signature. Otherwise you invoke undefined behavior. Use int main(void) instead.
Secondly, it's time for a quick crash course on strings, arrays, and pointers.
Unlike Java, C doesn't have a dedicated string datatype; rather, strings are represented as sequences of char values terminated by a 0. They are stored as arrays of char. The string literal "hello" is stored as a 6-element array of char (const char in C++). This array has static extent, meaning it is allocated at program startup and held until the program terminates. Attempting to modify the contents of a string literal invokes undefined behavior; it's best to act as though they're unwritable.
When an array expression appears in most contexts, the type of the expression is converted from "N-element array of T" to "pointer to T", and the value of the expression is the address of the first element of the array. That's one of the reasons the string = "hello"; statement doesn't work; in that context, the type of the expression "hello" is converted from "6-element array of char" to "pointer to char", which is incompatible with the target type (which, being char, isn't the correct type anyway). The only exceptions to this rule are when the array expression is an operand of either the sizeof or unary & operators, or if it is a string literal being used to initialize another array in a declaration.
For example, the declaration
char foo[] = "hello";
allocates foo as a 6-element array of char and copies the contents of the string literal to it, whereas
char *bar = "hello";
allocates bar as a pointer to char and copies the address of the string literal to it.
If you want to copy the contents of one array to another, you need to use a library function like strcpy or memcpy. For strings, you'd use strcpy like so:
char string[MAX_LENGTH];
strcpy(string, "hello");
You'll need to make sure that the target is large enough to store the contents of the source string, along with the terminating 0. Otherwise you'll get a buffer overflow. Arrays in C don't know how big they are, and running past the end of an array will not raise an exception like it does in Java.
If you want to guard against the possibility of a buffer overflow, you'd use strncpy, which takes a count as an additional parameter, so that no more than N characters are copied:
strncpy(string, "hello", MAX_LEN - 1);
The problem is that strncpy won't append the 0 terminator to the target if the source is longer than the destination; you'll have to do that yourself.
If you want to print the contents of a string, you'd use the %s conversion specifier and pass an expression that evaluates to the address of the first element of the string, like so:
char string[10] = "hello";
char *p = string;
printf("%s\n", "hello"); // "hello" is an array expression that decays to a pointer
printf("%s\n", string); // string is an array expression that decays to a pointer
printf("%s\n", p); // p is a pointer to the beginning of the string
Again, both "hello" and string have their types converted from "N-element array of char" to "pointer to char"; all printf sees is a pointer value.
Here's a handy table showing the types of various expressions involving arrays:
Declaration: T a[M];
Expression Type Decays to
---------- ---- ---------
a T [M] T *
&a T (*)[M]
*a T
a[i] T
&a[i] T *
Declaration: T a[M][N];
Expression Type Decays to
---------- ---- ---------
a T [M][N] T (*)[N]
&a T (*)[M][N]
*a T [N] T *
a[i] T [N] T *
&a[i] T (*)[N]
*a[i] T
a[i][j] T
&a[i][j] T *
Remember that the unary & operator will yield the address of its operand (provided the operand is an lvalue). That's why your char fnamn[] = &fname; declaration threw up the "invalid initializer" error; you're trying to initialize the contents of an array of char with a pointer value.
The unary * operator will yield the value of whatever its operand points to. If the operand isn't pointing anywhere meaningful (it's either NULL or doesn't correspond to a valid address), the behavior is undefined. If you're lucky, you'll get a segfault outright. If you're not lucky, you'll get weird runtime behavior.
Note that the expressions a and &a yield the same value (the address of the first element in the array), but their types are different. The first yields a simple pointer to T, where the second yields a pointer to an array of T. This matters when you're doing pointer arithmetic. For example, assume the following code:
int a[5] = {0,1,2,3,4};
int *p = a;
int (*pa)[5] = &a;
printf("p = %p, pa = %p\n", (void *) p, (void *) pa);
p++;
pa++;
printf("p = %p, pa = %p\n", (void *) p, (void *) pa);
For the first printf, the two pointer values are identical. Then we advance both pointers. p will be advanced by sizeof int bytes (i.e., it will point to the second element of the array). pa, OTOH, will be advanced by sizeof int [5] bytes, so that it will point to the first byte past the end of the array.
#include <stdio.h>
void main()
{
int k = 10;
char string;
char *sptr;
sptr = "hello!";
int *ptr;
ptr = &k;
printf("%s \n", sptr);
printf("Sending pointer.\n");
sendptr(ptr, sptr);
}
Related
Please take a look at the following code. It tries to pass an array as a char** to a function:
#include <stdio.h>
#include <stdlib.h>
static void printchar(char **x)
{
printf("Test: %c\n", (*x)[0]);
}
int main(int argc, char *argv[])
{
char test[256];
char *test2 = malloc(256);
test[0] = 'B';
test2[0] = 'A';
printchar(&test2); // works
printchar((char **) &test); // crashes because *x in printchar() has an invalid pointer
free(test2);
return 0;
}
The fact that I can only get it to compile by explicitly casting &test2 to char** already hints that this code is wrong.
Still, I'm wondering what exactly is wrong about it. I can pass a pointer to a pointer to a dynamically allocated array but I can't pass a pointer to a pointer for an array on the stack. Of course, I can easily work-around the problem by first assigning the array to a temporary variable, like so:
char test[256];
char *tmp = test;
test[0] = 'B';
printchar(&tmp);
Still, can someone explain to me why it doesn't work to cast char[256] to char** directly?
test is an array, not a pointer, and &test is a pointer to the array. It is not a pointer to a pointer.
You may have been told that an array is a pointer, but this is incorrect. The name of an array is a name of the entire object—all the elements. It is not a pointer to the first element. In most expressions, an array is automatically converted to a pointer to its first element. That is a convenience that is often useful. But there are three exceptions to this rule:
The array is the operand of sizeof.
The array is the operand of &.
The array is a string literal used to initialize an array.
In &test, the array is the operand of &, so the automatic conversion does not occur. The result of &test is a pointer to an array of 256 char, which has type char (*)[256], not char **.
To get a pointer to a pointer to char from test, you would first need to make a pointer to char. For example:
char *p = test; // Automatic conversion of test to &test[0] occurs.
printchar(&p); // Passes a pointer to a pointer to char.
Another way to think about this is to realize that test names the entire object—the whole array of 256 char. It does not name a pointer, so, in &test, there is no pointer whose address can be taken, so this cannot produce a char **. In order to create a char **, you must first have a char *.
Because test is not a pointer.
&test gets you a pointer to the array, of type char (*)[256], which is not compatible with char** (because an array is not a pointer). This results in undefined behavior.
The type of test2 is char *. So, the type of &test2 will be char ** which is compatible with the type of parameter x of printchar().
The type of test is char [256]. So, the type of &test will be char (*)[256] which is not compatible with the type of parameter x of printchar().
Let me show you the difference in terms of addresses of test and test2.
#include <stdio.h>
#include <stdlib.h>
static void printchar(char **x)
{
printf("x = %p\n", (void*)x);
printf("*x = %p\n", (void*)(*x));
printf("Test: %c\n", (*x)[0]);
}
int main(int argc, char *argv[])
{
char test[256];
char *test2 = malloc(256);
test[0] = 'B';
test2[0] = 'A';
printf ("test2 : %p\n", (void*)test2);
printf ("&test2 : %p\n", (void*)&test2);
printf ("&test2[0] : %p\n", (void*)&test2[0]);
printchar(&test2); // works
printf ("\n");
printf ("test : %p\n", (void*)test);
printf ("&test : %p\n", (void*)&test);
printf ("&test[0] : %p\n", (void*)&test[0]);
// Commenting below statement
//printchar((char **) &test); // crashes because *x in printchar() has an invalid pointer
free(test2);
return 0;
}
Output:
$ ./a.out
test2 : 0x7fe974c02970
&test2 : 0x7ffee82eb9e8
&test2[0] : 0x7fe974c02970
x = 0x7ffee82eb9e8
*x = 0x7fe974c02970
Test: A
test : 0x7ffee82eba00
&test : 0x7ffee82eba00
&test[0] : 0x7ffee82eba00
Point to note here:
The output (memory address) of test2 and &test2[0] is numerically same and their type is also same which is char *.
But the test2 and &test2 are different addresses and their type is also different.
The type of test2 is char *.
The type of &test2 is char **.
x = &test2
*x = test2
(*x)[0] = test2[0]
The output (memory address) of test, &test and &test[0] is numerically same but their type is different.
The type of test is char [256].
The type of &test is char (*) [256].
The type of &test[0] is char *.
As the output shows &test is same as &test[0].
x = &test[0]
*x = test[0] //first element of test array which is 'B'
(*x)[0] = ('B')[0] // Not a valid statement
Hence you are getting segmentation fault.
You cannot access a pointer to a pointer because &test is not a pointer—it's an array.
If you take the address of an array, cast the array and the address of the array to (void *), and compare them, they will (barring possible pointer pedantry) be equivalent.
What you're really doing is similar to this (again, barring strict aliasing):
putchar(**(char **)test);
which is quite obviously wrong.
Your code expects the argument x of printchar to point to memory that contains a (char *).
In the first call, it points to the storage used for test2 and is thus indeed a value that points to a (char *), the latter pointing to the allocated memory.
In the second call, however, there is no place where any such (char *) value might be stored and so it is impossible to point to such memory. The cast to (char **) you added would have removed a compilation error (about converting (char *) to (char **)) but it would not make storage appear out of thin air to contain a (char *) initialized to point to the first characters of test. Pointer casting in C does not change the actual value of the pointer.
In order to get what you want, you have to do it explicitly:
char *tempptr = &temp;
printchar(&tempptr);
I assume your example is a distillation of a much larger piece of code; as an example, perhaps you want printchar to increment the (char *) value that the passed x value points to so that on the next call the next character is printed. If that isn't the case, why don't you just pass a (char *) pointing to the character to be printed, or even just pass the character itself?
Apparently, taking the address of test is the same as taking the address of test[0]:
#include <stdio.h>
#include <stdlib.h>
static void printchar(char **x)
{
printf("[printchar] Address of pointer to pointer: %p\n", (void *)x);
printf("[printchar] Address of pointer: %p\n", (void *)*x);
printf("Test: %c\n", **x);
}
int main(int argc, char *argv[])
{
char test[256];
char *test2 = malloc(256);
printf("[main] Address of test: %p\n", (void *)test);
printf("[main] Address of the address of test: %p\n", (void *)&test);
printf("[main] Address of test2: %p\n", (void *)test2);
printf("[main] Address of the address of test2: %p\n", (void *)&test2);
test[0] = 'B';
test2[0] = 'A';
printchar(&test2); // works
printchar(&test); // crashes because *x in printchar() has an invalid pointer
free(test2);
return 0;
}
Compile that and run:
forcebru$ clang test.c -Wall && ./a.out
test.c:25:15: warning: incompatible pointer types passing 'char (*)[256]' to
parameter of type 'char **' [-Wincompatible-pointer-types]
printchar(&test); // crashes because *x in printchar() has an inva...
^~~~~
test.c:4:30: note: passing argument to parameter 'x' here
static void printchar(char **x)
^
1 warning generated.
[main] Address of test: 0x7ffeeed039c0
[main] Address of the address of test: 0x7ffeeed039c0 [THIS IS A PROBLEM]
[main] Address of test2: 0x7fbe20c02aa0
[main] Address of the address of test2: 0x7ffeeed039a8
[printchar] Address of pointer to pointer: 0x7ffeeed039a8
[printchar] Address of pointer: 0x7fbe20c02aa0
Test: A
[printchar] Address of pointer to pointer: 0x7ffeeed039c0
[printchar] Address of pointer: 0x42 [THIS IS THE ASCII CODE OF 'B' in test[0] = 'B';]
Segmentation fault: 11
So the ultimate cause of the segmentation fault is that this program will try to dereference the absolute address 0x42 (also known as 'B'), which your program doesn't have permission to read.
Although with a different compiler/machine the addresses will be different: Try it online!, but you'll still get this, for some reason:
[main] Address of test: 0x7ffd4891b080
[main] Address of the address of test: 0x7ffd4891b080 [SAME ADDRESS!]
But the address that causes the segmentation fault may very well be different:
[printchar] Address of pointer to pointer: 0x7ffd4891b080
[printchar] Address of pointer: 0x9c000000942 [WAS 0x42 IN MY CASE]
The representation of char [256] is implementation dependent. It is must not be the same as char *.
Casting &test of type char (*)[256] to char ** yields undefined behavior.
With some compilers, it may do what you expect, an on others not.
EDIT:
After testing with gcc 9.2.1, it appears that printchar((char**)&test) passes in fact test as value cast to char**. It is as if the instruction was printchar((char**)test). In the printchar function, x is a pointer to the first char of the array test, not a double pointer to the first character. A double de-reference x result in a segmentation fault because the 8 first bytes of the array do not correspond to a valid address.
I get the exact same behavior and result when compiling the program with clang 9.0.0-2.
This may be considered as a compiler bug, or the result of an undefined behavior whose result might be compiler specific.
Another unexpected behavior is that the code
void printchar2(char (*x)[256]) {
printf("px: %p\n", *x);
printf("x: %p\n", x);
printf("c: %c\n", **x);
}
The output is
px: 0x7ffd92627370
x: 0x7ffd92627370
c: A
The weird behavior is that x and *x have the same value.
This is a compiler thing. I doubt that this is defined by the language.
I'm new to the C language.
While studying char arrays, I have a question.
I know a pointer to an array is the address of first element of the array. Below code is perfect:
char* c1 = "test"
printf("%s\n",c1); =>[output] "test"
But I thought that c1 is the address of the letter "t," such as "00x1928." So, *c1 is a backreference to c1:
char* c1 = "test"
printf("%s\n",*c1) => error!
Why is this code an error?
*c1 dereferences the pointer c1, which is declared to point to char. Therefore, *c1 is a single char, which needs to be printed using %c instead of %s. To print multiple characters, you need %s, for which you need a pointer such as c1, but not an individual char such as *c1.
String literals like "test" are constant; you can't overwrite them. To prevent accidental overwriting, always declare pointers to string literals as const:
const char* c1 = "test"
printf("%s\n",c1);
If you enable compiler warnings (always a good idea, especially when learning), you should get a warning for your original code.
I know a pointer to an array is the address of first element of the
array. Below code is perfect:
Yes a pointer to an array contains the address of first element of the array. However you are determining its type incorrectly.
Let's consider the following declaration of an array
char s[] = "test";
then a pointer to the array is defined the following way
char ( *p )[5] = &s;
Now indeed there is declared a pointer to an array of the type char[5] because the array s has five elements (including the terminating zero of the string literal with which the array was initialized).
So dereferencing the pointer you will get lvalue of the array.
Consider the following demonstrative program
#include <stdio.h>
int main(void)
{
char s[] = "test";
char ( *p )[5] = &s;
printf( "The size of the pointed array is %zu\n", sizeof( *p ) );
return 0;
}
The program output is
The size of the pointed array is 5
What you mean is that arrays with rare exceptions are converted to pointers to their first elements.
For example
#include <stdio.h>
int main(void)
{
char s[] = "test";
printf( "The size of the pointed first alement of the array is %zu\n"
"and the pointed element is '%c'\n", sizeof( *s ), *s );
return 0;
}
The program output is
The size of the pointed first alement of the array is 1
and the pointed element is 't'
That is in this expression *s the array designator is implicitly converted to pointer to its first element. Dereferencing the pointer you get the first element of the array.
To create a pointer to the first element of an array you can write for example
char s[] = "test";
char *p = s;
Again the array s used as an initializer of the pointer is implicitly converted to pointer to its first element. That is the expression s used as an initializer has the type char *.
I wrote the following code which works, and I can see that a pointer only takes up (I think) 4 bytes of memory; while a plain old array takes up 20 bytes. However, I am not sure entirely why it works!
When I declare my character array I had to make it a pointer, otherwise it would not compile and run. I then was able to make my character an int type pointer; but I am not sure why this works.
Why can I do this "int *ptrOfChars = arrayOfChars;" and not do this "char *ptrOfChars = arrayOfChars;" ?
Here's my code:
int main(void)
{
int i;
int arrayOfNums[] = {1,2,3,4,5};
char *arrayOfChars[] = {"hello","how","are","you","doing"};
float arrayOfFloats[] = {1.23,2.34,3.45,4.56,5.67};
int *ptrOfNums = arrayOfNums;
int *ptrOfChars = arrayOfChars;
float *ptrOfFloats = arrayOfFloats;
printf("\n\n");
printf("----- Array of Numbers -----\n");
printf("The size of this array is %d\n\n", sizeof(arrayOfNums));
for (i = 0; i < 5; i++)
{
printf("Value in the array is %d\n", *ptrOfNums);
printf("Address of pointer is %x\n", ptrOfNums++);
}
printf("\n\n");
printf("----- Array of Characters -----\n");
printf("The size of this array is %d\n\n", sizeof(*arrayOfChars));
for (i = 0; i < 5; i++)
{
printf("Value in the array is %s\n", *ptrOfChars);
printf("Address of pointer is %x\n", ptrOfChars++);
}
printf("\n\n");
printf("----- Mardi Gras -----\n");
printf("The size of this array is %d\n\n", sizeof(arrayOfFloats));
for (i = 0; i < 5; i++)
{
printf("Value in the array is %f\n", *ptrOfFloats);
printf("Address of pointer is %x\n", ptrOfFloats++);
}
system("pause");
return 0;
}
Thanks for your assistance!!
//This is an array of strings.
char *arrayOfChars[]={"hello","how","are","you","doing"};
//This is an array of characters.
char arrayOfChars[]={'h','e','l','l','o',};
Each pointer in your array char *arrayOfChars[] , points to the first character of each word.
This is all the information you need to find a word from your array.
For example to get the first word of your array you need a pointer to the first character of the string 'hello'.
Then you will find the next character and the next and so on.
h --> e --> l --> l --> o --> EndOfString.
You declares a array of pointer which is of (char *) type.
char *arrayOfChars[] = {"hello","how","are","you","doing"};
which means you have:
arrayOfChars[0] -> "hello"
arrayOfChars[1] -> "how"
arrayOfChars[2] -> "are"
arrayOfChars[3] -> "you"
arrayOfChars[4] -> "doing"
And the sizeof(arrayOfChars[0]) is 4 (4 bytes, this is the size of char * variable), the strlen(arrayOfChars[0]) is 5 (the length of the string "hello"). Hope it helps.
arrayOfChars is not a array of characters but an array of pointers to strings. Therefore, the correct definition for ptrOfChars should be:
char **ptrOfChars = arrayOfChars;
Assigning arrayOfChars to int *ptrOfChars works because in this case, the size of a pointer and of an int are both 4 bytes and therefore are identical in size. Furthermore, because C doesn't really do any strong type checking, passing the value of int as the value of a char pointer still works correctly. However, under a different memory model such as 64 bit, the size of both the pointer and of an int could be different and therefore your code wouldn't work with int *ptrOfChars.
To print the size of the array arrayOfChars, you need to use sizeof(arrayOfChars) instead of sizeof(*arrayOfChars).
Finally, the type for the values for 1.23, 2.34, etc. is double and not float. To have float, you should add a little f at the end: 1.23f, 2.34f, etc.
it is because when you will print the value of arrayOfChars it is an integer(an address of arrayOfChars[0] i.e"Hello").
and your ptrOfChars is pointing to this integer.
Why can I do this "int *ptrOfChars = arrayOfChars;" and not do this "char *ptrOfChars = arrayOfChars;" ?
Because the type of the expression arrayOfChars "decays" to type char **, not char *.
Except when it is the operand of the sizeof or unary & operators, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression is the address of the first element of the array.
So, given the declaration:
char *arrayOfChars[] = {"hello","how","are","you","doing"};
the expression arrayOfChars has type "5-element array of char *" (each of the string literals is also an array expression, and also subject to the conversion rule mentioned above). If arrayOfChars appears in an expression where it is not the operand of the sizeof or unary * operators, it will be converted to an expression of type "pointer to char *", or char **.
I would have expected the compiler to complain about initializing an int * with a char ** expression.
Finally, use the %p conversion specifier to print out pointer values:
printf( "address of arrayOfChars = %p\n", (void *) arrayOfChars );
This is one of the few places where an explicit cast to void * is needed.
I'm trying to make a function that changes a char array from the main function, that's what I'm trying to do:
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
void change(char *a);
int main()
{
char a[] = "hello";
printf("\na = %s", a);
change(a);
printf("%\na = %s", a);
getch();
}
void change(char *a)
{
a = "goodbye";
}
Several problems with this code, but first we need to take a step back and talk about how arrays are handled in C.
Except when it is the operand of the sizeof or unary & operators, or is a string literal used to initialize another array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array.
In the declaration
char a[] = "hello";
"hello" is a string literal, which has type "6-element array of char" (5 characters plus the 0 terminator). Since it is being used to initialize the array a in a declaration, the rule above doesn't apply; instead, the size of a is set to be the same as the size of the literal (6), and the contents of the string literal are copied to the array.
When you call change from main as
change(a);
the expression a has type "6-element array of char". Since it is neither a string literal nor the operand of the sizeof or unary & operators, that expression will be converted to type "pointer to char", and the value of the expression will be the address of the first element of the aray. Hence why the change function is declared as
void change(char *a);
In this context, a is simply a pointer. When you write
a = "goodbye";
the string literal "goodbye" is not being used in an initializer, and it's not the operand of the sizeof or unary & operators, so the expression is converted to type "pointer to char", and the value of the expression is the address of the first character. So what happens here is that you're copying the address of the string literal "goodbye" to a. This overwrites the value in a, but this a is not the same object in memory as the array a in main, so any changes to it are not reflected in main.
If you want to update the contents of an array, you will need to use the library functions strcpy/strncpy (for 0-terminated strings) or memcpy (for everything else), or update each element explicitly (a[0]='g'; a[1]='o'; a[2]='o';, etc).
To update the contents of a, you'd use
strcpy( a, "goodbye" );
Except...
a is only large enough to hold 5 characters plus a 0 terminator; "goodbye" is 7 characters plus the 0 terminator; it's two characters larger than what a is capable of storing. C will happliy let you perform the operation and trash the bytes immediately following a, which may lead to any number of problems (buffer overruns such as this are a classic malware exploit). You have a couple of choices at this juncture:
First, you could declare a to be large enough to handle either string:
#define MAX_LEN 10
...
char a[MAX_LEN] = "hello";
Second, you could limit the size of the string copied to a:
void change( char *a, size_t size )
{
strncpy( a, "goodbye", size - 1 );
a[size - 1] = 0;
}
Note that you will need to pass the number of elements a can store as a separate parameter when you call change; there's no way to tell from a pointer how big the array it points to is:
change( a, sizeof a / sizeof *a ); // although in this case, sizeof a would be
// sufficient.
The main problem is that you are sending a copy of the char pointer a by doing this:
void change(char *a)
{
a = "goodbye";
}
if you want to change a value in another function you should do this:
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
void change(char **a);
int main()
{
char *a = "hello";
printf("\na = %s", a);
change(&a);
printf("%\na = %s", a);
getch();
}
void change(char **a)
{
*a = "goodbye";
}
I changed the function, now it works, this way:
void change(char *a)
{
strcpy(a, "goodbye");
}
#include <stdio.h>
// this works
void print_stuff (void* buf) {
printf ("passed arg as buf*: %s\n", buf);
}
/* This works */
void print_stuff_3 (char* buf) {
printf ("passed arg as char*: %s\n", buf);
}
// this does not work
void print_stuff_2 (char** buf) {
printf ("%s\n", *buf);
}
int main () {
char s [] = "hi";
printf ("s = %s\n", s);
// these work
print_stuff (&s);
print_stuff_3 (&s);
// this results in a Segfault
print_stuff_2(&s);
return 0;
}
I am a bit confused about the way things are passed around in C. I feel like &s should be of type char**, but it behaves as if it is of type char* when passed to a function. Why does this behaviour happen?
In particular, print_stuff_2 segfaults, whereas I thought that print_stuff_3 would give an error.
EDIT: To clarify, I expected print_stuff(&s) and print_stuff_3(&s) to fail (while they succeed), while print_stuff_2(&s) fails, whereas I feel it should succeed.
You need to remember that strings are not fundamental types in C. They are arrays of characters. Therefore
char s [] = "hi";
makes s a char * (in terms of variable type), i.e. a pointer to the first character of a 3 character array (h, i and NUL).
So in order to pass a pointer to the string, you what to use your print_stuff_3, as printf()'s %s argument takes exactly that (a pointer to the string, i.e. a pointer to the first character). Call this with print_stuff_3(s).
print_stuff works because a pointer is a pointer. It will be translated to a void * pointer on calling print_stuff, then printf()'s %s will convert it back to a char *. Call this with print_stuff(s).
print_stuff_2 doesn't work because you are taking the address of where s is stored. Had you written char *s = "hi"; that would work if you used print_stuff_2(&s). You'd pass the address of the pointer, then dereference that (to get the value of the pointer, i.e. the pointer to the first character) in by using *buf. Except buf then would be a poor choice of name, as you would be passing a pointer to a pointer to characters.
The complication is as follows. As it is, you are doing &s which just returns s when you have
char s [] = "hi";
(see How come an array's address is equal to its value in C? ), but returns the address at which the pointer variable s is stored on the stack if you have:
char *s = "hi";
Taking the address of an array doesn't really make sense (so evaluates to the address of the first element). You need to use char *s = "hi"; if you want to take the address of the pointer.
In C, array names are decays to pointer to its first element when passed to a function in most cases. When passing s to the function print_stuff, s decays to pointer to h. No need to pass it with &. &s is of pointer to array (char (*)[3]) type, i.e, it is giving the address of the entire array s.
In function call
print_stuff_3 (&s);
your compiler should warn you
[Warning] passing argument 1 of 'print_stuff_3' from incompatible pointer type [enabled by default]
I feel like &s should be of type char**, but it behaves as if it is of type char* when passed to a function. Why does this behavior happen?
No. You thought wrong. &s is of type char (*)[3].
void print_stuff (void* buf) & void print_stuff_3 (char* buf) In both functions, buf is of char * taking address as argument. Which should be print_stuff (s) & print_stuff_3 (s) respectively as s is the base address of char array s. So you shouldn't pass &s which is address of s.
As the below function buf is of type char **, it will expect address of address like print_stuff_2(&s) provided your declaration is char *s = "hi",
void print_stuff_2 (char** buf) {
printf ("%s\n", *buf);
}