1指针
1.1指针的定义
指针就是变量,用来存放地址的变量.
指针在32位平台是4个字节
int main()
{
int a = 10;//定义了一个整型变量a
/*
①要存放a 的地址,首先要取a 的地址:&a
②a的地址拿出来之后就要保存,接收int 类型的地址使用int 类型的指针
*/
int *p = &a;//①p是一个指针变量,p用来保存[int *]类型的数据②*证明p是一个指针
printf("%d\n", *p);
//将a的值改为100
*p = 100;//a=100②*证明间接访问,访问p保存的地址里面的内容(即10)
//*p:解应用,间接访问
printf("%d\n", a);
}
1.2void类型指针
1)可以接受任何类型的指针,但是不能解引用
2)不能进行指针的加法运算
int main()
{
char ch = 9;
char *c = &ch;
short sh = 10;
short *sv = &sh;
//short 类型的指针接受short类型的地址
void *pv = &ch;
void *pc = &sh;
//void 类型的指针可以接受任意类型的指针,但不能进行解应用
printf("%d\n", sv);
printf("%d\n", sv + 2);
printf("%d\n", sv + 3);//sv是short类型,2个字节加3就是加6
printf("%d\n",pv);
//printf("%d\n",pv+2);pv是无符号类型指针,不能进行加法运算因为无法判断是几个字节
return 0;
}
//13630176
//13630180
//13630182
1.3指针的运算
①指针±正数
!!!指针加法运算的时候一定要看清楚指针类型
指针是什么类型占几个字节就加几的倍数
②指针-指针
(只有在同一个数组里的指针减指针才有意义,之差是两个指针之间的个数)
两个指针作用于同一块内存时才有意义
int main()
{
int arr[] = { 1, 2, 3, 4, 5, 6 };
int arr2[] = { 1, 2, 3, 4, 5, 6 };
int *p = &arr[2];
int *p2 = &arr[5];
int *p3 = &arr2[2];
printf("%d\n", *p2 - *p);
//printf("%d\n", *p2 - *p3);没有意义
return 0;
}
③指针的关系运算
int main()
{
int arr[5];//定义一个数组
int *p;
for (p = &arr[0]; p < &arr[5];)
{
*p++ = 0;
//*p=0;将0号下标赋值为0
//p++;p是int类型向后移动四个字节,也就是挪到1号下标
//*--p=0等价于p--;*p=0
}
return 0;
}
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针相比较,但是不允许与指向第一个元素之前的那个内存位置的指针相比较
在这里插入代码片
1.4野指针
!!!面试问题:如何预防野指针
野指针
:在定义的过程中没有被赋值
1)没有定义可空指针均不能被访问
int main()
{
int *p;//p没有被赋值,称为野指针
printf("%d\n", *p);//野指针不能被访问
return 0;
}
int main()
{
int *p = NULL;//相当于定义Int类型被赋值为0
printf("%d\n", *p);//0地址不能被访问
return 0;
}
1.5指针的解引用
!!!什么类型的指针就在解引用的时候访问几个字节
int是4个字节,但int占1个字节所以在访问的时候只能访问1个字节即:什么
int main()
{
int x = 0x12345678;//存放16进制的数
char *ch = (char*)&x;//①*ch是char类型的指针,赋值要进行强转换int是16位②ch是1个字节int是4个字节,十六进制0x12345678占了四个字节所以被分为12|34|56|78
//!!!解应用的时候关注访问的是谁以及能访问几个字节
printf("%x\n", *ch);//通过*ch解应用的时候先打印第一个,又因为电脑是小段低地址存放低数据,所以打印78
return 0;
}
//78,输出78就是小端
1.6将字符串赋值给指针
将相同字符串赋值给不同的指针,不同的指针指向同一处内存
int main()
{
char *str1 = "hello";
char *str2 = "hello";
//用双引号定义一个字符串保存在rodata段,当有新的还是那个不会有两个一模一样的,两个指针指向的是同一处内存
if (str1 == str2)
{
printf("str1==str2\n");
}
return 0;
}
①用双引号引起的字符串赋值给数组,数组存放在栈上可以修改里面的值
②用双引号引起的字符串赋值给指针存放在rodata段只能读不能修改
int main()
{
/*char str[10] = "abcdef";
str[1] = 'g';
printf("%s\n", str);*/
char *str = "abcdef";
//*(str + 1) = 'g';
printf("%s\n", str);
return 0;
}
1.7二级指针
二级指针放着一级指针的地址
int main()
{
int a = 10;
int b = 20;
int *p = &a;
int **p2 = &p;
*p2 = &b;
**p2 = 100;
printf("%d,%d\n", a, b);
return 0;
}
1.8地址的调用
void swap(int *pa, int *pb)//从主函数中把地址传过来
{
int tmp = 0;
tmp = *pa;
*pa = *pb;
*pb = tmp;
}
int main()
{
int a = 10;
int b = 20;
swap(&a, &b);//取地址
printf("%d,%d\n", a, b);
}
1.9打印地址
int main()
{
int num = 10;
#//取出Num的地址
printf("%p\n", &num);//打印地址,%p:以地址的形式打印
return 0;
}
1.10数组名和数组首元素的关系
一维数组中,数组名代表首元素地址
int main()
{
int arr[] = { 1, 2, 3, 4, 5 };
printf("%d\n", arr);
printf("%d\n",&arr[0]);
return 0;
}
//7928180
//7928180
2数组与指针
2.1指针数组
指针数组存放指针的数组(把地址取出来放在数组里),指针数组就是数组
①
```c
int main()
{
int a = 1;
int b = 2;
int c =3;
int* arr[3] = { &a, &b, &c };
int arr1[3] = { 1, 2, 3 };
int arr2[3] = { 1, 2, 3 };
int arr3[3] = { 1, 2, 3 };
int arr4[3] = { 1, 2, 3 };
int*ar[4] = { arr1, arr2, arr3 ,arr4};
for (int i = 0; i < 4; i++)//4个数组
{
int j = 0;
for (j = 0; j < 3; j++)//每个数组里的内容
{
printf("%d", *(*(ar + i) + j));
}
printf("\n");
}
return 0;
}
②
int* arr1[10];//整形指针的数组
char *arr2[4];//一级字符指针的数组
char **arr3[5];//二级字符指针的数组
char *str[3]={"he","zhang","sam"};//指针数组
2.2数组指针
指针数组指向数组的指针(把数组的地址给指针),指针数组就是指针
①定义长度为10的数组,初始化为0:int arr[10]={0};
②取地址:&arr
③保存数组的地址要有一个指向数组的指针
*p =&arr;
(*p)=&arr;加个括号证明是指针
④数组长度是10加上[10]:(*p)[10]=&arr
⑤数组是int类型,前面再加上数组类型
int (*p)[10]=&arr
根据以上步骤p是数组指针
②
int arr[5];//整型数组
int *arr1[10];//指针数组
int (*arr2)[10];//数组指针
int (*parr3[10])[5];
int (*p)[10]:p里面放的是int 类型的数组,数组长度为10
int(*parr3[10])[5]:parr3里面放的是int(*)[5]类型的数组,数组长度是5
2.2.1指针,数组指针,指针数组的区别
int *p1[10];指针数组
int (*p2)[10];数组指针
(数组指针在指针处多了一个括号)
int a =10;
int *p=&a;//指针
int arr[10]={10};
int (*p2)[10]=&arr;//p2是数组指针(有(),所以是指针)
int* p3[10];//p3是指针数组(有[],优先级最高所以是数组)
(自己理解:指针数组:把不同的地址[可以是数据也可以是数组]放进数组里面;数组指针:把一个数组的地址放进指针里面)
2.2.3数组指针中arr和&arr的区别
int(p2)[10]
①arr和&arr的区别
a)不同之处:arr:代表数组首元素地址,&arr:代表整个数组的地址;
相同之处:arr和&arr的值是一样的
b)arr:int类型数据;&arr:int(*p)[10]类型
②arr+1和&arr+1的区别
arr的地址是14546684首元素地址,+1由于是int类型的则在原地址基础上加1×4(int类型占4个字节)
&arr代表整个数组的地址,&arr+1先在首元素的地址上加4*(10-1),再加1×4
int main() {
int arr[10] = { 10 };
int *p = arr;
int(*p2)[10] = &arr;//数组指针
printf("%d,%d\n", arr, &arr);// 14546684, 14546684
//arr:数组首元素地址,&arr:整个数组的地址;arr和&arr的值是一样的
//arr:int*类型数据;&arr:int(*p)[10]
printf("%d,%d\n", arr + 1, &arr + 1); //14546688, 14546724
//arr的地址是14546684首元素地址,+1由于是int类型的则在原地址基础上加4
//&arr代表整个数组的地址,&arr+1先在首元素的地址上加4*(10-1),再加4
return 0;
}
数组名代表整个数组的两种情况
1)sizeof(arr):整个数组的字节数
2)&arr+1:整个数组地址+1×数据类型字节数
③如何取到数组指针当中数组具体的地址
int main()
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int(*p)[10] = &arr;
printf("%d\n", *((*p) + 2));
return 0;
/*
①(*p):数组名代表数组首元素
②(*p)+2:+2首元素向右移两位
③*((*p)+2)”:解引用访问保存的值
*/
}
2.3二维数组指针
int main()
{
int arr[][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
printf("%d\n", arr);//3341120,代表第一行的地址
printf("%d\n", arr + 1);//3341136,代表第二行的地址
printf("%d\n", arr[0]);//3341120,第一行第一个元素
printf("%d\n", arr[0] + 1);//3341124,第一行第二个元素,+1右移了
printf("%d\n", arr[1]);//3341136,等价于*(arr+1)代表第二行首元素地址
printf("%d\n", arr[1] + 1);//3341140,代表第二行第二个元素地址
printf("%d\n", *(arr[1] + 1));//可以访问里面的内容,即第二行第二个元素的内容
printf("%d\n", *(*(arr + 1) + 1));//()
return 0;
}
(??自带解引用不能理解)
void Show1(int arr[][4], int low, int row)
{
int i = 0;
for (i = 0; i < low; i++)
{
int j = 0;
for (j = 0; j < row; j++)
{
printf("%-3d", arr[i][j]);
}
printf("\n");
}
}
void Show(int (*arr)[4], int low, int row)//show 和show1效果相同
//二维数组名也代表数组首元素地址,此时首元素地址是一个一维数组
{
int i = 0;
for (i = 0; i < low; i++)
{
int j = 0;
for (j = 0; j < row; j++)
{
//printf("%-3d", arr[i][j]);
printf("%-3d",*(*(arr+i)+j));
//*(*(arr+i)+j)等价于*(arr[i]+j)
}
printf("\n");
}
}
int main()
{
int arr[][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
Show(arr, 3, 4);
return 0;
}
2.4数组传参
2.4.1一维数组传参
void test(int arr[])
{}
void test(int arr[10])
{}
void test(int *arr)//一级指针,数组名代表数组首元素
{}
void test2(int *arr[20])//指针数组数组名还是代表数组首元素
{}
void test2(int **arr)
{}
int main()
{
int arr[10]={0};
int *arr2[20]={0};//指针数组,数组名还是代表数组首元素地址
test(arr);
test2(arr2);
return 0;
}
2.4.2二维数组传参
①二维数组传参,函数形参只能省略行,不能省略列,对于二维数组,可以不知道有多少行,必须知道一行有多少列
void test(int arr[3][5])
{}
void test(int arr[][])//不行
{}
void test(int arr[][5])
{}
void test(int *arr)//不行
{}
void test(int* arr[5])//不行
{}
void test(int (*arr)[5])
{}
void test(int **arr)//不行
{}
int main()
{
int arr[2][3]={1,2,3,4,5,6};
test(arr );
return 0;
}
②二维数组名代表数组首元素地址,二维数组首元素地址是一个一维数组,二维数组传参传的是数组名(指针指向地址,所以二维数组传参传的是地址)
void test(int *arr)//不行
{}
void test(int* arr[5])//不行,int* arr[5]是指针数组
{}
void test(int (*arr)[5])//可以,int (*arr)[5]是数组指针
{}
void test(int **arr)//不行,二级指针不行
{}
int main()
{
int arr[2][3]={1,2,3,4,5,6};
test(arr );
return 0;
}
2.4.3二级指针传参
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;
}
//函数参数为二级指针
void test(char *p)
{}
int main()
{
char c ='b';
char *pc=&c;
char **ppc=&pc;
char* arr[10];//指针数组
test(&pc);
test(ppc);
test(arr);
return 0;
}
//函数指针
3.1函数指针
①函数名代表函数指针
void test()
{
printf("hello\n");
}
int main()
{
printf("%d\n", test);//函数名代表函数地址
printf("%d\n", &test);//&test:对函数名取地址
(*(&test)) ();
/*
&test:test 函数的地址
*(test):对地址解引用
(*(test)) ():调用函数再加上小括号
*/
(*test)();
(*****test)();//解引用的个数对函数调用没有影响
test();
return 0;
}
②函数指针:指向函数的指针,保存函数的地址,也就是函数名
void test()
{
printf("hello\n");
}
int main()
{
void(*p)() = test;//p是一个指针,指向函数,没有参数,返回值是void
/*
①=test:函数的地址
②(*p) =test :p是一个指针指向函数
③(*p) () =test :函数没有参数,之间写个括号
④void(*p)()=test :函数返回值是void 类型
*/
int(*p2)(int,int) = test1;
p2(10,30);
return 0;
}
((void()())0) ()
void(signal(int,void()(int))) (int)
函数简化:
typedef void(*pFun)(int);
int main()
{
void(*signal(int,void(*)(int)))(int)
pFun signal(int,pFun)
return 0;
}
3.2函数指针数组
void (*arr[])()
函数指针的用途:转移表
函数指针数组
数组是一个存放相同类型数据的存储空间
int arr[10];//数组的每个元素是int
把函数的地址存到一个数组中,数组就叫做函数指针数组
int (*parr1[10])()
int parr210
int ()()parr3[10]
parr中parr1先和[]结合,说明parr1是数组
函数指针数组的用途:转移表
//用函数实现
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 dev(int a, int b)
{
return a / b;
}
int main()
{
int input = 1;
int x = 0;
int y = 0;
int ret = 0;
while (input)
{
printf("*************************\n");
printf("***1.add******2.sub******\n");
printf("***3.mul******4.dev******\n");
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("%d", ret);
break;
case 3:
printf("请输入两个操作数:");
scanf("%d%d", &x, &y);
ret = mul(x, y);
printf("%d", ret);
break;
case 4:
printf("请输入两个操作数:");
scanf("%d%d", &x, &y);
ret = dev(x, y);
printf("%d", ret);
break;
default:
break;
}
}
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 dev(int a, int b)
{
return a / b;
}
int main()
{
int input = 1;
int x = 0;
int y = 0;
int ret = 0;
int(*p[5])(int, int) = { 0, add, sub, mul, div };
//p是一个函数指针数组;数组长度为5,里面存放是函数指针int(*)(int,int)
//菜单在1,2,3,4的时候分别是add,sub,mul,div;所以下标1,2,3,4分别是add,sub,mul,div,所以0号下标是0代表空指针
//add是函数名,函数名代表函数的地址,(函数地址的类型就是int(*)(int,int))
printf("*************************\n");
printf("***1.add******2.sub******\n");
printf("***3.mul******4.dev******\n");
printf("请输入你的操作:");
scanf("%d",&input);
if (input >= 1 && input <= 4)//input代表数组的下标
{
printf("请输入两个操作数:");
scanf("%d%d", &x, &y);
ret = (*p[input])(x, y);
/*
①如果输入的是1,应该调用1对应的元素
1)p[input]:访问数组p里的input元素
2)元素对应的是(比如1对应的是add函数的地址)地址,访问地址对应的内容,解引用 *p[input]
3)调用函数,加上()和对应对参数(*p[input])(x,y)
4)函数有返回值用ret接收
*/
printf("ret=%d\n", ret);
}
return 0;
}
3.3函数指针数组指针
函数指针数组指针:指向函数指针数组的指针
int main()
{
void (*p1)();//函数指针
void (*p2[5])();//函数指针数组
void(*(*p3)[5])();//函数指针数组指针
//p3是一个指针,指向数组,长度为5
//数组里面放的是void(*)();函数指针
return 0;
}
4回调函数
回调函数就是通过函数指针调用的函数.如果你把函数的指针(地址)作为函数参数传递给另外一个函数,当这指针被用来调用其所指向的函数时,我们就说这是回调函数.回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件下发生时由另外的一方调用,用于对该事件或条件进行响应
①qsort函数
int _cmlnt(const void *p1, const void *p2)//p1只能进行存放地址,不能解引用或者加减
{
return *(int *)p1 - *(int *)p2;
}
void Show(int *arr, int len)
{
int i = 0;
for (i = 0; i < len; i++)
{
printf("%d", arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = { 12, 6, 3, 1, 9, 7, 2, 8, 19, 5 };
int len = sizeof(arr) / sizeof(arr[0]);
Show(arr, len);
qsort(arr,len,sizeof(int),_cmlnt);
Show(arr, len);
return 0;
}
qsort函数可以实现对数据进行排序
a)qsort函数:
void qsort(void *base,size_t num,size_t width,int (_cdecl *compare)(const void *elem1,const void *elem2));
b)qsort函数参数的含义
1)void *base:说明qsort可以对任意类型数据排序;base作用:传入将要排序的数据的地址
2)size_t num:要给多少个数字排序
3)size_t width:将要排序的每一个数字的字节大小
4)int (_cdecl *compare[指针])(const void *elem1,const void *elem2[两个参数])):指针函数,返回值为int两个参数的参数;定义函数进行排序
②冒泡排序
#if 0
//自己写的
void BubbleSort(int *arr, int len)
{
int i = 0;
int x;
int j = len-1;
for (j = len-1; j >0; j--)
{
for (i = 0; i <j; i++)
{
if (arr[i]>arr[i + 1])
{
x = arr[i + 1];
arr[i + 1] = arr[i];
arr[i] = x;
}
}
}
}
#endif
//比较p1和p2的大小
void BubbleSort(int *arr, int len)
{
int i = 0;
int flg = 0;
for (i = 0; i < len - 1; i++)//控制趟数
{
int j = 0;
flg = 0;//有可能第一趟交换了第二趟没有交换
for (j = 0; j < len - 1 - i; j++)
{
if (arr[j]>arr[j + 1])
{
int x = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = x;
flg += 1;
}
}
if (!flg)//(flg != 1)一趟里交换可能不止一次,所以交换完不一定等于1
{
break;
}
}
}
void Show(int *arr, int len)
{
int i = 0;
for (i = 0; i < len; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = { 12, 6, 3, 1, 9, 7, 2, 8, 19, 5 };
int len = sizeof(arr) / sizeof(arr[0]);
Show(arr, len);
BubbleSort(arr, len);
Show(arr, len);
return 0;
}
③用冒泡排序实现qsort函数(自动排序)
//输出数据
void Show(int *arr, int len)
{
int i = 0;
for (i = 0; i < len; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
//交换数据
void _swap(void *p1, void *p2, int size)
{
int i = 0;
for (i = 0; i < size; i++)
{
char tmp = *(((char *)p1) + i);
*(((char *)p1) + i) = *(((char *)p2) + i);
*(((char *)p2) + i) = tmp;
}
}
//为什么用void,因为MyQsort可以排序任何类型数据,所以用字节比较大小,交换
//比较大小
int _cmplnt(const void *p1, const void *p2)
{
return *(int *)p1 - *(int *)p2;
}
void MyQsort(void *base, int len, int size, int(*cmp)(const void *p1, const void *p2))
//base:要排序的数据;len:数据总数;size:数据字节大小int(*cmp)(const void *p1, const void *p2):函数指针也就是函数名
{
int i = 0;//交换的趟数
for (i = 0; i < len - 1; i++)
{
int j = 0;
for (j = 0; j < len - 1 - i; j++){
if (cmp((char *)base + j*size, (char *)base + (j + 1)*size)>0){
_swap((char *)base + j*size,(char *)base + (j + 1)*size, size);
}
}
}
}
//调用MyQsort的时候将 _cmplnt函数以参数的形式传递
int main()
{
int arr[] = { 12, 6, 3, 1, 9, 7, 2, 8, 19, 5 };
int len = sizeof(arr) / sizeof(arr[0]);
Show(arr, len);
MyQsort(arr, len, sizeof(int), _cmplnt);
Show(arr, len);
return 0;
}
通过函数指针int(*cmp)(const void *p1, const void *p2)调用函数_cmplnt,将函数_cmplnt的地址在MyQsort函数里作为参数
5字节大小
①一维数组字节大小
int main()
{
int a[] = { 1, 2, 3, 4 };
printf("%d\n", sizeof(a));//16
//sizeof后面跟数组名,数组名代表整个数组字节大小
printf("%d\n", sizeof(a+0));//4
//发生了运算a+0代表第一个元素的地址(一维数组名代表数组首元素地址)
printf("%d\n", sizeof(*a)); //4
//数组名代表数组首元素的地址,解引用得到的就是数组第一个元素,在这里就是第一个元素的字节大小
printf("%d\n", sizeof(a+1));//4
//a+1代表第二个元素的地址
printf("%d\n", sizeof(a[1]));//4
//下标为1的元素的字节大小
printf("%d\n", sizeof(&a));//4
//a:数组首元素的地址,&a:数组的地址;但是值是一样的
//&a是地址,所以字节数是4
printf("%d\n", sizeof(*&a));//16
//&a是整个数组的地址,解引用解的也是整个数组,所以是16
printf("%d\n", sizeof(&a+1));//4
//代表元素4后面的地址
printf("%d\n", sizeof(&a[0]));//4
//给下标为0的元素的地址
printf("%d\n", sizeof(&a[0]+1));//4
//给下标为0元素再后移一位的地址
return 0;
}
②
int main()
{
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));//7
//sizeof求字节大小,双引号引起来的默认后面有\0
printf("%d\n", sizeof(arr+0));//4
//发生运算arr+0代表第一个元素的地址
printf("%d\n", sizeof(*arr));//1
//第一个元素的字节大小
printf("%d\n", sizeof(arr[1]));//1
//第二个元素的字节数
printf("%d\n", sizeof(&arr));//4
//地址
printf("%d\n", sizeof(&arr+1));//4
//地址
printf("%d\n", sizeof(&arr[0]+1));//4
//地址
return 0;
}
③
int main()
{
char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };
printf("%d\n", sizeof(arr));//6
//数组名是整个数组字节大小
printf("%d\n", sizeof(arr+0));//4
//发生运算arr+0代表第一个元素的地址
printf("%d\n", sizeof(*arr));//1
//第一个元素的字节大小的大小
printf("%d\n", sizeof(arr[1]));//1
//字符b的字节大小
printf("%d\n", sizeof(&arr));//4
//地址
printf("%d\n", sizeof(&arr+1));//4
//地址
printf("%d\n", sizeof(&arr[0]+1));//4
//地址
}
④
int main()
{
char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };
//strlen的参数是指针类型
printf("%d\n", strlen(arr));//19(随机值)
//arr代表数组名,数组首元素地址
printf("%d\n", strlen(arr + 0));//19
//printf("%d\n", strlen(*arr));//语法错误
//strlen求长度,*arr代表元素a,不是地址
//printf("%d\n", strlen(arr[1]));//语法错误
//strlen求长度,*arr代表元素b,不是地址
//printf("%d\n", strlen(&arr));//语法错误
//&arr数组名取地址代表整个数组地址,需要用数组指针
//printf("%d\n", strlen(&arr + 1));//语法错误
//+1还是代表整个数组地址
printf("%d\n", strlen(&arr[0] + 1));
//数组第二个元素地址,随机值减1
return 0;
}
⑤
int main()
{
char *p = "abcdef";
printf("%d\n", sizeof(p));//4
printf("%d\n", sizeof(p+1));//4
printf("%d\n", sizeof(*p));//1
printf("%d\n", sizeof(p[0]));//1
printf("%d\n", sizeof(&p));//4
printf("%d\n", sizeof(&p+1));//4
printf("%d\n", sizeof(&p[0]+1));//4
printf("%d\n", strlen(p));//6
printf("%d\n", strlen(p+1));//6
printf("%d\n", strlen(&p[0]+1));//5
return 0;
}
⑥
int main()
{
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]));//16
//a[0]数组第一行元素
/*
a[0]代表第一行元素=第一行第一个元素的地址;
&a[0]代表第一行元素的地址
*/
printf("%d\n", sizeof(a[0]+1));//4
//数组第一行第二个元素地址,a[0]+1=&a[0][1]
printf("%d\n", sizeof(*a[0]+1));//4
//*解引用,数组第一行第二个元素元素字节大小
printf("%d\n", sizeof(a+1));//4
//a:二维数组数组名(第一行元素地址),a+1数组第二行元素的地址
printf("%d\n", sizeof(*(a+1)));//16
//*解引用,数组第二行元素
printf("%d\n", sizeof(&a[0]+1));//4
//&a[0]:数组指针,第一行元素的地址,+1第二行元素的地址
printf("%d\n", sizeof(*(&a[0] + 1)));//16..
printf("%d\n", sizeof(*a));//16
//数组名代表首元素地址,二维数组数组名代表第一行元素地址,解引用就是4个元素的字节
printf("%d\n", sizeof(a[3]));//16
//在编译期间预测a[3]de 字节并不会真正运算
return 0 ;
}
a)sizeof(数组名),数组名代表整个数组,得到整个数组字节大小
b)&数组名,数组名代表整个数组,得到整个数组地址
c)除了以上两种情况,数组名代表首元素地址
6大端和小端
面试问题:大端小端
大端:低地址存放高数据
小端:低地址存放低数据
(对于数字124,1认为是高数据,4认为是低数据[千位,百位,个位,千位高于个位])
一般情况下电脑是小端,手机是大端
int main()
{
int x = 0x12345678;
char *ch = (char*)&x;
printf("%x\n", *ch);//通过*ch解应用的时候先打印第一个,又因为电脑是小段低地址存放低数据,所以打印78
return 0;
}
//78,输出78就是小端
//如何判断大小端
int main()
{
int x = 0x12345678;
char *cg = (char*)&x;
if (*cg == 0x78)//加0X表明是16进制
{
printf("is little\n");
}
else if (*cg == 0x12)
{
printf("is big\n");
}
return 0;
}
7联合体
利用联合体判断大小端
①联合体也称为共用体,变量共用一片内存
②利用关键字union定义联合体
因为a和ch共用内存,虽然ch 每条赋值依旧有值
union Un
{
int a;
char ch;//变量a和变量ch共用一片内存
};
int IsLittle()
{
union Un uu;
uu.a = 0x12345678;
if (uu.ch == 0x78)
{
return 0;
}
return 1;
}
int main()
{
int ret = IsLittle();
if (ret == 0)
{
printf("is little\n");
}
else
{
printf("is big\n");
}
return 0;
}
习题
①
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);
printf("%d,%d", *(a + 1), *(ptr - 1));//2,5
return 0;
}
②
//p占20个字节
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p的值是0x100000,
int main()
{
p = (struct Test *)0x100000;
printf("%p\n", p + 0x1);//(p+1,p是指针,p占几个字节就加1+几个字节)
//p结构体占20个字节,转换为16进制就是14,所以p+0x1=0x100014
printf("%p\n", (unsigned long)p + 0x1);//此时p被转换为无符号类型长整数,+1就是1=0x0010 0001
printf("%p\n", (unsigned int*)p + 0x1);//此时p是int*类型,加一就是加4=0x0010 0004
return 0;
}
③
int main()
{
int a[4] = { 1, 2, 3, 4 };
int *ptr1 = (int *)(&a + 1);//取地址再解引用
int *ptr2 = (int *)((int)a + 1);
printf("%x,%x", ptr1[-1], *ptr2);
return 0;
}
④
int main(int argc,char * argv[])
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };//(0,1):逗号表达式={1,3,5,0,0,0}
int *p;
p = a[0];//a[0]是数组第一行元素数组名,数组名代表数组首元素地址,a[0]代表1的地址
printf("%d", p[0]);//p[0]=*(p+0)
return 0;
}
⑤
int main()
{
int a[5][5];
int(*p)[4];
p = a;
printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
/*p[4][2]=*(*(p+4)+2)
p+1:p指针数组int (*p)[4],p+1加4个整型
+2:加2个int
同一个数组里,两个地址相减代表差了几个元素,数组是从低地址存到高地址所以是-4
%p &p[4][2] - &a[4][2]将-4以地址的形式打印出来
*/
return 0;
}
⑥
int main()
{
int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int *ptr1 = (int *)(&aa + 1);
int *ptr2 = (int *)(*(aa + 1));
printf("%d,%d",*(ptr1 - 1), *(ptr2 - 1));
//10,1
//aa+1加到第二行,代表第二行的地址,加上解引用代表第二行首元素的地址
return 0;
}
⑦
int main()
{
int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int *ptr1 = (int *)(&aa + 1);
int *ptr2 = (int *)(*(aa + 1));
printf("%d,%d",*(ptr1 - 1), *(ptr2 - 1));
//10,1
//aa+1加到第二行,代表第二行的地址,加上解引用代表第二行首元素的地址
return 0;
}
⑧
int main()
{
char *c[] = { "ENTER", "NEW", "POINT", "FIRST" };
char **cp[] = { c + 3, c + 2, c + 1, c };
char ***cpp = cp;
printf("%s\n", **++cpp);
//new
printf("%s\n", *--*++cpp + 3);
//finst
printf("%s\n", *cpp[-2] + 3);
//
printf("%s\n", cpp[-1][-1] + 1);
return 0;
}