CDay07
指针基础
计算机的最小寻址单位:字节
变量的地址:变量第一个字节的地址
指针:简单来说指针就是地址
指针变量
指针变量:存放地址的变量,有时候也把指针变量称为指针
思考:指针变量只是存放变量的首地址,那怎么通过指针访问指针指向的对象?
答:声明时需要指明指针的基础类型(指针指向对象的类型),这样既知道了变量的首地址也知道了这个变量所占的内存大小,便可以访问此对象了。
int* p;
int 是指针的基础类型
*p 相当于变量 i 的别名,修改 *p 相当于修改 i
注意事项:
1、指针变量是 p ,不是 *p
2、指针变量类型是int*,而不是int
两个基本操作:&(取地址) 和 *(解引用)
例:
//取地址
int i = 1;
int *p;
p = &i;
解引用:通过指针访问指针指向的对象
通过 i 去访问:直接访问(访问一次内存)
通过 *p 去访问:间接访问(访问两次内存)
野指针问题
野指针:未初始化或指向未知区域的指针
例:
int *p1;
int *p2 = 0x7F;
对野指针进行解引用运算,会导致未定义的行为!
未定义的行为:程序可能崩溃也可能正常运行
指针变量的赋值
- 通过取地址符赋值,p = &i
- 通过另外一个指针赋值,p = q
注意事项:p = q 和 *p = *q 的区别
p = q:
*p = *q:
指针作为参数传递
指针作为参数传递的好处:可以在被调函数中访问并修改主调函数的参数。
示例:
#include<stdio.h>
void swap(int* pa, int* pb);
int main(void)
{
int a = 3, b = 4;
printf("交换前:a = %d, b = %d\n", a, b);
swap(&a, &b); //pa = &a, pb = &b
printf("交换后:a = %d, b = %d\n", a, b);
return 0;
}
void swap(int* pa, int* pb)
{
int tmp = *pa;
*pa = *pb;
*pb = tmp;
}
指针和数组的关系
指针的算术运算
当指针指向数组元素时,可以通过指针的算术运算访问数组的其他元素
- 指针加上一个整数,结果为指针
- 指针减去一个整数,结果为指针
- 两个指针相减(这两个指针应该指向同一个数组的元素),结果为整数
示例:
#include<stdio.h>
int main(void)
{
int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
int* p = &arr[2];
int* q = p + 3;
p += 6;
printf("*p = %d,*q = %d", *p, *q);
return 0;
}
运行结果:
用指针处理数组
#include<stdio.h>
int main(void)
{
int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
int sum = 0;
for (int* p = arr; p < &arr[10]; p++)
{
//&arr[10]只会计算地址,并不会访问arr[10],因此不会发生数组越界现象
sum += *p;
}
printf("sum = %d\n", sum);
return 0;
}
注意 * 和 ++/-- 的组合形式:
-
*p++等价于*(p++):表达式的值为 *p,副作用为 p 自增。
-
(*p)++:表达式的值为*p,副作用为 *p 自增。
-
*++p 等价于 *(++p):表达式的值为 *(p + 1),副作用为 p 自增。
-
++*p 等价于 ++(*p):表达式的值为 (*p) + 1,副作用为 (*p) 自增。
-
* 与 -- 的组合同理。
数组名可以作为指针使用(指向数组的第一个元素);指针也可以作为数组名使用。
即 p[i] = *(p + i)
字符串
C语言中没有专门的字符串类型,C语言中的字符串依赖字符数组存在。(C语言中的字符串是一个逻辑类型)
字符串的字面值
字符串的两种书写方式:
printf("I love xjl\n");
printf("I love xjl"
"--by cz"); //在编译时,编译器会将两个相邻(两个字符串字面值之间仅有空白字符)字符串合并成一个
字符串字面值在内存中如何存储?
"abc" -----> (以空字符结尾)
'a' | 'b' | 'c' | '\0' |
---|
"" ----->
'\0' |
---|
字符串变量
初始化
char name[10] = "Allen"; //浪费存储空间
char name[5] = "Allen"; //编译可通过,但是使用时会出现问题
char name[] = "Allen"; //推荐使用
字符串的初始化和字符指针的初始化
读写字符串
写
- printf + %s
- puts函数
注意:puts函数会在字符串后自动添加换行符
读
- scanf + %s
- gets函数
scanf + %s
匹配规则:跳过前置的空白字符,读取字符存入数组,直到遇到空白为止,并在后面添加 '\0'
缺陷
- 不能读取空白字符
- 不会检查数组是否越界
gets函数
匹配规则:不会跳过前置的空白字符,读取字符存入数组,直到遇到换行符为止,并将换行符替换成 '\0'
缺陷:
- 不会检查数组是否越界(可能会越界写入数据,造成未定义的行为)
以上两种读字符串的方法均不推荐,建议自己创造函数,做越界处理
字符串的库函数
strlen函数
//在标头 <string.h> 定义
size_t strlen( const char *str );
不会计算空字符
strcmp函数
int strcmp( const char *lhs, const char *rhs );
按照字典顺序比较(ASCII)
若字典序中 lhs 先出现于 rhs 则为负值。
若 lhs 与 rhs 比较相等则为零。
若字典序中 lhs 后出现于 rhs 则为正值。
strcpy函数
strcat函数
惯用法
//搜索字符串末尾
//str指向空字符
while(*str)
{
str++;
}
//str指向空字符后一位
while(*str++)
;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了