指针知识点

以下仅记录小白的学习进程,若有缺陷,还望各位大佬指教

1、何谓指针?

可以理解为当我们创建一个变量a,编译器会在内存中申请一个空间存放这个变量a(请参照下列图形)这个存放变量a的空间在c语言中称之为”地址“,这个地址内会自动对地址进行编号(也可以理解为门牌号)。当我们知道了你的门牌号,就可以清晰的到访这个房间,而到访你房间的这一过程就称之为指针。

上述图片中的地址则是指针变量的大小

那指针该如何书写呢?

指针的书写形式:

类型+指针变量  = &目标地址  (int a =10;int *b=&a。表示地址的类型表示指针变量

请大家试着完善以下画横线的类型?

 

  2、&和*是什么?(取地址以及解引用)

这里的int*b=&a(理解为找到了门牌号,去该房间的过程)*b存放的就是a变量。这个操作符将a的10修改为20.

接下来我将通过内存将创建和修改一并展示出来(我是按照代码的执行顺序进行板书)

将会创建一个变量a的地址,为0x0133F908(首元素的地址)

当代码执行到retrun 0;指针变量b中存放的是a的地址,通过解引用地址修改a中的元素。

指针变量大小:

1byte=8bite(一个字节是8个比特位)x86是32个bite位构成,x64是64个bite位构成

因为x64是64个比特位,一个字节是8个比特位,8个字节换算结果位64个比特位。

因为x86是32个比特位,一个字节是8个比特位,4个字节换算结果位32个比特位。

问题:既然x86中所有的类型是4,x64中所有的类型是8,那是否意味着类型可以相互通用呢?

先说结论:并不是!!!

观察上述的4张图片,会发现当代码执行到636行时,原来存储n的地址有一次性初始化4个字节,有初始化一个字节的。

(原因是int 类型是一次访问4个字节,char类型是一次访问一个字节)

总结:*就是解引用:一次访问多少个字符。 &是取地址:设置门牌号。

       

3、野指针

当一个变量未初始化,编译器会随机为该变量进行初始化值(该操作仅能在gcc类型的编译器可以使用例如devc++,小熊猫)当我们知道这一概念,指针也是如此。可以理解为随机给你一个门牌号,这个就是你的房间,这一过程称之为”非法访问“,为避免此情形,我们就需要将改变量置换为NULL(相当于把随机给你个房间号这一规则限制,只要NULL不结束,规则永不结束)

使用指针一定要初始化

指针越界访问:(超出数组定义的部分)

4、const常变量

#include<stdio.h>
int main( )
{
const int a =10;
a=20;
printf("%d",a);//猜测一下最终的结果是多少?

上述的结果为10,const相当于一把锁将变量a固定到这个房间内,假如我们要进入房间内把值修改,该如何操作呢?(通过将指针变量进行修改)可以理解为撬锁,绕过锁住的大门走小窗或者撬锁,这一过程都称之为”指针变量修改元素值“。

#include<stdio.h>
int main( )
{
const int a =10;//是一个常变量
a =20;//无法修改原参数值
int *x= &a;
*x = 20;
printf("%d",a);

const修饰指针变量(放置*的左边  const int * a =10;该指针不能指向其他地址,但是可以修改自己的地址。放置*的右边 int *const a = 10;该指针不得修改地址内容,但是可以指向其他指针。)

问题:假如我不想将a修改为20,我该将const放置在什么位置呢? 

5、assert断言 

实施条件(当条件为真,改代码将不会实现。条件为假,改代码将会实现)

头文件为(assert.h)

若不想使用可以在头文件上方定义一个#define NDEBUG

6、strlen的含义以及用法:

用法:strlen是求字符串长度的个数,以\0作为终止条件

含义:strlen是库函数,使用时需要包含string.h

#include <stdio.h>
#include<string.h>//头文件的包含

// 实现 strlen 函数
size_t my_strlen(const char *str) {
    size_t length = 0;
    while (*str != '\0') {  // 遍历字符串直到遇到终止符
        length++;
        str++;
    }
    return length;
}

int main() {
    const char *testStr = "Hello, world!";//字符串创建末尾编译器会自动加上\0.
    printf("Length of \"%s\" is %zu\n", testStr, my_strlen(testStr));  // 使用 %zu 格式说明符打印 size_t 类型
    return 0;
}

strlen的返回值为size_t(无符号型),%zu可以理解为size_t的专属打印方式。类似于你要打印整型数字,需要加上%d。

传值调用和传址调用:

该函数并没有将5和6的位置进行交换,原因是函数接受值,仅在函数内部实现交换,出了该函数编译器会将这部分空间回收,故答案为5和6.

#include<stdio.h>
void Swap(int a ,int b)
{
int temp=a;
 a =  b;
 b = temp;
}
int main( )
{
int a = 5;
int b = 6;
printf("%d %d\n",a,b);//调用前
Swap(a,b);//传值调用,
printf("%d %d\n",a,b);//调用后
return 0;
}

传址调用:(指针内存放的是地址,上文我们将a的变量10通过指针取地址修改为20,这里也是同理)

解析:

1、主函数Swap传地址给Swap函数内

2、Swap指针进行接受变量(当前指针a和b内部存放的就是5和6这个内存地址)

​
#include<stdio.h>
void  Swap(int* a, int* b)//指针变量接受
{
	int temp = *a;
	*a = *b;
	*b = temp;
}
int main()
{
	int a = 5;
	int b = 6;
	printf("%d %d\n", a, b);//调用前
	Swap(&a, &b);//传址调用
	printf("%d %d\n", a, b);//调用后
	return 0;
}

​

总结:需要修改主函数内的元素时,使用传址调用。不需要修改函数值时,使用传值调用。

7、指针+-整数

通过+整数,实现元素前移(-则是实现元素后移)。但是这些前进和后移的个数,取决于指针变量的大小。

比如:

int +1 一次只能访问4个字节、

float +1访问4个字节、

double +1访问8个字节、

char +1访问1个字节

这一点和上文的指针变量的有点儿相似.当我们知道了指针+-整数与解引用时,我们就可以通过这俩者写出一个修改数组内容的代码。

补充知识点:sizeof(数组名),&arr这俩表示整个数组的地址,除上述的俩种,其余均表示首元素

#include<stdio.h>
int main ( ) 
{

int arr[5]={10,20,30,40,50};//整型数组一个元素是4个字节
int* c = arr;//arr表示首元素的地址
*c = 200;//通过解引用将首元素的地址取出来
*(c + 1) = 20;//指针+-整数,一次访问4个字节。取出第二元素
int i = 0;
for (i = 0; i < 5; i++)
{
	printf("%d\n", arr[i]);//最后将每个元素打印出来
}

指针-指针(前提俩个指针必须指向同一块内存空间)

指针-指针算出的是元素个数(有点儿类似于前面的strlen的概念)

#include<stdio.h>
#include<string.h>
size_t My_strlen(char*p)
{
char *start = p;//起始指针 同时指向首元素arr
char *end =p;//末尾指针  同时指向首元素arr   
while(end!='\0')
{  
end++;
}
return end-start;
 }
int main( )
{
char arr[]="abcdef";
size_t p = My_strlen(arr);//arr表示首元素个数;
printf("%zu\n",p);
return 0;
}

数组名的含义:

前文提到了除sizeof(数组名)和&arr,其他都是首元素。

解析:

1、第一个先判断是否为整个数组地址,指针+-整数,访问个数取决于(   )

这里的c表示16进制数

1,2,3,4,5,6,7,8,9,A, B, C, D, E, F, G, H,I,J,K(中间差了4个字节)

2、第二个也是如此

3、判断为数组的地址,数组地址和首元素地址均是同一个地址,但是由于第三个是数组的地址,整个数组共有10个元素,一个元素占4个字节,4*10共40个字节。


 

#include<stdio.h>
int main( )
{
int arr[6]={1,2,3,4,5,6};
int *p= arr;
int sz=sizeof(arr)/sizeof(arr[0]);
int i =0;
for(i=0;i<sz;i++)
{
printf("%d",arr[i]);//arr[i] = *(arr+i)
printf("%d",*(arr+i));
printf("%d",*(i+arr));
printf("%d",i[arr]);
}
return 0;
}

一维数组传参的本质:(传的是首元素地址)

有的同学这里会说:既然函数传的是首元素地址,那就应该用指针进行接受啊!那这里写成数组的形式编译器为什么不报错呢?

在C语言中,当数组作为参数传递给函数时,它会退化为指向数组首元素的指针。这意味着在函数内部,无论你声明形参是数组还是指针,编译器实际上都会将其解释为指针。因此,虽然在函数定义中将参数声明为数组形式,但编译器仍然会将其视为指针,这就是为什么你的代码能够编译和运行的原因。解决完!!!

我们看一下sz1和sz2.   俩者打印的结果为多少呢?10?答案为10和1&&2

着重解析一下这里的1,因为函数接受的是首元素地址,x86中是4个字节,首元素地址为4

4/4=1;

当编译器为x64是,字节个数为8,8/4=2.

#include<stdio.h>
int test(int arr[10])//!!!
{
	int sz2 = sizeof(arr) / sizeof(arr[0]);
	printf("sz2 = %d", sz2);//???
}

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz1 = sizeof(arr) / sizeof(arr[0]);
	printf("%d", sz1);//???
	test(arr);//!!!
	return 0;
}

二级指针:

书写形式:地址类型  *元素名 =  &。

含义:

一级指针用来存放变量的地址,二级指针是用来存放一级指针变量的地址,当然三级指针也是如此。(可以理解为俄罗斯套娃,需要打开最外层的才能打开最里面的,一层一层相互制约,不得越界)

#include<stdio.h>
int main( )
{
int a =0;
int *p = &a;
int **pa = &p//这个就是二级指针
return 0;
}

指针数组:用来存放相同类型的值(请参照下列图片)

指针模拟二维数组的使用:

#include<stdio.h>
int main( )
{
int arr1[5]={1,2,3,4,5};
int arr2[5]={2,3,4,5,6};
int arr3[5]={3,4,5,6,7};
int *arr[ ] ={arr1,arr2,arr3};
int i =0;
for(int i =0;i<3;i++)
{
int j = 0;
for(j=0;j<5;j++)
{
printf("%d",arr[i][j]);//(*(arr+i)+j)
}
}return 0;

暂时完 

  • 29
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值