%#
%#
is used for formatted output, typically in the form %#p
.
%x
is used to display in hexadecimal format, but the output format may vary depending on the environment.
%p
displays a hexadecimal representation of a system address as an integer within the range of valid values. The number of digits printed corresponds to the address size: 8 digits for a 32-bit system and 16 digits for a 64-bit system. If there are fewer than 8 digits, it automatically pads with 0s;
for example, for an int of 4 bytes, %p
prints the value in a 32-bit binary representation, padding with 0s as needed. It is mainly used for outputting addresses and pointers.
%#
indicates formatted output, automatically adding 0x
for hexadecimal representations.
Size of Pointer Addresses
Pointer addresses are stored using 4 bytes.
Types like char *
, int *
, double *
, etc., are all pointer types that occupy four bytes!
(char*)
This denotes converting a value to a pointer of type char.
This is a typecast and does not imply special meaning; it has the same significance as the assignment in the following example:
char a = '6';
int b = (int)a;
Example
#include <stdio.h>
int main(void)
{
char a = 'a';
int b = (int)&a; /* Assigning the address as a value to b, pointers are stored in 4 bytes */
printf(b);
char *p;
p = (char *)b; /* Converting value to address form */
printf(*p);
return(0);
}
while({expression})
In C, there is no boolean type, https://www.whuanle.cn/archives/738.
A while
condition continues to run while it is true (or 1, or non-zero). It stops when encountering false, 0, '\0', etc.
whille(a > b)
while(i)
while(2)
char *p
vs char* p
Both essentially define a pointer. However, there is a distinction when defining multiple variables.
Using char*
indicates that all declared variables are pointers; for example:
char* a, b, c;
In the following example, only j is a pointer variable while the others are char variables.
char *j, p, k;
However, combining normal and pointer variables in the same declaration can lead to ambiguity, so it is generally advisable to declare them on separate lines.
If necessary to declare them together, the following format is often used:
char l, m, *n;
For function declarations, the position of the *
is not significant.
char* test()
char *test()
malloc and calloc
Both are used for memory allocation and are defined in the header file stdlib.h
.
malloc
does not initialize the allocated memory, leaving the contents of all bytes with random values from before allocation.
On the other hand, calloc
initializes all bytes to zero while allocating memory.
In newer C language standards, both malloc
and calloc
return a pointer of type void, meaning the returned address can be assumed to point to any valid data type.
Typically, pointers are defined with data types, such as char *p
or int *i
.
When using malloc
and calloc
, they can be converted to any type of pointer.
The following is incorrect usage due to malloc
returning a void pointer:
#include <stdio.h>
#include <stdlib.h>
int main() {
char *p;
p = malloc(200);
gets(p);
printf(p);
free(p);
return 0;
}
Therefore, to allocate memory and receive the pointer, the correct approach is:
p = (char *)malloc(200);
Another issue is that memory allocation may not always succeed; if it fails, it returns NULL.
Thus, it should be written as follows:
#include <stdio.h>
#include <stdlib.h>
int main() {
char* p;
p = (char*)malloc(200);
if (p == NULL)
{
printf("Allocation failed");
exit(1);
}
gets(p);
printf(p);
free(p);
return 0;
}
Pointer Initialization
There are two methods for initializing pointers: using the address of an existing variable or using dynamically allocated memory.
Example
char a = 'a';
// Method 1
char* p = &a;
// Method 2
p = (char*)malloc(200);
Pointer Variables Are Also Variables
Pointer variables are also variables and have scopes.
#include <stdio.h>
char* test(char* p) {
printf("Address of p %#p \n", &p);
return p;
}
int main() {
char a = 'a';
char* p1 = &a;
printf("Address of p1 %#p \n", &p1);
char* p2 = test(p1);
printf("Address of p2 %#p \n", &p2);
return 0;
}
The output is as follows:
Address of p1 000000C166F5FA08
Address of p 000000C166F5F9C0
Address of p2 000000C166F5FA28
We can see that the addresses of each p
are different. Pointer variables also have scopes.
Big Endian and Little Endian Storage
Words and bytes:
A byte consists of 8 bits, which is nearly universal across all computers.
In terms of CPU bitness, the number of bits it can process at one time is translated into byte size, referred to as the full word size. There are also double words and half-words:
in a 64-bit computer, a full word is 8 bytes (64 bits). A double word is 16 bytes, and a half-word is 4 bytes.
We can understand it this way: in a 32-bit system, a full word is 4 bytes, a half-word is 2 bytes, and a double word is 8 bytes.
A short
is a half-word (2 bytes), long
is a full word (4 bytes), and double
is a double word.
Generally, for a byte, the high-order bits are on the left and the low-order bits on the right. For multiple bytes, the high-order byte is on the left and the low-order byte on the right.
In the case of registers and memory, if both have high-order bits on the left and low-order bits on the right, it is referred to as little-endian storage. When a long is converted to an int, only the low-order 2/4 bytes are received by the int. In a union, variables share memory and may affect the low-order bytes.
Void Pointers
Just like C# or Java's object type.
object a = new MyClass();
int b = (MyClass)a.Test();
A void pointer can point to any type of variable address.
However, to use the value at this address, it must first be cast to the corresponding pointer type.
#include <stdio.h>
int main() {
char a = 'a';
int b = 0;
void* p = &a;
printf("%d", *(char*)p);
p = &b;
printf("\n");
printf("%d", *(int*)p);
}
文章评论