在 C 语言的世界里,指针是一种强大而灵活的工具,它允许我们直接操作内存地址。今天,我们就通过一个程序练习来复习指针的使用。
#include <stdio.h>
int main() {
int a[] = {1, 2, 3, 4}; // 内存大小为4 * 4 = 16字节
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(a + 0));
printf("%d\n", sizeof(*a));
printf("%d\n", sizeof(a + 1));
printf("%d\n", sizeof(a[1]));
printf("%d\n", sizeof(&a));
printf("%d\n", sizeof(*&a));
printf("%d\n", sizeof(&a[0]));
printf("%d\n", sizeof(&a[0] + 1));
return 0;
}
请分析程序的输出结果,并解释每条 printf
语句的含义。
在接下来的文章中,我们会一起把指针的知识与题目结合起来,学习这一知识点。
sizeof
是一个编译时运算符
,用于计算变量或数据类型在内存中所占用的字节数。它在处理数组和指针时有一些需要注意的地方。
sizeof
的操作数是数组名时,它会返回整个数组占用的字节数。例如,sizeof(a)
返回的是数组 a
的总大小,即 4 * sizeof(int)
。sizeof
的操作数是指针时,它返回的是指针本身占用的字节数,而不是指针所指向的内容。在 32 位系统中,指针通常占用 4 字节;在 64 位系统中,指针通常占用 8 字节。在 C 语言中,数组名在大多数情况下可以被视为指向数组首元素的指针。例如,a
可以被视为 &a[0]
,即数组第一个元素的地址。因此,a + 1
表示数组第二个元素的地址。
sizeof(a + 0)
:a + 0
是数组首元素的地址,sizeof(a + 0)
返回的是指针占用的字节数。sizeof(*a)
:*a
是数组首元素的值,sizeof(*a)
返回的是数组首元素占用的字节数,即 sizeof(int)
。sizeof(a + 1)
:a + 1
是数组第二个元素的地址,sizeof(a + 1)
返回的是指针占用的字节数。sizeof(a[1])
:a[1]
是数组第二个元素的值,sizeof(a[1])
返回的是数组第二个元素占用的字节数,即 sizeof(int)
。在 C 语言中,地址运算符 &
用于获取变量的地址,而解引用运算符 *
用于获取指针所指向的值。
sizeof(&a)
:&a
是整个数组的地址,sizeof(&a)
返回的是指针占用的字节数。sizeof(*&a)
:&a
是整个数组的地址,*&a
表示整个数组,sizeof(*&a)
返回的是整个数组占用的字节数。sizeof(&a[0])
:&a[0]
是数组第一个元素的地址,sizeof(&a[0])
返回的是指针占用的字节数。sizeof(&a[0] + 1)
:&a[0] + 1
是数组第二个元素的地址,sizeof(&a[0] + 1)
返回的是指针占用的字节数。#include<stdio.h>
int main() {
int a[] = {1, 2, 3, 4}; // 内存大小为4*4=16字节
printf("%d\n", sizeof(a));
// sizeof()中直接放入数组名,计算整个数组的内存大小:16字节
printf("%d\n", sizeof(a + 0));
// 先计算a+0,代表第一个元素的地址,地址在内存中为4/8个字节
printf("%d\n", sizeof(*a));
//*a为第一个元素,也就是1,int类型占4字节
printf("%d\n", sizeof(a + 1));
// a+1为第二个元素,同上为4字节
printf("%d\n", sizeof(a[1]));
// 第二个元素,4字节
printf("%d\n", sizeof(&a));
// &a为整个元素的地址,地址就占据4/8个字节
printf("%d\n", sizeof(*&a));
// &a表示整个数组的地址,*&a表示整个数组
// 将数组传入得到数组的大小,即4*4=16字节
printf("%d\n", sizeof(&a[0]));
// 第一个元素的地址,地址占4/8字节
printf("%d\n", sizeof(&a[0] + 1));
// 第二个元素的地址,4/8字节
return 0;
}
sizeof
的返回值是 size_t
类型,它是一个无符号整数类型,表示对象的大小。在不同的系统中,size_t
的大小可能不同,但它通常与系统的指针大小一致。因此,在 32 位系统中,size_t
通常占用 4 字节;在 64 位系统中,size_t
通常占用 8 字节。
指针的大小与系统的位数有关。在 32 位系统中,指针占用 4 字节;在 64 位系统中,指针占用 8 字节。因此,sizeof(a + 0)
和 sizeof(&a)
的返回值可能因系统而异。
虽然数组名在大多数情况下可以被视为指针,但数组和指针在某些情况下是有区别的。例如,sizeof(a)
返回的是整个数组的大小,而 sizeof(&a)
返回的是数组的地址占用的字节数。
在进行地址运算时,需要注意指针的类型和大小。例如,&a[0] + 1
是数组第二个元素的地址,但它的类型是指针,因此 sizeof(&a[0] + 1)
返回的是指针占用的字节数,而不是数组第二个元素的大小。
指针可以作为函数的参数或返回值,这使得函数可以操作内存中的数据。
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int *create_array(int size) {
int *array = (int *)malloc(size * sizeof(int));
return array;
}
指针可以与结构体结合使用,这使得我们可以操作结构体的成员。
struct stu {
int num;
char name[10];
int age;
};
struct stu *p = (struct stu *)malloc(sizeof(struct stu));
p->num = 1001;
strcpy(p->name, "Alice");
p->age = 20;
struct stu *p = (struct stu *)malloc(3 * sizeof(struct stu));
p[0].num = 1001;
strcpy(p[0].name, "Alice");
p[0].age = 20;
p[1].num = 1002;
strcpy(p[1].name, "Bob");
p[1].age = 21;
p[2].num = 1003;
strcpy(p[2].name, "Charlie");
p[2].age = 22;
指针可以用于动态内存分配,这使得我们可以根据需要分配和释放内存。
malloc
函数用于动态分配内存,它返回一个指向分配的内存的指针。例如:int *array = (int *)malloc(10 * sizeof(int));
free
函数用于释放动态分配的内存。例如:free(array);
指针可以用于处理字符串,这使得我们可以操作字符数组。
char *str = "Hello, World!";
printf("%s\n", str);
char *strs[] = {"Hello", "World", "C", "Language"};
printf("%s\n", strs[0]);
指针可以用于处理多维数组,这使得我们可以操作多维数组的元素。
int array[2][3] = {{1, 2, 3}, {4, 5, 6}};
int (*p)[3] = array;
printf("%d\n", p[0][1]);
int **array = (int **)malloc(2 * sizeof(int *));
for (int i = 0; i < 2; i++) {
array[i] = (int *)malloc(3 * sizeof(int));
}
array[0][0] = 1;
array[0][1] = 2;
array[0][2] = 3;
array[1][0] = 4;
array[1][1] = 5;
array[1][2] = 6;
printf("%d\n", array[0][1]);
指针是 C 语言中一个非常重要的概念,它允许我们直接操作内存地址。通过指针,我们可以实现高效的数据操作和复杂的程序功能。在使用指针时,需要注意 sizeof
的返回值、指针的大小、数组与指针的区别以及地址运算的陷阱。此外,指针还可以与函数、结构体、动态内存分配、字符串和多维数组结合使用,实现更强大的功能。希望这篇文章能帮助你更好地理解和使用指针。