目录
指针
1.内存
(1)先理解一下指针
我们假设内存是一个大长方形,然后我们对其进行等分,然后我们将每一个小格子称作内存单元,我们可以对每一个内存单元进行编号,实践中,每一个内存单元的大小是一个字节,我们将右边的称作内存单元编号,也就是地址,而在C语言中地址就是指针的意思。
那是怎么产生内存单元的地址的呢
先说32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电平(低电压)就是(1或者0)
那么32根地址线产生的地址就会是:
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
...
11111111 11111111 11111111 11111111
这里就有2的32次方个地址。
2的32次方字节内存大小我们换算一下
2^32Byte == 2^32/1024KB ==2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB
因此2的32次方字节等于4GB
也就是说32位机器最多拥有4GB内存,这也就是为什么我们以前最常见到的内存是4GB的一个原因。而对于64位的机器,就是相同的计算原理。
这里我们就明白: 在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以 一个指针变量的大小就应该是4个字节。 那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。
总结: 指针是用来存放地址的,地址是唯一标示一块地址空间的。 指针的大小在32位平台是4个字节,在64位平台是8个字节。
(2)打印a的地址再次理解
在这里我们有个习惯,因为使用32个二进制位(也就是比特位)表示内存单元的编号,太过于冗杂,由于四位二进制数等于一位十六进制数,于是我们采用十六进制数来表示内存,这样我们就只需要八个数字即可清晰的表示出地址,也就是我们c语言中的指针。
//&a的意思是取出a的地址,%p是打印地址的意思
#include<stdio.h>
int main()
{
int a = 1;//向内存申请四个字节的空间
printf("%p", &a);
return 0;
}
//那里有一个x86和x64的选项,其中x64是64位环境,x86是32位环境
//32位打印出来是8个十六进制的数,64位打印出来是16个十六进制数
运行后,我们会发现,只打印出来了一个地址,但是我们的a占了四个字节,也就是四个内存单元,应该占据了四个地址,那我们这个地址是这四个地址中的哪一个呢?
我们调试打开内存窗口
选择内存1,列选择4,我们得到这样一个结果
左边是内存得到地址,后面是内存的数据,两个为一个字节。
当我们选择列数为1的时候
前四个就是我们申请的四个字节,后面竖着排的就是所存的数据,每一个就是一个字节,前面对应就是地址,所以我们打印出来的a的地址,是我们申请的四个字节中的第一个字节的地址。
结论:a的地址为它所占的几个内存单元中最小的地址,也就是第一个字节的地址,这是一个很重要的结论。
(3)指针变量的使用
int main()
{
int a = 1; // 定义的时候向内存申请4个字节
int * pa = &a // pa 是一个变量,是用来放地址的,又叫指针
// 所以 pa 叫指针变量,存放指针
// int a 后就是申请四个字节 ,这四个字节叫 a
// 前面的int代表着pa这个指针(地址)指向的是一个int类型
// *告诉我们pa是指针,前面的int告诉我 pa 指向的是int类型的变量
// 合起来pa的类型我们称作int*
// &取地址操作符
// &a取出的是a所占内存的4个字节中的第一个字节
printf("%p\n",&a);
printf("%p\n",pa); //打印的时候是一样的这俩
}
这段代码中,我们创建了a,并申请了四个字节的空间,然后我们使用一个变量pa来存放a的地址,这个变量pa就叫做指针,pa因为也是一个变量,所以我们习惯上叫它为指针变量。pa前面的那颗*表示pa是一个指针,而前面的int代表着pa这个指针(地址)指向的是一个int类型,所以合起来pa的类型我们称作int*,也就是所谓的指针类型。
(4)解引用操作
那么我们使用指针变量可不仅仅只是记录一下a的地址什么的,我们是需要通过这个地址来改变它里面的数据。这才是指针的用处。
int * pa = &a; // 知道了pa,怎么找到a,加个*
*pa = 20; // 解引用操作 - 作用就是通过pa中的地址,找到a, *pa就是a
// 相当于pa就是变量,只有加上*后才能作为指针找到a的地址进行修改
// &a 取出的地址存放到变量pa里,
// 对pa前加上*,通过pa这个指针变量存的地址,找到a进行更改
printf("%d\n", a); // 打印的时候就是20,相当于把a变为20
如图所示,这个*pa中,*的意思是解引用,也就是说这个操作可以使我们通过pa这个地址找到a的值。然后我们对其赋值,从而篡改了a的值,此时a的值已经变成了20
int main()
{
char ch = 'w';
char* pc = &ch; // &ch 取出的地址存放到变量pa里,对 pa 前加上*
// 通过 pa 这个指针变量存的地址,找到 ch 进行更改
// 通过pc把ch的值改'b'
*pc = 'b'; // 这个时候把*pc改为b,就是通过pa的地址
// pa加上*就是去找到这个地址上的数w,改成b
printf("%c\n", *pc);//b
ch = 'q'; // 直接改ch的值,当我的 pc 指向 ch 时候,*pc 就是跟 ch 是一回事
printf("%c\n", *pc);//q
return 0;
}
这里用char也是相同的道理,再加深理解一下。
(4)指针小总结
1. 指针就是地址
2. 口头语中指针一般指的是指针变量
#include <stdio.h>
int main()
{
int num = 10;
# //取出num的地址
//注:这里num的4个字节,每个字节都有地址,取出的是第一个字节的地址(较小的地址)
printf("%p\n", &num); //打印地址,%p是以地址的形式打印
return 0;
}
//对于变量取地址,存放到指针变量里,然后用解引用操作符,但是*跟前面的类型一起的
//就是char* int* 后面加指针变量,让其成为解引用
2.指针变量的大小
#include<stdio.h>
int main()
{
int* a;
char* b;
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(b));
return 0;
}
指针变量的大小取决于存的是什么
指针变量是用来存放地址,而地址
在32位机器上,就是32个比特位,
指针变量存放的是32bite的地址,指针变量的大小是4个字节
在64位机器上,就是64个比特位,
指针变量存放的是64bite的地址,指针变量的大小是8个字节
结构体
C语言中有很多就是自带的内置类型
还有很多的自定义类型:结构体,枚举,联合体
而结构体的关键字:struct
struct 结构体名
{
成员列表(可以是基本的数据类型,指针,数组或其他结构类型)
};
上面就是最基础的设置
结构体由:(1)结构体标签 (2)结构体成员 (3)结构体变量组成
注意:
(1)、关键字struct是数据类型说明符,指出下面说的是结构体类型;
(2)、标识符Student是结构体的类型名;
(3)、最后的分号一定要写;
#include <stdio.h>
struct Stu //这是一个类型,stu是名字
{
char name[20]; //这是定义结构体,里面包含的变量进行存储
char sex[5];
int age;
};
void print(struct Stu* ps) //结构体加个*就是结构体的指针,ps是指针变量
{ //ps得到了s的地址,现在ps就指向这个s
printf("%s %s %d\n", (*ps).name, (*ps).sex, (*ps).age);
//打印的时候,*ps就是s,找到s的成员,就是*ps加上. ,结构体的指针解引用一下,
//找到了指向的对象s,s成员加上.就能找到了,结构体的地址传过去
//第二种表示
printf("%s %s %d\n", ps->name, ps->sex, ps->age);
//ps指向了s,ps指向了name,就是ps指向的s对象里面成员name
//-> 就是结构体的指针->成员名
}
int main()
{
int num = 0;
struct Stu s = {"张三","男",20};
//用类型创建个变量
//前面是类型,用类型创建的变量是s,后面是初始化,对于s的初始化
struct Stu s2 = {"如花","女",18};
printf("%s\n", s2.name);
printf("%s\n", s2.sex);
printf("%d\n", s2.age);
//想得到结构体里的成员就是加个 . 后面跟上自己定义成员名
//结构体变量.成员名来引用或者打印
print(&s);
//先取地址,取出来放到指针里面,然后上面的函数搞个指针
//&s,取地址s,拿到他的地址,现在把地址交给print了
//需要写个print函数来打印,这个地址存到指针变量里面了
return 0;
}
在主函数中调用的时候,记得加上{},并给他一个变量,这样好去打印,引用,而在变量后加个 .就是找到自己定义的结构体中的成员名。
主函数中是打印的s2,还有s是由上面的函数来打印
下面我们来看s,s在主函数中是用print来取的地址,然后在外面定义函数来引用,并且用指针找地址,结构体加个*就是结构体的指针,ps是指针变量,ps得到了s的地址,现在ps就指向这个s,
下面的print(&s)取地址,上面的struct Stu *ps 就是ps指针指向s的地址,再进行操作。
两种表示
(1)(*p). 成员名(.的优先级高于*,(*p)两边括号不能少)
(2) p->成员名(->结构体的指针指向成员名)
结构体很多表示,都在这个代码里,详细看看代码,后续会有更详细的介绍。
结束语
到这里,我们已经初始C语言了,下面我们开始C语言的初阶学习,大家保持关注!