【c语言】初阶指针
目录
1.指针是什么
2.指针和指针类型
2.1 指针的解引用
2.2 指针+-整数
3.野指针
3.1 野指针成因
3.2 如何规避野指针
4.指针的运算
4.1 指针+-整数
4.2 指针-指针
4.3 指针的关系运算
5.指针和数组
6.二级指针
7.指针数组
1.指针是什么
1. 指针是内存中一个最小单元(一个字节)的编号,也就是地址,每个内存单元有唯一的地址标识2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量内存单元的编号=地址=指针
int main() {
int a = 10;//向内存中的栈区申请4个字节的空间,用这4个字节的空间存放10这个数值
int* pa = &a;//将a的4个字节的第一个字节的地址存放在p变量中,p就是一个指针变量
return 0;
}
注意:存放在指针中的值都被当成地址处理
地址的产生:对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电平(低电压)就是1或者0;那么32根地址线产生的地址就会是:2^32个地址。每个地址标识一个字节,那我们就可以给(2^32Byte == 2^32/1024KB == 2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB) 4G的空间进行编址。则64位的机器可以给8GB的空间编址。在32位(X86)的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节。那如果在64位(X64)机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。指针的大小在 32 位平台是 4 个字节,在 64 位平台是 8 个字节
指针变量的定义:int *p,*q(可以同时定义多个)或者int* p
2.指针和指针类型
2.1 指针的解引用
指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。
int main()
{
int n = 0x11223344;//两个16进制为一个字节
char* pc = (char*)&n;//强制类型转换
int* pi = &n;
*pc = 0;
*pi = 0;
return 0;
}
char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。
八位二进制 / 两位16进制是一个字节
2.2 指针+-整数
指针的类型决定了指针向前或者向后走一步有多大(距离)
#include <stdio.h>
int main()
{
int n = 10;
char *pc = (char*)&n;
int *pi = &n;
printf("%p\n", &n);
printf("%p\n", pc);
printf("%p\n", pc+1);//pc向后跨一个字节
printf("%p\n", pi);
printf("%p\n", pi+1);//pc向后跨四个字节
return 0; }
3.野指针
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
int main() {
int* p = (int*)0x11223344;//将整型转换为int*类型
*p;//解引用——野指针(编造的地址)
return 0;
}
3.1 野指针成因
1.指针变量未初始化
2.指针越界访问
*p = -1;p++;
可以写成:*p++ = 1;等价于*(p++) = 1;
//++的优先级高于间接访问操作符,但p++的结果是p,解引用得到原指针指向的变量
3.指针指向的空间释放
#include <stdio.h>
//空间释放
int* test() {
int a = 0;
return &a;
}
int main() {
int* p = test();
//空间回收;p变为野指针
printf("%d\n", *p);
return 0;
}
3.2 如何规避野指针
1. 指针初始化
- 如果明确指针应该指向哪里,就初始化正确的地址;int a = 10;int* p = &a;
- 如果指针不知道初始化什么值,为了安全,初始化NULL(空指针——0(0作为地址,用户程序是不能访问的))
2. 小心指针越界3. 指针指向空间释放,及时置NULL4. 避免返回局部变量的地址5. 指针使用之前检查有效性
4.指针的运算
4.1 指针+-整数
使用指针打印数组内容
#include <stdio.h>
int main() {
int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
int* p = arr;
for (int i = 0; i < 10; i++) {//数组在内存中连续存放
printf("%d", *(p + i));
//p指向的是数组首元素
//p+i是数组下标为i的元素的地址
//p+i跳过了i*sizeof(int)个字节
//arr==p
//arr+i==p+i
//*(arr+i)==*(p+i)==arr[i]
//*(i+arr)==*(i+p)==i[arr]——加法交换律3+5
//上述变换说明了数组访问的本质(首元素地址+偏移量)以及操作符的本质
// 也可以写成:
//printf("%d", *(arr + i));
//printf("%d", arr[i]);
//printf("%d", i[arr]);
}
return 0;
}
4.2 指针-指针
指针-指针的前提:两个指针指向同一块区域,指针类型一定相同
指针-指针差值的绝对值是指针和指针之间的元素个数
#include <stdio.h> int main() { int arr[10] = { 0 }; printf("%d\n", &arr[9] - &arr[0]); printf("%d\n", &arr[0] - &arr[9]); return 0; }
模拟实现strlen
1.计数器
2.递归3.指针-指针
#include <stdio.h>
size_t my_strlen(char* str) {
char* start = str;
while (*str != '\0') {
str++;
}
return str - start;
}
int main() {
char arr[] = "abcdef";
size_t len = my_strlen(arr);
printf("%zd\n", len);
return 0;
}
4.3 指针的关系运算
#define N_VALUES 5
float values[N_VALUES];
float *vp;
for (vp = &values[0]; vp < &values[N_VALUES];)
{
*vp++ = 0; }
for(vp = &values[N_VALUES]; vp > &values[0];)
{
*--vp = 0; }
//最后一个与&values[0]比较的指针vp指向数组前
改变每一个元素的指针指向该元素的后面
代码简化:但是应该避免这样写,因为标准并不保证它可行。
for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--) {
*vp = 0; }
标准规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。![]()
p可以与p2比较,但与p1比较可能不安全 为什么这么规定?
c++解释,数组前可能存在一片区域储存数组的大小以维护数组,指针访问数组最好从前向后遍历
5.指针和数组
指针就是指针变量,存放一个地址,指针变量的大小:4/8
数组就是数组,可以存放一组数,数组的大小取决于元素的类型和个数
联系:
数组的数组名是数组首元素的地址,这个地址可以放在指针中;
int arr[] = {0,1,2,3,4,5,6,7,8,9};
int* p = arr;
通过指针可以访问数组的元素;
arr与p的相同点:*(arr+i)==*(p+i)==arr[i]==p[i]
注意:arr与p的不同点:p++;(正确,p是指针变量)arr++;(error,arr是常量)
数组名表是数组首元素的地址,但是有两个例外:
- sizeof(数组名),数组名单独放在sizeof内部,表示整个数组,计算的是数组大小,单位是字节
- &数组名,此时数组名表示整个数组,取出的是数组的地址,数组的地址和数组首元素的地址值相同,但是类型和意义不同
#include <stdio.h>
int main() {
int arr[] = { 1,2,3,4,5,6,7,8 };
//数组首元素的地址
printf("%p\n", arr);
printf("%p\n", arr+1);//跳过4个字节
printf("%p\n", &arr[0]);
printf("%p\n", &arr[0]+1);//跳过4个字节
//整个数组的地址
printf("%p\n", &arr);
printf("%p\n", &arr+1);//跳过32个字节
return 0;
}
6.二级指针
二级指针变量是存放一级指针的地址
#include <stdio.h>
int main() {
int a = 10;
int* p = &a;//一级指针变量
int* * pp = &p;//二级指针变量
//第一个*与int一起表示pp所指变量的类型为int*,第二个*表示pp为指针变量
//int** * ppp = &pp;
//.....
**pp = a;
return 0;
}
7.指针数组
指针数组是数组,是存放指针的数组。eg:int* arr3[5];——存放整型指针的数组,五个元素,每个元素是int*类型
使用指针数组模拟一个二维数组
#include <stdio.h>
int main() {
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 2,3,4,5,6 };
int arr3[] = { 3,4,5,6,7 };
int* arr[] = { arr1,arr2,arr3 };
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 5; j++) {
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
//不是二维数组:二维数组在内存中连续存放