首页 > 其他分享 >【C语言有这个就够了】五.指针(1)

【C语言有这个就够了】五.指针(1)

时间:2022-10-14 21:31:50浏览次数:58  
标签:10 arr return int 就够 C语言 printf 指针

(一)指针的定义

1. 指针是内存中一个最小单元的编号,也就是地址

2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量

int main()
{
int a=10;
int* p=&a; //指针变量
return 0;
}

这样,我们就可以理解为:

通过&取出变量的内存地址,把地址放在一个变量中,这个变量就是指针变量。

总结:

指针就是变量,用来存放地址的单位变量。(存放在指针中的值,都被当做地址处理)

1.一个单元大小为一个字节

2.编址

对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电

平(低电压)就是(1或者0);那么32根地址线产生的地址就会是:

00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
...
11111111 11111111 11111111 11111111

这里就有2^32个地址;每个地址标识一个字节,那我们就可以给 (2^32Byte == 2^32/1024KB ==

232/1024/1024MB==232/1024/1024/1024GB == 4GB) 4G的空闲进行编址。


32位机器上,地址用4个字节空间来存储,一个指针大小为4字节

64为机器上,地址用8个字节空间来存储,一个指针大小为8字节


总结:

1.指针是用来存放地址的,地址是唯一标示一块地址空间的;

2.指针的大小在32位平台是4个字节,在64位平台是8个字节。

(二)指针和指针类型

#include<stdio.h>
int main()
{
printf("%d\n", sizeof(char*));
printf("%d\n", sizeof(short*));
printf("%d\n", sizeof(int*));
printf("%d\n", sizeof(double*));
return 0;
}

【C语言有这个就够了】五.指针(1)_#include

可以看到,笔者的电脑是64位,所以无论什么类型,大小都为8

那既然如此,我们为什么还要有不同类型的指针?

举个例子:

#include<stdio.h>
int main()
{
int a = 0x1122334455667788;//两个16进制位是八个2进制位正好占一个字节
int* pa = &a;
char* pc = &a;
printf("%p\n", pa);
printf("%p\n", pc);
return 0;
}

这段代码,我们发现两个输出结果相同

【C语言有这个就够了】五.指针(1)_#include_02

但细心观察会发现:

【C语言有这个就够了】五.指针(1)_i++_03

深入研究:我们发现不同类型有其独特的作用


1.决定指针进行解引用时可以访问空间的大小

  • int:

#include<stdio.h>
int main()
{
int a = 0x1122334455667788;//两个16进制位是八个2进制位正好占一个字节
int* pa = &a;
*pa=0;
return 0;
}

【C语言有这个就够了】五.指针(1)_数组_04

*pa=0后

【C语言有这个就够了】五.指针(1)_i++_05

  • char:

int main()
{
int a = 0x1122334455667788;//两个16进制位是八个2进制位正好占一个字节
char* pa = &a;
*pa = 0;
return 0;
}

【C语言有这个就够了】五.指针(1)_#include_06

*pa = 0后

【C语言有这个就够了】五.指针(1)_#include_07

我们发现int类型改变了所有字节,而char类型只改变了第一个字节

所以指针类型有意义:决定指针进行解引用时可以访问空间的大小

int*p   *p可以访问4个字节
char*p *p可以访问1个字节
doubl*p *p可以访问8个字节

2.指针类型决定了指针的步长(指针+-整数)

【C语言有这个就够了】五.指针(1)_#include_08

我们发现int类型加1后,向后跳了4个字节(跳过一个整型值的大小);

char加1后,向后跳了1个字节(跳过了一个字符的大小)。

(double 8 个字节)

例如:

int:

#include<stdio.h>
int main()
{
int arr[10] = { 0 };
int*p=arr;
//char* p = arr;
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = 1;
}
return 0;
}

【C语言有这个就够了】五.指针(1)_#include_09

char:

#include<stdio.h>
int main()
{
int arr[10] = { 0 };
//int*p=arr;
char* p = arr;
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = 1;
}
return 0;

【C语言有这个就够了】五.指针(1)_#include_10

(三)野指针

1.概念

野指针就是指指针的位置是不可知的

2.成因

2.1指针未初始化

int main()
{
int a;//局部变量不初始化,默认是随机值;
int *p; //局部的指针变量,未初始化,默认为随机值
*p=20;//内存中随便找一个地址放20
return 0;
}

2.2指针越界访问

#include <stdio.h>
int main()
{
int arr[10] = {0};
int *p = arr;
int i = 0;
for(i=0; i<=12; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*p= i;
p++;
}
return 0; }

2.3指针指向的内存空间释放

后面会细讲,这里只做简介:

int* test()
{
int a=10;
return &a;返回地址后,a被销毁,其地址释放
}

int main()
{
int *p=test();
*p=20;
return 0;
}

3.如何避免

1.指针初始化

2.小心指针越界

3.指针指向空间释放即使置NULL(NULL用来初始化指针,给指针赋值)

4. 避免返回局部变量的地址

5. 指针使用之前检查有效性

(四)指针运算

1.指针+-整数

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

此处用上了指针加整数


#define N_VALUES 5
float values[N_VALUES];
float *vp;
for (vp = &values[0]; vp < &values[N_VALUES];)
{
*vp++ = 0;
}

先将vp地址对应的值改为0,再后置++发生作用,将vp地址想后移动1。

2.指针-指针

#include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%d", &arr[9] - &arr[0]);
return 0;
}

【C语言有这个就够了】五.指针(1)_数组_11


我们可以看到指针-指针结果是两个指针之间的距离(两个指针之间元素个数加1)同时,当小地址-大地址时,结果为负:


【C语言有这个就够了】五.指针(1)_i++_12

一般情况,两个相减的指针要位于同一个数组空间;


  • 模拟实现strlen
int my_strlen(char *s) 
{
char *p = s;
while(*p != '\0' )
p++;
return p-s;
}

3.指针的关系运算

两种代码在绝大多数编译器中都可以运行,但第一种更优

for(vp = &values[N_VALUES]; vp > &values[0];)
{
*--vp = 0;
}
for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
{
*vp = 0;
}

允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与 指向第一个元素之前的那个内存位置的指针进行比较。

(五)指针和数组

关于数组名,我们看个例子:

#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
printf("%p\n", arr);
printf("%p\n", &arr[0]);
printf("%p\n", &arr);
return 0;
}

【C语言有这个就够了】五.指针(1)_#include_13

我们发现:

  • arr相当于arr[0],是首元素的地址;
  • &arr看似和arr,arr[0]相同,其实取出的是整个数组的地址。

对代码做出变形,可以看到&arr型在加1后

【C语言有这个就够了】五.指针(1)_数组_14

&arr在加1后,地址加了40(图中是16进制表达式),相当于跳过了跳过了一个数组(一个int 4字节)

  • sizeof(数组名)计算的是整个数组的大小。

再有:

#include<stdio.h>
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%p========%p\n", p + i, &arr[i]);
}
return 0;
}

【C语言有这个就够了】五.指针(1)_#include_15

我们可以看到p+i是数组arr下标为i的地址

如此,我们可以直接通过指针访问数组

#include<stdio.h>
int main()
{
int arr[10] = {0};
int* p = arr;
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i; //对数组内容赋值
}
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
//printf("%d ", *(p + i));
}
return 0;
}

(六)二级指针

#include<stdio.h>
int main()
{
int a = 10;
int* pa = &a;
int** ppa = &pa;//ppa就是二级指针变量
int*** pppa = &ppa;//int**说明ppa的类型,*pppa说明pppa是指针

**ppa = 20;
printf("%d\n", **ppa);
printf("%d\n", a);
return 0;
}

【C语言有这个就够了】五.指针(1)_数组_16

**ppa 先通过 *ppa 找到 pa ,然后对 pa 进行解引用操作: *pa ,那找到的是 a .

(七)指针数组

指针数组————数组

数组指针————指针

实例:

#include<stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = 30;
int i = 0;
//int* pa = a;
//int* pb = b;
//int* pc = c;
//整形数组存放整型
//字符数组存放数组
//指针数组存放指针
int* arr2[3]={&a,&b,&c};//将地址放入创建好的指针数组中
for (i = 0; i < 3; i++)
{
printf("%d ", *(arr2[i]));//arr2中的数据为指针,通过*解引用
}
return 0;
}

【C语言有这个就够了】五.指针(1)_i++_17

与以下代码对比

#include<stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = 30;
int i = 0;
//int* pa = a;
//int* pb = b;
//int* pc = c;
//整形数组存放整型
//字符数组存放数组
//指针数组存放指针
int arr2[3]={a,b,c};
for (i = 0; i < 3; i++)
{
printf("%d ", arr2[i]);
}

return 0;
}


【C语言有这个就够了】五.指针(1)_i++_17









标签:10,arr,return,int,就够,C语言,printf,指针
From: https://blog.51cto.com/u_15796276/5757973

相关文章

  • 36.智能指针类
    程序1:#pragmawarning(disable:4996)//2022年10月13日21:47:46#include<iostream>usingnamespacestd;classMaker{public:Maker(){cout<<......
  • C语言内嵌汇编学习笔记
    参考:gnugcc中关于ExtendedAsm的文档​​​https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html​​​BasicAsm文档​​https://gcc.gnu.org/onlinedocs/gcc/Basic-A......
  • C语言思维导图前五章总结(超详细,复习必备)
    源文件在process on(在线流程图)上面,和这个同一个名字哦。......
  • 解决C语言中scanf()、strcpy()函数报错问题。
    在C语言的学习中,使用VisualStudio2019编译器中的scanf()、strcpy()函数和一些数组里的函数会报错,这是编译器预处理没有设置好,下面看一下如何设置。先找到源文件中自己的项......
  • C语言之不常用的预编译命令
    C语言之不常用的预编译命令最近在做单片机的开发,我发现一些功能需要反复使用(个人习惯),所以今天决定写一些常用的函数作为自己的库。然后发现了一个自己忽略了很久的问题,那......
  • C语言操作符大全和详解(上)
    ......
  • 实验一 C语言开发环境使用和编程初体验
    实验任务一//实验任务一#include<stdio.h>intmain(){printf("O\n");printf("<H>\n");printf("II\n");return0;}task1_1.c//实验任......
  • 学习日记--(复习qsort、学习枚举、指针)
    1、两数之和方法一:暴力枚举思路:枚举数组中每一个数,寻找之后的数字是否存在target-x。int*twoSum(int*nums,intnumsSize,inttarget,int*returnSize){for......
  • 初探c语言第四天
    循环结构程序设计while语句循环变量初始化在此语句之前完成while(条件){         }条件为真,执行下面的语句。dowhile语句do{       }while(条......
  • c语言-大数阶乘
    大数阶乘的由来一个正整数的阶乘(Factorial)是所有小于及等于该数的正整数的积,并且0的阶乘为1。自然数n的阶乘写作n!。亦即n!=1×2×3×...×(n-1)×n。阶乘亦可以......