I have a pointer array defined declared as
char (*c)[20]
When allocating memory using malloc
c=malloc(sizeof(char)*20);
or
c=(char*)malloc(sizeof(char)*20);
I get a warning as "Suspicious pointer conversion"
Why?
In this declaration
char (*c)[20];
c object has type char (*)[20].
We know that in C malloc return type is void * and that there is an implicit conversion between void * to any object pointer types.
So c = malloc(whatever_integer_expression) is valid in C. If you get a warning, you are probably using a C++ compiler or you are using a C compiler but forgot to include the stdlib.h standard header.
But c = (char*) malloc(whatever_integer_expression) is not valid C because there is no implicit conversion between char * type and char (*)[20] type. The compiler has to (at least) warn.
First of all, make sure you have stdlib.h included.
Secondly, try rewriting it as
c = malloc(sizeof *c);
I suspect you're getting the diagnostic on the second case because char * and char (*)[20] are not compatible types. Don't know why the first case would complain (at compile-time, anyway) unless you don't have stdlib.h included.
edit
Remember that you will have to dereference the pointer before applying the subscript; that is, your expressions will have to be
(*c)[i] = val;
printf("%c", (*c)[j]);
etc.
Alternately you could write c[0][i] in place of (*c)[i], but that's probably more confusing if c isn't supposed to act like a 2-d array.
Because you are defining C as a pointer to a static array of chars, not as a pointer to an array of chars, that is, a pointer to the first char.
Change
char (*c)[20];
for
char * c;
a
For an array of 20 characters, counting the NUL terminator, you don't need a pointer
char array[20];
for a pointer to char, you don't need an array
char *pointer;
A pointer to char can point to an array
pointer = array;
to part of the array (assuming no 'funny' business with the NUL terminator)
pointer = &array[10];
or to a bunch of bytes allocated with malloc
pointer = malloc(20);
just use as
c=(char(*)[20])malloc(sizeof(char)*20);
Reason:
1, as #ouah said.
2, in K&R style compiler, the above one is valid and expected.
Related
I'm confused about the way C handles strings and char * vs char[].
char name[10] = "asd";
printf("%p\n%p", &name, &name[0]); //0x7ffed617acd
//0x7ffed617acd
If this code gives the same addresses for both arguments, does it mean that the C compiler takes char arrays (strings) as a pointer to the first char in the array and moves in the memory till it gets the null terminator? Why wouldn't the same happen if we changed the char name[] to char *name? (I know they differ but what makes C take both in a different way?)
I know that arrays can't be assigned after declaration (unless you used something like strcpy, strcat) which is also confusing. Why wouldn't C take them as any other data type? (Something tells me the compiler has a specific addr for it while you can assign char* to whatever location in the mem since its a pointer).
I know that char * have fixed size unlike char[] which makes char * not usable for first argument of strcat.
in C a "string" is an array of type "char" (terminated with \0).
When you are referring to an array in C, you are using a pointer to the first element. In this case (char *).
According to the ANSI-C standard the name of an array is a pointer to the first element.
Being able to write name instead of &name[0] is syntactical sugar.
In the same way accessing an array element writing name[i] is analogue to writing *(name+i).
does it mean that the c compiler takes char arrays (strings) as a pointer to the first char in the array
An array is not a pointer. But an array will implicitly convert to a pointer to first element. Such conversion is called "decaying".
... and moves in the memory till it gets the null terminator???
You can write such loop if you know the pointer is to an element of null terminated string. If you write that loop, then the compiler will produce a program that does such thing.
Why wouldn't the same happen if we changed the char name[] to char *name?
Your premise is faulty. You can iterate an array directly, as well as using a pointer.
If this code gives the same addresses for both arguments, does it mean
The address of an object is the first byte of the object. What this "same address" means is that the first byte of the first element of the array is in the same address as the first byte of the array as a whole.
I know that arrays can't be assigned after declaration (unless you used something like strcpy, strcat) which is also confusing.
Neither strcpy nor strcat assign an array. They assign elements of the array which you can also do without calling those functions.
Why wouldn't C take them as any other data type?
This question is unclear. What do you mean by "C taking them"? Why do you think C should take another data type? Which data type do you think it should take?
char name[10] = "asd";
printf("%p\n%p", &name, &name[0]);
The arguments are of type char(*)[10] and char* respectively. The %p format specifier requires that the argument is of type similar to void* which isn't similar to those arguments. Passing an argument of a type other than required by the format specifier results in undefined behaviour. You should cast other pointer types to void* when using %p.
char *charPtr = malloc(50);
char *charPtr; *charPtr = malloc(50);
I have been creating pointers in C like the examples above. Is it okay to say that these two are the same in C?
Is it okay to say that these two are the same in C?
No, you need to either initialize the pointer.
char *charPtr = malloc(50); // initialization
Or, you declare it first, then assign it later:
char *charPtr; // declaration
charPtr = malloc(50); // <-- assignment - do NOT add * here as the you already declared `charPtr` as a pointer
Note that doing it like you did (2nd case) was wrong:
char *charPtr;
*charPtr = malloc(50); // <-- WRONG, the * here is deference operator
No, they are not the same. In this case
char *charPtr;
*charPtr = malloc(50);
You are
Dereferencing an uninitialized pointer and that would cause Undefined Behavior.
Assigning a pointer to an integer of different size without casting. Although conversion from pointer to integer and the other way is defined according to the c standard, there are some things you should consider. If this code has been working for you it's because of Undefined Behavior mentioned above. You never know that it's happening until something bad happens which might happen all the time or never, or sometimes, you really never know.
The * plays two different roles here, in the first case
char *charPtr;
it simply indicates that charPtr is a pointer, whereas in
*charPtr = malloc(50);
it's the dereference operator.
Of course, the * is also the multiplication operator but it's interpretation by the compiler depends on the context where it appears.
No. The first declares charPtr as a pointer to char, and it contains the return value of malloc.
The second declares charPtr as a pointer to char. It then says that the value charPtr points to should be set to the return value of malloc. This is wrong. The pointer is uninitialized, and to say what it should point at is undefined behavior. In addition, assigning a void * to a char should generate a compiler diagnostic.
In the book Learn C The Hard Way at excercise 15 there is suggestion to break program by pointing integer pointer at array of strings and using C cast to force it. How can I do it?
Here is a small example. the result depends on the endianness of your system and the size of int. I would expect the first or fourth character to change to the next character in the alphabet.
#include<stdio.h>
int main(void) {
char string[100] = "Somestring";
int *p;
/* Let p point to the string */
p = (int*)string;
/* modify a value */
(*p)++;
/* Let's see if any character got changed */
printf("%s", string);
return 0;
}
It should be pointed out that not all casts are safe and that the result could be implementation defined or undefined. This example is actually undefined, since int could have stricter alignment constraints than char.
When writing portable code you need to take great care when using casts.
The code above could break on any system where sizeof(int) is greater than the string length regardless of alignment issues. In this case, where the string has size 100, we wouldn't expect that to happen in a long while. Had the string been 4-7 bytes it could happen sooner. The jump from 32- to 64-bit pointers broke a lot of old code that assumed that pointers and int were the same size.
Edit:
Is there an easy fix to the alignment problem? What if we could somehow make sure that the string starts in an address that is also suitable for an int. Fortunately, that is easy. The memory allocation function malloc is guaranteed to return memory aligned at an address that is suitable for any type.
So, instead of
char string[100] = "Somestring";
we can use
char *string = malloc(100);
strcpy(string, "Somestring");
The subsequent cast is now safe alignment-wise and is portable to systems where int is smaller than 100.
Note that malloc is declared in stdlib.h, so we should add the following at the top of our code file:
#include<stdlib.h>
That's simply an abusive way of casting.
// setup the pointers to the start of the arrays
int *cur_age = ages;
char **cur_name = names;
What the author of that link meant by "to break program by pointing integer pointer at array of strings and using C cast to force it." He meant that you can write something like this int *cur_age = (int *)names; That is to cast a pointer to pointer to char to a pointer to int. You can do that in C, which allows you to cast from one type of pointer to another type of pointer; but be warned you need to know what you are doing.
Here the author wanted to show how to break a program by pointing a pointer to a wrong type. His example, however, is probably making you more confused rather than helping you to understand pointers.
To cast, use the cast operator: (type)expression. For example, to cast an expression of type double to int:
(int)sqrt(2);
In your specific case, cast names to int* (the type of cur_age) to break the program:
cur_age = (int*)names;
To point incompatible pointer in c you only need to cast it to void.
//array of string declaration
char aStr[50][50];
Int *pint;
//do whatever you need with string array
pint = (*int)(*void)aStr;
I'm writing this from my cell phone.
if you increment your pointer past the allocated memory, you might end up in your program stack and change value to it.
static char st[][8192];
void foo ( int tab_size){
st = (char**) malloc ((tab_size+1)*sizeof(char)*8192);
}
I receive the compilation error in "malloc" line that st has incomplete type. What is wrong? Thanks.
Since you don't specify the size of the inner dimension for st, the compiler doesn't know how big it needs to be; hence the type is incomplete, and you never complete it before the malloc call.
Since it looks like your intent is to allocate st dynamically, go with Oli's advice and declare it as a pointer to an 8192-element array of char:
static char (*st)[8192];
and rewrite your malloc statement as
st = malloc(sizeof *st * (tab_size+1));
sizeof *st == sizeof (char [8192]) == 8192. This form is a bit cleaner and easier to read. Note also that in C, you don't have to cast the result of malloc (unless you're using a pre-C89 implementation, in which case, I'm sorry), and the practice is discouraged.
This will allocate enough space to hold tab_size + 1 arrays of 8192 characters each.
It is only within the context of a function parameter declaration that T a[] declares a as a pointer to T.
Your definition for st is indeed not a complete type.
Try this instead:
static char (*st)[8192];
void foo (int tab_size){
st = malloc ((tab_size+1)*sizeof(*st));
}
You are confusing two completely different types: an array vs pointer to an array.
static char st[][8192];
declares st to be of type array. It is not a modifiable lvalue and can't be assigned to.
If your intent was to allocate space for an array of integers and assign it to a pointer use the pointer declaration suggested by Oli Charlesworth.
Note that your declaration of st won't allocate space though. if you try something like this, you would get an error..
printf("0x%x\n", st);
Compiler is treating it like a declaration and expects to see the actual definition elsewhere.
Its not a definition because you haven't given the value for the array's first dimension.
I need to hold an array of C strings. Now I know C strings are just an array of chars so essentially what I want is a 2d array of chars. The strings I'm trying to store will also never exceed 6 characters. My plan is to initialize a char array with 50 "string slots" and then if I hit 50 strings reallocate the array's memory to double it's capacity. I've tried something simple like:
int main() {
char strings[50][6];
strings[0] = "test";
printf("The string is: %s", strings[0]);
return(0);
}
But, when I go to compile it I get the following error:
test.c: In function ‘main’: test.c:3:
error: incompatible types when
assigning to type ‘char[6]’ from type
‘char *’ test.c:4: warning:
incompatible implicit declaration of
built-in function ‘printf’
Can anyone point in me in the right direction?
strncpy(strings[0], "test", 6); unless your C library has strlcpy(). However if you are going to need to vary the size of the storage, you're better off using a char ** with malloc(), realloc() and free().
One can't assign arrays directly in that way. In your current case, you would need to do something like...
strcpy (strings[0], "test");
It would be more idiomatic to use an array of pointers, though. Have a look at p111 and onwards of K & R.
Use strncpy (if at all possible) or strcpy for your assignment.
First the easy part. You do need to
#include <stdio.h>
to get rid of the incompatible printf warning. This has to do with the way the standard says C works, which is to allow you to make some function that is unlike the standard printf, the implicit declaration of that function with its signature (incorrectly) guessed by the compiler, and the compiler knowing that while you can define a different printf you probably didn't actually mean to.
Ok, now the more complicated part. Arrays in C are a little special. The can evaluate to pointer literals (which can't be assigned to, which is similar to trying to 6 = 4;), or they can evaluate to an entire array, depending on context. Usually they are pointer literals, but in this case strings[0] is seen as an array, which is why you get the error you got rather than one stating that strings[0] was an invalid l-value (left-value, meaning something that can be on the left side of a =). Either way you can't copy a character pointer literal (which is what "test" evaluates to) to an array. When you do this on the line where you declare a string (char array) the compiler treats it differently, though, which can cause some confusion. Anyway, you need to either use strcpy to copy the characters that make up "test" or initialize strings[0] to "test" like this:
char strings[50][6] = { "test" }; // only initializes the first member of the array
You can't assign array contents using the = operator. First of all, an array object cannot be a target of the assignment operator (Online C Standard, draft n1256, section 6.5.16.1, paragraph 1). strings[0] is an array object of type char [6], so it can't appear on the LHS of the = operator.
Second of all, when an array expression is not an operand of either the sizeof or address-of & operators and is not a string literal being used to initialize the contents of another array, the type of the expression is implicitly converted ("decays") from "N-element array of T" to "pointer to T", and the value of the expression is the address of the first element in the array (section 6.3.2.1, paragraph 3).
The string literal "test" is a 5-element array of char (const char in C++) with static extent (meaning the memory for it is allocated at program startup and held until the program exits). However, when it appears in the expression
strings[0] = "test";
its type is converted from "5-element array of char" to "pointer to char" and its value is the address of the first element, so what you wind up doing is attempting to assign a pointer value to an array object, which is not a compatible type; bad juju, over and above not being able to assign an array object anyway.
If you want to copy the contents of one array to another, then you will need to either assign each array element individually, such as
strings[0][0] = 't';
strings[0][1] = 'e';
strings[0][2] = 's';
strings[0][3] = 't';
strings[0][4] = 0;
or even
size_t len = strlen("test");
size_t i;
for (i = 0; i < sizeof strings[0] - 1 && i < len; i++)
strings[0][i] = "test"[i]; // yes, you can subscript a string literal
strings[0][i] = 0;
or use a library function like memcpy(), strcpy(), strncpy(), strcat(), sprintf(), etc.:
strcpy(strings[0], "test");
or
strncpy(strings[0], "test", sizeof strings[0] - 1); // -1 to leave room
// for 0 terminator
// if necessary
or
sprintf(strings[0], "%*s", (int) sizeof strings[0] - 1, "test");
Note that you can initialize the array's contents when you declare it, like so:
char foo[] = "test"; // foo is implicitly sized to 5 (+1 for 0 terminator)
int bar[] = {1,2,3,4,5}; // again, size is implied from initializer
float f[3] = {1.0, 2.0, 3.0}; // Initializer cannot contain more items than
// array is sized for
I see there's a merry war over the use of strcpy() vs. strncpy() in the comments to another answer; my position is to use whichever one is appropriate to the given situation. If you know that your buffers are big enough to handle the largest possible input, use strcpy(). If not, use strncpy(), but be aware that you may have to add the 0 terminator manually.
The problem you have is in the way that the compiler interprets the statement char strings[50][6];
Instead of what you hoped, a char array with 50 slots for 6 char strings, you got a char array of single chars with dimesions 50x6.
Rather than char strings[50][6];, the way you want to initialise your array is as follows (I only know how to do this on the heap, sorry):
char ** strings[50] = malloc(50 * sizeof(char *));
for(int i = 0; i < 50; ++i)
{
strings[i] = malloc(50 * 6 * sizeof(char *));
}
Don't forget to clean with frees afterwards.
EDIT: And as said above. Before your main method. Inlcude the line #include <stdio.h>. This is where the printf() function is located.