《指针进阶-2》

//#include
//int test(const char* str)
//{
//    printf("test()\n");
//    return 0;
//}
//int main()
//{
//    int (*p)(const char*) = &test;
//    int sz = sizeof(&test);//虽然函数名与&函数名都表示函数地址,
//    //但是对于关键字来说,有些关键字的操作数不可以是函数,比如sizeof
//    printf("%d\n",sz);
//    p("abc");
//    return 0;
//}
//————————5.5函数指针的用途
//转移表
//例子:(计算器)普遍写法,内容相似率太高,占用空间太大
//#define _CRT_SECURE_NO_WARNINGS 1
//#include
//int menu()
//{
//    printf("*************************\n");
//    printf(" 1:add           2:sub \n");
//    printf(" 3:mul           4:div \n");
//    printf("*************************\n");
//    return 0;
//}
//int add(int a, int b)
//{
//    return a + b;
//}
//int sub(int a, int b)
//{
//    return a - b;
//}
//int mul(int a, int b)
//{
//    return a * b;
//}
//int div(int a, int b)
//{
//    return a / b;
//}
//int main()
//{
//    int x, y;
//    int input = 1;
//    int ret = 0;
//    do
//    {
//        menu();
//        printf("请选择:");
//        scanf("%d", &input);
//        switch (input)
//        {
//        case 1:
//            printf("输入操作数:");
//            scanf("%d %d", &x, &y);
//            ret = add(x, y);
//            printf("ret = %d\n", ret);
//            break;
//        case 2:
//            printf("输入操作数:");
//            scanf("%d %d", &x, &y);
//            ret = sub(x, y);
//            printf("ret = %d\n", ret);
//            break;
//        case 3:
//            printf("输入操作数:");
//            scanf("%d %d", &x, &y);
//            ret = mul(x, y);
//            printf("ret = %d\n", ret);
//            break;
//        case 4:
//            printf("输入操作数:");
//            scanf("%d %d", &x, &y);
//            ret = div(x, y);
//            printf("ret = %d\n", ret);
//            break;
//        case 0:
//            printf("退出程序\n");
//            break;
//        default:
//            printf("选择错误\n");
//            break;
//        }
//    } while (input);
//
//    return 0;
//}
优化版计算器(使用函数指针)(回调函数法)
//#define _CRT_SECURE_NO_WARNINGS 1
//#include
//int menu()
//{
//    printf("*************************\n");
//    printf(" 1:add           2:sub \n");
//    printf(" 3:mul           4:div \n");
//    printf("*************************\n");
//    return 0;
//}
//int Add(int a, int b)
//{
//    return a + b;
//}
//int Sub(int a, int b)
//{
//    return a - b;
//}
//int Mul(int a, int b)
//{
//    return a * b;
//}
//int Div(int a, int b)
//{
//    return a / b;
//}
//回调函数法:通过函数指针回头在去调用它,在适当的时候调用它所指向的函数
//回调函数法(通俗解释):字面上的理解,回调函数就是一个参数,将这个函数作为参数传到另一个函数里面,
当那个函数执行完之后,再执行传进去的这个函数。这个过程就叫做回调。
//void calc(int (*p)(int, int))//此处的(*p)不可以换成p,因为*表示函数指针类型:int (*)(int, int)
//{//封装函数calc,并通过传不同的参数实现不同的功能
//    int x, y;
//    int ret = 0;
//    printf("输入操作数:");
//    scanf("%d %d", &x, &y);
//    ret = (*p)(x, y);//此处的(*p)可以换成p
//    //代码“ret = (*p)(x, y)”在对应区域调用对应的函数(加减乘除),就把谁的地址传进来,
//    // 因此我们封装一个函数calc,并把此函数抽离出一个函数指针,用于接收该函数的地址
//    //并在对应的地方将函数的地址传进去,然后在内部根据函数指针,找对应的函数,并执行函数
//    printf("ret = %d\n", ret);
//}
//int main()
//{
//    int input = 1;
//    do
//    {
//        menu();
//        printf("请选择:");
//        scanf("%d", &input);
//        switch (input)
//        {
//            case 1:
//                calc(Add);//也可以写成*Add
//                break;
//            case 2:
//                calc(Sub);//也可以写成*Sub
//                break;
//            case 3:
//                calc(Mul);//也可以写成*Mul
//                break;
//            case 4:
//                calc(Div);//也可以写成*Div
//                break;
//            case 0:
//                printf("退出程序\n");
//                break;
//            default:
//                printf("选择错误\n");
//                break;
//        }
//    } while (input);
//    return 0;
//}
——代码陷阱1
(*(void (*)())0)();//第一个*可有可无
0是int,在此处是一个地址
(void (*)())是函数指针类型
最外层的():表示(*(void (*)())0)是函数,并且不传参
(void (*)())把0强制类型转换成(再去解引用):"()"(无参数), 
"void"(返回类型是void的函数指针(或函数的地址))
调用0地址处的这个函数
——代码陷阱2
void (*signal(int, void(*)(int)))(int);//此代码是一次函数声明
signal对于*和(int, void(*)(int))他先和(int, void(*)(int))结合,所以signal为函数名
signal有两个参数,第一个参数的类型是int,第二个参数的类型是函数指针,
该函数指针指向的函数参数是int,返回类型是void,
signal函数的返回类型也是一个函数指针,该函数指针指向的函数参数是int,返回类型是void
int:整型,void(*)(int):函数指针类型
void (*signal)(int):表示signal最后返回的还是函数指针类型
typedef可以把类型重命名
eg:
//typedef unsigned int uint;//unsigned int = uint
//typedef void(*pf)(int);//把void(*)(int)类型重命名为pf
//因此,函数陷阱2,可以简化为
//typedef void(*pf)(int);
//pf signal(int, pf);

————————6.函数指针数组
//函数指针数组
//数组是一个存放相同类型数据的存储空间,那我们已经学习了指针数组,
//比如:int* arr[10];数组的每个元素是int*
//那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?
//int (*parr1[10])();
答案是:parr1
parr1 先和[] 结合,说明 parr1是数组,数组的内容是int (*)() 类型的函数指针。
//#include
//int Add(int x, int y)
//{
//    return x + y;
//}
//int Sub(int x, int y)
//{
//    return x - y;
//}
//int Mul(int x, int y)
//{
//    return x * y;
//}
//int Div(int x, int y)
//{
//    return x / y;
//}
//int main()
//{
//    int(*p)(int, int) = Add;//p为函数指针,int(*)(int,int)为函数指针类型
//    int(*arr[4])(int, int) = {Add,Sub,Mul,Div};//arr是函数指针的数组
//    //Add, Sub, Mul, Div分别为下标0,1,2,3的数组元素。并且这四个元素分别能找到他们对应的函数,
//    // 因此在访问函数指针数组元素的时候,其实能够找到这些函数的地址,然后就可以调用这些函数
//    int i = 0;
//    for (i = 0; i < 4; i++)
//    {
//        int ret = arr[i](8,4);//随着i的增加,可以访问所有的数组元素,并调用函数。
//        //此处的arr[i]可以换成*arr[i],并且arr[i]等同于函数
//        printf("%d\n",ret);
//    }
//    return 0;
//}
计算器(引用函数指针数组)
//#define _CRT_SECURE_NO_WARNINGS
//#include
//int menu()
//{
//
//    printf("*************************\n");
//    printf(" 1:add           2:sub \n");
//    printf(" 3:mul           4:div \n");
//    printf("*************************\n");
//    return 0;
//}
//int add(int a, int b)
//{
//    return a + b;
//}
//int sub(int a, int b)
//{
//    return a - b;
//}
//int mul(int a, int b)
//{
//    return a * b;
//}
//int div(int a, int b)
//{
//    return a / b;
//}
//int main()
//{
//    int x, y;
//    int i = 1;
//    int ret = 0;
//    int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表:提供跳转的功能
//    //由于菜单本设计中,我们设置的函数均有对应数字,所以要仔细考虑到上述问题,
//    // 因此,数组元素设置为5个,并且,add的下标遵循菜单栏的设计进行更改,使其下标为1
//    //i之所以初始值为1,add的下标对应只是原因之一,第二个原因是,while()语句,
//    // ()里为不为0的数才会执行,因此i的初始值就没使用1
//    while (i)
//    {
//        menu();
//        printf("请选择:");
//        scanf("%d", &i);
//        if ((i <= 4 && i >= 1))
//        {
//            printf("输入操作数:");
//            scanf("%d %d", &x, &y);
//            ret = (*p[i])(x, y);
//        }
//        else
//            printf("选择错误\n");
//        printf("ret = %d\n", ret);
//    }
//    return 0;
//}
——————————————7.指向函数指针数组的指针
指向函数指针数组的指针是一个指针
指针指向一个数组 ,数组的元素都是函数指针;
//#include
//void test(const char* str)
//{
//    printf("%s\n", str);
//}
//int main()
//{
//    //函数指针p
//    void (*p)(const char*) = test;
//    //函数指针的数组pArr
//    void (*pArr[5])(const char* str);
//    pArr[0] = test;
//    //指向函数指针数组pArr的指针ppArr
//    void (*(*ppArr)[5])(const char*) = &pArr;
//    return 0;
//}

————————8.回调函数
//回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个
//函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数
//的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进
//行响应。

//qsort是比较函数,可以排序任意类型的数据
//qsort是库函数,需要引用对应头文件#include 或者#include
// void qsort——————————————————————————参数详解
// void qsort(void* base,size_t num,size_t width,int(* cmp)(const void* e1,const void* e2))
// void* base//我们要排序的数据的起始位置(arr:首元素地址)
// size_t num//待排序的数据元素的个数
// size_t width//待排序的数据元素的大小(占几个字节)
// int(* cmp)(const void* e1,const void* e2)//函数指针
// 
//如果我们实现一个排序算法,排序不同数据的话(结构体,整型...),其比较方式不同,
// 因此我们把比较函数的功能(比较的模块)提取出来,放在“int(* cmp)(const void* e1,const void* e2)”的“cmp”中,
// cmp函数就是一个比较函数的地址,我们可以把一个比较函数的地址传给cmp,cmp就是一个函数指针,
// 此时e1指向了我们要比较的第一个元素,e2指向了我们要比较的第二个元素。
// e1和e2是我们要比较的两个元素的地址,此时cmp就会主动去调用它指向的比较函数,
// 然后把e1,e2指向的两个元素进行比较,此时,不管我们排序什么类型的数据,我们都根据提供的比较函数进行比较并排序。
//#include
//#include
//int int_cmp(const void* e1, const void* e2)//比较两个整型元素,e1.e2分别指向一个整数
//{
//    //void*的指针不能直接进行解引用操作//例如e1,e2就不可以
//    /*if (*(int*)e1 > *(int*)e2)
//        return 1;
//    else if (*(int*)e1 == *(int*)e2)
//        return 0;
//    else
//        return -1;*/
//    return (*(int*)e1 - *(int*)e2);//"/*...*/"替换为此代码更加简洁
//    //强制类型转换e1,e2为int*(整型指针)//return中e1-e2为升序、e2-e1为降序
//}
//int main()
//{
//    int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
//    int i = 0;
//    qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);
//    //此处我们并没有调用int_cmp函数,int_cmp函数的“实现方”是程序员,也就是“我”,
//    //而调用函数的是qsort,qsort在其内部某个特定的条件下调用了int_cmp所指向的函数
//    for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
//    {
//        printf("%d ", arr[i]);
//    }
//    printf("\n");
//    return 0;
//}
//————void*的指针不能直接进行解引用操作
例如:
//#include
//int main()
//{
//    int a = 10;
//    //char* pa = &a;//不可以,会报错"int *" 类型的值不能用于初始化 "char *" 类型的实体
//    void* pv = &a;//void*是无具体类型的指针,可以接受任意类型的地址,
//    //但是不能解引用操作和+或-整数的操作,因为进行解引用操作时,到底访问多少字节,
//    // 需要知道确切的类型,才清楚+或-一个整数时,跳过几个字节但是void*无类型
//    return 0;
//}
为什么使用void*的指针?
答:当我们不清楚传过来的是什么类型的地址时,我们要去接收此地址只能使用void*(包容性)
//——————————————————————————结构体排序(采取回调函数)
//#include
//#include
//#include
//struct Stu
//{
//    char name[5];
//    int age;
//};
//int cmp_Stu_byname(const void* e1, const void* e2)//通过比较名字(字符串比较大小必须要用strcmp函数比较,不可以用+-)
//{
//    return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);strcmp返回的值有>0、==0、<0
//}//    //比较方法:kk和hh比较,先是第一个相互比,k>h,所以kk大
通过比较年龄(直接进行比较,可以用+-)
//int cmp_Stu_byage(const void* e1, const void* e2)
//{
//    return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
//}
//void test1()//————名字比较
//{
//    //测试使用qsort来排序结构数据
//    struct Stu s[] = { {"kk",15}, {"hh",16} ,{"ss",17}};
//    int sz = sizeof(s) / sizeof(s[0]);
//    qsort(s, sz, sizeof(s[0]), cmp_Stu_byname);
//    printf("%s\n", s);
//}
//void test2()//————年龄比较
//{
//    //测试使用qsort来排序结构数据
//    struct Stu s[] = { {"kk",15}, {"hh",16} ,{"ss",17} };
//    int sz = sizeof(s) / sizeof(s[0]);
//    qsort(s, sz, sizeof(s[0]), cmp_Stu_byage);
//    printf("%s\n", s);
//}
//int main()
//{
//    test1();
//    test2();
//    return 0;
//}
————————————————————————————自创函数冒泡排序
qsort函数内部是基于快速排序的思想来实现的
//#include
//int int_cmp(char* p1, char* p2)
//{
//    return (*(int*)p1 - *(int*)p2);
//}
//void Swap(char* p1, char* p2, int width)
//{
//    int i = 0;
//    for (i = 0; i < width; i++)
//    {
//        /*char tmp = *((char*)p1 + i);
//        *((char*)p1 + i) = *((char*)p2 + i);
//        *((char*)p2 + i) = tmp;*///等同于
//        char tmp = *p1;
//        *p1 = *p2;
//        *p2 = tmp;
//        p1++;
//        p2++;
//    }
//}
//void bubble(void* base, int sz, int width, int(*cmp)(char*, char*))
//{
//    int i = 0;//趟数
//    for (i = 0; i < sz - 1; i++)
//    {
//        int flag=1;//假设数组已经排好顺序
//        //一趟冒泡排序的过程
//          int j = 0;
//        for (j = 0; j < sz - i - 1; j++)
//        {
//            //由于计算之前一切都是未知的,所以我们传参时的每一项都要进行同等条件下最精细的等价转换
//            if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
//            //此处的两个参数是待比较两个元素的地址
//             //此处的强制类型转换用char*而不是int*是因为我们不清楚其真实类型,而int*+1跳过四个字节过于大,
//             //所以我们采用char*,每次只跳过一个字节,更为稳妥
//            {
//                //交换
//                Swap((char*)base + j *width, (char*)base + (j + 1) * width, width);//交换时,需交代一下元素的宽度width
//                //width:待排序的数据元素的大小(占几个字节)
//                flag = 0;
//            }
//        }
//        if (flag == 1)
//        {
//            break;
//        }
//    }
//}
//int main()
//{
//    int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
//    //char *arr[] = {"aaaa","dddd","cccc","bbbb"};
//    int i = 0;
//    bubble(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);
//    for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
//    {
//        printf("%d\n", arr[i]);
//    }
//    printf("\n");
//    return 0;
//}
//
——————sizeof的计算,以及类型匹配
//#include
//#include
//int main()
//{
//    char a = 10;
//    const char arr[] = {1,2,3,4,5};
//    printf("%d\n",strlen(&a));
//    printf("%d\n", strlen(arr));
//    printf("%d\n", sizeof(a));
//    printf("%d\n", sizeof(arr));
//    return 0;
//}

————————9.指针和数组笔试题解析
//注意:
// //strlen的参数是const char*,任何类型传给strlen都要变成const char*类型,
// 关注的是字符串中的'\0',计算的是'\0'之前出现的字符串长度
// strlen是库函数,只针对字符串。sizeof是操作符
// //元素的大小:元素所占内存空间的大小
// //相等关系(依据上题内容)
// a==&a[0],a+0==&a[0]+0,*a==*&a[0],*a==a[0],*&a==a,&a[0] + 1=&a[1],&和*可以抵消,p[0]=*(p+0)=*p{p为指针变量}


一维数组,数组名的理解
数组名没有sizeof和&时,表示首元素地址
数组中,sizeof求数组的地址,结果永远是是4字节(32位平台)或8字节(64位平台)
//#include
//int main()
//{
//    int a[] = { 1,2,3,4 };//由初始化内容可知数组元素个数为4字节
//    printf("%d\n", sizeof(a));//sizeof(数组名)数组名表示整个数组元素的大小,单位是字节4*4=16字节
//    //a不是单独放在sizeof内部,也没有取到地址,所以a就是首元素地址,a+0还是首元素地址,是地址,大小就是4/8个字节
//    printf("%d\n", sizeof(a + 0));//a+0:首元素的地址//4字节(32位平台)或8字节(64位平台)
//    printf("%d\n", sizeof(*a));//*a:首元素的地址//4字节
//    printf("%d\n", sizeof(a + 1));//a:首元素的地址,a+1:第2个元素的大小//4字节(32位平台)或8字节(64位平台)
//    printf("%d\n", sizeof(a[1]));//a[1]='2'的地址(第2个元素的大小)//4字节(32位平台)
//    printf("%d\n", sizeof(&a));//&a得到的是整个数组的地址//4字节(32位平台)或8字节(64位平台)
//    printf("%d\n", sizeof(*&a));//*&a相当于数组名a,计算的是数组的大小//4*4=16字节
//    //&a得到的是整个数组的地址,类型为int(*)[4],指针类型决定了我对其进行*操作时有多大权限(访问几个字节),
//    // 因为是一个指向整型数组的指针(数组指针),所以*一个指针访问的是一个数组
//    printf("%d\n", sizeof(&a + 1));//&a得到的是整个数组的地址,+1,跳过整个数组的大小
//    //,&a+1还是地址,大小就是4或8//4字节(32位平台)或8字节(64位平台)
//    printf("%d\n", sizeof(&a[0]));//第1个元素的地址大小//4字节(32位平台)或8字节(64位平台)
//    printf("%d\n", sizeof(&a[0] + 1));//第2个元素的地址大小//4字节(32位平台)或8字节(64位平台)
//}


字符数组
//#include
//#include
//int main()//sizeof只关注占用内存空间的大小,不在乎内存中放的是什么
//{
//    char arr[] = { 'a','b','c','d','e','f' };//6个元素
//    printf("%d\n", sizeof(arr));//sizeof(数组名)数组名表示整个数组元素的大小,单位是字节1*6=6字节
//    printf("%d\n", sizeof(arr + 0));//首元素地址+0=首元素地址,地址的大小//4字节(32位平台)或8字节(64位平台)
//    printf("%d\n", sizeof(*arr));//数组首元素大小//1字节
//    printf("%d\n", sizeof(arr[1]));//第2个元素大小//1字节
//    printf("%d\n", sizeof(&arr));//数组地址的大小//4字节(32位平台)或8字节(64位平台)
//    printf("%d\n", sizeof(&arr + 1));//数组地址的大小(+1跳过一整个数组)//4字节(32位平台)或8字节(64位平台)
//    printf("%d\n", sizeof(&arr[0] + 1)); //第2个元素的地址的大小//4字节(32位平台)或8字节(64位平台)
//    //(1为整型,arr[0]发生整型提升,计算的是int的大小,结果为4/8)
//    return 0;
//}


//#include
//#include
//int main()
//{
//    char arr[] = { 'a','b','c','d','e','f' };
//    printf("%d\n", strlen(arr));//arr首元素地址//strlen计算长度时,直至遇到'\0'时,才会停止,所以结果为随机值>=6
//    printf("%d\n", strlen(arr + 0)); //arr+0=arr首元素地址//strlen计算长度时,直至遇到'\0'时,才会停止,所以结果为随机值>=6
//    //printf("%d\n", strlen(*arr));//此代码有问题,strlen(*arr)相当于strlen('a')而ASCII值为97,
//    //即strlen(97)把97作为地址传给strlen,所以97被视为野指针,因为97并不是我们的地址,
//    //printf("%d\n", strlen(arr[1]));//此代码有问题//此代码传的是b,同理为野指针
//    //printf("%d\n", strlen(&arr));//&arr取出整个数组的地址,strlen计算长度时,直至遇到'\0'时,才会停止//随机值
//    //printf("%d\n", strlen(&arr + 1));//&arr + 1跳过一整个数组为6个字符所以结果-6//随机值-6
//    printf("%d\n", strlen(&arr[0] + 1));//&arr[0]是第一个元素的地址,+1为第二个元素的地址。 + 1跳过了第一个元素所以结果-1//随机值-1
//}


//#include
//int main()
//{
//    char arr[] = "abcdef";
//    printf("%d\n", sizeof(arr));//初始化时'f'后默认存在一个'\0',sizeof包括'\0'//7
//    printf("%d\n", sizeof(arr + 0));//首元素地址+0=首元素地址,地址的大小//4字节(32位平台)或8字节(64位平台)
//    printf("%d\n", sizeof(*arr));//数组名未单独放在sizeof,未加&,所以表示首元素地址,*后访问首元素a//1
//    printf("%d\n", sizeof(arr[1]));//数组名未单独放在sizeof,未加&,所以arr[1]表示下标为1的元素b//1
//    printf("%d\n", sizeof(&arr));//数组地址的大小//4字节(32位平台)或8字节(64位平台)
//    printf("%d\n", sizeof(&arr + 1));//跳过整个数组,跳到了'\0'的后面,即使跳过了数组,但仍是地址//4字节(32位平台)或8字节(64位平台)
//    printf("%d\n", sizeof(&arr[0] + 1));//&arr[0]=首元素地址,+1为b的地址//4字节(32位平台)或8字节(64位平台)
//    return 0;
//}


//#include
//#include
//int main()
//{
//    char arr[] = "abcdef";
//    printf("%d\n", strlen(arr));//首元素地址//6
//    printf("%d\n", strlen(arr + 0)); //arr + 0 = arr首元素地址//strlen计算长度时,直至遇到'\0'时,才会停止,所以结果为6
//    //printf("%d\n", strlen(*arr));//此代码有问题,strlen(*arr)相当于strlen('a')而ASCII值为97,
//    //即strlen(97)把97作为地址传给strlen,所以97被视为野指针,因为97并不是我们的地址,
//    //printf("%d\n", strlen(arr[1]));//此代码有问题//此代码传的是b,同理为野指针
//    printf("%d\n", strlen(&arr));//&arr=取出整个数组的地址//6
//    printf("%d\n", strlen(&arr + 1));//&arr=取出整个数组的地址,+1跳过整个数组包括\0,所以后面无\0//随机值
//    printf("%d\n", strlen(&arr[0] + 1));//&arr[0]=首元素地址,+1为b的地址,strlen会从b开始往后计算长度//5
//    return 0;
//}


指针的运算与指针类型的意义
sizeof求指针变量的大小为4字节(32位平台)或8字节(64位平台)
//#include
//int main()
//{
//    const char* p = "abcdef";//把首字符a的地址放到p里//p只是存放了字符串的地址,而他们的内存空间并不同
//    //假设指针变量p的地址为0x12ff40
//    printf("%d\n", sizeof(p));//sizeof求指针变量p的大小//4字节(32位平台)或8字节(64位平台)
//    printf("%d\n", sizeof(p + 1));//p+1,地址为0x12ff41,仍为地址//4字节(32位平台)或8字节(64位平台)
//    printf("%d\n", sizeof(*p));//*p为首字符a//1
//    printf("%d\n", sizeof(p[0]));//p[0]为首字符a//1
//    printf("%d\n", sizeof(&p));//1.&p取出的是一级指针p起初的地址(2.pw为一级指针,&p取出的是二级指针)//4字节(32位平台)或8字节(64位平台)
//    printf("%d\n", sizeof(&p + 1)); // 1. & p取出的是一级指针p起初的地址,+1跳过p指向末尾的位置,也是地址//4字节(32位平台)或8字节(64位平台)
//    printf("%d\n", sizeof(&p[0] + 1));//&p[0]为首字符a的地址,+1为b的地址//4字节(32位平台)或8字节(64位平台)
//    return 0;
//}


//#include
//#include
//int main()
//{
//    const char* p = "abcdef";
//    printf("%d\n", strlen(p));//6
//    printf("%d\n", strlen(p + 1));//p+1指向第二个元素b//5
//    printf("%d\n", strlen(*p));//此代码有错误,相当于strlen('a')而ASCII值为97,
//    //即strlen(97)把97作为地址传给strlen,所以97被视为野指针,因为97并不是我们的地址。
//    printf("%d\n", strlen(p[0]));//此代码有错误,相当于strlen('a')而ASCII值为97,
//    //即strlen(97)把97作为地址传给strlen,所以97被视为野指针,因为97并不是我们的地址。
//    printf("%d\n", strlen(&p));//p与字符串的内存空间不同,所以&p与字符串无关,而p的地址中\0的位置未知所以为随机值//随机值
//    printf("%d\n", strlen(&p + 1));//p的地址中\0的位置未知所以为随机值//随机值
//    printf("%d\n", strlen(&p[0] + 1));//(&p[0] + 1第二个元素b//5
//    return 0;
//}

//二维数组
对于二维数组来说,unt arr[i][j]={0}中。arr[i]就相当于一维数组int a[n]的数组名a,[j]相当于一维数组的下标
//#include
//int main()
//{
//    int a[3][4] = { 0 };
//    printf("%d\n", sizeof(a));//数组名单独放在sizeof内部,表示整个数组//4*3*4=48
//    printf("%d\n", sizeof(a[0][0]));//第一行第一个元素//4
//    printf("%d\n", sizeof(a[0]));//二维数组的第一行//16
//    printf("%d\n", sizeof(a[0] + 1));//a[0]+1,不是单独的数组名,所以a[0]相当于&a[0][0]指向第一行第一个元素的地址,
//    //+1指向的是第一行第二个数组的地址//4字节(32位平台)或8字节(64位平台)
//    printf("%d\n", sizeof(*(a[0] + 1)));//同上句代码,*后为第一行第二个元素//4
//    printf("%d\n", sizeof(a + 1));//a+1表示第二行的地址//4字节(32位平台)或8字节(64位平台)
//    printf("%d\n", sizeof(*(a + 1)));//*(a + 1)表示对第二行的地址进行*,表示第二行//16
//    //*(a + 1)==a[1], sizeof(a[1])== sizeof(a[1])//a[1]和*(a+1)都表示第二行地址
//    printf("%d\n", sizeof(&a[0] + 1));//&a[0]表示第1行的地址,+1表示第二行的地址//4字节(32位平台)或8字节(64位平台)
//    printf("%d\n", sizeof(*(&a[0] + 1)));//&a[0]表示第1行的地址,+1表示第二行的地址,*后为第二行//16
//    printf("%d\n", sizeof(*a));//a为第一行地址,*a表示第一行数组//16
//    printf("%d\n", sizeof(a[3]));//a[3]是第四行的一维数组,虽然不存在,但是sizeof求大小的时候只需要知道其类型和个数即可(4个整型)//16
//    printf("%d\n", sizeof(&a));//整个二维数组的地址//4字节(32位平台)或8字节(64位平台)
//    printf("%d\n", sizeof(&a+1));
//    return 0;
//}


总结:
数组名的意义:
1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
2. & 数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
3. 除此之外所有的数组名都表示首元素的地址。

你可能感兴趣的:(c语言,开发语言)