认识指针
内容来自《深入理解C指针》
声明指针
在数据类型后面跟上星号*
,如下的声明都是等价的
int* pi;
int * pi;
int *pi;
int*pi;
阅读声明
如下:
const int *pci;
1.
pci
是一个变量 const int *pci
;
2.pci
是一个指针变量 const int*pci
;
3.pci
是一个指向整数的指针变量 constint *pci
;
4.pci
是一个指向整数常量的指针变量const int *pci
;
地址操作符
地址操作符&
会返回操作数的地址
int num;
int *pi = #
打印指针的值
int num = 0;
int *pi = #
printf("Address of num: %p Value: %d\n", &num, num);
printf("Address of pi: %p Value: %p\n", &pi, pi);
输出可能如下:
Address of num: 0x7ffeefbff51c Value: 0
Address of pi: 0x7ffeefbff510 Value: 0x7ffeefbff51c
在不同的平台上用一致的方式显示指针的值比较困难。一种方法是把指针转换为void
指针,然后使用%p
格式说明符来显示:
printf("Value of pi: %p\n", (void *)pi);
间接引用操作符
间接引用操作符(*
)返回指针变量指向的值
NULL
NULL
被赋值给指针就意味着指针不指向任何东西
NULL
宏是强制类型转换为void
指针的整数常量0
,在很多库中的定义如下:
#define NULL ((void *)0)
测试指针是否设置为NULL
。如果pi
被赋值为NULL
值,那就会被解释为二进制0
pi = NULL;
if (pi) {
printf("pi is null\n");
} else {
printf("pi is not null\n");
}
或者
if (pi == NULL) {
}
if (pi != NULL) {
}
void指针
void
指针是通用指针,用来存放任何数据类型的引用
void *pv;
任何指针都可以被赋给void
指针,它可以被转换回原来的指针类型,指针的值和原指针的值是相等的
int num = 100;
int *pi = #
printf("Value of pi: %p\n", pi);
void *pv = pi;
pi = (int *)pv;
printf("Value of pi: %p\n", pi);
输出如下,显示指针地址是一样的:
Value of pi: 0x7ffeefbff51c
Value of pi: 0x7ffeefbff51c
void
指针只能用做数据指针,而不能用做函数指针
sizeof
操作符可以用在void
指针上
size_t size = sizeof(void *);
size_t
是用来表示长度的数据类型
全局和静态指针
指针被声明为全局或静态,就会在程序启动时被初始化为NULL
int *globalpi;
void foo() {
static int *staticpi;
}
int main(int argc, const char * argv[]) {
return 0;
}
size_t
size_t
类型表示C中任何对象所能达到的最大长度。是无符号整型,负数在这里没有意义
size_t
用做sizeof
操作符的返回值类型,同时也是很多函数的参数类型,如malloc
和strlen
size_t
的典型定义如下:
size_t
推荐的格式说明符是%zu
size_t sizet = 5;
printf("%d\n", sizet);
printf("%zu\n", sizet);
sizeof
操作符可以用来判断指针的长度
printf("Size of *char: %d\n", sizeof(char *));
intptr_t和uintptr_t
intptr_t
和uintptr_t
用来存放指针的地址。
它们提供了一种可移植且安全的方法声明指针,而且和系统中使用的指针长度相同,对于把指针转化成整数形式来说很有用
uintptr_t
是intptr_t
的无符号版本
指针操作
给指针加上整数
给指针加上一个整数实际上加的数是这个整数和指针数据类型对应字节数的乘积
int vector[] = {
28, 41, 7};
int *pi = vector;
printf("%d\n", *pi); //28
pi += 1;
printf("%d\n", *pi); //41
pi += 1;
printf("%d\n", *pi); //7
这里的数组名字vector
,是数组的地址,也是数组第一个元素的地址
从指针减去整数
和加法类似,减去整数时,地址值会减去数据类型的长度和整数值的乘积
int vector[] = {
28, 41, 7};
int *pi = vector + 2;
printf("%d\n", *pi); //7
pi -= 1;
printf("%d\n", *pi); //41
pi -= 1;
printf("%d\n", *pi); //28
指针相减
指针相减的差值通常没什么用,但可以判断数组中的元素顺序
ptrdiff_t
类型表示指针差值的可移植方式
指针的常见用法
多层间接引用
把变量声明为指针的指针,有时称之为双重指针
#include <stdio.h>
int main(int argc, const char * argv[]) {
char *titles[] = {
"A Tale of Two Cities",
"Wuthering Heights",
"Don Quixote",
"Odyssey",
"Moby Dick",
"Hamlet",
"Gulliver's Travels"
};
char **bestBooks[3];
char **englishBooks[4];
bestBooks[0] = &titles[0];
bestBooks[1] = &titles[3];
bestBooks[2] = &titles[5];
englishBooks[0] = &titles[0];
englishBooks[1] = &titles[1];
englishBooks[2] = &titles[5];
englishBooks[3] = &titles[6];
printf("%s\n", *englishBooks[1]); //Wuthering Heights
return 0;
}
如下的声明:
char **bestBooks[3];
char **englishBooks[4];
两个数组都声明为字符指针的指针。每个数组元素包含一个指向char
指针的指针
本例的内存分配如下:
常量和指针
指向常量的指针
将指针定义为指向常量,意味着不能通过指针修改它所引用的值,但可以改变指针
如下的语句是等价的:
const int *pci
int const *pci
指向非常量的常量指针
指向非常量的常量指针意味指针不可变,但它指向的数据可变
指向常量的常量指针
这种指针本身不能修改,指向的数据也不能通过它来修改
const int * const pci = #
指向“指向常量的常量指针”的指针
指向常量的指针也可以有多层间接引用
int num = 100;
const int * const pci = #
const int * const *pcpci;
printf("%d\n", *pci); //100
pcpci = &pci;
printf("%d\n", **pcpci); //100