内存和地址
内存单元的编号 == 地址 == 指针
指针p的3个相关值
1.p:变量p里面放着一个地址(p叫做指针变量)
2.*p:p指向的那个对象
3.&p:表示的是p变量的地址
指针变量和地址
int a = 10;
printf("%d",&a); //&是取地址符
指针变量怎么声明+初始化?
方法一
int * p = &a; //这里*表示p是指针变量,int表示p指向的变量a的类型是int,p的类型是int*
*p = 200; // 这里*是解引用操作符(间接访问操作符)
方法二(分开写)
int * p;
p = &a;
*p = 200;
指针变量类型的意义
指针变量大小 == 一个地址存放空间大小
1.指针类型决定了,解引用的时候访问多大空间
2.指针类型决定了,在指针进行整数倍的加或减的时候,向前或向后偏移多大距离
int* pa : pa + 1 -----> +1*sizeof(int)
pa + n -----> +n*sizeof(int) 如果是+n就是向后偏移n个整型字节
void* : 无具体类型的指针(泛型指针),不能进行解引用操作,无法进行指针运算
const修饰指针变量
被const修饰的变量就成了常变量
int m = 0;
m = 10; //可以这样写,变量m可以被修改
const int n = 0;
n = 5 //错误,不能这样写,常变量不能被修改
int * p;
int count * p; //count可以放在*左边或者右边
int main(){ int m = 10; int n = 100; int *p = &m; *p = 20; p = &n; //以上代码如图 int const * p = &n; *p = 20 //报错,const限制* p,说明 p指向的对象不能改变 p = &n; //正确,const没有限制 p,可以改变 p本身的地址 int * const p = &n; *p = 20 //正确,const在*右边,没有限制* p p = &n; //报错,限制 p return 0; }指针运算
1. 指针 +- 整数
2. 指针 - 指针
3. 指针的关系运算
1. 指针 +- 整数
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> int main() { //指针 + 整数 int arr[5] = { 1,2,3,4,5 }; int len = sizeof(arr) / sizeof(arr[0]); int* p = &arr[0]; for (int i = 0; i < len; i++) { printf("%d ", *(p+i)); } }2.指针 +- 指针
1. 指针 + 指针 跟日期+日期一样没有意义
2. 指针 - 指针 的绝对值 是 指针和指针之间的元素个数
指针 - 指针 计算的前提条件是 两个指针指向的是同一个空间
示例:使用指针 - 指针计算数组长度
#include <stdio.h> int main() { int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // 获取数组首元素地址 int *start = &arr[0]; // 获取数组末尾元素的下一个位置地址 int *end = &arr[10]; // 注意:arr[10]不存在,但&arr[10]是合法地址(数组末尾之后) // 指针 - 指针 = 两个指针之间的元素个数 int length = end - start; printf("数组长度: %d\n", length); // 输出: 数组长度: 10 // 另一个例子:计算中间部分元素个数 int *mid = &arr[3]; int elements_between = end - mid; printf("从arr[3]到数组末尾的元素个数: %d\n", elements_between); // 输出: 7 return 0; }解释:
start指向数组第一个元素arr[0]end指向数组最后一个元素arr[9]之后的位置(即arr[10]的地址,虽然这个元素不存在)end - start的结果是 10,表示两个指针之间有 10 个int类型的元素- 指针减指针的结果类型是
ptrdiff_t(有符号整数),表示元素个数,而不是字节数
3.指针的关系运算(比较大小)
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; int sz = sizeof(arr) / sizeof(arr[0]); int* p = &arr[0]; while (p < &arr[sz]) { printf("%d", *p); p++; } return 0; }野指针
1.指针未初始化
int *p;
*p = 10; //会报错,这里的p叫野指针,解引用操作符会形成非法访问
2.指针越界访问
int arr[10] = {0};
int *p = &arr[0];
for(int i = 0;i <= 10;i++){
*p = i;
p++; //这里越界访问了
}
3.指针指向的空间释放
#include <stdio.h> int main() { int num = 42; // 静态分配的整型变量 int *p = # // 指针指向num的地址 printf("指针指向的值: %d\n", *p); // 静态变量无需手动释放,此处仅演示指针的“释放”逻辑(将指针置为NULL) p = NULL; // 避免悬垂指针 if (p == NULL) { printf("指针已释放(置空)\n"); } return 0; }规避野指针
1.指针初始化
int *p = NULL;
2.小心指针越界,指针不能超出使用范围
3.指针变量不再用的时候,要把它置为空值
#include <stdio.h> int main() { int num = 10; int *p = # printf("数值: %d\n", *ptr); p = NULL; // 使用完毕置空 if(p == NULL) { printf("指针已置空\n"); } return 0; }assert断言
#include<assert.h>int
int main(){
int a = 10;
int *p = NULL;
assert( p != NULL);
return 0;
}
assert里面的表达式为假就报错,为真就正常运行
如果不想要宏assert()那就在assert头文件前面定义一个宏NDEBUG
#include NDEBUG
注意: VS里的Release版本禁用assert ,不影响用户使用的时候程序的效率,Debug版本写利于程序员排错
指针的使用和传址调用
Strlen的模拟实现
strlen:求字符串 \0 之前的长度
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include<assert.h> //strlen函数的实现 size_t my_strlen(const char* s) { //不想改变数组里的数,常变量 size_t count = 0; //返回值类型和函数保持一致 assert(s != NULL); //指针s不能为空 while (*s) { //只要*s不为\0(\0的ASCLL码是0),就执行循环体 count++; s++; } return count; } int main() { char arr[] = "abcdef"; size_t len = my_strlen(arr); //记录的大小永远为非负整数,用size_t printf("%zd", len); //size_t必须用%zd return 0; }传值调用和传址调用
传值调用:不需要改变原变量,只需要读取变量的值来计算
传址调用:需要修改主函数里原始变量的值
( 解决问题非指针不可的情况 )
练习题:写一个函数,交换两个整型变量的值
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> void swap1(int a,int b) { //传值调用 int z = 0; z = a; a = b; b = z; } int main() { //写一个函数,交换两个整型变量的值 int a = 9; int b = 8; printf("交换前:a = %d b = %d\n", a, b); printf("%d %d\n", &a, &b); swap1(&a, &b);//传值调用 printf("交换后:a = %d b = %d\n", a, b); printf("%d %d", &a, &b); }void swap2(int *pa,int *pb) { //传址调用 int z = 0; z = *pa; *pa = *pb; *pb = z; } int main() { //写一个函数,交换两个整型变量的值 int a = 9; int b = 8; printf("交换前:a = %d b = %d\n", a, b); printf("%d %d\n", &a, &b); swap2(&a, &b);//传址调用 printf("交换后:a = %d b = %d\n", a, b); printf("%d %d", &a, &b); }