C与指针(个人笔记总结,初始化、数组、指针与内存、const关键字)

C与指针

1 变量与指针

变量是某个地址空间的别名

int a = 1;

c与内存

1 指针就是地址0x11110000

2 变量名就是地址的概念,int a = 1; 就是向a地址(编译器随机分配,本文默认为0x11110000)的存储空间写入数据1的补码。

2 指针运算和指针类型

下面po两张图方便理解。

在这里插入图片描述

在这里插入图片描述

注意:

1️⃣int a = 1;表示a地址处所存放的变量所占字节为4,编译器读的时候取4个字节;

2️⃣ char a = “1”; 表示a地址处所存放的变量所占字节为1,编译器读的时候取1个字节;

3️⃣ int *p = &i; 表示p所保存的地址处所存放的数据类型为int型,对应上图表示 *P所对应的值所占字节数为4,即0x11110000 ~ 0x11110004处是一个int值,取出来之后按int进行编译。

↪️ 换句话说(可以数星号):

↗️ int * p 表示p这个变量是int* 类型,就是表示一个int存储空间的地址值,既然是地址值,那么它的定义处就应该是int* p = &a;(p表示的是int数据的地址)

↘️ int** q表示q这个变量是int**类型,即q表示一个int* 类型的地址值,那么它的初始化就应该是:int**q = &q;(q表示的是int数据地址的地址)

3 空指针、野指针、空类型

/*空指针,任何栈和进程都不得访问0x00000000,一旦使用会进行报错*/
/*空指针适用机会:指针只是声明了,但暂时还不知道如何用它,可以先定义为NULL*/
int *p = NULL;
/*野指针,只进行*p的声明,但不能直接使用*/
/*如果只是进行读取的话,有的编译器报错,有的不报错,但却没有任何意义*/
int *p;
/*但是进行写的话,绝对禁止,因为你不知道p到底指向哪里,如果是操作系统内核或者内部保留的寄存器不允许访问*/
int *p;
*p = 1;
/*一般不知道什么类型的时候使用viod*类型,是模板类型,能赋予除函数指针外的任何类型*/
/*在定义函数形参时使用较多*/
void * p;

4 指针初始化

初始化是指指针初始化,即指针一定要指向一段已知内存的地址空间,未经初始化的指针严禁使用

/*****************************************************
*第一种方法
*****************************************************/
int i;				//编译器自动分配了i变量的内存地址,是一段已知内存的地址
int* p = &i;		//p变量是指针,指向的上面i的地址,此时的p是一段内存分配好的地址,此后就可以正常操作了
*p = 2;			    //向i地址处写入值:2

/*****************************************************
*第二种方法
*****************************************************/
int* p = NULL;       //先定义一个空指针,一用就报错,因为0x00000000地址所有的进程和栈都无法使用
int i = 2;			//定义一段内存,有一段已知的内存
p = i;				//令p指向i

5 指针运算

常用的指针运算包括以下这些

  • & 取地址
  • * 取值
  • < > = 关系运算符(主要是指向连续存储空间时使用较多)
  • ++ 指针自加1
  • – 指针自减1

但注意++、--并不是地址的简单地址+1,而是根据指针的数据类型所占的空间大小进行加减

具体观察以下代码:

    char a = 'a';
    char* p_char = &a;

    int i = 1;
    int *p_int = &i;
    printf("p_int = %p  ",p_int);        //int型每次地址加4,int占4个字节
    p_int++;
    printf("p_int = %p  ",p_int);        //int型每次地址加4,int占4个字节
    p_int++;
    printf("p_int = %p  ",p_int);        //int型每次地址加4,int占4个字节
    p_int++;
    printf("p_int = %p\n",p_int);        //int型每次地址加4,int占4个字节

    printf("p_char = %p  ",p_char);      //char型每次地址加1,char占1个字节
    p_char++;
    printf("p_char = %p  ",p_char);      //char型每次地址加1,char占1个字节
    p_char++;
    printf("p_char = %p  ",p_char);      //char型每次地址加1,char占1个字节
    p_char++;
    printf("p_char = %p\n",p_char);      //char型每次地址加1,char占1个字节

    system("pause");
    return 0;

/*************************************************************************
**********************************测试结果如下*****************************
p_int = 0061FF10   p_int = 0061FF14   p_int = 0061FF18   p_int = 0061FF1C
p_char = 0061FF17  p_char = 0061FF18  p_char = 0061FF19  p_char = 0061FF1A
*************************************************************************/

6 指针与一维数组

6.1 相似之处

数组是一段连续存放的特定数据类型的内存

/**********************************************
*其实也可以换个角度看int arr[3];
*该数组就是arr开始的地址,因为是int型,每个占4字节
*因此这段内存就是[arr,arr+3]、[arr+4,arr+7]、[arr+8,arr+11]
*每个区间里面存放的值按照int类型进行编译
*具体内存数据显示如下图所示:
**********************************************/
int arr[3] = {1,2,3};
int *p = arr;
int i;
for(i=0;i<sizeof((arr)/sizeof(*arr));i++)
{
    printf("%p --------> %d",a+i,arr[i]);
    //printf("%p --------> %d",p+i,*(p+i));
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mOiEeCTL-1652836652196)(c-指针/1652685550997.png)]

总结:

value: a[i] = *(a+i) = *(p+i) = p[i]

&a[i]: &a[i] = a+i = p+i = &p[i]

6.2 区别之处

/**********************************************
*	p是指针变量,arr数组名是指针常量
*	p++可以改变p的值,arr不可以进行arr++操作
*	因此p++操作之后一定要注意p指向的地址值已经发生变化
**********************************************/
int arr[3] = {1,2,3};
int *p = arr;
int i;

for(i=0;i<sizeof(arr)/sizeof(*arr);i++)
    printf("%p--->%d\n",&arr[i],arr[i]);

for(i=0;i<sizzeof(arr)/sizeof(*arr);i++)
    scanf("%d",p++);				
p = arr;					//p已经发生了变化,要进行复原
for(i=0;i<sizzeof(arr)/sizeof(*arr);i++)
    printf("%p--->%d\n",p,*(p++);

笔记:

p++:p的指向发生了变化

p+1:p的指向没有发生变化

7 指针与二维数组

二维数组有点意思,可以帮你进行指针的进一步理解,我知道我的理解和传统介绍不尽相同,但请先按我的笔记进行理解。

你可以把二维数组当成一个新的复合数据类型:int a[2][3]二维数组表示以下内容:

a是顶层结构:

a[0]和a[1]是第一层结构,隶属于a;

a[0][0]、a[0][1]、a[0][2](a[1][0]、a[1][1]、a[1][2])是第二层结构,隶属于第一层结构;

注意:

所谓*是指进入下一层结构,也就是a是最外层的地址,加一个*表示进入第一层,他有两个元素a[0]和a[1],*a表示*(a+0)的复合数据类型,表示a[0][0]、a[0][1]、a[0][2]的整体,**a表示*(*(a+0)+0)的数据类型,即a[0][0]的值

a+1是指地址从a加上一个它元素大小的数值,(对比之前的int a[2],a+1是加一个int(4)的大小)即地址元素相加3*sizeof(int)的值。*a的包含元素是int型,故*a+1只是加4的值

int a[2][3] = {1,2,3,4,5,6};
int i,j;


for(i=0;i<2;i++)
{
    for(j=0;j<3;j++)
    {
        printf("a[%d][%d]--->%p\n",i,j,&a[i][j]);
    }
    printf("\n");
}

printf("a--->%p\n",a);
printf("*a--->%p\n",*a);
printf("a+1--->%p\n",a+1);

printf("*a+1--->%p\n",*a+1);

printf("**a--->%d\n",**a);
printf("*a[0]--->%d\n",*a[0]);
printf("a[0][0]--->%d\n",a[0][0]);

printf("*(a+1)--->%d\n",*(a+1));
printf("**(a+1)--->%d\n",**(a+1));
system("pause");
return 0;

/*************************************************************************
**********************************测试结果如下*****************************
a[0][0]--->0061FF00 a[0][1]--->0061FF04 a[0][2]--->0061FF08
a[1][0]--->0061FF0C a[1][1]--->0061FF10 a[1][2]--->0061FF14
a--->0061FF00
*a--->0061FF00
a+1--->0061FF0C
*a+1--->0061FF04
**a--->1
*a[0]--->1
a[0][0]--->1
*(a+1)--->6422284
**(a+1)--->4
*************************************************************************/


/**********************************************
*如果上面的例子你看懂了,那么你应该明白p初始化应该怎么初始化了
*有以下方法
**********************************************/

int a[2][3] = {1,2,3,4,5,6};
int i;

int *p = *a;
// 等价int *p = &a[0][0];

for(i=0;i<6;;i++,p++)
    printf("%d",p[i]);
printf("\n");

8 指针数组与数组指针

数组指针,主要是指针指向一种数据类型的数组

int (*p) [3] 它表示两层意思:

1️⃣ p是一个指针,

2️⃣ 他指向的内容是一个 int [3]这样的数组,因此每一次p+1内存地址增加的值为12

此时p和a[2][3]中的a类型等价。

​ 指针数组:

int * arr[3]; 指针数组;int * 是表示一个类型 ,即整型指针

char *name[2] ={“dan”,“san”};

9 指针与字符数组

/**********************************************
*普通用法
**********************************************/
char str[] = "hello";
char *p = str+1;

/**********************************************
*赋值,字符串等号左边的str是常量,不能直接赋值
*但str的本质是一段连续的存储空间
**********************************************/
char str[] = "hello";
 //str = "world";     错误
sizeof(str);   //6算上结尾的0
strlen(str);   //5不算结尾的0
strcpy(str, "world");

/**********************************************
*字符串指针
*字符串指针的本质还是一个地址
**********************************************/
char *str = "hello";   //注意hello是char常量,字符堆放在一起,不允许改变,str = 0x00406044
sizeof(str);   //4,其实就是指针的长度,32位地址为4
strlen(str);   //5不算结尾的
//strcpy(str, "world");  不能这么用,str是一个地址
str = "world";     //注意此时str指向的地址发生了变化,不是在源地址“hello”段上修改的,str = 0040604E

10 const与指针

常量两种方法

1️⃣ #define PI 3.14

​ 缺点:不检查语法错误

2️⃣ const float pi = 3.14;

​ const作用是将变量pi保存了常量3.14,表示我不希望该变量的值发生任何改变

int main{
    const float pi = 3.14;
    // pi = 3.1415926     错误,const类型无法修改
    
    /************************************************
    float *p = &pi;
    *p = 3.1415926        只有警告,不会报错,内容可以修改
    ************************************************/
    system("pause");
    return 0;
}

指针常量与常量指针

1️⃣ 常量指针:const int *p; int const *p; (const在*前,修饰的是*p)

​ 指针指向的内容不能发生变化,地址可以

2️⃣ 常量指针:int *const p; (const在*后,修饰的是p)

​ 指针指向的地址不能发生变化,内容可以变化

int main{
    
    int i =1;
    int const *p = &i;
    
    i=10; //可以通过i来修改,i没有被const修饰,可以这样修改
    //F *p=10; 错误,*p是被const修饰了
    
    int j = 100;
    p = &j; //可以修改p的指向
    /************************************************
    int *const p = &pi;
    //p =  &j;      报错,指针的指向不能发生变化
    ************************************************/    
}

const int *const p p的指向和p的内容都不能发生变化

常见的函数函数:char *strcpy( char *dest , const char *src) :证明我对你传过来的src内容不会进行任何修改


本文主体结构及主体代码参考李慧琴老师公开课,但是加入了大量自己的思考,如果有时间,B站搜索相关视频进行观看。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值