本质作用:合理的利用空间
#include
#include
void test01(){
//基本类型 char short int long float double
//char 占1字节空间
char ch;
printf("sizeof(ch)=%d\n",sizeof(ch));//1字节 (1B)
int num;
printf("sizeof(int)=%d",sizeof(num)); //4字节 (2B)
}
int main(){
test01();
}
1B == 8位(b 二进制位)
1b只能存放0或1
1B == 1地址
#include
#include
void test02(){
int num = 100;
//取变量的地址 用&
//&num 代表标量num的起始地址
printf("%p\n",&num);
//需求:定义一个指针变量 保存num的地址
//在定义的时候:*说明是指针变量 而不是普通变量
int *p = NULL;
printf("sizeof(p)=%d\n",sizeof(p));
//num的地址 与 p 建立关系
p = #
printf("num = %d\n",num);
//使用中:*p 表示取p保存的地址编号 对应空间的内容
printf("*p = %d\n",*p); //*p-- 为0 --*p 减一
}
int main(){
test02();
}
void test03(){
int num = 10;
//指针变量两种类型:自身的类型 指向的类型
//自身的类型:在指针变量定义的时候 将变量名拖黑 剩下啥类型 指针变量就是啥类型
//int *p = NULL; p自身的类型就是int *
//指向的类型:在指针变量定义的时候 将变量名和离他最近的一个 * 一起拖黑 剩下啥类型 指针变量指向的类型就是啥类型
//int *p = NULL; p指向的类型就是int
//指针变量指向的类型的作用:决定了指针变量 所取空间内容的宽度 决定了指针变量+1跳过的单位跨度
int *p = NULL;
p = #
//指针变量的跨度
printf("&num=%u\n",&num); // 6684132
printf("p=%u\n", p); // 6684132
printf("p+1=%u\n",p+1); // 6684136 指向类型是int
//
printf("*p = %d\n",*p); //num的值
}
void test04(){
//指针类型决定了指针进行解引用操作的时候,能够访问空间的大小
//int *p; *p 能够访问4个字节
//char *p; *p 能够访问1个字节
//double *p; *p 能够访问8个字节
int num = 0x01020304;
int *p1 = #
printf("*p1 = %#x\n",*p1); //0x1020304
short *p2 = # // short *p2 = (short *)# 有些编译器要强制转化
printf("*p2 = %#x\n", *p2); //0x304
printf("*p2 = %#x\n", *(p2+1)); //0x201
char *p3 = # x //char *p3 = (char *)#
printf("*p3 = %#x\n", *p3); //0x4
*p1 = 0;
printf("num = %d\n",num); //0
}
int main(){
test04();
}
野指针就是指针指向的位置是不可知的(随机的,不正确的,没有明确限制的)
#inclued
int main(){
int *p; //局部变量指针未初始化,默认为随机值
*p = 20;
return 0;
}
#include
int main(){
int arr[10] = {0};
int *p = arr;
int i = 0;
for(i;i<=11;i++){
//当前指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}
例:
int* test06(){
int a = 60;
return &a;
}
int main(){
int *p = test06(); // a 是一个局部变量 函数开始调用a产生,调用结束就a的地址也就还给系统了
*p = 20;
return 0;
}
int main(){
int arr[10] = { 1,2,3,4,5,6,7,8,9,10};
int i = 0;
int sz = sizeof(arr)/sizeof(arr[0]);
int *p = arr;
for(i;i
得到的是中间元素的个数
int main(){
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
printf("%d\n",&arr[9]-&arr[0]); // 9
}
int my_strlen(char* str){
char *start = str;
char *end = str;
while(*end != '\0'){
end++;
}
return end - strart;
}
int main(){
//strlen - 求字符串长度
//递归 - 模拟实现了strlen - 计数器的方式1,递归的方式2
//
char arr[] = "bit";
int len = my_strlen(arr);
printf("%d\n",len);
return 0;
}
指针数组 - 数组,用来存放指针
int main(){
int *parr[4]; //存放整形指针的数组 - 指针数组
char *parr2[4]; //一级字符指针的数组
char **parr3[5]; //二级字符指针的数组
int a=10;
int b=20;
int c=30;
int d=40;
int * arr[4]={&a,&b,&c,&d};
int i =0;
for(i=0;i<4;i++){
printf("%d ",*(arr[i])); //10 20 30 40
}
}
int main(){
int arr1[] = {1,2,3,4,5};
int arr2[] = {2,3,4,5,6};
int arr3[] = {3,4,5,6,7};
int *parr[] = {arr1,arr2,arr3};
}
数组指针 - 指针 指向数组的指针 存放数组的地址
int arr[10] =0;
//arr - 首元素地址
//&arr[0] - 首元素地址
//&arr - 数组的地址
int (*p)[10] = &arr;
char* arr[5];
char* (*p)[5] = &arr;
int main(){
int arr[10] = {1,2,3,4,5,6,7,8,9,0};
int (*p)[10] = &arr;
int i =0;
for(i=0;i<10;i++){
printf("%d ", (*p)[i]); //1 2 3 4 5 6 7 8 9 0
}
for(i=0;i<10;i++){
// 1 2 3 4 5 6 7 8 9 0
printf("%d ", *(*p+i)); //*p == arr
}
}
//参数是指针的形式
void printf2(int (*p)[5],int x,int y){
int i =0;
for(i=0;i
int main(){
int i=0,arr[10]={1,2,3,4,5,6,7,8,9,0}, *p=arr;
for(i=0;i<10;i++){
printf("%d ",arr[i]); // arr[i] == *(arr+i) == *(p+i) == p[i]
}
}
int arr[5]; // arr是一个5个元素的整形数组
int *parr1[10]; // parr1是一个数组,数组有10个元素,每个元素的类型是int *,parr1是指针数组
int (*parr2)[10];// parr2是一个指针,该指针指向了一个数组,每个元素的类型是int
int (*arr3[10])[5]; // parr3是一个数组,该数组有10个元素,每个元素的类型是数组指针,该数组指针指向的数组有5个元素,每个元素是int
void test05(){
int arr[10] = {0};
int *p = arr; //数组名-首元素的地址
// 数组每个元素的值变为1
int i= 0;
for(i=0;i<10;i++){
*(p+i) = 1;
}
}
int main(){
int arr[10] = {0};
printf("%p\n",arr);// 地址-首元素地址 00EFF8E0
printf("%p\n",arr+1); //00EFF8E4
printf("%p\n",&arr[0]);// 地址-首元素地址 00EFF8E0
printf("%p\n",&arr[0]+1); //00EFF8E4
printf("%p\n",&arr); //地址-整个数组地址 从 00EFF8E0 开始
printf("%p\n",&arr+1); // 00EFF904
// 2个例外
//1. &arr - &数组名 - 数组名不是首元素地址 - 数组名表示整个数组 - &数组名 取出的是整个数组的地址
//2. sizeof(arr) - sizeof(数组名) - 数组名表示整个数组 - sizeof(数组名)计算的是整个数组的大小
}
int main(){
int arr[10] = { 0 };
int *p = arr;
int i =0;
for(i=0;i<10;i++){
*(p+i) = i;
}
for(i=0;i<10;i++){
printf("%d ", *(p+i));
}
}
int main(){
int a = 10;
int b =20;
int c = 30;
int *arr[3] = {&a, &b,&c}; //指针数组
int i = 0;
for(i;i<3;i++){
printf("%d ", *(arr[i]));
}
}
int main(){
int a = 10;
int *pa = &a;
int **ppa = &pa; //ppa就是二级指针
printf("%d\n",**ppa); //10
}
int main(){
char arr[] = "abcdef";
char* p = arr;
printf("%s\n",arr); //abcdef
printf("%s\n",p); //abcdef
}
int main(){
char ch = 'w';
char *p = &ch;
char *p2="abcdef"; //指针p2存放的是a的首地址,这是一个常量字符串不允许被修改
}
void test1(int a[3][5]){}
void test2(int a[][5]){}
void test3(int (*a)[5]){}
int main(){
int a[3][5]={0};
test1(a);
test2(a);
test3(a);
}
void test(int *p){}
void test2(char *p){}
int main(){
int a = 10;
int *p = &a;
test1(p); //ok
test1(&a); //ok
int ch = 'w';
int *pc = &ch;
test2(pc); //ok
test2(&ch); //ok
}
void test(int **p){}
int main(){
int *ptr;
int **pp = &ptr;
test(&ptr);
test(pp);
int *arr[10]; //指针数组
test(a);
}
函数指针 - 指针 - 存放函数地址的指针
int add(int x,int y){
int z = 0;
z = x+y;
return z;
}
void Print(char *str){
printf("%s\n",str);
}
int main(){
int a = 10;
int b = 20;
int arr[10] = {0};
//&函数名 和 函数名 都是函数的地址
printf("%p\n",&add);
printf("%p\n",add);
//函数指针
int (*pa)(int,int) = add;
printf("%d\n", (*pa)(2,3)); //5
void (*pstr)(char *) = Print;
(*)("hello bit"); //hello bit
return 0;
}
//把0强制类型转换成:void(*)() 函数指针类型 -0 就是一个函数的地址
//调用0地址处的该函数
(*(void (*)())0)();
//signal是一个函数声明
//signal函数的参数有2个,第一个是int,第二个是函数指针,该函数指向的函数的参数是int,返回类型是void
//signal函数的返回类型也是一个函数指针:该函数指针指向的函数的参数是int,返回类型是void
void(* signal(int,void(*)(int))(int);
//简化
typedef void(*pfun_t)(int);
pfun_t signal(int,pfun_t);
int add(int x,int y){
int z = 0;
z = x+y;
return z;
}
void Print(char *str){
printf("%s\n",str);
}
int main(){
int (*pa)(int,int) = add;
printf("%d\n", (*pa)(2,3)); //5
printf("%d\n", (pa)(2,3)); //5
printf("%d\n", add(2,3)); //5
return 0;
}
用途:转移表
int Add(int x,int y){
int z = 0;
z = x+y;
return z;
}
int Sub(int x,int y){
int z = 0;
z = x-y;
return z;
}
int Mul(int x,int y){
int z = 0;
z = x*y;
return z;
}
int Div(int x,int y){
int z = 0;
z = x/y;
return z;
}
int main(){
//指针数组
int *arr[5];
//指针函数
int (*pa)(int,int) = Add;
//需要一个数组,这个数组可以存放4个函数的地址 - 函数指针的数组
int (*parr[4])(int,int) = {Add , Sub , Mul , Div};
int i =0;
for(i=0;i<4;i++){
printf("%d\n",parr[i](2,3)); //5 -1 6 0
}
return 0;
}
int Add(int x,int y){
int z = 0;
z = x+y;
return z;
}
int main(){
int arr[10] = {0};
int (*p)[10] = &arr;
//pfarr是一个数组,函数指针的数组
int (*pfArr[4])(int,int) = {Add};
//ppfArr是一个数组指针,指针指向的数组有四个元素
//指向的数组的每个元素的类型都是函数指针 int (*)(int,int)
int (*(*ppfArr)[4])(int,int) = &pfArr;
}
回调函数就是一个通过函数指针调用的函数。
void printf(char *str){
printf("hehe:%s",str);
}
void test(void (*p)(char*)){
printf("test\n");
p("bit");
}
int main(){
test(printf);
//test
//hehe:bit
return 0;
}
int main(){
int a = 10;
void * p =&a;
//void* 类型的指针 可以接收任意类型的地址
//void* 类型的指针 不能进行解引用操作 eg: *p = 0 是错误的
//void* 类型的指针 不能进行+=整数的操作
//典型案例 qsort函数
}
void test(){
int arr[] = {1,3,6,8,0,9,7,5,4,2};
int sz = sizeof(arr)/sizeof(arr[0]);
qsort(arr,sz,sizeof(arr[0]),cmp_int);
//第一个参数:待排序数组的首元素地址
//第二个参数:待排序数组的元素个数
//第三个参数:待排序数组的每个元素的大小-单位是字节
//第四个参数:是函数指针,比较两个元素的所用函数的地址
// 函数指针的两个参数是:待比较的两个元素的地址
}
int cmp_int(const void* e1,const void* e2){
return *(int*)e1 - *(int*)e2; // (int*)强制类型转换
}
int cmp_float(const void* e1,const void* e2 ){
if(*(float*)e1 == *(float*)e2){
return 0;
}
else if(*(float*)e1 > *(float*)e2){
return 1;
}
else return -1;
}
//按照结构体中的元素排序
typedef struct Stu{
char name[20];
int age;
}
int cmp_Stu_age(const void* e1,const void* e2){
return ((Stu*)e1)->age - ((Stu*)e2)->age;
}
int cmp_Stu_name(const void* e1,const void* e2){
return strcmp(((Stu*)e1)->name , ((Stu*)e2)->name); //strcmp 字符串比较函数 需要引用 stdlib.h
}
void test(){
Stu arr[] = {{"zhangsan",27},{"wangwu",18},{"lisi",19}};
int sz = sizeof(arr)/sizeof(arr[0]);
//qsort(arr,sz,sizeof(arr[0]),cmp_Stu_age);
qsort(arr,sz,sizeof(arr[0]),cmp_Stu_name);
}
int cmp_int(const void* e1,const void* e2){
return *(int*)e1 - *(int*)e2; // (int*)强制类型转换
}
void Swap(char* buf1,char* buf2,int width){
int i =0;
for(i=0;i0){
//交换
Swap((char*)base+j*width,(char*)base+(j+1)*width);
}
}
}
}
int main(){
int arr[] = {1,3,6,8,0,9,7,5,4,2};
int sz = sizeof(arr)/sizeof(arr[0]);
bubble_sort(arr,sz,sizeof(arr[0]),cmp_int);
}
int main(){
//数组名是首元素的地址
//1. sizeof(数组名) - 数组名表示整个数组
//2. &数组名 - 数组名表示整个数组
//
//一维数组
int a[] = { 1,2,3,4}; //4*4 = 16
printf("%d\n",sizeof(a)); //sizeof(数组名) - 计算的是数组的总大小 - 单位是字节-16
printf("%d\n",sizeof(a+0));//4/8 -数组名这里表示的是首元素的值,a+0是首元素地址,地址的大小是 4/8个字节
printf("%d\n",sizeof(*a)); //4 - 数组名这里表示首元素地址,*a是首元素
printf("%d\n",sizeof(a+1));//4/8 -数组名这里表示的是首元素的值,a+1是第二个元素地址,地址的大小是 4/8个字节
printf("%d\n",sizeof(a[1]));//4 -第二个元素的地址
printf("%d\n",sizeof(&a)); //4/8 - &a是取出数组的地址,但是数组的地址也是地址,地址大小就是 4/8个字节
printf("%d\n",sizeof(*&a)); //16 - &a是数组的地址,数组的地址解引用访问的数组,sizeof计算的就是数组的大小单位是字节
printf("%d\n",sizeof(&a+1));//4/8 - &a是数组的地址,&a+1虽然跳过整个数组,但是还是地址,所以是 4/8个字节
printf("%d\n",sizeof(&a[0]));//4/8 - &a[0]是第一个元素的地址
printf("%d\n",sizeof(&a[0]+1));//4/8 &a[0]+1是第二个元素的地址
//字符数组
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n",sizeof(arr));//sizeof计算的数组的大小 6*1 = 6 字节
printf("%d\n",sizeof(arr+0));//4/8 arr是首元素的地址,arr+0还是首元素的地址 地址的大小是4/8字节
printf("%d\n",sizeof(*arr)); //1 arr是首元素的地址,*arr就是首元素,首元素是字符大小是一个字节
printf("%d\n",sizeof(arr[1]));//1
printf("%d\n",sizeof(&arr)); //4/8 &arr虽然是数组的地址,但还是地址,地址大小是4/8个字节
printf("%d\n",sizeof(&arr + 1));//4/8 &arr+1 是跳过整个数组后的地址,地址大小是4/8个字节
printf("%d\n",sizeof(&arr[0] + 1));//4/8 第二个元素的地址
}
int main(){
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n",strlen(arr)); //随机值
printf("%d\n",strlen(arr+0))//随机值
//printf("%d\n",strlen(*arr)) //err
//printf("%d\n",strlen(arr[1])) //err
printf("%d\n",strlen(&arr)) //随机值
printf("%d\n",strlen(&arr + 1)) //随机值-6
printf("%d\n",strlen(&arr[0] + 1)) //随机值-1
}