当前位置:   article > 正文

【C语言】【指针】

【C语言】【指针】


无关紧要的发现:读取过任意类型后,会留下一个缓冲区,在读取下一个字符前要么在 %c 前加上一个空格,要么提前用 getchar() 吃掉缓冲区

参考文章:链接: link

指针变量和地址

在C语⾔中创建变量其实就是向内存申请空间
在这里插入图片描述
上述的代码就是在(X86)也就是32位环境下创建了整型变量a,内存中申请4个字节,⽤于存放整数10,其中每个字节都有地址,上图中4个字节的地址分别是:
0x00CFFB34
0x00CFFB35
0x00CFFB36
0x00CFFB37

  1. 虽然整型变量占⽤4个字节,我们只要知道了第⼀个字节地址,顺藤摸⽠访问到4个字节的数据也是可行的
  2. 我们通过取地址操作符( & )拿到地址
  3. 我们只要拿到了地址(指针),就可以通过地址(指针)找到地址(指针)指向的对象,这⾥必须学习⼀个操作符叫解引⽤操作符( * )
int a=12; int b; int *p; int **ptr;
p=&a; //&a 的结果是一个指针,类型是int*,指向的类型是
//int,指向的地址是a 的地址。
*p=24; //*p 的结果,在这里它的类型是int,它所占用的地址是
//p 所指向的地址,显然,*p 就是变量a。
ptr=&p; //&p 的结果是个指针,该指针的类型是p 的类型加个*,
//在这里是int **。该指针所指向的类型是p 的类型,这
//里是int*。该指针所指向的地址就是指针p 自己的地址。
*ptr=&b; //*ptr 是个指针,&b 的结果也是个指针,且这两个指针
//的类型和所指向的类型是一样的,所以用&b 来给*ptr 赋
//值就是毫无问题的了。
**ptr=34; //*ptr 的结果是ptr 所指向的东西,在这里是一个指针,
//对这个指针再做一次*运算,结果是一个int 类型的变量。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

指针的大小

在这里插入图片描述
我们可以简单理解,32位机器( X86 )有32根地址总线,每根线只有两态,表⽰0,1【电脉冲有⽆】,那么⼀根线,就能表⽰2种含义,2根线就能表⽰4种含义,依次类推。32根地址线,就能表⽰2^32种含义,每⼀种含义都代表⼀个地址。
同理64位机器( X64 )

由此可得

  • 32位机器假设有32根地址总线,每根地址线出来的电信号转换成数字信号后是1或者0,那我们把32根地址线产⽣的2进制序列当做⼀个地址,那么⼀个地址就是32个bit位,需要4个字节才能存储。
  • 同理64位机器,假设有64根地址线,⼀个地址就是64个⼆进制位组成的⼆进制序列,存储起来就需要8个字节的空间,指针变的⼤⼩就是8个字节

指针运算

  • 指针 ± 整数
    因为数组在内存中是连续存放的,只要知道第⼀个元素的地址,顺藤摸⽠就能找到后⾯的所有元素,例下述例子。指针每加1实质上是往后移这个指针类型大小的字节数,具体情况可看下一个例子。
#include <stdio.h>
//指针+- 整数
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];
int i = 0;
int sz = sizeof(arr)/sizeof(arr[0]);
for(i=0; i<sz; i++)
{
printf("%d ", *(p+i));//p+i 这⾥就是指针+整数
}
return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

再比如指针ptr 的类型是int*,它指向的类型是int,它被初始化为指向整型变量a。接下来的第3句中,指针ptr被加了1,编译器是这样处理的:它把指针ptr 的值加上了sizeof(int),在32 位程序中,是被加上了4,因为在32 位程序中,int 占4 个字节。由于地址是用字节做单位的,故ptr 所指向的地址由原来的变量a 的地址向高地址方向增加了4 个字节。由于char 类型的长度是一个字节,所以,原来ptr 是指向数组a 的第0 号单元开始的四个字节,此时指向了数组a 中从第4 号单元开始的四个字节。

char a[20];
int *ptr=(int *)a; //强制类型转换并不会改变a 的类型
ptr++;
  • 1
  • 2
  • 3

总的来说一个指针ptrold 加(减)一个整数n 后,结果是一个新的指针ptrnew,ptrnew 的类型和ptrold 的类型相同,ptrnew 所指向的类型和ptrold所指向的类型也相同。ptrnew 的值将比ptrold 的值增加(减少)了n 乘sizeof(ptrold 所指向的类型)个字节。就是说,ptrnew 所指向的内存区将比ptrold 所指向的内存区向高(低)地址方向移动了n 乘sizeof(ptrold 所指向的类型)个字节。

  • 指针 ± 指针
    指针和指针进行加减:两个指针不能进行加法运算,这是非法操作,因为进行加法后,得到的结果指向一个不知所向的地方,而且毫无意义。两个指针可以进行减法操作,但必须类型相同,一般用在数组方面。
//指针-指针
#include <stdio.h>
int my_strlen(char *s)
{
char *p = s;
while(*p != '\0' )
p++;
return p-s;
}
int main()
{
printf("%d\n", my_strlen("abc"));
return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

指针类型

指针的类型很多,是枚举不完的,于是我们只需要将几种重要的指针类型给琢磨清楚,别的就只需要稍加观察就能立即理解了。
要理解复杂类型其实很简单,一个类型里会出现很多运算符,他们也像普通的表达式一样,有优先级,其优先级和运算优先级一样。原则就是,从变量名处开始,根据富豪的优先级,既可以退出指针的类型。

int p;//最朴实无华的整型变量
int* p;//从p开始,与*结合,说明p是一个指针,再与int结合,说明指针所指向的元素是整形,所以p是一个返回整型类型的指针
int p[3];//从p开始,与[]结合,说明p是一个数组,再与int结合,说明数组里的元素是整形的,所以p是由整型类型组成的的数组
int* p[3];//从p开始,[]的优先级比*高,所以p先与[]结合,是一个数组,再与*结合,说明数组里的元素是指针类型,最后与int结合,说明指针所指向的元素是整形,所以p是一个由指向整形元素的指针组成的数组
int(*p)[3];//从p开始,先与*结合,是一个指针,再与[]结合,说明这是一个指向数组的指针,再与int结合,说明数组里的元素是整形的,所以p一个指向由整形元素组成的数组的指针,是数组指针
int** p;//从p开始,先与右边的*结合,说明是一个指针,再与左边的*结合,说明这是一个指向(左*)指针的(右*)指针,再与int结合,说明(左*)指针的返回类型是整形的,所以p是一个返回整型类型的指针的指针
int p(int);//从p开始,与int结合,说明是一个有一个整形变量参数的函数,再与左边的Int相结合,说明这个函数的返回类型是整形,说明p是一个返回整型类型且有一个整形变量参数的函数
int* p(int);//从p开始,与int结合,说明p是一个有一个整形变量参数的函数,再与*结合,说明这个函数的返回的是一个指针,再与int结合,说明这个指针的返回的是整形类型,所以这是一个返回的是一个整型类型指针的函数
int (*p)(int);//从p开始,与*结合,说明p是一个指针,再与int结合,说明是指向一个有一个整形变量参数的函数的指针,再与int结合,说明函数的返回类型是整形,所以p是一个指向一个返回整型类型且只有一个整形变量参数的函数的指针
int *(*p(int))[3];//从p开始,与int结合,说明指针指向的是一个有一个整型变量参数的函数,再与*结合,说明函数返回的是一个指针,再与外面的[]结合,说明指针指向的是一个数组, 再与*结合,数组里的元素是指针,再与int结合,说明p是一个返回指向一个返回类型为整型的指针类型组成的数组的指针的函数
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

这是绝大多数会用到的指针类型,如果上述指针类型都能够很好地理解了,其他的指针理解也就有了规律。
规律大致如下:

指针的类型

从语法的角度看,你只要把指针声明语句里的 指针名字 去掉,剩下的部分就是这个指针的类型。这是指针本身所具有的类型。for example:
char* p 的指针类型是 char*
char** p 的指针类型是 char**
int* p[3] 的指针类型是 int* ()[]
以此类推…

指针所指向的类型

从语法上看,你只须把指针声明语句中的 指针名字和名字左边的指针声明符* 去掉,剩下的就是指针所指向的类型。
char* p 所指的指针类型是 char
char** p 所指的指针类型是 char*
int* p[3] 所指的指针类型是 int ()[]
以此类推…

strlen 和 sizeof

sizeof
在学习操作符的时候,我们学习了 sizeof , sizeof 计算变量所占内存内存空间⼤⼩的,单位是字节,如果操作数是类型的话,计算的是使⽤类型创建的变量所占内存空间的⼤⼩。sizeof 只关注占⽤内存空间的⼤⼩,不在乎内存中存放什么数据。

#inculde <stdio.h>
int main()
{
int a = 10;
printf("%d\n", sizeof(a));
printf("%d\n", sizeof a);
printf("%d\n", sizeof(int));
return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

strlen
统计的是从 strlen 函数的参数 str 中这个地址开始向后, \0 之前字符串中字符的个数。
strlen 函数会⼀直向后找 \0 字符,直到找到为⽌,所以可能存在越界查找

#include <stdio.h>
int main()
{
char arr1[3] = {'a', 'b', 'c'};
char arr2[] = "abc";
printf("%d\n", strlen(arr1));
printf("%d\n", strlen(arr2));
printf("%d\n", sizeof(arr1));
printf("%d\n", sizeof(arr1));
return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/223534
推荐阅读
相关标签
  

闽ICP备14008679号