bilibiliclass30_C语言_指针的进阶总结篇

指针的进阶目录

1. 字符指针
2. 数组指针
3. 指针数组
4. 数组传参和指针传参
5. 函数指针
6. 函数指针数组
7. 指向函数指针数组的指针
8. 回调函数
9. 指针和数组面试题的解析

指针

1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。
3. 指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候访问的字节个数。
4. 指针的运算。

 

字符指针

在指针的类型中我们知道有一种指针类型为字符指针 char* ;
一般使用:

int main()
{
   char ch = 'w';
   char* pc = &ch;
   *pc = 'w';
   return 0;
}

通过指针打印字符串
 

int main()
{
    char arr[]="abcdef";
    char* pc=arr;
    printf("%s\n",arr);
    printf("%s\n",pc);
    return 0;
}

/*
结果:
abcdef
abcdef
*/

常量字符串

int main()
{
    char* p="abcdef";//"abcdef\0"是一个常量字符串,p中存的是a的地址
    //本质是把字符串 abcdef 首字符的地址放到了p中
    printf("%c\n",*p);//a
    printf("%s\n",p);//abcdef
    return 0;
}

错误写法

尝试修改常量字符串
err:Segmentation fault - 段错误

int main()
{
    char* p="abcdef";//"abcdef\0"是一个常量字符串,p中存的是a的地址
    *p = 'W';//常量字符串不可以改,正确的写法应该在char* p前面加const
    printf("%s\n",p);
    return 0;
}


例题:

请问打印结果是什么?
 

int main()
{
    char arr1[]="abcdef";
    char arr2[]="abcdef";
    char * p1="abcdef";
    char * p2="abcdef";
    if(arr1==arr2)
    {
        printf("hehe\n");
    }
    else
    {
        printf("haha\n");
    }
    if(p1==p2)
    {
        printf("hehe\n");
    }
    else
    {
        printf("haha\n");
    }
}

结果:

haha,首元素的地址

hehe,常量字符串只有一份

解析:
这里p1和p2指向的是一个同一个常量字符串。
C/C++会把常量字符串存储到单独的一个内存区域,当几个指针。
指向同一个字符串的时候,他们实际会指向同一块内存。

但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。

 

指针数组

指针数组是一个存放指针的数组。
int*      arr1[10];      //存放整形指针的数组-指针数组
char*   arr2[4];     //一级存放字符指针的数组-指针数组
char**arr3[5];   //二级存放字符指针的数组-指针数组


使用实例

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 i=0;
for(i=0;i<3;i++)
{
    int j=0;
    for(j=0;j<5;j++)
    {
        printf("%d".*(parr[i]+j));
    }
    printf("\n");
}

数组指针

数组指针的定义
数组指针是指针?还是数组?
答案是:指针。

指针的类型

整形指针: int * p; 能够指向整形数据的指针,可以存放整形的地址
字符指针: char * p;能够指向字符数据的指针,可以存放字符的地址
浮点型指针: float * p; 能够指向浮点型数据的指针,可以存放浮点型的地址
数组指针:能够指向数组的指针,可以存放数组的地址

arr不同地方不同意义

int arr[10]={0};
arr-首元素的地址
&arr[0]-首元素的地址
&arr-数组的地址

数组指针

int arr[10]={1,2,3,4,5,6,7,8,9,10};
int(*p)[10] = &arr;//数组的地址要存起来
//上面的p就是数组指针
//p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组
//[]的优先级要高于*号的,所以必须加上()来保证p先和*结合

例子:
char* arr[5];
char*(*pa)[5]=&arr;

pa-变量名字
*pa-指针
[5]-pa指向的数组有5个元素
char*-pa指向的数组的类型是char*


&数组名VS数组名

arr是数组名,数组名表示数组首元素的地址。
&arr和arr,虽然值是一样的,但是意义应该不一样的。
&arr 表示的是数组的地址,而不是数组首元素的地址
数组的地址+1,跳过整个数组的大小// &arr+1
 
 
 
数组指针的使用:二维数组以上才方便些
两种方式打印二维数组示例

//参数是数组的形式
void print1(int arr[3][5],int x,int y)
{
    int i=0;
    int j=0;
    for(i=0;i<x;i++)
    {
        for(j=0;j<y;j++)
        {
            printf("%d ",arr[i][j]);
        }
        printf("\n");
    }
}

//参数是指针的形式
void print2(int(*p)[5],int x,int y)
{
    int i = 0;
    for(i=0;i<x;i++)
    {
        int j=0;
        for(j=0;j<y;j++)
        {
            printf("%d ",*(*(p+i)+j));
            //写法2:printf("%d ",(*(p+i))[j]);
            //写法3:printf("%d ",*(p[i]+j);
            //写法4:printf("%d ",p[i][j];
        }
    }
}
/*
类比解析
int i=0
int* p =arr;
*(p+i)==*(arr+i)==arr[i]==p[i]

解析
*(*(p+i)+j)==(*(p+i))[j])==*(p[i]+j)==p[i][j]
p+i是数组指针,*可以拿到这个数组首元素地址
*/


int main()
{
    int arr[3][5]={{1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7}};
    print1(arr,3,5);//数组名就是首元素地址(sizeof和&arr除外)
    print2(arr,3,5);//把arr想象成一维数组,首元素为{1,2,3,4,5}的地址,传过去是一位数组的地址
}


 深入理解
int arr[5];//arr是一个5个元素的整形数组
int *parr1[10];//parr1是一个10个元素的数组,每个元素类型为int*,parr1是指针数组
int (*parr2)[10];//parr2是指针,指向10个元素的数组,数组类型是int,parr2是数组指针
int (*parr3[10])[5];//parr3是一个10个元素的数组,每个元素是一个数组指针,该数组指针指向的数组有5个元素,该数组的类型是int
详解:
int (*                )[5];//数组类型:数组指针
        parr3[10]//数组名[元素个数]

 

数组参数、指针参数

在写代码的时候难免要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?

一维数组传参

#include <stdio.h>
void test(int arr[])//ok
{}
void test(int arr[10])//ok
{}
void test(int *arr)//ok,传过去的也是首元素的地址
{}
void test2(int *arr[])//ok
{}
void test2(int *arr[20])//ok
{}
void test2(int **arr)//ok,二级指针
{}
int main()
{
int arr[10] = {0};
int *arr2[20] = {0};
test(arr);
test2(arr2);
}

二维数组传参

void test(int arr[3][5])//ok
{}
void test(int arr[][])//err列不能省略
{}
void test(int arr[3][])//err列不能省略
{}
void test(int arr[][5])//ok行可以省略
{}
void test(int *arr)//err二维数组的首元素地址是第一行的地址
{}
void test(int* arr[5])//err指针数组
{}
void test(int (*arr)[5])//ok指针,指向五个元素的数组,数组里面是int类型
{}
void test(int **arr)//err
{}
int main()
{
int arr[3][5] = {0};
test(arr);//二维数组传参
}


一级指针传参

#include <stdio.h>
void print(int *p, int sz)//变量地址,一级指针
{
int i = 0;
for(i=0; i<sz; i++)
{
printf("%d\n", *(p+i));
}
}
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9};
int *p = arr;
int sz = sizeof(arr)/sizeof(arr[0]);
//一级指针p,传给函数
print(p, sz);
return 0;
}


二级指针传参

#include <stdio.h>
void test(int** ptr)
{
printf("num = %d\n", **ptr);//一级指针变量地址,二级指针,接受指针数组数组名
}
int main()
{
int n = 10;
int*p = &n;
int **pp = &p;
test(pp);
test(&p);
return 0;
}

 

 

函数指针

数组指针-指向数组的指针
函数指针-指向函数的指针-存放函数地址的一个指针

函数
int Add(int x,int y)
{
    return x+y;
}

打印函数地址
printf("%p\n",&Add);
printf("%p\n",Add);
结果一样:&函数名 和 函数名都是函数的地址

存储函数的地址
int (*pa) (int int)=Add;

调用函数指针
(pa)(2,3)
(*pa)(2,3)
(**pa)(2,3)
(***pa)(2,3)
*pa(2,3)//err:pa和(2,3)结合==5,对5解引用报错
*是摆设,只是方便理解
结果都是5

 

阅读两段《C陷阱和缺陷》的有趣的代码:
//代码1
 

(*(void (*)())0)();

解释:
去掉变量名字以后就是类型
void (*)()-函数指针
(void (*)())-强制类型转化,转化成函数指针
(void (*)())0-0是某函数的地址
*(void (*)())0-*进行解引用-找到0为地址函数
(*(void (*)())0)()-调用地址为0的函数

//代码2
 

void (*signal(int , void(*)(int)))(int);

解释:
signal---------是一个函数声名
signal()-------signal函数
signal(int , void(*)(int))----------siganl函数参数有整形和函数指针
void (*                            )(int)---------里面函数返回值的类型也是一个函数指针

简化:
typedef void(*pfun_t)(int);//typedef 起名字:给void (*        )(int)起名字为pfun_t
pfun_t signal(int, pfun_t);//signal(int, pfun_t)函数的类型是pfun_t

 

函数指针数组

什么是函数指针数组?

1.函数指针数组是一个数组

2.数组里面存放的是函数指针

3.函数指针是指向函数的指针,也就是存放函数地址的变量


数组是一个存放相同类型数据的存储空间,指针数组int *arr[10];//数组的每个元素是int*

函数的地址存到一个数组中,就叫函数指针数组
 

int (*pa)(int,int)=Add;//函数指针
parr//指针名字
parr[4]//四个指针的数组
int (* )(int,int)//数组元素的类型为函数指针
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));
}

例题练习:

char* my_strcpy(char* dest,const char* src);

1.写一个函数指针pf,能够指向my_strcpy
 

//类型-变量名=赋值的错误写法
//char* (*)(char* ,const char*)              pf=&my_strcpy

//正确写法
char* (*pf)(char* ,const char*) =my_strcpy;


2.写一个函数指针数组pfArr,能够存放4个my_strcpy函数的地址
 

//类型-变量名=赋值的错误写法
//char*(*)(char* ,const char*)              pfArr[4]={my_strcpy,my_strcpy,my_strcpy,my_strcpy}

//正确写法
char*(*pfArr[4])(char* ,const char*) ={my_strcpy,my_strcpy,my_strcpy,my_strcpy};

函数指针数组的用途:转移表

例子:(计算器)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void menu()
{
    printf("*********************\n");
    printf("1:add            2:sub\n");
    printf("3:mul            4:div  \n");
    printf("0.exit                    \n");
    printf("*********************\n");
}

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 = 0;
    do
    {
        menu();
        printf("请选择:");
        scanf("%d", &input);
        switch (input)
        {
        case 1:
            printf("输入操作数:");
            scanf("%d %d", &x, &y);
            printf("%d\n", Add(x, y));
            break;
        case 2:
            printf("输入操作数:");
            scanf("%d %d", &x, &y);
            printf("%d\n", Sub(x, y));
            break;
        case 3:
            printf("输入操作数:");
            scanf("%d %d", &x, &y);
            printf("%d\n", Mul(x, y));
            break;
        case 4:
            printf("输入操作数:");
            scanf("%d %d", &x, &y);
            printf("%d\n", Div(x, y));
            break;
        case 0:
            printf("退出程序\n");
            break;
        default:
            printf("选择错误\n");
            break;
        }
    } while (input);
    return 0;
}


代码冗余:
printf( "输入操作数:" );
scanf( "%d %d", &x, &y);
回调函数改进:
 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void menu()
{
	printf("*********************\n");
	printf("1:add            2:sub\n");
	printf("3:mul            4:div  \n");
	printf("0.exit                    \n");
	printf("*********************\n");
}

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 Calc(int (*pf)(int x, int y))
{
	int x = 0;
	int y = 0;
	printf("输入操作数:");
	scanf("%d %d", &x, &y);
	printf("%d\n", pf(x, y));
}
int main()
{
	int x, y;
	int input = 0;
	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			Calc(Add);
			break;
		case 2:
			Calc(Sub);
			break;
		case 3:
			Calc(Mul);
			break;
		case 4:
			Calc(Div);
			break;
		case 0:
			printf("退出程序\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
	return 0;
}

 

 

 

 

 


使用函数指针数组的实现:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void menu()
{
    printf("*********************\n");
    printf("1:add            2:sub\n");
    printf("3:mul            4:div\n");
    printf("0.exit                  \n");
    printf("*********************\n");
}

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;
    int(*pfArr[5])(int x, int y) = { 0, Add, Sub, Mul, Div }; //函数指针数组:转移表
    while (input)
    {
        menu();
        printf("请选择:");
        scanf("%d", &input);
        if ((input <= 4 && input >= 1))
        {
            printf("输入操作数:");
            scanf("%d %d", &x, &y);
            ret = pfArr[input](x, y);
            printf("%d\n", ret);
        }
        else if (input == 0)
        {
            printf("退出");
        }
        else
        {
            printf("选择错误");
        }
        return 0;
    }
}

 

回调函数

详细定义:

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

方便理解的例子:

#include<stdio.h>
void print()//被指针指向的函数
{
    printf("结果是:\n");
}
int Add(int x, int y,void(*p)())
{
    (*p)();//这个指针被用来调用其所指向的函数时,就是回调函数
    return x + y;
}

int main()
{
    int a = 2, b = 3;
    void (*p)() = &print;//函数指针
    int sum = Add(2,3,p);//把函数的指针(地址)作为参数传递给另一个函数
    printf("%d", sum);
    return 0;
}

 

冒泡排序

冒泡排序的原理

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。

  2. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。

  3. 针对所有的元素重复以上的步骤,除了最后一个。

  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

 

回忆之前写过的冒泡排序
 

#include<stdio.h>
void bubble_sort(int arr[], int sz)
{
    int i = 0;
    //趟数
    for (i = 0; i < sz - 1; i++)
    {
        //一趟冒泡排序
        int j = 0;
        for (j = 0; j < sz - 1 - i; j++)
        {
            if (arr[j] > arr[j + 1])
            {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
            }
        }
    }
}

int main()
{
    int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
    int sz = sizeof(arr) / sizeof(arr[0]);

    bubble_sort(arr, sz);
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%d ",arr[i]);
    }
    return 0;
}

 

qsort-快速排序任意类型的数据

1.头文件stdlib.h
2.两个元素的比较方法不一样,需要使用者自己输入一个函数来比较两个数据

3.使用方法:

#include<stdlib.h>

int cmp_XXX (const void *e1,const void *e2)
{

//整形return *(int*)e1    -   *(int*)e1;
//浮点型return (int)(*(float*)e1    -   *(float*)e1);
//结构体return ((struct Stu*)e1)->指向结构体内部的元素-((struct Stu*)e2)->指向结构体内部的元素;
}

int sz=sizeo数组名(数组名)/sizeo数组名(数组名[0]);

qsort(      数组首元素的地址也就是数组名      ,        数组的元素个数sz           ,          单个元素的字节数sizeo数组名(数组名[0])          ,         使用者自己编写的函数cmp_XXX      );

 

函数原型
void qsort(void* base,//快速排序的目标,待排序数组的首元素的地址
                size_t num,//待排序数组的元素的个数
                size_t width,//待排序数组的单个元素的字节数
                int(*compare)(const void *e1,const void *e2)//比较两个目标的地址,相等返回0,小于返回负数,大于返回正数,
                //这个函数需要使用者自己实现
                );

void* 类型的指针

void* 类型的指针可以接受任意类型的地址
void* 类型的指针 不能进行解引用操作
void* 类型的指针 不能进行加减整数的操作
存储例如:void* p=&a;(a是任意类型的数据)

 


下面通过代码来体验一下qsort函数和自己编写的冒泡排序升级(可以接受任意类型的数据)

附有较为详细的注释

    test1();//qsort排序整形
    test2();//qsort排序浮点型
    test3();//qsort排序结构体
    test4();//冒泡排序整形
    test5();//冒泡排序结构体

 

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//比较整数数据
int cmp_int(const void* e1, const void* e2)
{
    //比较两个整形值的
    return *(int*)e1 - *(int*)e1;
}

void test1()
{
    int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    qsort(arr, sz, sizeof(arr[0]), cmp_int);

    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

//比较浮点型数据
int cmp_float(const void* e1, const void* e2)
{
    return (int)(*(float*)e1 - *(float*)e1);
}

void test2()
{
    float f[] = { 9.0,8.0,7.0,6.0,5.0,4.0,3.0,2.0,1.0 };
    int sz = sizeof(f) / sizeof(f[0]);

    qsort(f, sz, sizeof(f[0]), cmp_float);

    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%f\n", f[i]);
    }
}

//比较结构体型数据
struct Stu
{
    char name[20];
    int age;
};

int cmp_stu_by_age(const void* e1, const void* e2)
{
    return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}

int cmp_stu_by_name(const void* e1, const void* e2)
{
    //比较名字就是比较字符串,应该用strcmp函数,比较字符串的,头文件为string.h
    return strcmp(((struct Stu*)e1)->name , ((struct Stu*)e2)->name);
}

void test3()
{
    struct Stu s[3] = { {"zhangsan",20},{"lisi",30},{"wangwu",10} };
    int sz = sizeof(s) / sizeof(s[0]);
    qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
    qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}


// void qsort(void* base,//快速排序的目标,待排序数组的首元素的地址
                // size_t num,//待排序数组的元素的个数
                // size_t width,//待排序数组的单个元素的字节数
                // int(*compare)(const void *e1,const void *e2)//比较两个目标的地址,相等返回0,小于返回负数,大于返回正数,
                // //这个函数需要使用者自己实现
                // );


//改造冒泡排序
//实现bubble_sort的程序员,他不知道未来排序的数据类型
//那程序员也不知道,待比较两个元素的类型
void(Swap)(char* buf1, char* buf2, int width)
{
    int i = 0;
    for (i = 0; i < width; i++)
    {
        char tmp = *buf1;
        *buf1 = *buf2;
        *buf2 = tmp;
        buf1++;
        buf2++;
    }
}

void bubble_sort(void* base, int sz, int width, int (*cmp)(const void* e1, const void* e2))
{
    int i = 0;
    //趟数
    for (i = 0; i < sz - 1; i++)
    {
        //一趟冒泡排序
        int j = 0;
        for (j = 0; j < sz - 1 - i; j++)
        {
            //两个元素的比较
            //*void无法直接加减
            //(char*)将其强制转成字符指针可以加减,加减1跳过一个字节
            if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
            {
                //交换
                Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
            }
        }
    }
}


void test4()
{
    int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    //使用bubble_sort函数的程序员一定知道自己排序的是什么数据
    //就应该知道如何比较待比较的数据的方式
    bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
}

void test5()
{
    struct Stu s[3] = { {"zhangsan",20},{"lisi",30},{"wangwu",10} };
    int sz = sizeof(s) / sizeof(s[0]);
    bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_age);
    bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}

int main()
{
    test1();
    test2();
    test3();
    test4();
    test5();
    return 0;
}

 

指针和数组笔试题解析

本章总结

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

一维数组

整形数组

int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a+0));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(a[1]));
printf("%d\n",sizeof(&a));
printf("%d\n",sizeof(*&a));
printf("%d\n",sizeof(&a+1));
printf("%d\n",sizeof(&a[0]));
printf("%d\n",sizeof(&a[0]+1));

解析:
 

//地址字节数:
//win32:4
//win64:8
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));
//sizeof(数组名)-计算的是整个数组的大小-单位字节
//16
printf("%d\n",sizeof(a+0));
//数组名是首元素的地址
//1.sizeof(数组名)-计算的是整个数组的大小
//2.&数组名-数组名表示的是整个数组
//这里的a+0还是首元素的地址
//4/8
printf("%d\n",sizeof(*a));
//*首元素的地址=首元素的大小
//4
printf("%d\n",sizeof(a+1));
//第二个元素的地址
//4/8
printf("%d\n",sizeof(a[1]));
//第二个元素的大小=整形大小=int=4字节
//4
printf("%d\n",sizeof(&a));
//&a取出数组的地址
//地址的大小为4
//4/8
printf("%d\n",sizeof(*&a));
//&a取出数组的地址
//*&a访问数组==a
//16
printf("%d\n",sizeof(&a+1));
//&a是一个数组的地址
//&a+1虽然地址跳过一个数组的地址,但还是一个地址
//4/8
printf("%d\n",sizeof(&a[0]));
//第一个元素的地址
//4/8
printf("%d\n",sizeof(&a[0]+1));
//第二个元素的地址
//4/8

字符数组
 

char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));


解析:
 

char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));
//sizeof计算的是数组的大小,6*1=6
//6
printf("%d\n", sizeof(arr+0));
//arr首元素的地址,arr+0也还是首元素的地址
//4/8
printf("%d\n", sizeof(*arr));
//arr是首元素的地址,*arr是首元素
//1
printf("%d\n", sizeof(arr[1]));
//arr[1]是第二个元素
//1
printf("%d\n", sizeof(&arr));
//数组的地址还是地址
//4/8
printf("%d\n", sizeof(&arr+1));
//地址1+1=地址2
//地址大小为4/8
//4/8
printf("%d\n", sizeof(&arr[0]+1));
//第二个元素的地址
//4/8


字符数组
 

char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));

解析:
 

char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", strlen(arr));
//strlen找到'\0'才停止
//随机值
printf("%d\n", strlen(arr+0));
//随机值
printf("%d\n", strlen(*arr));
//strlen要地址
//ASCII码中'a'=97
//读取0x00000061的地址然后向后非法读取
//代码错误
printf("%d\n", strlen(arr[1]));
//strlen要地址
//ASCII码中'b'=98
//读取98的地址然后向后非法读取
//代码错误
printf("%d\n", strlen(&arr));
//&arr是首元素的地址,也是'a'的地址
//随机值
printf("%d\n", strlen(&arr+1));
//&arr+1跳过数组的地址向后走
//随机值
printf("%d\n", strlen(&arr[0]+1));
//从b开始向后找'\0'
//随机值

字符数组

char arr[] = "abcdef";
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));

解析:
 

char arr[] = "abcdef";
//'a' 'b' 'c' 'd' 'e' 'f' '\0'
printf("%d\n", sizeof(arr));
//sizeof计算的是整个空间的大小,strlen整个空间的大小-1('\0')
//sizeof计算的是数组的大小,7*1=7
//7
printf("%d\n", sizeof(arr+0));
//地址
//4/8
printf("%d\n", sizeof(*arr));
//'a'的大小
//1
printf("%d\n", sizeof(arr[1]));
//'b'的大小
//1
printf("%d\n", sizeof(&arr));
//地址
//4/8
printf("%d\n", sizeof(&arr+1));
//地址
//4/8
printf("%d\n", sizeof(&arr[0]+1));
//地址
//4/8

 

字符数组

char arr[] = “abcdef”;
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));

解析:

char arr[] = “abcdef”;
printf("%d\n", strlen(arr));
//'a' 'b' 'c' 'd' 'e' 'f' '\0'
//strlen不算'\0'
//6
printf("%d\n", strlen(arr+0));
//首元素地址
//从首元素地址向后数
//6
printf("%d\n", strlen(*arr));
//strlen接受的是地址const char* str
//*arr是'a'ASCII码为97
//地址为97==0x00000061发生访问错误
//err
printf("%d\n", strlen(arr[1]));
//err
printf("%d\n", strlen(&arr));
//warning:const char和char(*)[7]的类型不同
//&arr是数组的地址-数组指针-char(*)[7]
//6
printf("%d\n", strlen(&arr+1));
//warning:const char和char(*)[7]的类型不同
//随机值
printf("%d\n", strlen(&arr[0]+1));
//'b'向后读取
//5

 

字符数组

char *p = “abcdef”;
printf("%d\n", sizeof(p));
printf("%d\n", sizeof(p+1));
printf("%d\n", sizeof(*p));
printf("%d\n", sizeof(p[0]));
printf("%d\n", sizeof(&p));
printf("%d\n", sizeof(&p+1));
printf("%d\n", sizeof(&p[0]+1));

解析:
 

char *p = “abcdef”;
//p存放"abcdef"的地址
printf("%d\n", sizeof(p));
//地址
//计算指针变量的大小
//4/8
printf("%d\n", sizeof(p+1));
//p存的是'a'的地址
//p+1是'b'的地址
//4/8
printf("%d\n", sizeof(*p));
//*p=首元素
//'a'
//1
printf("%d\n", sizeof(p[0]));
//int arr[10];arr[0] == *(arr+0)
//p[0] == *(p+0) == 'a'
//1
printf("%d\n", sizeof(&p));
//地址
//4/8
printf("%d\n", sizeof(&p+1));
//地址
//4/8
printf("%d\n", sizeof(&p[0]+1));
//'b'的地址
//4/8

 

字符数组

char *p = “abcdef”;
printf("%d\n", strlen(p));
printf("%d\n", strlen(p+1));
printf("%d\n", strlen(*p));
printf("%d\n", strlen(p[0]));
printf("%d\n", strlen(&p));
printf("%d\n", strlen(&p+1));
printf("%d\n", strlen(&p[0]+1));

解析:
 

char *p = “abcdef”;
printf("%d\n", strlen(p));
//p的地址传过去='a'的地址
//6
printf("%d\n", strlen(p+1));
//5
printf("%d\n", strlen(*p));
//err
printf("%d\n", strlen(p[0]));
//err
printf("%d\n", strlen(&p));
//a b c d e f \0
//取p的地址向后读
//地址不可知
//随机值
printf("%d\n", strlen(&p+1));
//随机值
printf("%d\n", strlen(&p[0]+1));
//'b'的地址
//5

 

二维数组


int a[3][4] = {0};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a[0][0]));
printf("%d\n",sizeof(a[0]));
printf("%d\n",sizeof(a[0]+1));
printf("%d\n",sizeof((a[0]+1)));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof((a+1)));
printf("%d\n",sizeof(&a[0]+1));
printf("%d\n",sizeof(*(&a[0]+1)));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a[3]));

解析:
 

//二维数组
int a[3][4] = {0};
printf("%d\n",sizeof(a));
//数组总大小
//48
printf("%d\n",sizeof(a[0][0]));
//一个整形元素的大小
//4
printf("%d\n",sizeof(a[0]));
//a[0]相当于第一行作为一维数组的数组名
//sizeof(数组名)=第一行的数组的大小
//16
printf("%d\n",sizeof(a[0]+1));
//a[0]相当于第一行作为一维数组的数组名 == 第一行第一个元素的地址
//a[0]+1第一行第二个元素的地址
//4/8
printf("%d\n",sizeof(*(a[0]+1)));
//4
printf("%d\n",sizeof(a+1));
//二维数组看成一维数组
//二维数组的首元素是他第一行
//a == 第一行的地址
//a+1 ==第二行的地址
//4/8
printf("%d\n",sizeof(*(a+1)));
//a+1 ==第二行的地址
//计算第二行的大小
//16
printf("%d\n",sizeof(&a[0]+1));
//第二行的地址
//4/8
printf("%d\n",sizeof(*(&a[0]+1)));
//第二个行的大小
//16
printf("%d\n",sizeof(*a));
//a是首元素的地址 == 第一行的地址
//16
printf("%d\n",sizeof(a[3]));
//sizeof不会访问()内的内容,不参加真实运算,只会通过括号内的类型判断大小
//16

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值