Related
I am new to programming and C in general and the last few weeks I try to get the concept of pointers, arrays and how they are connected.
At the moment I experiment with command line arguments in C and I read here on this platform that argv can be syntactically defined differently, however the semantic stays the same.
int main(int argc, char *argv[])
is equal to
int main(int argc, char **argv)
Okay, but why is my code behaving differently when I try to initialize an array in these ways:
char *s[] = {"hallo", "12345"};
printf("%c und %c", s[0][4], s[1][2]);
I get as output as expected: o and 3.
But when I initialize the array like
char **s = {"hallo", "12345"};
printf("%c und %c", s[0][4], s[1][2]);
I get a segmentation fault or other errors which I cannot understand like (near initialization for ‘s’)
I guess you cannot initialize an pointer to a pointer array with 2 asterisks.
Maybe someone can provide me with more information about these relation and how these 2 definitions differ from each other.
They are completely different:
char *s[]
is an array of pointers. Arrays cannot be assigned or used as values
This code will not compile:
char *a[] = {"hallo", "12345"};
char *a1[2];
char **s = (char *[]){"hallo", "12345"};
char **s1;
void foo(void)
{
a++;
}
void bar(void)
{
a1 = a;
}
char **s
is a pointer to a pointer and can be assigned or used as an lvalue.
This code will compile:
char *a[] = {"hallo", "12345"};
char *a1[2];
char **s = (char *[]){"hallo", "12345"};
char **s1;
void foo(void)
{
s++;
}
void bar(void)
{
s1 = s;
}
https://godbolt.org/z/vqxv913WY
Please explain why the below code fails at line 10.
How can I print values of p, i.e. Hello World.
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
char ar[200] = "Hello World";
strcat(ar, " !");
printf("%s\n", ar);
char **p = &ar;
printf("%s\n", *p[0]);
return 0;
}
EDIT:
Just to clarify more on what I want to achieve. I have a function that accepts char** as argument, hence why I want to convert char ar[200] into char**, but the application freezes.
You have declared a character array and assigned value to it. If you just mention the name of the array, you are actually mentioning the base address of this array. Pointers can store address, you can define a char pointer and assign the base address of your character array to it.
for example
char ar[200]="Hello World";
char *p=a; //assign the base address of a to p;
You can then print the string using the %s format specifier with the base address of your character array(string).
printf("%s",a);// print the string "Hello World". here a is the base address
printf("%s",p);//print the string "Hello World". here p is the base address
This will work
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
char ar[200] = "Hello World";
strcat(ar, " !");
printf("%s\n", ar);
char *p = ar;
printf("%s\n", p);
return 0;
}
Edited for your convenience
char *q=a;
char **P=&q;
printf("%s",*p);
if this is what you want
Arrays already "decay" to the address of their first element in most contexts. This behavior differs from other variables, even from the other aggregates, structs. The reasons are rooted deep in the history of C (namely, no joke, in B1).
One of the contexts though where an array does not decay is when its address is taken: the address operator yields a pointer to array, not a pointer to pointer to char:
char arr[6] = "abcde"; // 5 letters plus '\0'
char (*parr)[6] = &arr; // pointer to array of 6 elements
Curiously enough, dereferencing this pointer twice yields the first char in the array, as if parr were a pointer to pointer to char. The reason is that *parr simply is the original array, and dereferencing that is quite normally its first element. This is likely the source of much confusion:
printf("%c", **parr);
Traditionally (i.e. in the 1970s) pointer was pointer, and their types didn't matter much. (There is a big difference though: What value does parr+1 have?) But it's nice to compile without warnings and to understand what one is doing. So how do we get your requirement, a pointer to a pointer to a char, for printing arr? The answer is, just use a pointer to a pointer!
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
char ar[200] = "Hello World";
strcat(ar, " !");
printf("%s\n", ar);
char *p = ar; // pointer to char:
// array "decays" to address of first char
char **pp = &p; // pp is a pointer to a pointer to char.
// The point is, it points to the "pointer to char" p.
printf("%s\n", *pp); // *pp is what pp points to:
// p, a simple pointer to char. That's what "%s"
// expects. p is initialized and points to
// the first char in ar.
return 0;
}
1Cf. the interesting paper by Dennis Ritchie, "The Development of the C Language", p.7
gcc main.c -o main
main.c: In function ‘main’:
main.c:9:16: warning: initialization from incompatible pointer type [enabled by default]
char **p = &ar;
^
main.c:10:5: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat=]
printf("%s\n", *p[0]);
^
expects argument of type ‘char *’, but argument 2 has type ‘int’ seems very clear :P
You can print your string by changing you program by
int main(int argc, char **argv) {
char ar[200] = "Hello World";
strcat(ar, " !");
char *p = ar;
printf("%s\n", p);
return 0;
}
In c ar is equal to &ar, because arrays are basically memory addresses to their first elements.
So you only need a pointer to a char not a pointer to a pointer to a char.
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
char ar[200] = "Hello World";
strcat(ar, " !");
printf("%s\n", ar);
char *p = &ar;
printf("%s\n", p);
return 0;
}
This question is an interesting example of the difference between arrays and pointers. It is common to think of arrays as pointers, but that isn't quite true. The fact that char ar[200] doesn't make ar into a char pointer is why you can't use a char** to point to ar. On the other hand, arrays decay to pointers in function calls, so the following code does work (though it is terribly unmotivated):
#include <stdio.h>
#include <string.h>
void printString(char ar[]){
char **p = &ar; // as in original code
printf("%s\n",*p);
}
int main(int argc, char **argv) {
char ar[200] = "Hello World";
strcat(ar, " !");
printf("%s\n", ar);
printString(ar);
return 0;
}
How can I print values of p, i.e. Hello World.
You do not need a pointer to pointer for this purpose. A simple pointer will do.
char *p;
p = ar;
printf ("%s\n",p);
Example of when to use **pointers
#include <stdio.h>
void foo(char **bar)
{
*bar = "some text";
}
int main(int argc, char *argv[])
{
char *temp;
foo (&temp);
printf ("temp=%s\n", temp);
return 0;
}
I explain in the comments of your code.
EDIT: Do not copy following code
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
char ar[200] = "Hello World";// technically an array is a pointer, so char *ar and char[] are the same
strcat(ar, " !");// pass the pointer to treat strings
printf("%s\n", ar);// pass the pointer to treat strings
char **p;// here is the problem you were giving the address of your pointer to a pointer of pointer in inline wich makes more confusing
(*p) = &ar; // here is the correct assingment
printf("%s\n", *p);// so here you should pass *p
return 0;
}
EDIT: This solution is far safer in real life situations
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
char ar[200] = "Hello World";// technically an array is a pointer, so char *ar and char[] are the same
strcat(ar, " !");// pass the pointer to treat strings
printf("%s\n", ar);// pass the pointer to treat strings
// This solution initializing is better because it avoids editing other variables
char *p = ar;
char **pp = &p;
printf("%s\n", *pp);// so here you should pass *pp
return 0;
}
#include<stdio.h>
void display(int n, char *str[])
{
int i=0;
while(i<n) printf("%s ",str[i++]);
}
int main()
{
display(1,"Hello");return 0;
}
when I run this above code I get warnings as
arr.c: In function 'main':
arr.c:11:12: warning: passing argument 2 of 'display' from incompatible pointer
type
display(1,"hello");
^
arr.c:3:6: note: expected 'char **' but argument is of type 'char *'
void display(int n,char *str[])
But then how is it different from
int main(int argc, char * argv[])
And what is the difference between
char **argv and char *argv[]
I am strictly not asking about something like char *argv[100]
As a parameter of a function both char **ptr and char *ptr[] are equivalent, otherwise they are different. Former is a pointer to pointer to a char while latter is an array of pointers to char.
When a string literal is passed to a function, then pointer to its first character (char * type) is passed. You need to change the function's second parameter to char *str.
void display(int n, char *str)
{
// Function body
}
Well, char **ptr is a double pointer (pointer to a pointer) of char while char *ptr[] is an open array of pointer to char.
According to cdel, char **p; gives the result of "declare p as pointer to pointer to char" while char *p[]; gives "declare p as array of pointer to char."
Character strings are always arrays, and arrays are generally always pointers, so the two are generally equivalent which means that **ptr = *ptr[]. What you end up with is an array of an array of chars, or an array of strings. Take main() for instance:
int main(int argc, char **argv)
{
int i;
for (i = 0; i < argc; i++)
{
if (strcmp(argv[i], "some string") == 0) do something;
}
Do some stuff;
return(0);
}
Another way to declare it is
int main(int argc, char *argv[])
Programmatically, it's easier to understand *ptr[] than **ptr.
Can someone please explain to me what's wrong with the following, and more importantly why?
int main( int argc, char *argv[] )
{
char array[] = "array";
char **test;
test = &array;
*test[0] = 'Z';
printf( "%s\n", array );
return 0;
}
EDIT
My example above was based on a function like this that was crashing:
void apple( char **pp )
{
*pp = malloc( 123 );
*pp[0] = 'a'; // technically this is correct but in bad form
*pp[1] = 'b'; // incorrect but no crash
*pp[2] = '\0'; // incorrect and crash
}
As pointed out to me by Vaughn Cato although *pp[0] = 'a'; does not crash it is in bad form. The correct form is the parenthesis
void apple( char **pp )
{
*pp = malloc( 123 );
(*pp)[0] = 'a'; // correct
(*pp)[1] = 'b'; // correct
(*pp)[2] = '\0'; // correct
}
Also as another poster MK pointed out the FAQ covers the difference between arrays and pointers:
http://www.lysator.liu.se/c/c-faq/c-2.html
test = &array
is wrong because test is of type char** and &array is a char(*)[6] and is a different type from char**
An array isn't the same type as char* although C will implicitly convert between an array type and a char* in some contexts, but this isn't one of them. Basically the expectation that char* is the same as the type of an array (e.g: char[6]) is wrong and therefore the expectation that taking the address of an array will result in a char** is also wrong.
This would be the way to do what you seem to be trying to do:
int main( int argc, char *argv[] )
{
char array[] = "array";
char (*test)[6];
test = &array;
(*test)[0] = 'Z';
printf( "%s\n", array );
return 0;
}
test is a pointer to an array, and an array is different from a pointer, even though C makes it easy to use one like the other in my cases.
If you wanted to avoid having to specify a specific sized array, you could use a different approach:
int main( int argc, char *argv[] )
{
char array[] = "array";
char *test;
test = array; // same as test = &array[0];
test[0] = 'Z';
printf( "%s\n", array );
return 0;
}
char **test; is a pointer to a pointer, but if you're going to take the address of an entire array then it needs to be a pointer to entire array i.e char (*test)[6];
I've read several discussions of passing char * in C.
stackoverflow: passing-an-array-of-strings-as-parameter-to-a-function-in-c
stackoverflow: how-does-an-array-of-pointers-to-pointers-work
stackoverflow: whats-your-favorite-programmer-ignorance-pet-peeve
drexel.edu: Character arrays
Many of them include discussions of arrays, but I want to stay away from that.
I'm writing a sample program to teach myself about the passing of char * and char ** in C. This is an exercise in passing char *, without using (pointers to) arrays. Also no concerns for execution efficiency. :-)
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void get_args_works(int, char **, char **);
void get_args_broken(int, char **, char *);
char *get_string(int, char **);
int main(int argc, char **argv)
{
char *string_works;
char *string_broken;
get_args_works(argc, argv, &string_works);
get_args_broken(argc, argv, string_broken);
printf("in main string_works (%p) = %s\n",string_works,string_works);
free(string_works);
printf("in main string_broken (%p) = %s\n",string_broken,string_broken);
free(string_broken);
}
void get_args_works(int argc, char **argv, char **string)
{
*string = get_string(argc, argv);
printf("in get_args_works %p string %s\n",*string,*string);
}
void get_args_broken(int argc, char **argv, char *string)
{
string = get_string(argc, argv);
printf("in get_args_broken %p string %s\n",string,string);
}
char * get_string(int argc, char **argv)
{
int i;
char *string;
string = malloc(40);
// placeholder in case -s switch not found below
strcpy(string,"-s switch not found below");
for(i = 0; i < argc; i++)
{
if(argv[i][0] == '-')
{
switch(argv[i][1])
{
case 's':
// release above malloc(40) for "-s switch not found below"
free(string);
// make room for storing variable
string = malloc(strlen(argv[++i]) + 1);
// the argv just after -s
strcpy (string,argv[i]);
break;
}
}
}
return string;
}
You can also view the same code on github
The above code is somewhat self documenting. main() declares two char * variables, and passes them as parameters to their respective get_args() functions.
Each get_args() function calls char * get_string(int, char **), using the exact same call (but different way to collect the return value).
get_string() works fine; it does a malloc() and returns the pointer back to the calling function. That code works, and each get_args() function receives the return value as I expect.
But then, when the get_args() functions return to main(), why does the dereferenced pointer value get back to main (from get_args_works(), but not the pointer's value (from get_args_broken())?
(i.e. I can see that if I dereference the pointer (&string_works) when sending as a parameter, it works. But why? Isn't char * string_broken already a pointer? Why does it need the "extra" dereference when sending as a parameter?)
I'm hoping for a winning answer that explains how you (yes, you) conceptualize sending char * as a parameter vs receiving it as the function's return value.
int get_args_broken(int argc, char **argv, char *string)
{
string = get_string(argc, argv);
printf("in get_args_broken %p string %s\n",string,string);
}
You're only modifying the string local (automatic) variable. That's not visible to the caller in any way. Note that this means you're freeing a wild pointer in main.
It's wrong for the same reason:
int get_sum(int sum, int a, int b)
{
sum = a + b;
}
is; the parameter is copied by value. Also, you're not returning an int (as you declared you would).
int get_args_works(int argc, char **argv, char **string)
{
*string = get_string(argc, argv);
printf("in get_args_works %p string %s\n",*string,*string);
}
is correct (except the missing return). You're not modifying string, which would be pointless. You're modifying the object at the location in string, which in this case is a char *.
EDIT: You would need to triple * the argv if there was a function calling main, and you wanted to set that function's variable to a different char **. E.G.
void trip_main(int *argc, char ***argv)
{
*argc = 10;
*argv = malloc(*argc * sizeof(char *));
}
void caller()
{
char **argv;
int argc;
trip_main(&argc, &argv);
}
One of the needs to use Pointer to a pointer (here get_args_works()) is to modify (or return) more than on variable from a function, as in C it's not possible to return more than one variable.
get_args_works() works 'coz, you are passing pointer to a pointer & a reference to it is there in your main().
But in get_args_broken() you are passing just a pointer. Nothing wrong here, now you do malloc() & return back the memory allocated string to get_args_broken(), still nothing wrong here. But now, this mem allocated string is local & main() does not have a reference to this var. So when you dereference char *string_broken; in main() it might cause undefined behavior.
Hope this's clear.