大家好呀!👋这个是付青云同学的博客,是一名大一在校生哦!😁😁
目前一直在学习C语言。🐸
写博客是为了来记录我的学习过程,同时也希望通过博客能够帮助到需要帮助的人。
如果我的博客可以帮助到你,不妨给我一个关注哦😁
初识C语言
接上文
这个是初级指针的笔记
这里使用的软件是vs2022
文章目录
指针
什么是指针
在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向存在电脑储存器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。意思是通过它能找到以它为地址的内存单元。
所以总的来说:指针=地址
%p - 用来打印地址
关于内存
内存是电脑上特别重要的存储器,计算机中所有程序的运行都是在内存中进行的。
为了有效使用内存,就把内存划分为一个个小的内存单元,每个内存单元的大小是1字节(byte)。
为了能够有效的访问到内存的每个单元,就给内存进行了编号,这些编号被称为该内存的地址。
数据在内存中
负数:
存放在内存中
存放的是二进制的补码
-1:
原码:100000000000000000000001
反码:111111111111111111111111110
补码:111111111111111111111111111
正整数:
原码、补码、反码相同
其中整数在内存中储存的是补码
整数的二进制表示形式
整数二进制有3种表示形式:原码、反码、补码
- 原码:直接根据数值写出的二进制序列就是原码
- 反码:原码的符号位不变,其他位按位取反就是原码
- 补码:反码+1,就是补码
而为什么数值用补码储存呢?
原因在于:
- 使用补码,可以将符号位和数值域统一处理;
- 同时,加分和减法也可以统一处理(CPU只有加法器);
- 此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
关于32位:
00000000000000000000000000000000
00000000000000000000000000000001
00000000000000000000000000000010
.
.
1111111111111111111111111111111111111
共2**32组合
64位同理
字节序
大端(储存)模式
是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中
小端(储存)模式
是指数据的低位保存在内存的低地址中,而数据的高位,保存在内存的低地址中
为什么
指针变量
*的作用
指针的解引用
指针变量的大小
如上图可以看到:指针变量的大小都是相同的
为什么呢?
指针是用来存放地址的
指针需要多大空间,取决于地址的存储需要多少空间
32位 32bit — 4byte
64位 64bit — 8byte
指针和指针类型
指针类型的意义
指针类型决定了:指针解引用的权限有多大
char*(1bit) int*(4bit)
野指针
野指针,就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
野指针成因
指针未初始化
int main()
{
int* p;//p是一个局部的指针变量,局部变量不初始化的话,默认是随机值
*p = 20;//非法访问内存了
return 0;
}
指针越界访问
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i <= 10; i++)
{
//指针越界访问
*p = i;
p++;
}
指针指向的空间释放
如何规避野指针
指针初始化
int* p = NULL;//当不知道如何初始化时用NULL
小心指针越界
C语言本身是不会检查数据的越界行为的
指针指向空间释放及时置NULL
指针使用之前检查有效性
if(p != NULL)
{
*p = 10;
}
指针运算
指针±整数
指针-指针
两个指针相减得到的是它们之间元素的个数
int my_strlen(char* arr)
{
char* p = arr;
while( *arr != '\0' )
{
arr++;
}
return arr - p;//利用两个指针相减求字符串长度
}
int main()
{
char arr[] = "abc";
int len = my_strlen(arr);
return 0;
}
指针的关系运算
标准规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较
二级指针
int main()
{
int a = 20;
int* pa = &a;//pa是指针变量,一级指针
//ppa就是二级指针变量
int* *ppa = &pa;//pa也是个变量,&pa取出pa在内存中起始地址
//*ppa == pa
//*pa == a
//* *ppa == a
return 0;
}
指针数组
数组名是数组首元素地址
// arr[2] <==> *[arr+2] <==> 2[arr]
// p[2]--> *(p+2)
定义
int arr[10];//整形数组 - 存放整型的数组
char ch[5];//字符数组 - 存放字符的数组
//指针数组 - 存放指针的数组
int* parr[5];//整型指针的数组
char* pch[5];//字符指针的数组
关于调试的技巧
快捷键
F5:启动调试
CTRL+F5:开始执行不调试
F9:设置断点:
断点还可以设置条件
F10:逐过程
F11:逐语句
寄存器
调用堆栈
局部变量是放在栈区上的
栈区内存的使用习惯是(注意与数据结构的栈区分):
先使用高地址空间,再使用低地址空间
数组随着下标增长地址是由低到高变化的
int main()
{
int i=0;//栈区地址是由高到低存放的
int arr[]={1,2,3,4,5,6,7,8,9,10};//所以数组下标在增长的过程中可能越界访问到i
for(i=0;i<=12;i++)//此循环为死循环
{
arr[i]=0;//经vs2019调试,i的地址与arr[12]地址相同。
//在vs2022中,此问题好像解决了
printf("hehe\n");
}
return 0;
}
常见的错误
编译型错误
语法错误
一般可双击错误信息查看错误解决
链接型错误
- 可能函数未定义
- 标识名错误
- 符号写错或不存在
运行时错误
借助调试,逐步定位问题
初级C语言笔记完…