1、学习前沿
- 指针的大小与数据类型无关,只与系统的位数有关。
32位的操作系统,指针长度为4字节
;64位的操作系统,指针长度为8个字节
。 - 指针是用来存放地址的。
- LSB和MSB------内存存储数据的两种方式
LSB
:Least Significant Bit. “最低有效位”------小端字节序
MSB
:Most Significant Bit. “最高有效位”------大端字节序
一般系统默认的内存存储方式为小端字节序
。
- 虚拟地址空间
当创建一个进程时,操作系统会为该进程分配一个4GB的虚拟地址空间
。(以32 位的操作系统
为例,一个指针长度是 4 字节,4字节指针的寻址能力是从 0x0000 0000~0xFFFF FFFF)。
每个进程都会有一个自己的 4GB 虚拟地址空间。这 4GB 的地址空间是“虚拟”的,并不是真实存在的。
2、指针基础
*
:取值运算符
&
:取地址符
解析:
int main (int argc, char **argv) //int argc:参数个数;char **argv:指向指针的指针
{
int a;
int *p=&a; //定义p是一个指向int类型的指针
char ch='a';
char *p=&ch; //定义p是指向char类型的指针
char *str="hello";
char **p=&str; //定义*p是指向char *类型的指针
}
代码演示:
#include<stdio.h>
int main(int argc, char **argv)
{
int a = 10;
int b = 20;
int *p = &a;
int **pp = &p;
printf("&a = %p\n", &a);
printf("p = %p\n", p);
printf("*pp = %p\n",*pp);
printf("a = %d\n", a);
printf("*p = %d\n", *p);
printf("\n");
printf("&p = %p\n", &p);
printf("pp = %p\n", pp);
printf("&a = %p\n", &a);
printf("p = %p\n", p);
printf("a = %d\n", a);
printf("**pp = %d\n",**pp);
}
代码解析:
-
int a =10 :
在内存中开辟了一段内存空间,大小为4个字节(int类型4个字节);a表示这段内存单元,&a表示这段内存单元的地址;10以16进制(0x0000000a)的形式存储在那段内存单元中。 -
&a
:
&a是int *类型。 -
&p
:
&p是int **类型。 -
int *p = &a:
定义指针p指向a,p的内存单元存放a的地址。 -
int **pp = &p:
指针pp指向指针p,pp的内存单元存放指针p的地址。
执行结果:
&a = 0x7fff27afb130
p = 0x7fff27afb130
*pp = 0x7fff27afb130
a = 10
*p = 10
&p = 0x7fff27afb138
pp = 0x7fff27afb138
&a = 0x7fff27afb130
p = 0x7fff27afb130
a = 10
**pp = 10
结果解析:
*p
:
p表示取指针p的内存单元存储的值,由于p的内存单元存储了a的地址,所以p取了a的值[^1]。**pp
**pp
表示取*pp的值,*pp
取指针pp的内存单元储存的值,由于pp存储的是p的地址,所以*pp
取p的内存单元存储的值[^1]。又由于p的内存单元存储了a的地址,所以**pp
取了a的值[^2]。
拓展:
如果a = 20;a出现在等号的左边,就是把20这个值写到a的内存单元
里面去。
如果b = a;a出现在等号的右边,就是把a的内存单元的值
取出来给b。
通过*p改变a的值:*p = 20;
通过**pp改变a的值:**pp = 20;
3、指针与地址
代码演示:
#include <stdio.h>
int main(void)
{
int a = 0x12345678;
int *pint = &a;
char *pchar = (char *)&a;
short *pshort = (short *)&a;
printf("*pint = 0x%x, *pchar = 0x%02x, *pshort = 0x%hx\n", *pint, *pchar, *pshort);
printf("&a = %p, &pint = %p, &pchar = %p, &pshort = %p\n", &a, &pint, &pchar, &pshort);
pint++;
pchar++;
pshort++;
printf("*pint = 0x%x, *pchar = 0x%02x, *pshort = 0x%hx\n", *pint, *pchar, *pshort);
}
代码解析:
char *pchar = (char *)&a:
因为a是int类型的,所以要强制转换为char *类型
。
short *pshort = (short *)&a:
因为a是int类型的,所以要强制转换为short *类型
。
执行结果:
*pint = 0x12345678, *pchar = 0x78, *pshort = 0x5678
&a = 0x7ffe171b01dc, &pint = 0x7ffe171b01e0, &pchar = 0x7ffe171b01e8, &pshort = 0x7ffe171b01f0
*pint = 0x171b01e0, *pchar = 0x56, *pshort = 0x1234
结果解释:
-
一般系统默认的内存存储空间为
小端字节序
。 -
*pchar = 0x78:
因为char只有1个字节,所以只输出78[^1]。 -
*pshort = 0x5678:
因为short只有2个字节,所以只输出5678[^1]。 -
pchar++:
有效位自加1个字节。
所以*pchar = 0x56[^2]。 -
pshort++:
有效位自加2个字节。
所以*pshort = 0x1234[^2]。
4、数据交换
代码演示:
int main()
{
int a = 8;
int b = 10;
printf("before: a = %d, b = %d\n", a, b);
swap(a,b);
printf("after: a = %d, b = %d\n", a, b);
printf("before: a = %d, b = %d\n", a, b);
pswap(&a,&b);
printf("after: a = %d, b = %d\n", a, b);
}
int swap(int a,int b)
{
int temp;
temp = a;
a = b;
b = temp;
}
int pswap(int *p1, int *p2)
{
int temp;
temp = *p1;
*p1 = *p2;
*p2 = temp;
}
运行结果:
before: a = 8, b = 10
after: a = 8, b = 10
before: a = 8, b = 10
after: a = 10, b = 8
结果解释:
调用swap函数交换不成功,是因为函数是按值传递
,把a,b传过去在函数swap里面交换了,却只在函数swap里生效
,没有改变main里面的a,b。
通过调用pswap(&a,&b)函数后,
5、const关键字
定义:
const定义的是常量
,值不能被改变。
const int a = 100
const 和指针变量一起使用时,可以限制指针变量本身,也可以限制指针指向的数据
。
const 和指针一起使用会有3种不同的顺序,如下所示:
const int *p1 // int const *p1 = &a;
int * const p2 = &a;
const int * const p3 = &a;
- 第一种情况
指针所指向的数据*p1是只读的不能修改,指针p1本身的值可以修改
。
*p1 = 30;//不合法 p1 = &b;//合法 a = 20;//不合法
- 第二种情况
指针本身的值p2是只读的不能修改,指针所指的数据*p2可以修改
。
*p2 = 30;//合法 p2 = &b;//合法
- 第三种情况
指针本身的值p3和它指向的数据*p3都有是只读的,不能修改
。
*p3 = 30;//不合法 p3 = &b;//不合法
由此可见:指针可操作两个内存空间,一个是自己的内存空间,另一个是指向的内存空间。
6、C程序内存布局
程序所占内存的分类
- 当程序没有运行,只有
文本段(代码段)和数据段
。 - 系统空间:每个程序都有4GB的虚拟内存空间(32位的操作系统)
- 参数区,堆区,栈区,数据段是用来
存放数据
的。 - 栈区的空间是
由上往下增长
的。 - 堆区的空间是
由下往上增长
的。 - 参数区:
参数区放命令行传给main()函数的参数:argc,argv。 - 栈区:
栈区存放局部变量
,当它在花括号内,就在栈区
占一段内存空间,用完后,这段内存就被释放了。
局部变量:
定义:花括号内所以定义的变量是局部变量,只能在本花括号使用。
int main(void)
{
int stack_var;
const int rodata_var = 50;
static int s_var;
char *ptr = "hello world";
char arr[] = "hello";
}
未定义的局部变量,其值是随机值
。
未初始化的静态局部变量,其值为0
。
- 堆区:
malloc()动态类型分配内存,在内存中分配一段内存使用。
int *p = (malloc)100;
free (p);
p = NULL;
free(p)
,是放弃了指针对这个内存的占用,放弃之后,内存的值会改写成随机值。但是指针本身并没有被删除!指针仍然指向原来的那块内存!
因此在free掉指针后,还要把指针指向null,即p = NULL
。
- 数据段:
数据区存放全局变量
、静态变量
以及文字常量
。
全局变量:
定义:在花括号外定义的变量,是全局变量。
#include <stdio.h>
int g_data_var = 20;
int g_bss_var;
static int s_bss_var;
已初始化的全局变量,可以被所有其他c文件或本C文件中的函数调用
,如果另一个c文件想调用全局变量,要用extern声明
。
extern int g_data_var;
未初始化的全局变量,其值为0
,可以被所有其他c文件或本C文件中的函数调用
。
未初始化的静态全局变量,其值为0
,static关键字声明的函数或变量,只能被本C文件调用
。
文字常量区:
char *ptr = "hello world";
*p指向"hello world","hello world"在数据段的文字常量区,不可改变
,报段错误。
*(str+1) = 'a' //segment fault
数组arr在栈区,可以改。
arr[1] = 'a'
指针和数组同等的写法:
*(str+1) = 'a'
str[1] = 'a'
arr[1] = 'a'
*(arr+1) = 'a'
代码演示:
#include <stdio.h>
#include <stdlib.h>
int g_data_var = 20; //已初始化的全局变量
int g_bss_var; //未初始化的全局变量
static int s_bss_var; //未初始化的静态全局变量
int main(int argc, char **argv)
{
int stack_var; //未初始化局部变量
const int c_rodata_var = 50; //常量
static int s_data_var = 30; //静态局部变量
static int s_bss_var;
char *str = "hello world"; //局部变量
char arr[] = "hello"; //局部变量
char *ptr = NULL;
ptr = malloc(100);
printf("g_bss_var: %d stack_var: %d s_bss_var: %d\n", g_bss_var, stack_var, s_bss_var);
printf("arr address: %p\n", arr);
printf("str address: %p str point address: %p\n", &str, str);
printf("c_rodata_var address: %p\n", &c_rodata_var);
printf("stack_var address: %p\n", &stack_var);
printf("ptr address: %p ptr point address: %p\n", &ptr, ptr);
printf("g_bss_var address: %p\n", &g_bss_var);
printf("s_bss_var address: %p\n", &s_bss_var);
printf("g_data_var address: %p\n", &g_data_var);
printf("s_data_var address: %p\n", &s_data_var);
free(ptr);
ptr = NULL;
return 0;
}
g(global):全局变量
s(static):静态变量
data:已初始化
bss:未初始化
stack:在栈区中
rodata:常量
执行结果:
g_bss_var: 0 stack_var: 0 s_bss_var: 0
arr address: 0x7ffc3e0d0052
str address: 0x7ffc3e0d0040 str point address: 0x561485266948
c_rodata_var address: 0x7ffc3e0d003c
stack_var address: 0x7ffc3e0d0038
ptr address: 0x7ffc3e0d0048 ptr point address: 0x5614863c3260
g_bss_var address: 0x561485467024
s_bss_var address: 0x561485467020
g_data_var address: 0x561485467010
s_data_var address: 0x561485467014
&**后续会更新《指针再复习》,全是硬货,等着你们来实操~~~~~~**