目录
sizeof ( 指针 )
// 指针是地址, 指向某数据空间, 地址常是4字节.
char *p="老师,早上好!";
printf("%ld", sizeof(p);
// 结果:4, 返回指针大小,指针本身就是一个无符号整型数. 不是所指向的内存空间的大小
// char *a[8], 在数组内存放8个地址, 8个*4字节=32字节, 而不是8个*1字节=8字节
*
有地址,跳转地址对应内容
无地址,获取地址
指针的作用
(1)为函数修改实参提供支持
(2)为动态内存分配提供支持
(3)为动态数据结构提供支持
(4)为内存访问提供了另外一种途径
指针变量
内存地址:系统为了内存管理的方便,将内存划分为一个个内存单元,并进行内存单元编码,内存单元的编号就是该内存单元的地址。
变量指针:变量的地址称为该变量的指针。变量的地址往往是内存单元的编号(首地址)。
指针变量:存放变量指针的变量。
指向:指针变量中存放"a"的地址,该指针变量就指向"a"
指针变量的定义:
语法格式:类型 * 变量列表;
eg:
int p ; //p为普通变量
int * p ; //p为指针变量
int * a , * b ; //定义了指针变量a和b
注意事项:
(1)虽然定义了指针变量,但是变量名仍为:a,b,p;
(2)使用指针变量访问数据时,指针变量必须要有明确的指向;
(3)int a = 3 ; int * p = & a ;
(4)指针变量只能访问同类型的变量,借助指针访问内存,一次访问内存的大小取决于指针变量类型
(5)指针变量的初始化类似于普通变量的初始化
指针变量的使用:
赋值:
(1)
int a = 3 ;
int * p ;
p = & a ;
(2)
int a , * p , * q ;
p = & a ;
q = p ;
操作指针变量的值:
int a = 3 ;
int * q; q = & a ;
* q = 6 ;
演示内存数据访问:
int a = 9 ;
int * p = & a ;
直接访问:a a=999;
间接访问:* p * p = 888 ;
指针变量做函数参数:指针变量做函数的参数往往传递的是变量地址。
由于传递的是地址,所以修改实参数据。
数组指针&指针数组
指针变量访问数组元素:
数组指针:指向数组中第一个元素的地址(指针)。
int a[]={1,4,9};
int * p = &a[0];
int * q = a;
指针的运算:
指针变量必须要有明确的指向,指向数组中的元素。
自增 ++p p++ p=p+1 让指针变量指向下一个元素 自减 --p p-- p=p-1 让指针变量指向上一个元素
加一个数 p+3 向后偏移3个类型的宽度
减一个数 p-2 向前偏移2个类型的宽度
指针的比较 p<q 在同一个数组中可以比较
数组指针:指向一维数组的指针变量。
定义:假设该指针变量指向有N个元素的一维数组。
数据类型 (* 数组指针变量名)[N];
e.g.(1)
int a[N]={1,4,3};
int (*p)[N];
p=&a;
(2)
int(*p)[N]=&a;
e.g.(1)
int a[][N]={{1,3},{5,7}};
int (*p)[N]=a;
a[i][j]<---->*(*(p+i)+j)<---->p[i][j]<---->*(p[i]+j)
指针数组:元素是指针变量的数组
指针数组的定义:类型* 数组名[N];
int* p[6];
字符指针:char* str="I love China";
函数指针
函数指针:指向函数的指针变量(地址入口)
应用场景之一:传递动作、方法、流程
定义:类型 (*p)(形参列表);
int max(int a,int b)
{
return a>b?a:b;
}
int (*p)(int,int);
p=max;
利用函数指针调用函数:
(*p)(3,6);
p(3,6);
二级指针
指向指针变量的指针变量(二级指针):
定义格式:类型** p;
int a;
int* p =&a ;
int**q=&p;
二级指针访问元素需要一级一级的解引用;
二级指针往往用来引用指针数组;
char* name[4]={"zhangsan","lisi","wangwu",NULL};
面试题
在不改变数组的数组元素顺序的情况下,将数组升序输出
#include <stdio.h>
int main()
{
// 原数组
int arr[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
int n = sizeof(arr) / sizeof(arr[0]);
// 复制原数组到新数组
int sortedArr[n];
for (int i = 0; i < n; i++)
{
sortedArr[i] = arr[i];
}
// 使用冒泡排序对复制后的数组进行升序排序
for (int i = 0; i < n - 1; i++)
{
for (int j = 0; j < n - i - 1; j++)
{
if (sortedArr[j] > sortedArr[j + 1])
{
// 交换元素
int temp = sortedArr[j];
sortedArr[j] = sortedArr[j + 1];
sortedArr[j + 1] = temp;
}
}
}
// 输出排序后的数组
printf("升序排序后的数组: ");
for (int i = 0; i < n; i++)
{
printf("%d ", sortedArr[i]);
}
printf("\n");
// 验证原数组未改变
printf("原数组: ");
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
指针常量&常量指针
指针常量:类型* const 指针变量名
int * const p ;
常量指针:const 类型* 指针类型名
const int * p ;
区分:
(1)先读常量,先写const ; 后读常量,后写const
constant : 常量
* : 指针
(2)*是指针,const是常量
名字在前,对应参数不可修改
int a = 9 ;
int b = 3 ;
const int * p = & a ;
p = & b ; //right
*p=999; //error
*p可以访问,但是不能修改常量(*p);
int * const p = & a ;
p = & a ; //error
* p = 999 ;//right
p = & b ; //error
内存申请与释放
动态内存管理:在堆段进行内存的申请与释放。
(1)malloc()
头文件:#include<stdlib.h>
函数原型:void* malloc(size_t size);
函数功能:在堆上申请size大小(单位是字节)
函数参数:size:字节大小
返回值:成功:返回申请内存的首地址
失败:返回NULL
int * p =(int*)malloc(100);
(2)calloc()
头文件:#include<stdlib.h>
函数原型:void* calloc(unsigned n,unsigned size);
函数功能:在堆上申请n个长度为size大小的连续空间(单位是字节)
函数参数:size:字节大小
返回值:成功:返回申请内存的首地址
失败:返回NULL
int * p =(int*)calloc(50,4);
(3)realloc()
头文件:#include<stdlib.h>
函数原型:void* realloc(void *p,unsigned int size);
函数功能:在堆上将p指向的临时分配域的大小改变为size大小(单位是字节)
函数参数:size:字节大小
返回值:成功:返回申请内存的首地址
失败:返回NULL
realloc(p,100);
(4)free()
头文件:#include<stdlib.h>
函数原型:void free(void *p);
函数功能:释放由指针变量p指向的动态空间,使这部分空间能被其它变量使用。
函数参数:p:是最近一次调用callo或malloc函数时的返回值。
返回值:该函数无返回值。
free(p);