Can you dereference a char pointer inside of a function? - c

I’m trying to deference a char pointer to a char array inside a function:
void getword(char *word)
{
*word = "bar";
}
int main()
{
char defword[4] = "foo";
getword(defword);
printf("%s\n", defword);
return 0;
}
I would expect to get "bar" as output but I seem to get the completely unrelated string '1oo'.

The type of a dereferenced char * is char — a primitive value type. In this case, *word is the same as word[0].
The type of "bar" is const char * — a pointer type.
You are assigning the value of a pointer to a character. I'm doubtful your compiler let that happen without squawking mightily.
In any event, look into strcpy. Leaving aside the horrible unsafety of it all, this will work better:
strcpy(word, "bar");

Related

Trying to print a string in a qsort function

I'm having some trouble passing a valid value to the qsort function but can't quite figure out what's going wrong with it. Here is what I have so far:
int main(void)
{
char* strings[4] = {"Onus", "deacon", "Alex", "zebra"};
printf("%zu\n", sizeof(strings));
qsort(strings, 4, 8, scmp);
}
int scmp(const void *p1, const void *p2)
{
printf("%s\n", (char*) p1);
return 0;
// ignore return value -- I'm just looking to print the string.
}
It just seems to print gibberish when I do this. Is this because qsort expects a value a pointer to a value and I'm passing it a pointer to a (char) pointer? What would be the correct way to reference it then?
It seems instead it should be: printf("%s\n", *(char* const*) p1); ? This is from some trial-and-error, though not sure why that works -- i.e., the *(char**).
For example, for passing an int I can do:
const int *v1 = p1;
But then a char* needs to be:
const char *s1 = *(char* const *) p1;
Why not just const char *s1 = (char*) p1; ?
strings is an array of pointers to char. The comparison function for qsort gets passed pointers to the two elements of the array that should be compared. Since the elements of the array are pointers to char, the arguments p1, p2 to scmp are pointers to (const) pointers to char, and should be cast to char ** (or rather char * const *).
What needs to be passed to printf is the pointer to char itself, so you have to dereference the argument to get that pointer. If you wanted to look at the individual characters of the string, you'd have to dereference again. There are two *s in the name of the type, so you have to apply * two times to get back to the underlying primitive type.
printf("The first character of the string is %c\n", **(char * const *)p1);

execv arguments error - "expected char * const* but argument is of type const char *"

I have a function whose argument is const char *array[]
array[0] is the path, the rest are the arguments and it ends with NULL.
However, if I try to do execv(array[0], array) I get expected char * const* but argument is of type const char *
How do I go about this, and what is the difference between char * const* and const char *?
void start(const char *array[]) {
execv(array[0], array);
}
First, the error message is not copied correctly. If I run your code in GCC it shows this message instead (note the final *):
note: expected ‘char * const*’ but argument is of type ‘const char **’
which makes more sense as the message you show in the question, does not match the code you show. There is a mismatch in level or indirection.
That said, let's look at this part:
and what is the difference between char * const* and const char *?
Actually it is
and what is the difference between char * const* and const char **?
The first is a pointer to a const pointer to a char. The char that is pointed to is not const and might in theory be changed by execv.
The latter is a pointer to a pointer to a const char. This means, the char that is pointed to mustn't be modified. It might be some read-only string literal in ROM. If you pass such a pointer to a function that will try to modify it, it will fail in one way or the other. Therefore you are not allowed to pass a "pointer to const" to a function that does not expect it to be const.
That is what the compiler is telling you.
Now, how can you get rid of that warning...
To silence your compiler you could try to use some cast and cheat about real nature of that parameter.
In the end the problem will stay the same. A function trying to modify your read-only memory will not be working properly.
Instead you need to make a copy of your data:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void start(const char *array[]) {
int i = 0;
// determine number of strings (including NULL)
while (array[i++] != NULL) ;
// Create an array able to hold pointers to copys
char *my_array[i];
// Copy strings into non-const memory
i = 0;
do
my_array[i] = array[i] ? strdup(array[i]) : NULL;
while (array[i++] != NULL);
execv(my_array[0], my_array);
// Free the memory for the copied strings
i = 0;
do
free(my_array[i]);
while (array[i++] != NULL);
}
int main(void)
{
const char *argv[] = {"ls", "ls", NULL};
start(argv);
return 0;
}

How is this const being used?

I was studying "C complete reference" by Herbert Schildt and got stuck on the "const" explanation due by the pointer * he used at the same time with the const explanation.
here is the code he used:
#include <stdio.h>
void dash(const char *str);
int main()
{
dash("this is a test");
return 0;
}
void dash(const char *str)
{
while (*str)
{
if (*str == ' ')
{
printf("%c", '-');
}
else
{
printf("%c", *str);
}
str++;
}
}
I've tried to search about the pointer * and got some answers about adresses but why did he use it in this example? His book didn't explain this and i haven't found other examples with this kinda use of pointer *.
Other question is, why is the loop "while (*str)" correct if it has no condition?
const char *str in a parameter declaration indicates that the function will not try to modify the values that the str pointer points to. This means that you can call the function with a constant string. If you don't have const in the declaration, it means that the function might modify the string, so you can only call it with writable strings.
As an example, a function like strcpy() declares has const on the second parameter (the source string), but not on the first parameter (the destination). It can (and usually does) modify the destination, but not the source.
Many people are confused when start learning C
const char *ptr
It is a pointer which is referencing the const char. The pointer can be modified. But is you try to write to the referenced object the compiler will complain: https://godbolt.org/z/d9znF-
Example:
const char c;
const char *ptr = &c;
*ptr = 'p'; // -- illegal - the compiler will complain
ptr++; // -- legal
to declare the constant pointer to the not constant object:
char * const ptr;
now ptr cannot be changed but the referenced object can: https://godbolt.org/z/h7WWex
char c;
char * const ptr = &c;
*ptr = 'p'; // -- legal
ptr++; // -- illegal - the compiler will complain
to declare const pointer to const object
const char * const ptr;
now the pointer and the referenced object cannot be modified: https://godbolt.org/z/x2xBcZ
const char c;
const char * const ptr = &c;
*ptr = 'p'; // -- illegal - the compiler will complain
ptr++; // -- illegal - the compiler will complain
It's a way of promising that the content the pointer is pointing at will not be altered. It's also a way of suppressing warnings without explicit casts.
Consider this:
void dash(char *str) // Removed const
{
// Code
}
int main() {
const char p[] = "this is a test";
dash(p);
}
Now the compiler will emit this:
k.c: In function ‘main’:
k.c:23:10: warning: passing argument 1 of ‘dash’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
23 | dash(p);
| ^
k.c:4:17: note: expected ‘char *’ but argument is of type ‘const char *’
4 | void dash(char *str)
| ~~~~~~^~~
Since you're not writing to it, this warning is nothing to worry about. But it's good practice to avoid warnings. In this case, we have two alternatives. Either the function may modify the string or it may not. If there's no way it will modify it, then there's no reason to explain to the compiler and the reader that this indeed is the case.
Sidenote. String literals, like "this is a test" has undefined behavior if you modify them, so the program might crash (or not). However, their type is is of type (char*) with no const. The reason is backwards compability. In C++, their type is const char*
Note that the const is a promise by convention, not by the compiler. This code will modify the original string and also compile without warnings:
#include <stdio.h>
void foo(const char *str)
{
// Casting comes with great responsibility
// You're just saying to the compiler
// "Trust me and shut up"
char *ptr = (char*) str;
ptr[2]='A';
ptr[3]='T';
}
int main()
{
const char p[] = "this is a test";
foo(p);
puts(p);
}
output:
$ ./a.out
thAT is a test
As I said, the above will compile without warning. If you remove the cast, you'll get this:
k.c:5:17: warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
5 | char *ptr = str;
| ^~~
Do note that since p is declared as const this is undefined behavior. However, you instead write main like this:
int main()
{
char p[] = "this is a test";
foo(p);
puts(p);
}
then, the program is completely valid. And even though you pass a writable string to the function foo, you'd expect it to not change, since foo takes a constant pointer as argument. But as you can see, such things can be bypassed.
Be very careful with void pointers
Note that this is perfectly valid for ANY type T:
T x;
T *p;
p = (void*) &x;
This is because you can safely cast a pointer to void and back. However, this is NOT valid in the general case:
T x;
Q *p;
p = (void*) &x;
However, because of the cast, you will not get a warning. But this code invokes undefined behavior.
Moral lesson
Casting is NOT the goto solution for warnings. Instead, you should REALLY carefully consider if your cast match your intentions. If you're intentions here is to just get rid of the warning, the right solution is to remove the const for the parameter. If you're intentions with adding the cast is "I know that this function promises to not modify the argument, but I have good reasons for both promising that and then instantly break that promise" then a cast is correct.
Real world example
Just to give a real world example of how it can go wrong. I looked in this question where I saw this:
void * func_return();
void (*break_ptr)(void) = (void *)func_return;
I told OP that the cast is wrong. I got the response that without a cast, the compiler complained. Well, it complained because the pointer is WRONG. The function prototype declares a function taking an unspecified number of arguments and returning a void pointer. The function pointer is a pointer to a function taking NO arguments returning nothing. So in this case, the proper pointer declaration and initialization would be this:
void * func_return();
void *(*break_ptr)() = func_return;
But this would probably be better:
void * func_return(void);
void *(*break_ptr)(void) = func_return;
Note that since a pointer of any type can be safely cast to void* and back. But in this case OP was not casting it back, but to another type. If OP had done it correctly, the cast would just be clutter, but in this case it did hide the REAL error.
In c we can manipulate an array like a pointer with the right pointer arithmatic like he used and we can manipulate it like an array!
const char *str
is a pointer to const char OR an array of const char data types!
In a function, all parameters are passed by value (arrays are no exception). When you pass an array in a function it "decays into a pointer". And when you compare an array to something else, again it "decays into a pointer"
so we can write the while loop again in different way:
void dash(const char *str)
{
int i = 0;
while (str[i])
{
if (str[i] == ' ')
{
printf("%c", '-');
}
else
{
printf("%c", str[i]);
}
++i;
}
}
Now, the first syntax (with the pointer deref operator * is more effecient than array syntax).
in general array name or the address of the first array element (of any type), can decays to a pointer of the same data type!
In his implementation, he behaves the str as a const char pointer, in the while loop he is derefrence the pointer (like str[i], with the brackets) and in the last line (str++) he is moving the pointer to points to the next char element (which usualy knwon as pointer arithmetics).
In this case, read the definition from right to left:
const char *str // str is a pointer to a const char
The address of str can change while the char it points to cannot.
To answer you other question, while (*str) will continue to interate until *str == '\0'. '\0' is used to mark the end of a string in C.
What the program does, if you're unsure, is print it, replacing ' ' with '-'. In your example, "this-is-a-test" would be printed. Note: the string "this is a test" is not modified.
The * is related to pointers but it has two uses.
In the declaration, * is used to declare the pointer type, as in:
const char *str;
Where str is a pointer to a const char (or multiple const char stored in sequence, C doesn't care about the difference).
In an expression, * is used to dereference a pointer, get the value it points to. As in:
printf("%c", *str);
Where *str is that const char itself that the pointer str is pointing to.
Related to pointers, there's also & that does the other way around. It gets the pointer of any value you have stored in memory.
The importance of const here is not related to pointers, it's related to the fact you're passing a string literal to dash(). Unlike strings that are stored in the heap or the stack, string literals cannot be modified and should be treated as const for their immutability.

Why can I have a const char pointer point to a mutable char array?

Why is this fine:
char a[2];
a[0]='a';
const char* b;
b=a;
printf("%s\n",b);
a[0]='b';
printf("%s",b);
why can a pointer to a constant string point to a non constant string? Also how do string constants work when assigning them to variables? Why can you do this:
const char* b="hello";
b="test";
or
char* b="hello";
b="test";
but you can only do the first line if it is an array? constant or not?
Why can I have a const char * point to a mutable char array?
The const here with b puts a restriction on its use. It is not allowing some new usage, but potentially less. So no opening of Pandora's Box here.
char a[2];
const char* b = a;
b has read-access to the array. The array a[] may still change via a.
a[0]='y'; // OK
b[0]='z'; // leads to error like "error: assignment of read-only location "
Also how do string constants work when assigning them to variables? constant or not?
"hello" is a string literal of type char [6]. With const char* b1 = "hello" declaration and initialization, b1 is assigned a pointer of type char *. This could have been const char *, yet for historical reasons1 it is char *. Since it is char*, char* b2 = "hello"; is also OK.
const char* b1 = "hello";
char* b2 = "hello";
Also how do string constants work when assigning them to variables?
b="test";
As part of the assignment, the string literal, a char array, is converted to the address and type of its first element, a char *. That pointer is assigned to b.
1 const was not available in early C, so to not break existing code bases, string literals remained without a const.
You can always add const-ness to a type. This is a good thing, because it allows you to write functions that make some guarantees.
//Here we know *str is const, so the function will not change what's being pointed to.
int strlength(const char *str) {
int length = 0;
while (*str++)
++length;
return length;
}
int main(void) {
char a[2] = "a"; //a[0] and a[1] are mutable in main(), but not in strlength().
printf("%d", strlength(a));
}
Note that casting away const-ness leads to undefined behavior:
void capitalize(char *str) {
if (isalpha(*str))
*str = toupper(*str);
}
int main(void) {
const char *b = "hello";
capitalize((char*) b); //undefined behavior. Note: without the cast, this may not compile.
}
As for your second question, your first example is correct because the type of a string literal in C (i.e. any sequence of characters between double quotes) is of type const char*. This is valid:
const char *b = "hello"; //"hello" is of type const char*
b = "text"; //"text" is of type const char*
Because casting away const leads to undefined behavior, this code is invalid:
char *b = "hello"; //undefined behavior; "hello" is const char*
b = "text"; //undefined behavior; "text" is const char*
The case for arrays is a little more involved. When used in expressions, arrays act as pointers, but arrays are a fundamentally different type than pointers:
char a[10];
a = "hello"; //does not compile - "hello" is a const char*; a is a char[10]
However, when used in an initialization statement, the rules state that a const char* can be used to initialize a character array:
char a[10] = "hello"; //initialization - a is a char[10], "hello" is a const char*
//a will contain {'h','e','l','l','o',0,0,0,0,0}
Also, don't forget that you can assign a string literal to an array with strcpy:
char a[10];
strcpy(a, "hello");
assert(strcmp(a, "hello") == 0);
//a will contain {'h','e','l','l','o',0,x,x,x,x}
//here x's mean uninitialized

Bad pointer type with a typedef

I'm having troubles when calling a function taking a pointer to a string as a parameter. I need to get an Element's name.
// method
void getStringFromCsv( char ** str );
Let me introduce the structures I'm working with (not written by me and part of a much bigger project, I can't modify them).
// typedefs
typedef char T_CHAR64[64];
typedef T_CHAR64 T_SYMBOL;
// generic element
typedef struct Element
{
T_SYMBOL name;
} T_Element;
// csv element
typedef struct CsvElement
{
Element * pElement;
int id;
} T_csvElement;
So, basically, I thought I would call the function like this :
T_Element * pData; // Not null, filled earlier
getStringFromCsv( &pData->pElement->name );
But this doesn't work (warning: passing argument 1 of ‘STR_toCsv’ from incompatible pointer type). I'm using gcc with NetBeans 6.8.
I tried many things...
T_SYMBOL foo = "foo";
T_SYMBOL * pFoo = &foo;
getStringFromCsv( pDef->name, &pFoo ); // error : passing from incompatible pointer type
T_CHAR * pBar = &foo; // error : init from incompatible pointer type
T_CHAR * pBaz = &(foo[0]); // OK
getStringFromCsv( pDef->name, &pBaz ); // OK
T_SYMBOL * pFooTest = &(foo[0]); // error : init from incompatible pointer type
...but ended up casting name to a char ** :
getStringFromCsv( (char**) &pData->pElement->name );
What is wrong with my code ?
Basically, SYMBOL = CHAR *, right ? Why is SYMBOL* != CHAR** ?
I'm pretty sure I'm missing something simple but right now... Nothing came.
EDIT
Here is getStringFromCsv :
void getStringFromCsv( char ** data )
{
// pDesc is defined and not null
csvDescriptorCat( pDesc, *data);
csvDescriptorCat( pDesc, "\t");
}
void csvDescriptorCat( CsvDescriptor * pDesc, char* str)
{
int len;
if( str != NULL)
{
len = strlen(str);
strcpy( &pDesc->line[pDesc->pos], str);
pDesc->pos += len;
}
}
If you wish to pass &pData->pElement->name to the function, the function must be declared as:
void getStringFromCsv(T_SYMBOL * str);
Alternatively you can use a temporary char * as Secure offered - but there's not much point in doing this, because any updates to that char *'s value can't be used - the ->name member can't be modified, as it's an array.
You might as well just declare the function as:
void getStringFromCsv( char * str );
...and call it as:
getStringFromCsv( pData->pElement->name );
(In this case, the function can still change the contents of the ->name array. What you can't do is to change the position of the array itself).
As well as Secure's option, there's another way if your compiler supports C99 compound literals:
getStringFromCsv( &(char *){ pData->pElement->name } );
name is an array of chars, so &name gives you a pointer to char[64], as Vicky already answered. But casting makes things worse, because it tells the compiler to treat the first chars of the array as a pointer to the real array.
See the C-FAQ: http://c-faq.com/aryptr/aryptr2.html
I think you can use a temporary char* here:
char *tmp = pData->pElement->name; // array decays to pointer
getStringFromCsv(&tmp);
If this is expected by the function. Expecting a char**, make sure that it doesn't try to reallocate the memory. For simply filling it, a char* would be enough.
Alas, one of the little secrets of C that people fail to tell you, an array is not the same thing as a pointer. if x is defined as int x[5] or whatever, &x == x. Try out this code below:
#include <stdio.h>
int main(int argc, const char *argv[])
{
char x [5];
char *y;
printf("%08x\n", x);
printf("%08x\n", &x);
printf("%08x\n", y);
printf("%08x\n", &y);
return 0;
}
Considering this : http://c-faq.com/decl/strlitinit.html
char a[4] = "hello";
char* p = "hello";
Aren't the same thing (even if they seem to be).
So my SYMBOL and CHAR* cannot be exchanged, right ?
Is there a workaround, or another solution ?
Yes, under the covers T_SYMBOL is handled like a char *. But you've declared it as a char[64], so you're passing in a pointer to a char[64] not a pointer to a pointer to a char. The compiler is keeping track of that for you.
Personally in this situation I would just cast it as you did at the end of your question.

Resources