void main() {
char var = 10;
char *ptr = &var;
printf("Pointer address before increment:%p\n", ptr);
printf("sizeof(ptr):%d\n", sizeof(ptr));
ptr++;
printf("Pointer address after increment:%p\n", ptr);
printf("sizeof(ptr):%d\n", sizeof(ptr));
}
输出:
Pointer address before increment:0x7fffb997144f
sizeof(ptr):8
Pointer address after increment:0x7fffb9971450
sizeof(ptr):8
为什么
char
指针只增加一个字节?它的大小是8,对吗?
当声明 ptr
时,编译器为其分配 8 个字节。当我们将其递增 1
时,它会根据数据类型递增。为什么?这是如何运作的?
对于初学者输出
size_t
类型的值,您需要使用转换说明符 zu
而不是 d
printf("sizeof(ptr):%zu\n",sizeof(ptr));
^^^
自增指针指向它所指向的对象之后的内存。也就是说,类型为
T *
的指针的值会增加值 sizeof( T )
。
考虑访问数组元素的例子。
T a[N];
表达式
a[i]
的计算结果类似于 *( a + i )
。所以指针的值会增加 i * sizeof( T )
。
这称为指针算术,
对于
char
类型的对象的大小,则根据 C 标准,sizeof( char )
始终等于 1
。
另请考虑以下演示程序。
#include <stdio.h>
int main( void )
{
char s[] = "Hello";
for ( const char *p = s; *p != '\0'; ++p )
{
putchar( *p );
}
putchar( '\n' );
}
它的输出是
Hello
如果指针
p
增加了值sizeof( char * )
,则无法使用指针输出数组。
对于指针算术,重要的不是指针的大小,而是它指向的类型的大小:
T a[10]; T *p = a;
定义了一个指向 p
类型的对象数组的指针 T
。p
包含a
第一个元素的内存地址。sizeof(T)
个字节,因此将指针 p
加 1 会使内存地址增加 sizeof(*p)
。这是修改后的版本:
#include <stdio.h>
int main() {
char char_array[2] = "a";
char *char_ptr = char_array;
printf("sizeof(char_ptr): %zu\n", sizeof(char_ptr));
printf("char_ptr before increment: %p\n", (void *)char_ptr);
printf("sizeof(*char_ptr): %zu\n", sizeof(*char_ptr));
char_ptr++;
printf("char_ptr after increment: %p\n", (void *)char_ptr);
int int_array[2] = { 1, 2 };
int *int_ptr = int_array;
printf("\nsizeof(int_ptr): %zu\n", sizeof(int_ptr));
printf("int_ptr before increment: %p\n", (void *)int_ptr);
printf("sizeof(*int_ptr): %zu\n", sizeof(*int_ptr));
int_ptr++;
printf("int_ptr after increment: %p\n", (void *)int_ptr);
return 0;
}
输出(64 位):
sizeof(char_ptr): 8
char_ptr before increment: 0x7fff52c1f7ce
sizeof(*char_ptr): 1
char_ptr after increment: 0x7fff52c1f7cf
sizeof(int_ptr): 8
int_ptr before increment: 0x7fff52c1f7c0
sizeof(*int_ptr): 4
int_ptr after increment: 0x7fff52c1f7c4
输出(32位):
sizeof(char_ptr): 4
char_ptr before increment: 0xbffc492e
sizeof(*char_ptr): 1
char_ptr after increment: 0xbffc492f
sizeof(int_ptr): 4
int_ptr before increment: 0xbffc4930
sizeof(*int_ptr): 4
int_ptr after increment: 0xbffc4934
简单来说,指针是一种特殊类型的变量,用于保存内存地址,编译器分配的位宽或字节大小可能会因平台而异。例如,如果在 8 位架构中使用
sizeof
运算符调整指针大小,则在大多数设备中,您将获得 2 个字节,也就是说,指针最多可以容纳 64kB 的地址值。这在逻辑上足够大,足以容纳仅具有最多 64kB ROM 和最多几 kB RAM 的设备的地址值。
由于编译代码的体系结构是 64 位体系结构,因此系统寄存器是 64 位宽,并且足够宽以容纳非常大的地址值 (2 * 10^64)。由于它足够大,它自然会大小为 64 位/8 字节。
如上所述,指针是特殊对象,因此您不能分配类似的常量
int* ptr = 0x7fffb997144f; // Compilers will not allow this
编译器不会允许这样做,因为指针不是常规类型的变量。因此,为了分配一个常量地址值,您必须将其转换为指针,如下例所示:
int* ptr = (int*) 0x7fffb997144f; // Compilers will allow this
这取决于指针指向的类型的字节大小。那就是;
char
类型short int
类型long
类型long long
类型某些字体大小(例如
int
和 float
类型)可能会因平台而异。
现在我们了解了指针以及它如何递增,让我们为上面的每种类型举一个例子。假设每个示例的起始地址是 0x7fffb997144f。
char
类型char vars[5];
char* ptr = vars; // 0x7fffb997144f
ptr++; // 0x7fffb9971450
ptr++; // 0x7fffb9971451
short int
类型short int vars[5];
short int* ptr = vars; // 0x7fffb997144f
ptr++; // 0x7fffb9971451
ptr++; // 0x7fffb9971453
long
类型long vars[5];
long* ptr = vars; // 0x7fffb997144f
ptr++; // 0x7fffb9971454
ptr++; // 0x7fffb9971458
long long
类型long long vars[5];
long long* ptr = vars; // 0x7fffb997144f
ptr++; // 0x7fffb9971458
ptr++; // 0x7fffb997145f
那是因为一个字符占用1个字节,而指向字符的指针是一个指针,而不是字符或其他东西,也就是说它存储的是内存地址,所以它占用8个字节。而且各种指针在你的机器上都是8字节。