C/C++ question - casting a char * into a char ** - Page 1
Forum section: electronics / general

author: sleemanj
author_rank: Super Contributor
timestamp: November 29, 2014, 04:38:15 am
content: Given this simplified example, how can I (or indeed, can I) construct a cast that will make the call to fn1() work the same was as it works when I pass it through fn2() to get there? It looks right to me, but obviously it's notCode:#include <stdio.h>#include <string.h>void fn1(const char **arrayOfStrings, int arrayLength){printf(arrayOfStrings);}void fn2(const char *singleString){fn1(&singleString, 1);}int main(){char buffer;strncpy(buffer, "HELLOWORLD\n", 11);// This worksfn2((const char *)buffer);// This doesn't workfn1((const char **)&buffer,1);}

author: IanB
author_rank: Super Contributor
timestamp: November 29, 2014, 05:00:56 am
content: This works:Code:#include <stdio.h>#include <string.h>void fn1(const char **arrayOfStrings, int arrayLength){printf(arrayOfStrings);}void fn2(const char *singleString){fn1(&singleString, 1);}int main(){char string[] = {"HELLOWORLD\n"};char *buffer = string;// This worksprintf("Calling fn2:\n");fn2(buffer);// This also worksprintf("Calling fn1:\n");fn1(&buffer,1);}Can you see why?

author: IanB
author_rank: Super Contributor
timestamp: November 29, 2014, 05:02:05 am
content: By the way, it's not C++, it's C. When you write <stdio.h> rather than <cstdio>, you are writing C.

author: sleemanj
author_rank: Super Contributor
timestamp: November 29, 2014, 05:26:42 am
content: Quote from: IanB on November 29, 2014, 05:00:56 amBut it does not answer my question.

author: sleemanj
author_rank: Super Contributor
timestamp: November 29, 2014, 05:27:13 am
content: Quote from: IanB on November 29, 2014, 05:02:05 amC++ is a superset of C, by definition, everything C is C++

author: IanB
author_rank: Super Contributor
timestamp: November 29, 2014, 05:32:17 am
content: Quote from: sleemanj on November 29, 2014, 05:27:13 amA common misconception, but no, not exactly. The intersection of C and C++ is a subset of both, but neither is fully contained in the other.If you are writing code it is a good idea to decide in advance whether you are writing C or C++ and then stick to the one or the other.

author: IanB
author_rank: Super Contributor
timestamp: November 29, 2014, 05:33:17 am
content: Quote from: sleemanj on November 29, 2014, 05:26:42 amThat was the exercise for the student. I changed the code in a subtle, but significant way.Hint: it's about data types.

author: sleemanj
author_rank: Super Contributor
timestamp: November 29, 2014, 05:34:49 am
content: Quote from: IanB on November 29, 2014, 05:32:17 amMmm, offtopic, but what's a piece of C code that is NOT valid C++ then. And by valid, I mean, will not compile in modern compilers and produce the result you desire - not just "technically invalid according to Stroustrup"

author: IanB
author_rank: Super Contributor
timestamp: November 29, 2014, 05:37:23 am
content: Quote from: sleemanj on November 29, 2014, 05:34:49 amYou can Google it, but here are a few examples:http://www.cprogramming.com/tutorial/c-vs-c++.html

author: sleemanj
author_rank: Super Contributor
timestamp: November 29, 2014, 05:39:59 am
content: Quote from: IanB on November 29, 2014, 05:33:17 amYes, I realise it is about datatypes, that's why I'm casting it.In my example, the one that works goes through effectively two casts,char * => const char * (cast into the call to fn2 from main)const char * => char ** (cast into the call to fn1 from fn2)The one that doesn't is a single type cast, which to my eye, is identical to the combination of the two typecasts.char * => const char ** (cast into the calll to fn1 from main)My question is, why is the typecast in the second example not equivalent to the effective two typecasts in the first example.

author: IanB
author_rank: Super Contributor
timestamp: November 29, 2014, 05:42:32 am
content: Quote from: sleemanj on November 29, 2014, 05:26:42 amIn your original program, the data type of "buffer" is "array of char". This data type is not the same as "pointer to char". If you take the address of "buffer", you get "pointer to array of char". This is not the same as "pointer to pointer to char". You can't cast one to the other because pointers in C know the size of the thing they are pointing at (well, at least the compiler knows).In my modified program, I made "buffer" be "pointer to char". Taking the address of "buffer" now yields "pointer to pointer to char" and the cast becomes permissible.

author: sleemanj
author_rank: Super Contributor
timestamp: November 29, 2014, 05:53:35 am
content: Quote from: IanB on November 29, 2014, 05:42:32 amOk, yeah, I see I think, I always tend to think of C arrays as being equivalent to an unchangable malloc'd pointer of the appropriate size, just arrays are on stack and malloc is on heap, but, yeah, it's not really I suppose.Is it not possible to cast the character array to a usable (const char **) [of length 1] in a single step?My first example achieves it (empirically), but through the indirect step another function call.

author: IanB
author_rank: Super Contributor
timestamp: November 29, 2014, 05:57:42 am
content: Quote from: sleemanj on November 29, 2014, 05:53:35 amI can't figure out how. To be honest, the intricacies of C make my brain hurt and I find it easiest to follow the path of least resistance...

author: IanB
author_rank: Super Contributor
timestamp: November 29, 2014, 06:00:40 am
content: By the way, it is very unusual that you should need to use casts in C (or C++). Using casts is a danger sign that you are entering dragon territory.

author: Royce
author_rank: Contributor
timestamp: November 29, 2014, 06:17:05 am
content: My best stab at an explanation is that there is no pointer variable that points to the beginning of the string when you define it that way.When the compiler is laying down memory, lets say it decides thatbufferstarts at memory location 34. Whenever, you saybufferin your code, it doesn't make an indirect reference to some OTHER piece of memory thatcontainsthe number 34, it just puts 34 into the op codes at the point that you refer tobuffer.That non-existent memory address containing variable is what you are trying to reference in your code. That's why you have to explicitly create the char** so the compiler lays down a chunk of memory capable of containing the memory address of the beginning of a string.Just plain buffer, in the sense that it has value 34, is kind of a pointer literal and taking its address is analogous to int* b = &5. You can't really get the address of the number 5.I could be off base, but that is my sense of what is going on.

author: adam1213
author_rank: Regular Contributor
timestamp: November 29, 2014, 06:34:18 am
content: Reason for not being able to cast:void fn1(const char **arrayOfStrings, int arrayLength)This function takes in an array of pointers.e.g.char string1[] = "string1";const char* arrayOfStrings[] = {string1};fn1(arrayOfStrings, 1);You could also writeconst char* string1 = "string1";fn1(&string1, 1);This is somewhat more cryptic but works the same way. fn1 uses &string1 as an array with one element.What does not work is writing:char string1[] = "string1";const char **temp = &string1;The reason this does not work is that this tries to set temp, a pointer to a pointer to a pointer.Quote from: sleemanj on November 29, 2014, 05:53:35 amIf you really wanted to write it e.g. for the purpose of understanding c it is possible. Have a look at the following. Just don't use it in a real program.Code:int main(void){char string1[] = "string1";char string2;char** string2AsArrayOfPointers = (char**) &string2; // Example for demonstration purposes only. DO NOT USE THIS CODE IN A REAL PROGRAM.string2AsArrayOfPointers = string1;printf("result: %s\n", string2AsArrayOfPointers);return 0;}The way this code works is that, string2 which is an array is cast into an array of pointers. The first element is set to point to the location of string1. This enables "string2" to be used as if it was really an array of pointers (note it would be much simpler to simply use use an array of pointers)Quote from: sleemanjYour original thinking is correct. What is going on here relates to how ** works. ** means you are passing a pointer to an array of pointers (or a single pointer). If you try to pass a pointer then the value pointed to by the pointer will be used to lookup the value rather than being used as a value.

author: adam1213
author_rank: Regular Contributor
timestamp: November 29, 2014, 06:39:27 am
content: In terms of the original program: if you wanted to include the code in a a real program rather than discuss casting, a simpler way of writing it would be to have a function which prints a single string. The function which prints using an array of string can call the one for a single string.Other things to note:char * => const char * - this can be done with an implicit cast e.g.char buffer = "";char* constString = buffer;printf(arrayOfStrings); // warning: format not a string literal and no format arguments [-Wformat-security]It is better to write "printf("%s", arrayOfStrings); or puts(arrayOfStrings). The reason for this is that if the string contains entries such as "%d", printf will try print an integer in place of just printing "%d". Worse if the string can be controlled by a user, your program may be vulnerable to arbitrary code execution.

author: IanB
author_rank: Super Contributor
timestamp: November 29, 2014, 07:25:22 am
content: Another way of looking at the problem of the first post is by comparison with a simpler example. Consider the following two declarations:Code:char buffer[] = "something";int value = 10;Here both "buffer" and "value" are variables. If we refer to "value" we get something of type "int". If we refer to "&value" we get the memory address of the variable, which is a constant. So if we tried to refer to "&&value" we would be trying to take the address of a constant, which is not permitted.Now consider a similar scenario with "buffer". The variable "buffer" is an array of characters. According to how the C language works, if we refer to "buffer" we get the memory address where the array begins, which is a constant (note the similarity with "&value"). Consequently, if we try to refer to "&buffer" we are trying to take the address of a constant, which similarly to "&&value" is not permitted. We can trick the compiler into doing it using casts, but the resulting program will not work.On the other hand, if we write:Code:char *string = "something";then "string" is now a variable (not a constant), and the value of the variable can be assigned to. If we refer to "string" we get a value of type "char *" (pointer to char). If we refer to "&string" we get the memory address of "string", which is allowed (just as "&value" was allowed).

author: coderAndHackerNW
author_rank: Newbie
timestamp: November 29, 2014, 07:55:33 am
content: You can compare the assembler output of the two to see what the difference is from the computer's point of view.gcc -save-temps test.cworked for me to get the assembler listing.Oh, and for those who didn't know, some compilers emit assembler code that can be hand tuned, and used for debugging.-Bob

author: westfw
author_rank: Super Contributor
timestamp: November 29, 2014, 10:25:35 am
content: QuoteIt seems that C and C++ have been diverging.Newer C standards apparently support "named address spaces" useful for Harvard architecture microcontrollers (avr-gcc 4.8.x uses this to implement "__flash", for example.)This is apparently NOT supported by C++ ...http://www.avrfreaks.net/forum/avr-g-lacks-flash

author: adam1213
author_rank: Regular Contributor
timestamp: November 29, 2014, 11:34:25 am
content: Quote from: sleemanj on November 29, 2014, 05:34:49 amCode:void main(){}c++: error: ‘::main’ must return ‘int’ void main()Code:int main(){functionLocatedAfterMain();}void functionLocatedAfterMain(){}In function ‘int main()’:c++: error: ‘functionLocatedAfterMain’ was not declared in this scopec: warning, depending on compiler flagsCode:void function(){}int main(){function(1);}c++: error: too many arguments to function ‘void function()’c: this compiles as by default the function has an integer parameter. In c++ the function has no parameters

author: Jeroen3
author_rank: Super Contributor
timestamp: November 29, 2014, 02:40:02 pm
content: Quote from: IanB on November 29, 2014, 07:25:22 amWhen you using a string literals such as"something". It will be replaced by the address of the first character.Like this:&'s'of the string"something". And a pointer is always one word wide.

author: IanB
author_rank: Super Contributor
timestamp: November 29, 2014, 02:50:36 pm
content: Quote from: Jeroen3 on November 29, 2014, 02:40:02 pmThat's not the case here. The string literal on the right of the '=' is the initializer for the array on the left. It's exactly the same as writing this:Code:char buffer[] = {'s', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g', 0};You can see from this that there is no address of the 's' character, it is just data to initialize the array. (The compiler will count the number of elements in the initializer and allocate space for an array of size 10 in this case.)If instead I wrote this:Code:char *not_a_buffer = "something";Then the situation would be as you describe. In this case I will have initialized a pointer to point to the first character of the string literal allocated to a constant data area by the compiler.

author: andersm
author_rank: Super Contributor
timestamp: November 29, 2014, 04:18:56 pm
content: Quote from: westfw on November 29, 2014, 10:25:35 amI believe the extensions for embedded C are still only at the draft technical report level.

author: Jeroen3
author_rank: Super Contributor
timestamp: November 29, 2014, 05:23:07 pm
content: Quote from: IanB on November 29, 2014, 02:50:36 pmchar buffer[]is equal tochar *buffer, the only thing different is that you can runsizeof(buffer)on first one.If you want to know the length of the second one you'd needstrlen().Remember the first lesson of pointers. An array is a pointer to its first element. (as is a struct)When used on an array you initialize it with a pointer to 's' of "something". And the compiler stores the length.

author: Howardlong
author_rank: Super Contributor
timestamp: November 29, 2014, 05:58:38 pm
content: Quote from: IanB on November 29, 2014, 06:00:40 amI think I'd rephrase that a little myself. Instead, I'd say you should only use casts if you know why you're doing it and there is a valid reason.For example, converting a double to a float is very common if you're trying to save a bit of memory, but by default C floating point works in doubles. When you're doing fixed point arithmetic, or many other things where you are doing bit shifting and byte aligning it's also very common.But it definitely shouldn't be used "pin the tail on the donkey" mode, or any other random button pushing methodology.But in general, yes, I agree, the data types for all variables and parameters should match as far as possible, it is good practice and it frequently saves you from yourself.

author: IanB
author_rank: Super Contributor
timestamp: November 29, 2014, 06:03:50 pm
content: Quote from: Jeroen3 on November 29, 2014, 05:23:07 pmNo, it isn't. You are mistaken.

author: Howardlong
author_rank: Super Contributor
timestamp: November 29, 2014, 06:15:36 pm
content: Quote from: Jeroen3 on November 29, 2014, 05:23:07 pmNot quite, char buffer[]="abcdef" will allocate 7 bytes of memory and initialise it (the extra byte is the terminating '\0' character). sizeof(buffer) is 7, whereas strlen(buffer) returns 6.You can also do char *buffer="abcdef", but this time sizeof(buffer) is 4 (assuming a 32 bit address machine) and strlen(buffer) is 6.

author: mikerj
author_rank: Super Contributor
timestamp: November 30, 2014, 11:20:45 am
content: Quote from: IanB on November 29, 2014, 06:00:40 amI disagree. Indiscriminate casting is sure to bite you sooner or later, but casts are often required (especially in low level embedded code that may rely on e.g. overlfow behaviour of variables) and are fine if used with care.

author: Galaxyrise
author_rank: Frequent Contributor
timestamp: November 30, 2014, 08:21:31 pm
content: Quote from: Jeroen3 on November 29, 2014, 05:23:07 pmWhenchar buffer[]is used without an initializer (such as in a function parameter), then it is the same aschar *--sizeof(buffer) == sizeof(char*). I believe this is intended to make the cod a little clearer when a function is expecting an array vs a pointer to a single thing.Whenchar buffer[]is used with an initializer, then it isnotthe same aschar*. It is an array that the compiler computes the size for.sizeof(buffer)will be the size of the array.Arrays are not pointers to the first element! The array is the memory with contiguous elements in it. C/C++ make this very confusing because the array variable by itself (with no brackets) is shorthand to get the pointer to the first element of the array. But it's just shorthand: in the case ofchar buffer,bufferand&bufferare the same!(Structs do not have this syntactical sugar, they are never pointers to the first element.)

author: Galaxyrise
author_rank: Frequent Contributor
timestamp: November 30, 2014, 09:01:13 pm
content: Quote from: sleemanj on November 29, 2014, 05:53:35 amHmm...You have an array of chars on the stack, and what you need is a pointer to a pointer to that array. So a pointer has to be created somewhere to then take the address of.&(&buffer)would be the obvious thing to try. However, that isn't legal:"error: lvalue required as unary '&' operand".You might think to do something like this:inline const char **stringToArray(const char *s) { return &s; }...fn1(stringToArray(buffer),1);While that compiles, you do get a warning. And the compiler isn't kidding: the stack memory for the pointer it's returning is no longer reserved for that purpose after it returns, and could be anything by the time fn1 tries to use it.I can fix stringToArray with C++ trickery; type cast syntax will even work if preferred:fn1((stringToArray)buffer, 1). stringToArray still has to be defined somewhere, but maybe that counts as "possible"But I strongly suspect that, in your case, the best is a local variable at the call site (which is what all the solutions turn into eventually anyway.)

author: Galaxyrise
author_rank: Frequent Contributor
timestamp: November 30, 2014, 09:05:39 pm
content: Quote from: IanB on November 29, 2014, 05:02:05 amI always use stdio.h in my C++ programs...

author: c4757p
author_rank: Super Contributor
timestamp: November 30, 2014, 09:18:10 pm
content: That's because the people who wrote the particular libraries you're using were thoughtful enough to include something like this bit of bilingual boilerplate:Code:#ifdef __cplusplusextern "C" {#endifblah blah code#ifdef __cplusplus}#endif

author: IanB
author_rank: Super Contributor
timestamp: November 30, 2014, 09:18:43 pm
content: Quote from: Galaxyrise on November 30, 2014, 09:05:39 pmStrictly speaking, you shouldn't. stdio.h does not place its declarations in the C++ std namespace, and while for many simple programs this won't cause an issue, there are times when problems may arise. Rather than be scratching your head over strange compiler errors way down the line it is far better (and correct C++) to include <cstdio> and use std::printf and its friends where needed.

author: Stonent
author_rank: Super Contributor
timestamp: December 01, 2014, 09:25:29 am
content: Quote from: IanB on November 30, 2014, 09:18:43 pmYou're just a jack of all trades aren't you? Chemical Engineering, Programming, and Electronics.

author: ttt
author_rank: Regular Contributor
timestamp: December 02, 2014, 08:55:09 am
content: Quote from: sleemanj on November 29, 2014, 04:38:15 amYour two expressions are exactly the same. Run this:Code:#include <stdio.h>int main() {char buffer;printf("%p %p\n", buffer, &buffer);return 0;}Notice that the output is the exact same. You are _force_ casting a 'char *' into a 'const char **'. The perils of C casts...If you want to do this the C++ clean way I recommend to use static_cast and dynamic_cast instead of C casts. In _all_ cases (and in case you want to really force a cast use reinterpret_cast). The compiler will correctly output an error:Code:#include <stdio.h>int main() {char buffer;printf("%p %p\n", static_cast<const char *>(buffer), static_cast<const char **>(&buffer));return 0;}The clang compiler will output:Code:test.cpp:6:55: error: static_cast from 'char (*)' to 'const char **' is not allowedprintf("%p %p\n", static_cast<const char *>(buffer), static_cast<const char **>(&buffer));^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~1 error generated.The reason you code does not work is that a char ** type needs to be an actual pointer stored in memory or variable. Others have hinted at the fix.

author: con-f-use
author_rank: Unknown
timestamp: December 02, 2014, 03:30:16 pm
content: I'll just leave this here.

author: mstevens
author_rank: Regular Contributor
timestamp: December 02, 2014, 04:59:27 pm
content: Hmm... reading the code, it seems that you would like to have an array of strings and then use one function f2(), to print only one of them and then use another function f1() to only print one of them.If using C++ the STL has avectorcontainer. I would use that, creating a vector of type=string. The vector container does all its own maintenance; no need for you to do it. So it can give you only one of its values, all of its values. Thus you can get the entire list of strings in the container which may contain 0, 1, or more strings.Tell the vector what you want and it'll give it to you. You don't have to keep track of how many types oftypethe container does all that for you. Garbage collection also becomes a non-issue.See the following links:http://msdn.microsoft.com/en-us/library/9xd04bzs.aspxhttp://msdn.microsoft.com/en-us/library/zzw4bwhd.aspxhttp://msdn.microsoft.com/en-us/library/1fe2x6kt.aspxNot sure if this will help you but hopefully it does.Other subject that my help, C++ Function Templates, C/C++ Function pointers

author: grumpydoc
author_rank: Super Contributor
timestamp: December 02, 2014, 08:35:32 pm
content: As has been pointed out the reason you can't cast from "char buffer" to char ** is that the type you need is "pointer to pointer" and using just plain buffer or "&buffer" if you prefer gives you a pointer type - you need something to cause another pointer to be allocated and the cast won't do that.You have to remember that a C-style cast or reinterpret_cast in C++ is that what it says to the compiler is the following:See that pattern of bits thatyouthought was an X, well it's really a Y - be a good chap and don't complain.It doesn'tdoanything to the bits so of it's not really a Y then chances are things won't work.