Is an int pointer an array of ints? - c

I have been following some examples that declare an int pointer
int *myInt;
and then turn that pointer into an array
myInt = (int*)malloc(1024);
this checks out
myInt[0] = 5;
cout << myInt[0]; // prints 5
myInt[1] = 7;
cout << myInt[1]; // prints 7
I thought an int pointer was a pointer to an int and never anything else. I know that pointers to strings just point to the first character of the string but it looks like the same sort of thing is happening here with an array of ints. But then if what we want is an array of ints why not just create an array of ints instead of a pointer to an int?
By the way I am interested in how this works in C not C++. This is in a C++ file but the relevant code is in C.

Is an int pointer an array of ints?
No.
I thought an int pointer was a pointer to an int and never anything else
That's right. Pointers are pointers, arrays are arrays.
What confuses you is that pointers can point to the first element of arrays, and arrays can decay into pointers to their first element. And what's even more confusing: pointers have the same syntax for dereferencing and pointer arithmetic that arrays utilize for indexing. Namely,
ptr[i]
is equivalent with
*(ptr + i)
if ptr is a pointer. Of course, similarly, arr[i] is the ith element of the arr array too. The similarity arises out of the common nature of pointers and arrays: they are both used to access (potentially blocks of) memory indirectly.
The consequence of this strong relation is that in some situations (and with some constraints), arrays and pointers can be used as if they were interchangeable. This still doesn't mean that they are the same, but they exhibit enough common properties so that their usage often appears to be "identical".

There is an alternative syntax for accessing items pointed by a pointer - the square brackets. This syntax lets you access data through pointers as if the pointer were an array (of course, pointers are not arrays). An expression a[i] is simply an alternative form of writing *(a+i)* .
When you allocate dynamic storage and assign it to myInt, you can use the pointer like a dynamic array that can change size at runtime:
myInt = malloc(1024*sizeof(int)); // You do not need a cast in C, only in C++
for (int i = 0 ; i != 1024 ; i++) {
myInt[i] = i; // Use square bracket syntax
}
for (int i = 0 ; i != 1024 ; i++) {
printf("%d ", *(myInt+i)); // Use the equivalent pointer syntax
}
* Incidentally, commutativity of + lets you write 4[array] instead of array[4]; don't do that!

Sort of, and technically no. An int pointer does point to the int. But an array of ints is contiguous in memory, so the next int can be referenced using *(myInt+1). The array notation myInt[1] is equivalent, in that it uses myInt pointer, adds 1 unit to it (the size of an int), and reference that new address.
So in general, this is true:
myInt[i] == *(myint + i)
So you can use an int pointer to access the array. Just be careful to look out for the '\0' character and stop.

An int pointer is not an array of ints. But your bigger question seems to be why both arrays and pointers are needed.
An array represents the actual storage in memory of data. Once that storage is allocated, it makes no significant difference whether you refer to the data stored using array notation or pointer notation.
However, this storage can also be allocated without using array notation, meaning that arrays are not necessarily needed. The main benefit of arrays is convenient allocation of small blocks of memory, i.e., int x[20] and the slightly more convenient notation array[i] rather than *(array+i). Thankfully, this more convenient notation can be used regardless of whether array came from an array declaration or is just a pointer. (Essentially, once an array has been allocated, its variable name from that point onwards is no different than a pointer that has been assigned to point to the location in memory of the first value in the array.)
Note that the compiler will complain if you try to directly allocate too big of a block of memory in an array.
Arrays:
represent the actual memory that is allocated
the variable name of the array is the same as a pointer that references the point in memory where the array begins (and the variable name + 1 is the same as a pointer that references the point in memory where the second element of the array begins (if it exists), etc.)
values in the array can be accessed using array notation like array[i]
Pointers:
are a place to store the location of something in memory
can refer to the memory that is allocated in an array
or can refer to memory that has been allocated by functions like malloc
the value stored in the memory pointed to by the pointer can be accessed by dereferencing the pointer, i.e., *pointer.
since the name of the array is also a pointer, the value of the first element in the array can be accessed by *array, the second element by *(array+1), etc.
an integer can be added or subtracted to a pointer to create a new pointer that points to other values within the same block of memory your program has allocated. For example, array+5 points to the place in memory where the value array[5] is stored.
a pointer can be incremented or decremented to point to other values with the same block of memory.
In many situations one notation will be more convenient than the other, so it is extremely beneficial that both notations are available and so easily interchanged with each other.

They are not the same. Here is the visible difference.
int array[10];
int *pointer;
printf ("Size of array = %d\nSize of pointer = %d\n",
sizeof (array), sizeof (pointer));
The result is,
Size of array = 40
Size of pointer = 4
If You do "array + 1", the resulting address will be address of array[0] + 40. If You do "pointer + 1", resulting address will be address of pointer[0] + 4.
Array declaration results in compile time memory allocation. Pointer declaration does not result in compile time memory allocation and dynamic allocation is needed using calloc() or malloc()
When you do following assignment, it is actually implicit type cast of integer array to integer pointer.
pointer = array;

Related

How do array pointers work in C and how to change pointers

I'm very new to C and I have trouble understanding array pointers. I'm trying to make a array bigger,I copy all of its element to new bigger array but I can't make original variable to point the new array. I'm use to C# where you can do
double[] array1 = new double[5];
double[] array2 = new double[10];
array1 = array2;
I did something similar using int array
int array1 [5];
int array2 [10];
*array1 = &array2;
and it compile but crash the program. Same lines but double or char[] (I was told to use char[] instead of sting in C) do not even compile
[Error] incompatible types when assigning to type 'double' from type 'double (*)[(sizetype)(newsize)]'
The results I found on the topic told me to use double* array1 for variable type but this change the interactions with that variable.
If someone can explain the concept to me or at least tell me what to search for that will be huge help.
I do know the basics of pointers!
There are a few things you need to know about arrays (and pointers):
The first is that arrays and pointers are two different things;
The second is that an array can decay to a pointer to its first element. So if you use array1 (from your example) when a pointer is expected, that's the same as doing &array1[0]. The type of such a pointer is a pointer to a single element type (so for array1 the type will be int *);
The third thing is that for any array of pointer a and index i, the expression a[i] is exactly equal to *(a + i). That means *array1 (again from your example) is the same as array1[0] (*array1 is equal to *(array1 + 0) which is equal to array1[0]);
An array will have a fixed size. Once defined the size of an array can't change;
Lastly when you get a pointer to an array (as in &array2) then you get a pointer to the actual array, not to one of its elements. The type of e.g. &array2 is int (*)[10].
Now we can puzzle together the statement
*array1 = &array2;
If we do the array-indexing replacement for *array1 then we get
array[0] = &array2;
And here we can see a big problem: The type of a single element of array1 is a plain int. So what the assignment is trying to do is to assign a pointer to an array (of type int (*)[10]) to a single int.
If you want to copy all the elements from one array to another, then use the memcpy function. You're not allowed to assign between arrays.
But beware of the different sizes for array1 and array2. If you go out of bounds of an array (or other allocated memory) you will have undefined behavior.
In C there is no way to make an array variable "reference" a different variable. If you need to use "references" they can be emulated using pointers:
int *pointer1 = array1; // array1 here will decay to &array[0]
int *pointer2 = array2; // Same here for array2
With the above definition pointer1 is (in a way) "referencing" array1. You can now use pointer1 and array1 almost interchangeably.
One major difference between using pointers and arrays is how their sizes are calculated: When you do sizeof on an array you get the size (in bytes) of the whole array. Assuming 32-bit int (the most common) then sizeof array1 will return 5 * 4 (or 20) as the size. If you get the size of a pointer, you get the size of the pointer itself, not what it might point to. So sizeof pointer1 will return either 4 or 8 (depending on if you're in a 32-bit or 64-bit system).
Going back to references, we can now change where pointer1 is pointing:
pointer1 = pointer2; // Assuming pointer2 is unchanged, equivalent to pointer1 = array2
Now pointer1 and pointer2 are pointing to the same array, array2.
In C# you can overload the = to copy the arrays. In C it is just simple assignment.
In C arrays decays to pointers for the sake of simplicity. In C *(array + N) == array[N] and *array == array[0]
int array1 [5]; it is not the array of pointers only integers so *array1 = &array2; assigns array[0] with address of the first element of the the array2 converted to signed integer which generally doesn't make too much sense and it does not copy array2 to array
To copy array you need to use memcpy or the loop to copy the element. You need to make sure that the destination array is large enough to accommodate the second array. C will not change the destination array size.
The assignments that your are doing is wrong. Basically a pointer points to a block of memory. from your code I can understand that array1 = array2; and *array1 = &array2; is wrong.
Syntax in C is something like this data-type* pointer-variable = (data-type*)malloc(no. of bytes you want);
See consider you want 10 block of memory of type int
int *p = (int *)malloc(10 * sizeof(int))
sizeof(int) return 4 bytes.
Now p points to 10 * 4 = 40 bytes of memory, I multiplied by 4 because int is usually of 4 bytes and double is of 8 bytes and so on.
Follow this link to understand C - Data Types
Now regarding changing pointers refer below example and read the comments
int *q = NULL // declare a pointer of same type as the block of memory it is going to point
q = p; //now q and p point same memory of 40 bytes, means value at q[0] is equal to p[0]
When you have an integer pointer and you increment it by p++ it will point to next memory location p[1], pointer will be exactly incremented by 4 bytes as int size is 4 bytes and for double it will be 8 bytes, for char it will be 1 byte and so on.
Now if you want to increase the size of dynamically allocated memory you can use realloc please follow this link to understand more.
Dynamic Memory Allocation in C
int *p = NULL;
// Dynamically allocate memory using malloc()
p = (int*)malloc(no. of bytes, sizeof(int));
// Dynamically re-allocate memory using realloc()
p = realloc(p, (no. of bytes) * sizeof(int));
// Avoid memory leaks
free(p);
Syntax in C++ is something like this data-type* pointer-variable = new data-type[size];
See consider you want 10 block of memory of type int
int *p = new int[10]
Just use new operator to allocate block of memory and use delete to free allocated memory to avoid memory leaks.follow this link
new and delete operators in C++ for dynamic memory
Or If you are looking for containers where you don't know how much memory should be allocated the use standard template library vector, it helps creating dynamic arrays.follow this link
Vector in C++ STL

Is a 2D Array an Array of Pointers?

If I have:
int A[10][20];
printf("%p",A[3]);
it will print the address of A[3][0].
However, I'd like to know if this one dimensional array A[3] containing pointers really exists, or it is calculated in some way.
The way you have defined A means that the compiler will allocate for it a contiguous block of memory large enough to hold 10 x 20 (200) integers; see here (scroll down to "Multidimesional arrays"). As I'm sure you realize, if you were to do printf("%p", A); you would see the address of the beginning of that allocated block.
Now, when the compiler sees the expression A[3], it will add what it calculates as the necessary amount of "integer sizes" to the base address (that of A, or A[0][0]); in this case, it will add "3" (the index specified) multiplied by the combined size of all the other dimensions (in this case, there's only one, which is 20).
So, in your case, there is no actual array of pointers; just a memory block that the compiler can interpret according to how you described any part(s) of it.
However, in a more versatile approach, one can actually define a 2D array in terms of an actual array of pointers, like so:
int **A;
A = malloc(10 * sizeof(int*));
for (int n = 0; n < 10; ++n) A[n] = malloc(20 * sizeof(int));
In this case, using printf("%p",A[3]); would still be valid, but it would give a very different offset value from printf("%p",A); or printf("%p",A[0]);.
It's also, perhaps, worth noting that, even though these two different declarations for A can both resolve an individual element through an expression like A[i][j] (but the compiler would evaluate the addresses differently), there is here scope for major confusion! When, for example, passing such an array to a function: if the function expects data allocated in the second form, and you give it an array defined in the first form (and vice versa), you're gonna get major undefined behaviour .
yes there is a way to calculate the position:
for A[i][j]
the position of the memory block will be
pos = A + i*(number_of_columns_in_each_row) + j
here A is the pointer to the first element of the array
However, I'd like to know if this one dimensional array A containing pointers really exists, or it is calculated in some way.
The way you defined the array A :
int A[10][20];
does not contain any pointers as elements of the array. it contains only integer elements.
if you want to make an array of pointers, which should be assigned to int-variables is defined like that:
int *A[10][20];
You also can set a pointer to the start of the array, which means element [0] [0]
by using:
int *pointer;
int *A[10][20];
pointer = &A;
You also be able to set the pointer slightly forwards according to each element by increase the pointer.
pointer++;

About pointer of a pointer array

I am checking out a tutorial video about heap from Bucky on YT.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
int n, howMany;
int total;
float average = 0.0;
int * pointsArray;
printf("How many numbers do you want to average?\n");
scanf(" %d", &howMany);
pointsArray = (int *) malloc(howMany * sizeof(int));
printf("Enter numbers! \n");
for(n = 0; n<howMany; n++){
scanf(" %d", &pointsArray[n]);
total += pointsArray[n];
}
average = (float)total /(float)howMany;
printf("average is %f", average);
free(pointsArray);
return 0;
}
I don't understand how pointer-pointsArray changed from a pointer to a pointer array in the above for loop.
I have knowledge on rvalue and Ivalue in variable. I would appreciate if you may include these concept in the answer.
At what line did pointsArray become a pointer array?
pointsArray = (int *) malloc(howMany * sizeof(int)); should only increase the memory size of pointsArray.
Why complier knows how to input value in array when complier sees &pointsArray[n]?
I am able to identify my problem should lie in my confusion of array and pointer that becomes an array. May I have a clear definition so that I can separate them?
Thanks a lot. Please let me know if my question is unclear. I will try to improve it.
I don't understand how pointer-pointsArray changed from a pointer to a
pointer array in the above for loop. I have knowledge on rvalue and
Ivalue in variable. I would appreciate if you may include these
concept in the answer.
At what line did pointsArray become a pointer array? pointsArray = (int *) malloc(howMany * sizeof(int)); should only increase the memory
size of pointsArray.
First, let's start with the basics. A pointer is not an array, and an array is not a pointer, they are two separate types in C. An array type is converted to a pointer to the first element of the array on access as provided in C11 Standard - 6.3.2.1 Lvalues, arrays, and function designators(p3)
int * pointsArray;
...
pointsArray = (int *) malloc(howMany * sizeof(int));
What is pointsArray? (it is a pointer to type int).
Well, What is a Pointer?
A pointer is simply a normal variable that holds the address of something else as its value. In other words, a pointer points to the address where something else can be found. Where you normally think of a variable holding an immediate values, such as int a = 5;, a pointer would simply hold the address where 5 is stored in memory, e.g. int *b = &a; declares b as a pointer to type int and initializes its value to the address where 5 is stored in memory (e.g. b points to a -- where 5 is currently stored). It works the same way regardless what type of object the pointer points to.
Pointer arithmetic will work the same for all pointers regardless of the type because the type of the pointer controls the pointer arithmetic, e.g. with a char * pointer, pointer+1 points to the next byte after pointer. For an int * pointer (normal 4-byte integer), pointer+1 will point to the next integer at an offset 4-bytes after pointer. (so a pointer, is just a pointer.... where arithmetic is automatically handled by the type)
At what line did pointsArray become a pointer array? pointsArray = (int *) malloc(howMany * sizeof(int)); should only increase the memory
size of pointsArray.
Recall, "a pointer is not an array and an array is not a pointer." An array is declared either with:
type name[CONST]; /* declares an array of type with CONST elements */
type name[var]; /* declares a VLA C99+ with var no. of elements */
type name[] = { 1, 2, 3 }; /* declares & initializes an array of 3 elements */
Constrast with:
type *name; /* declares a pointer to type */
In the case of an array with a constant number of elements or with an initialization list, automatic-storage is provided for the array. Storage for the variable-length array (VLA) is similar but within another segment (generally within .bss [block stated by symbol]). On access (except when "it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array") an array is converted pointer to type that points to the initial element of the array object and is not an lvalue. 6.3.2.1(p3) (You will often see the phrase an "Array decays to a pointer" -- which is referring to this conversion)
(Note: it does not work in reverse, a pointer is never converted to an array)
The declaration type *name; declares name as a pointer to type. It is uninitialized and it holds an indeterminate address. Any attempt to access or otherwise make use of an uninitialized pointer invokes Undefined Behavior (frequently leading to a Segmentation Fault). The pointer must hold a valid address (e.g. it must point to valid storage) before you can access the memory. The easiest way to provide valid storage for a pointer is to dynamically allocate a block of memory and assign the starting address for the block to the pointer. This is done in your code with:
pointsArray = (int *) malloc(howMany * sizeof(int));
(note: there is no need to cast the return of malloc, it is unnecessary. See: Do I cast the result of malloc?)
A better approach would be:
pointsArray = malloc (howMany * sizeof *pointsArray);
(pointsArray is type int*, so *pointsArray is type int. If you use the derefernced pointer as your type-size, you will eliminate the potential for type-size error)
Above, malloc reserves a valid block of memory guaranteed to be at least howMany * sizeof *pointsArray bytes in size. The starting address for the block of memory is assigned to pointsArray (e.g. pointsArray now points to that block) The memory has allocated storage duration and remains valid until you free() it, or the program ends. YOU are responsible for preserving a pointer holding the starting address of the block of memory so it can be freed when no longer needed.
You can now store up to howMany integers in the block of memory pointed to by pointsArray. To access the first address for an integer, you can use *(pointArray + 0) (which is just *pointsArray) using pointer notation or you can use pointsArray[0] using array index notation. (the key is to understand that [] acts as a defeference just the same as the '*' in *(name + offset) does. So to access the second element, you can use either *(pointsArray + 1) or pointsArray[1], etc.. While you can use array index notation with a pointer -- that does NOT make the pointer an array or cause it to be converted to one.
Why complier knows how to input value in array when complier sees &pointsArray[n]?
That has more to do with the requirements of scanf than anything funny with pointsArray. Think about it. From man 3 scanf() when using the %d format conversion specifier, scanf:
Matches an optionally signed decimal integer; the next pointer
must be a pointer to int.
So if instead you wanted to read an integer into the variable declared with int a;, your use of scanf would be:
if (scanf ("%d", &a) == 1) {
/* you have a good integer */
}
else {
/* a matching or input failure occurred */
}
For the integer value to be placed by scanf in a, you must provide a pointer to a. It is no different when you want to put the value in the integer pointsArray[n]. How do you provide a pointer to (address of) pointsArray[n]? (answer: you prefix it with the unary '&' address of operator -- &pointsArray[n])
Hopefully this will take care of your No. 3 as well. Look things over and let me know if you have further questions.
Array is contiguous block of memory and variable holds start of memory.
int* pointsArray
pointsArray = malloc(sizeof(*pointsArray) * howMany);
Now pointsArray holds (points to) some block of memory, in this case returned by malloc and you can normally use array annotation to access to memory entries.
Your pointer points to memory of type int which (I assume) is on your machine 4 bytes each. When you trying to access memory pointsArray[n] you are accessing memory locations:
pointsArray[0] = pointsArray + 4 * 0;
pointsArray[1] = pointsArray + 4 * 1;
pointsArray[n] = pointsArray + 4 * n;
If we look very generic way, each index has offset for sizeof(type), in your case type is int which is 4 bytes.
pointsArray[n] = pointsArray + sizeof(*pointsArray) * n
Please not that it is not a good idea to cast return of malloc and that it is better and safer to add sizeof(...) part to beginning of size calculation.
Do I cast the result of malloc?
At what line did pointsArray become a pointer array?
pointsArray = (int *) malloc(howMany * sizeof(int)); should only increase the memory size of pointsArray.
You are correct. It does not become a pointer array.
Why complier knows how to input value in array when complier sees &pointsArray[n]?
The compiler has nothing to do with this. The code is just reading input values to the extent of sizeof(int) (4 bytes) into the memory pointed to by pointsArray. At every iteration of the for loop it moves four bytes and writes the value of the input into that memory location.
I am able to identify my problem should lie in my confusion of array and pointer that becomes an array. May I have a clear definition so that I can separate them?
Since a pointer holds value of an address location, the address of any type including an array is a pointer. So pointer arithmetic can be applied on it.
Array is just a continuous memory location, so if you have address of the first element you can access other elements by adding to the first address.
So if you have a pointer which points to first element of the array like here
pointsArray = (int *) malloc(howMany * sizeof(int));
pointsArray points to the first element.
Now to access other elements you can use pointsArray[n] which translates to pointsArray + sizeof(*pointsArray) * n (address of the nth element) to access other elements.
Pointer is just an address (index) of a specific cell in the memory and it knows the basic length of its type, e.g. int32_t *p = 0x12345678 is an address at byte 0x12345678 with length 4 bytes. So when we do p+1, the result is 0x1234567c. Note Pointer itself is a type with a length of 4 or 8 bytes depending on your box.
For your question,
pointsArray is always and only a pointer of int. it doesn't know any information about the array, e.g. the length of the array. after the line pointsArray = (int *) malloc(howMany * sizeof(int)), pointsArray has a valid value (an address), which is the address of the memory allocated for an int array. (pointsArray doesn't know the length of the array).
for &pointsArray[n], compiler just write to the memory of address pointsArray + n.
The name of an array is very similar to a const pointer (the only difference I remember is that when you use sizeof to the name of an array you can get the size of the array while for a const pointer you get the size of the pointer). In C, I prefer not to use the concept of array, just consider it as a const pointer of some type.

Stack vs. heap pointers in C

I'm currently learning C and I'm confused about memory layout and pointers.
In the following code, it is my understanding that the array is allocated on the stack.
#include <stdio.h>
int main () {
int x[4];
x[0] = 3; x[1] = 2; x[2] = 1;
printf("%p\n",x);
printf("%p\n", &x);
}
My question is, why do the two print calls output the same value?
I tried a similar snippet using malloc (allocate on the heap), and the values differ.
#include <stdio.h>
#include <stdlib.h>
int main () {
int *x = malloc(sizeof(int) * 4);
x[0] = 3; x[1] = 2; x[2] = 1;
printf("%p\n",x);
printf("%p\n", &x);
}
The reason is that unlike you were probably taught, arrays are not pointers. Arrays in C decay into pointers1 under some circumstances. When you pass an array to a function, it decays into a pointer to the first element. The address of that element is the same as the address of the entire array (an address is always to the first byte of an object).
What you get from malloc is not an array, but the address of a chunk of memory. You assign the address to a pointer. But the pointer and the chunk are separate entities. So printing the value of the pointer, as opposed to its address, yields different results.
(1) Decay is a fancy term for a type of implicit type conversion. When an array expression is used in most places (such as being passed as an argument to a function that expects a pointer), it automatically turns into a pointer to its first element. The "decay" is because you lose type information, i.e. the array size.
Your two print calls print the same value because one tries to print the array, which decays to a pointer to the array, and the other prints the address of the array. A pointer to the array contains the address of the array, so they're the same value.
In the second case, one prints the value of x, the other prints the address of x. Since x is a pointer to the block of memory you allocated, these must be different values.
So in the first case, all you have is an array (x). In the second case, you have an allocated block of memory (unnamed) and a pointer to that allocated block (x).
It is perhaps surprising that one can indeed take the address of a whole array, partly because one doesn't need to very often. The array in a sense is a single object, which has one address, which is the address of its first byte. Like with all objects, the address is obtained with the address operator, &.
The first element of an array (like all of its elements) has an address, too, which is the address of its first byte. A pointer to its first element is what the array type is "adjusted" to when it is passed as an argument to a function.
These two bytes are identical, and have the same address. But they have different types, which becomes obvious if you add 1 to them and print them again.
The pointer y, by contrast, is its own distinct object (probably 4 or 8 bytes in size; enough to store an address in it). Like any object it has an address which can be obtained with the & operator. Perhaps confusingly, it also contains an address, in this case the address of the first byte of the array. The two are of course not equal: The pointer object resides at a different location than the array (namely next to it on the stack, even if Olaf doesn't like that).
Minor remark: You use %p for printing pointers, which is good. If you do that, you should strictly spoken cast the pointer which you print to a void pointer: printf("%p\n", (void *)x);.

Can malloc() be used to define the size of an array?

Here consider the following sample of code:
int *a = malloc(sizeof(int) * n);
Can this code be used to define an array a containing n integers?
int *a = malloc(sizeof(int) * n);
Can this code be used to define an array a containing n integers?
That depends on what you mean by "define an array".
A declaration like:
int arr[10];
defines a named array object. Your pointer declaration and initialization does not.
However, the malloc call (if it succeeds and returns a non-NULL result, and if n > 0) will create an anonymous array object at run time.
But it does not "define an array a". a is the name of a pointer object. Given that the malloc call succeeds, a will point to the initial element of an array object, but it is not itself an array.
Note that, since the array object is anonymous, there's nothing to which you can apply sizeof, and no way to retrieve the size of the array object from the pointer. If you need to know how big the array is, you'll need to keep track of it yourself.
(Some of the comments suggest that the malloc call allocates memory that can hold n integer objects, but not an array. If that were the case, then you wouldn't be able to access the elements of the created array object. See N1570 6.5.6p8 for the definition of pointer addition, and 7.22.3p1 for the description of how a malloc call can create an accessible array.)
int *a = malloc(sizeof(int) * n);
Assuming malloc() call succeeds, you can use the pointer a like an array using the array notation (e.g. a[0] = 5;). But a is not an array itself; it's just a pointer to an int (and it may be a block of memory which can store multiple ints).
Your comment
But I can use an array a in my program with no declaration otherwise
suggests this is what you are mainly asking about.
In C language,
p[i] == *(p + i) == *(i + p) == i[p]
as long as one of i or p is of pointer type (p can an array as well -- as it'd be converted into a pointer in any expression). Hence, you'd able to index a like you'd access an array. But a is actually a pointer.
Yes. That is exactly what malloc() does.
The important distinction is that
int array[10];
declares array as an array object with enough room for 10 integers. In contrast, the following:
int *pointer;
declares pointer as a single pointer object.
It is important to distiguinsh that one of them is a pointer and that the other as an actual array, and that arrays and pointers are closely related but are different things. However, saying that there is no array in the following is also incorrect:
pointer = malloc(sizeof (int) * 10);
Because what this piece of code does is precisely to allocate an array object with room for 10 integers. The pointer pointer contains the address of the first element of that array.(C99 draft, section 7.20.3 "Memory management functions")
Interpreting your question very literally, the answer is No: To "define an array" means something quite specific; an array definition looks something like:
int a[10];
Whereas what you have posted is a memory allocation. It allocates a space suitable for holding an array of 10 int values, and stores a pointer to the first element within this space - but it doesn't define an array; it allocates one.
With that said, you can use the array element access operator, [], in either case. For instance the following code snippets are legal:
int a[10];
for (int i = 0; i < 10; i++) a[i] = 0;
and
int *a = malloc(sizeof(int) * n);
for (int i = 0; i < n; i++) a[i] = 0;
There is a subtle difference between what they do however. The first defines an array, and sets all its elements to 0. The second allocates storage which can hold an equivalently-typed array value, and uses it for this purpose by initialising each element to 0.
It is worth pointing out that the second example does not check for an allocation error, which is generally considered bad practice. Also, it constitutes a potential memory leak if the allocated storage is not later freed.
In the language the Standard was written to describe (as distinct from the language that would be described by a pedantic literal reading of it), the intention was that malloc(n) would return a pointer that would, if cast to a T*, could be treated as a pointer to the first element of a T[n/sizeof T*]. Per N1570 7.22.3:
The
pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to
a pointer to any type of object with a fundamental alignment requirement and then used
to access such an object or an array of such objects in the space allocated (until the space
is explicitly deallocated).
The definition of pointer addition and subtraction, however, do not speak of acting upon pointers that are "suitably aligned" to allow access to arrays of objects, but rather speak of pointers to elements of actual array objects. If a program accesses space for 20 int objects, I don't think the Standard does actually says that the resulting pointer would behave in all respects as though it were a pointer to element [0] of an int[20], as distinct from e.g. a pointer to element [0][0] of an int[4][5]. An implementation would have to be really obtuse not to allow it to be used as either, of course, but I don't think the Standard actually requires such treatment.

Resources