c语言笔记4(指针)
1. 指针的应用
1.1 内存空间
32位机: 一次处理数据的大小 4B(字节)
64位机: 一次处理数据的大小 8B (字节)
计算处理数据的最小单位是 1B(字节), 计算存储数据的最小单位 二进制的1b(位)
一个程序启动后的进程分区: 栈、堆、全局区、常量区、代码区
内存寻址: (32位)最大范围(4B) 0xff ff ff ff, 最小 0x00 00 00 00
1.2 指针的概念
什么是指针: 是内存的地址编号(地址)。
指针是地址编号的类型。
1.指针变量:是个变量,即这个变量用来存放一个地址编号。
2.指针变量存放的是地址编号,一个地址编号是4B(32位机)或8B(64位机)。
3.指针变量也具有数据类型, 如 int * 表示int类型的指针。
4.定义指针变量
数据类型 *指针名;
5.*
和&
的关系
在定义指针时,*表示与前面的数据类型组合成`数据类型*`,*后变量名指针名。在获取指针的值(从地址编号的空间读取值) *指针名
&求变量的地址,将变量转化为它本身数据类型的*表示
小结:
1)无论什么类型的地址,都是存储单元的编号,在32位平台下都是4个字节,即任何类型的指针变量都是4个字节大小:在64位平台下都是8个字节。
2)对应类型的指针变量,只能存放对应类型的变量的地址
3)字符变量char ch;ch占1个字节,它有一个地址编号,这个地址编号就是ch的地址
4)指正变量的数据类型的作用:一次性读取数据的宽度及跨度
【注】小字节的变量的地址不能给大字节类型的指针,避免读取脏数据
1.3 指针的分类
1. 普通数据类型的指针
char,short,int,long,float,double
2. 函数指针:指向函数的指针
应用场景:函数回调(int *(函数指针名)(形参类型列表))
3. 结构体指针:struct x*名称
4. 指针的指针:char|short|int... **指针名
5. 数组的指针:char|short|int... (*指针名)[]
2. 指针与变量的关系
2.1 指针的赋值
变量操作:1)先定义变量 2)初始化 3)修改变量的值
指针变量获取到某一个变量的地址时,则指针指向这个变量地址,可以通过指针操作变量
int *p=NULL; //未初始化,不能使用。【建议】定义指针时,没有初始化值时,赋值为NULL
// 将p指针指向a;
p = &a;
// 指针初始化指向变量
int *q=&b;
2.2 指针的取值
对指针变量取值时,即对指针地址取值,也就是对指向的变量取值
【注意】*+指针变量
就相当于指针指向的变量
// a += 50;
*p += 50
// *q == b
int ret = *p + *q; //a + b
void*表示为任意类型(自由类型,无类型)
3. 指针与数组元素的关系
3.1 指针指向数组元素的地址
数组中的元素也是变量,它具有自己的内存地址编号,这个编号可以辅助指针,指针变量的类型同元素(变量)的数据类型,即同数组的数据类型保持一致
3.2 数组元素的引用
数组名[下标] 访问数组元素
如果一个指针指向数组的第一个元素的内存位置,也可以通过
指针名[下标]
访问指定位置的元素。【注意】c语言中,数组名是一个常量,存放数组元素的首地址(第一个元素的内存地址)
3.3 指针的运算
1)指针+数值
结果还是一个地址
2)指针比较
两个相同类型的指针可以进行比较,两个地址(指针)比较时,比较的还是地址。
3)指针相差
两个相同类型的地址相差,地址的8字节十六进制数相差之后得到的数值 除以数据类型的字节大小
4)指针赋值指针
将一个指针地址赋值给另一个相同类型的指正
int arr[10]; int*p=arr; int *q=p;
4. 指针数组
指针可以定义一个数组,数组中有若干个相同类型的指针变量,这个数组被称为指针数组。指针数组本质上也是一个数组,只不过存储的元素是指针变量。
4.1 指针数组的定义
语法:
数据类型 * 数组名[元素个数];
4.2 指针数组的分类
1)字符指针数组 char *names[5];
2)整数型指针数组 short *xxx[N];
int *xxx[N];
long *xxx[N];
3)浮点型指针数组 float * xxx[N];
double *xxx[N];
4)结构体指针数组 struct Stu *xxx[N];
5)函数指针数组 void (*xxx)(int,int)[]
5. 指针的指针
即指针的地址
p==&a
*p==a
q==&p
*q==p==&a
**q=*p=a
6. 字符串与指针
字符串
以'\0'结尾的多个字符组成的集合
字符串的存储形式:
字符数组: char name[128]="xxx";
字符串指针: char *name="xxx";
堆区: char *name = (char*)malloc(128)//动态从堆中分配空间
strcpy(name,"xxxx");//从string.h头文件中的复制字符串的函数
【注】p[0]='x'//不能修改的,常量区
7. 数组指针
7.1 二维数组名运算
数组名代表是第0行的地址,即是一个二维数组的首地址
数组名+1,代表取下一行的地址编号,跨了一整行(一行有N列,即跨了N列)
7.2 二维数组的指针定义
定义指针,指向一个二维数组,即为二维数组的指针
语法
数组元素的数据类型 (*指针名)[列数];
如:通过指针方式遍历一个二维数组
int m[3][5];
int (*p)[5]=m;
for(int i=0;i<3;i++){
for(int j=0;j<5;j++)
{
printf("第%d行第%d列,输入数据:",i,j);
scanf("%d",&m[i][j]);//m[i]是地址,指针[]取元素
}
}
for(int i=0;i<3;i++){
for(int j=0;j<5;j++){
printf("第%d行%d列 %d\n",i,j,m[i][j]);
}
}
7.3 不同数组指针
一维数组指针,指向是二维数组,+1即一次跨N列(一行)
int arr[行数][列数];
int (*p)[列数] = arr;
二维数组指针,指向是三维数组,+1即一次跨一个二维数组
int arr[N][行数][列数];
int (*p)[行数][列数] = arr;
三维数组指针,指向是四维数组,+1即一次跨一个三维数组,依次类推...
【注】
指针数组:本质上是数组,存储的是指针
数组指针:本质上是指针,指向是数组
指针的指针:本质上是指针,指针指向是地址(指针)。
7.4 数组名取地址
如果一个数组名取地址,即获取的是地址的地址,转成为数组指针
【注】数组名与指针变量的区别
- 共同点:
数组名也是一个第一个元素的地址,指向数组元素地址的指针同组名相同的。
2.不同点:
1)数组名取地址变量数组指针,&a == int (*)[10];
2)指针变量取地址,还是指针,&p == int **;
7.5 多维数组指针的转换
数组名前加*不是取值,是从行地址转化为行中的第一列的地址
1)一维数组指针名+[],取某行的首列地址,等价于*(指针+i)
2)*(指正变量 + 行号)获取此行的首列地址
8. 函数与指针
函数传递参数的形式:1)值 2)地址
地址作为实参传入到函数中,函数需要指针来接收
8.1 变量地址作为实参
调用函数时,求变量的地址,&变量名。
【提示】在函数内部可以通过地址修改变量值
void ch(int *p)
{
*p += 10;
}
int main(int argc,char const *argv[])
{
int x = 10;
ch(&x);
printf("x=%d\n",x);
return 0;
}
8.2 字符指针作为实参
字符指针作为实参,将指针指向常量区的内容传递函数。函数内部修改指针内容时,不会影响函数外部的值。
void ch(char *p)
{
printf("ch %s,p=%p\n",p,p);
p="xx";
printf("ch %s,p=%p\n",p,p);
//*p = 'x';//p指向常量区,常量区的内容不能更改
}
int main()
{
char *name = "xxx";
ch(name);//值传递
printf("%s,p=%p\n",name,name);
return 0;
}
8.3 字符指针地址作为实参
将字符指针的指针作为函数的实参,函数的形参使用**q(指针的指针),函数内部可以修改字符指针地址,即可以函数外部的结果
void ch(char **p)
{
*p = "xx";
printf("ch *p=%s,p=%p\n",*p,p);
}
int main()
{
char *name = "xxx";
ch(&name);//属于地址传递
printf("%s,&name=%p\n",name,&name);
return 0;
}
要想改变主调函数中变量的值,必须传变量的地址,而且还得通过*+地址去赋值。无论变量是什么类型
8.4 数组作为实参
数组名是地址(指针)作为函数的实参,属于地址传递
函数的形参定义:
1)接收一维数组的地址,按
int *p
格式定义2)接收二维数组的地址,按一维数组的指针来定义,
int (*p)[列数]
3)接收三维数组的地址,按二维数组的指定来定义
.......
设计一个函数,接收一个二维数组,指定函数、列数、查找的值和替换新值,实现所有查找值的更新或替换
void findPeplace(int rows;int cols,int (*p)[cols],int oval,int nval)
{
for(int i = 0;i < rows;i++)
{
for(int j = 0;j < cols;j++)
{
if(*(*(p+i)+j)==oval)
{
*(*(p+i)+j)=nval;
}
}
}
}
8.5 字符指针数组作为实参
字符指针数组作为实参时,函数的形参的写法:1)char*q[] 2)char **q
字符指针数组,数组中的每一个元素都是一个字符指针的地址
数组名,默认指向是每一个元素的地址
void show(char **q,int size)//char *q[]==char**q
{
for(int i=0;i<size;i++)
{
printf("%s\n",*(q+i));//q+i下一个字符指针地址
}
}
int main()
{
char *names[3] = {"x","y","z"};
show(names,3);
return 0;
}
8.6 指针作为函数的返回值
例:
void *rand_arr(int size,int min,int max)
{
int rand_nums[size];//本地变量,局部变量
srand(time(NULL));//设置随机数的种子
for(int i = 0;i<size;i++)
{
rand_nums[i] = read()%(max-min+1)+min;
}
return rand_nums;
}
int main()
{
int *nums = (int *)rand_arr(10,50,100);
for(int i = 0;i<10;i++)
{
printf("%d",*(nums +i));
}
printf("\n");
return 0;
}
这个代码的问题是:函数返回局部变量的地址,函数弹栈(结束),返回的地址空间会自动释放,在函数之外使用时是无效的。
解决办法:将返回地址的变量类型修改为静态局部变量即可。
static int rand_nums[10];
9. 经常容易混淆的指针
数组相关:
int *a[10]; 指针数组,本质上是数组,存放的元素是指针
int (*a)[10]; 数组指针,本质上是指针,指向的二维数组中第一个元素
函数相关:
int *f(void); //void表示函数无任何的形参
函数返回值是 int* 类型的指针
int (*f)(void);
函数指针,指向一个函数的地址
特殊指针:
void *:空类型的指针,可以强转成任意类型。
char *|short *|int *...
NULL:空,一般作为指针初始化的值,即代表空指针。默认值为0x0。
标签:语言,int,笔记,char,地址,数组,变量,指针
From: https://www.cnblogs.com/dijun666/p/17642473.html