指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。
不同语境下的指针:
1.定义一个指针,是指定义一个变量,数据类型是指针类型,这个变量里面存放地址,也就是内存单元的编号。
2.打印某个变量的指针,此时指针指的是地址
变量的指针就是变量的存储地址,指针变量就是存储指针的变量。
基类型* 指针变量名;
基类型 --- 数据类型
//基本数据类型
//数组类型
//基类型 表示 指针变量 指向的目标的 数据类型
指针变量初始化时,最好赋值“NULL”或“0”,用来防止野指针出现,如果赋值“0”,此时的“0”含义并不是数字“0”,而是 NULL 的字符码值。
指针变量再内存中的大小:在64位系统中占8字节,在32位系统中占4字节。
指针的核心功能:被调修改主调
1.想修改谁,就把谁的地址传过去
2.必须要做*运算(间接访问),实现修改
取地址运算符&:单目运算符&是用来取操作对象的地址。例:&i 为取变量 i 的地址。对于常量表达式、寄存器变量不能取地址(因为它们存储在存储器中,没有地址)。
指针运算符*(间接寻址符):与&为逆运算,作用是通过操作对象的地址,获取存储的内容。例:x = &i,x 为 i 的地址,*x 则为通过 i 的地址,获取 i 的内容。
&和* 的优先级相同,结合性自右向左。
*&p
表示对指针 p
解引用后再取其地址。也就是说,首先 *p
表达式会获取 p
指向的值,然后 &
运算符获取该值的地址。结果是对指针 p
自身的地址取值,类型仍然是指向 p
的指针。当p不是指针类型的变量时,此时*&抵消掉了。
&*p(p必须是指针)
表示对指针 p
解引用后再取其地址。与上面的情况不同的是,对 *p
解引用后直接获取的是 p
指向的内容,然后 &
运算符获取这个内容的地址。结果是一个新的指针,指向跟 p
指向的内容相同。
//声明了一个普通变量 a
int a;
//声明一个指针变量,指向变量 a 的地址
int *pa = NULL;
//通过取地址符&,获取 a 的地址,赋值给指针变量
pa = &a;
//通过间接寻址符,获取指针指向的内容
printf("%d", *pa);
*p 过程
1.首先拿出p指针变量中的值(地址) 到内存中定位
2.从定位处开始,偏移出sizeof(基类型)大小的一块空间
3.把这块空间当做一个 基类型的 变量来看
int *p,q; //p是指针变量 q是int型变量
可以对指针进行四种算术运算:++、--、+、-
p-q
前提: 同一类型的指针
表示之间差了几个基类型 常用在数组中
p+q //指针不能做加法运算
p+1 //指向了下一个一基类型的数据
- 指针变量的自增自减运算。指针加 1 或减 1 运算,表示指针向前或向后移动一个单元(不同类型的指针,单元长度不同)。这个在数组中非常常用。
- 指针变量加上或减去一个整形数。和第一条类似,具体加几就是向前移动几个单元,减几就是向后移动几个单元。
打印某个变量的地址 %p
#include
int main() { int num = 42; printf("变量 num 的地址是:%p\n", &num); return 0; }
指针的关系运算
相等性比较 (==
):比较两个指针是否指向同一个对象或地址。
不等性比较 (!=
):比较两个指针是否不指向同一个对象或地址。
大小关系比较 (<
, <=
, >
, >=
):比较两个指针所指向的地址大小。
#include
int main() {
int num1 = 42;
int num2 = 88;
int* ptr1 = &num1;
int* ptr2 = &num2;
int* ptr3 = &num1;
if (ptr1 == ptr2) {
printf("ptr1 和 ptr2 指向相同的对象\n");
} else {
printf("ptr1 和 ptr2 指向不同的对象\n");
}
if (ptr1 != ptr2) {
printf("ptr1 和 ptr2 指向不同的对象\n");
} else {
printf("ptr1 和 ptr2 指向相同的对象\n");
}
if (ptr1 == ptr3) {
printf("ptr1 和 ptr3 指向相同的对象\n");
} else {
printf("ptr1 和 ptr3 指向不同的对象\n");
}
if (ptr1 < ptr2) {
printf("ptr1 的地址位于 ptr2 之前\n");
} else {
printf("ptr1 的地址位于 ptr2 之后\n");
}
return 0;
}
在数组中,数组名即为该数组的首地址,且数组名本身就是一个指针常量。结合上面指针和整数的加减,我们就可以实现指针访问数组元素。
数组名不等价于指针变量,指针变量可以进行 p++和&操作,而这些操作对于数组名是非法的。数组名在编译时是确定的,在程序运行期间算一个常量
通过指针访问到数组元素:
*(p+i) <=> int型的变量 <=> a[i] <=>p[i]<=> *(a + i) ,i是偏移量
逆序
使用指针迭代的方法实现二分查找
用递归的方法实现二分查找
使用指针迭代的方法分别实现选择、冒泡、插入排序
注意:第二三步不能反过来
size_t Strlen(const char *s)
{
int cnt = 0;
while (*s!='\0')
{
cnt++;
++s;
}
return cnt;
}
char * Strcat(char *dest, const char *src)
{
char *ret = dest;
while (*dest != '\0')
dest++;
while((*dest = *src) != '\0')
{
dest++;
src++;
}
return ret;
}
char *Strcpy(char *dest,const char *src)
{
char *ret = dest;
while (*dest = *src)
{
dest++;
src++;
}
return ret;
}
int Strcmp(const char *s1,const char *s2)
{
while (*s1==*s2 && *s1!='\0' && *s2 != '\0')
{
++s1;
++s2;
}
return *s1 - *s2;
}
const 关键字
“const” 关键字可以让编译器确保程序中的常量不被修改,从而提高代码的可读性和安全性。
const int *p = &a; 和int const *p = &a; 的意义一样 都是表示 基类型 为只读
int *const p = &a; //限定p为只读
const int * const p = &a; //p不能被修改,指向的目标类型不能被修改
//(是不能通过*p)
char *p="world"; 和 char *q = "world";
在内存中的地址一样, 因为他们存储在字符常量区
而 chars[ ] ="world"; 和他们不一样 存储在栈区
const char * p 可以用来保存字符串常量的地址
#include
int main() {
const char * p = "Hello, World!"; // 声明一个指向字符串常量的指针
printf("%s\n", p); // 输出字符串常量内容
return 0;
}