针对于指针这一块区域,很多人都很头疼,但是又没有什么好办法,在这篇文章,我会带着大家一点一点分析指针,过程虽然会很痛苦,但是一旦学懂之后发现其实也没啥的,加油!冲冲冲!
指针的作用: 可以通过指针间接访问内存
内存编号是从0开始记录的,一般用十六进制数字表示
可以利用指针变量保存地址
指针变量的定义和使用
指针变量定义语法: 数据类型 * 变量名;
指针变量可以通过" * "操作符,操作指针变量指向的内存空间,这个过程称为解引用
指针所占内存空间:在32位平台下是四个字节,在64位平台是8个字节
空指针:指针变量指向内存中编号为0的空间
野指针:指针变量指向非法的内存空间
指针±整数:int 4个字节 char 1个字节
指针-指针:中间的元素个数
二级指针
字符指针的使用:
//实例1
#include
int main()
{
char ch = 'w';
char *pc = &ch;
*pc = 'w';
return 0;
}
//实例2
#include
int main()
{
char* p="abcdef";
printf("%s
",p);
}
//实例三
#include
int main()
{
char* p = "abcdef";
*p = 'w';
printf("%s
", p);
return 0;
}
解析:
那么有一道来自《剑指Offer》的题目,让我们look look吧
#include
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abcdef";
const char* str1 = "abcdef";
const char* str2 = "abcdef";
if (arr1 == arr2)
printf("arr1==arr2
");
else
printf("arr1!=arr2
");
if (str1 == str2)
printf("str1==str2
");
else
printf("str1!=str2
");
return 0;
}
答案是arr1!=arr2 str1==str2,做对了没?
那么接下来我在强调一下大家可能会出现错误的地方
1.int *pa,pb;请问pa pb是什么类型的?
答案:pa->int* pb->int
2.typedef int* PINT
PINT pa,pb;
答案:pa->int* pb->int*
3.#define pint int*
pint pa,pb;
答案:pa->int* pb->int
结论:推荐不要去连续定义指针
整型数组-存放整型的数组
字符数组-存放字符的数组
指针数组->存放指针的数组
下面有几个例子,我们来看一下:
1.int* arr1[10]; //整形指针的数组:arr1先与[]结合,代表是10个元素的数组,每个数组都是int*类型的
char *arr2[4]; //一级字符指针的数组:把字符串的首字母存入数组中
char **arr3[5];//二级字符指针的数组
备注:数组的类型 去掉数组名与元素个数
2.int a = 10;
int* p = &a;
int** pp = &p;//二级指针
int** arr2[4] = {0};//指针数组,存放二级指针
3.
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 2,3,4,5,6 };
int arr3[] = { 3,4,5,6,7 };
int* arr[] = {arr1, arr2, arr3};//数组名->首元素地址,把每个数组的首元素地址存入指针数组中
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ", arr[i][j]);//*(*(arr+i)+j)
}
printf("
");
}
return 0;
}//相当于模拟二维数组
备注:
arr[i] 相当于找到每一行的起始地址
arr[i][j]是找到当前行后面的数
*(*(arr+i)+j)的含义:
arr+i:找到行数
*(arr+i):对找到的行进行解引用操作
*(arr+i)+j:在当前行找到每个元素
*(*(arr+i)+j):对每个元素进行解引用操作
4.
int main()
{
char* arr[] = { "abcdef", "qwer", "zhangsan" };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < sz; i++)
{
printf("%s
", arr[i]);
}
return 0;
}
整形指针-指向整型的指针
字符指针-指向字符的指针
数组指针->应该是一种指针,是指向数组的指针
判断一下,下面代码哪个是数组指针
int *p1[10];//指针数组-p1先与[]结合,指向数组元素为10的数组,数组每个元素是int类型
int (*p2)[10];//数组指针-p2先和*结合,说明p2是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p2是一个指针,指向一个数组,叫数组指针。
注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。
arr是数组名,数组名表示数组首元素的地址。类型:int*
&arr 表示的是数组的地址,而不是数组首元素的地址。类型:数组指针
演示1:
int arr[10] = { 0 };
//数组名->首元素地址
printf("%p
", arr);//027CFA8C
printf("%p
", arr+1);//027CFA90
//首元素地址
printf("%p
", &(arr[0]));//027CFA8C
printf("%p
", &(arr[0])+1);//027CFA90
//数组的地址->放到数组指针里面
printf("%p
", &arr);//027CFA8C
printf("%p
", &arr+1);//027CFAB4
演示2:数组指针的类型就是去掉名字即可
char arr[5];
char (*pa)[5] = &arr;
int* parr[6];
int* (*pp)[6] = &parr;
演示3:数组指针的用途
//数组指针有什么??
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));//1 2 3 4 5 6 7 8 9 10
}
return 0;
}
//数组指针很少应?于?维数组
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int(*p)[10] = &arr;
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *((*p) + i));
}
return 0;
}
//数组指针常应?于?维数组
void print(int a[3][5], int r, int c)
{
int i = 0;
int j = 0;
for (i = 0; i < r; i++)
{
for (j = 0; j < c; j++)
{
printf("%d ", a[i][j]);
}
printf("
");
}
}
//int(*p)[5]是数组指针
void print(int(*p)[5], int r, int c)
{
int i = 0;
for (i = 0; i < r; i++)
{
int j = 0;
for (j = 0; j < c; j++)
{
//*(p+i) 相当于拿到了?维数组的第i?,也相当于第i?的数组名
//数组名表示?元素的地址,其实也是第i?第?个元素的地址
printf("%d ", *(*(p + i) + j));
//
//p是第??的地址
//p+i是第i?的地址
//*(p+i) 是第i?第?个元素的地址
//
}
printf("
");
}
}
int main()
{
int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
print(arr, 3, 5);
return 0;
}
演示4:
int main()
{
int arr[10];
int i = 0;
arr[i] == *(arr + i) == p[i] == *(p + i);
int* p = arr;//p和arr是一回事
*(p + i);
return 0;
}
练习: 看看下面代码的意思
1.int arr[5];//arr是?个整型数组,有5个元素,每个元素都是int类型的
2.int *parr1[10];//parr1是?个数组,数组有10个元素,每个元素的类型是int*,所以parr1是指针数组
3.int (*parr2)[10];//parr2和*结合,说明parr2是?个指针,该指针指向?个数组,数组是10个元素,每个元素都是int类型。parr2是数组指针
4.int (*parr3[10])[5];//parr3和[]结合,说明parr3是?个数组,数组是10个元素,数组的每个元素是?种数组指针,类型是int (*)[5],该类型的指针指向的数组有5个int类型的元素
第4题的图解
类型的判断:
数组名其实就是个地址,举个例子
int a=10;
int arr[5];
我们的变量以及数组名在编译器处理过之后就是地址
我们在看这样一句代码
int (*p)[5];//这是个数组指针
#include
void test(int arr[])//数组传参的时候,形参用指针接受也是没问题的
{ }
void test(int arr[10])//本质:其实并不会新创建一个数组,哪怕你形参元素个数写错了也无所谓
{ }
void test(int* arr)//本质:一维数组传参,传过去的是地址,地址用指针接收没问题
{ }
void test2(int* arr[20])//数组传参,形参写成数组没问题,20个元素每个元素都是int*(指针类型)
{ }
void test2(int** arr)//(地址->类型:)一级指针,用二级指针接收没问题
{ }
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[][5])//ok
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
void test(int* arr)//err-二维数组传参,传过来的是第一行的地址,是int*的,直接用int*接受是不可以的
{}
void test(int* arr[5])//err-传过来的是地址,应该用数组指针而不是指针数组来接收
{}
void test(int(*arr)[5])//ok-传过来的是第一行的地址,用数组指针接受没问题
{}
void test(int** arr)//err-只有传过来?级指针的地址时 才??级指针接收
{}
int main()
{
int arr[3][5] = { 0 };
test(arr);//数组名->首元素的地址,在二维数组中,首元素为第一行,所以传的是第一行的地址
}
一级指针传参,一级指针接收
#include
void print(int* p, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d
", *(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;
}
思考:当一个函数的参数部分为一级指针的时候,函数能接收什么参数?
void test(int* p)
{}
int main()
{
int a = 10;
int* ptr = &a;
int arr[10] = { 0 };
test(&a);//整个数组
test(ptr);//整型指针
test(arr);//数组名
return 0;
}
#include
void test(int** ptr)
{
printf("num = %d
", **ptr);
}
int main()
{
int n = 10;
int* p = &n;//一级指针
int** pp = &p;//二级指针,保存的是p的地址
test(pp);
test(&p);//一级指针变量的地址
return 0;
}
思考:当函数的参数为二级指针的时候,可以接收什么参数?
例子1:
void test(char** p)
{}
int main()
{
char ch = 'w';
char* p = &ch;
char** pp = &p;
char* arr[5];//指针数组
test(&p);//一级指针变量的地址
test(pp);//二级指针(存放一级指针变量的地址)
test(arr);//数组名-指针数组是char*类型的,用char**接收没问题
return 0;
}
例子2:二维数组的类型名->类型(*)[列数]
//可以通过这种方法检测两边类型是否相同
//报错信息:"int*"与"int(*)[5]的间接级别不同
int main()
{
int arr[3][5];
int p = arr;//err,arr是第一行的地址,是int(*)[5]类型的,应该用int* p来接收
return 0;
}
函数指针:指向函数的指针
首先看一段代码:
int Add(int x, int y)
{
return x + y;
}
void test(char* str)
{}
int main()
{
//下面这两个相同,都是函数的地址
//注意:函数是没有首元素的,不能说首元素地址
//printf("%p
", &Add);
//printf("%p
", Add);
//int arr[5];
//void (*pt)(char*) = test;//test函数的函数指针
//int pf(int, int) = &Add;//err->这样就成函数了
//int (* pf)(int, int) = &Add;//pf是函数指针
int (*pf)(int, int) = Add;//pf存放的是Add的地址
//下面两种写法都可以->*是个摆设
//int sum = (*pf)(2,3);//(*pf)->找到函数 (2,3)->函数调用 e.g.Add(2,3);
int sum = pf(2, 3);//pf从此和Add一样,可以替换
//int sum = Add(2, 3);
//int sum = *pf(2, 3);//err,先执行pf(2,3);在执行解引用操作
printf("%d
", sum);
return 0;
}
函数指针到底是怎么回事呢
void test()
{
printf("hehe
");
}
void (*pfun1)();//pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参
数,返回值类型为void。
阅读两段有趣的代码:
//代码1
(*(void (*)())0)();
//代码2
void (*signal(int , void(*)(int)))(int);
代码一:
代码二:
函数指针数组:函数指针数组-存放函数指针的数组,每个元素都是函数指针类型
int (*parr1[10])();
//parr1先和[]结合,说明parr1是数组,数组的内容是什么呢?是 int (*)() 类型的函数指针。
例子:计算器
//版本1
#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;
}
void menu()
{
printf("**********************************
");
printf("***** 1. add 2. sub *****
");
printf("***** 3. mul 4. div *****
");
printf("***** 0. exit *****
");
printf("**********************************
");
}
int main()
{
int input = 0;
int x = 0;
int y = 0;
int ret = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
printf("输?2个操作数:>");
scanf("%d %d", &x, &y);
ret = Add(x, y);
printf("ret = %d
", ret);
break;
case 2:
printf("输?2个操作数:>");
scanf("%d %d", &x, &y);
ret = Sub(x, y);
printf("ret = %d
", ret);
break;
case 3:
printf("输?2个操作数:>");
scanf("%d %d", &x, &y);
ret = Mul(x, y);
printf("ret = %d
", ret);
break;
case 4:
printf("输?2个操作数:>");
scanf("%d %d", &x, &y);
ret = Div(x, y);
printf("ret = %d
", ret);
break;
case 0:
printf("退出计算器
");
break;
default:
printf("选择错误
");
break;
}
} while (input);
return 0;
}
//版本2:转移表
#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;
}
void menu()
{
printf("**********************************
");
printf("***** 1. add 2. sub *****
");
printf("***** 3. mul 4. div *****
");
printf("***** 0. exit *****
");
printf("**********************************
");
}
int main()
{
int input = 0;
int x = 0;
int y = 0;
int ret = 0;
int (*pfArr[5])(int, int) = { 0, Add, Sub, Mul, Div };
//pfArr是?个函数指针的数组,也叫转移表
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
if (input == 0)
{
printf("退出计算器
");
break;
}
else if (input >= 1 && input <= 4)
{
printf("输?2个操作数:>");
scanf("%d %d", &x, &y);
ret = pfArr[input](x, y);
printf("ret = %d
", ret);
}
else
{
printf("选择错误
");
}
} while (input);
return 0;
}
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个
函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数
的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进
行响应。
//void qsort(void* base,
// size_t num,
// size_t width,
// int(* compare)(const void* e1, const void* e2)
// );
#include
void bubble_sort(int arr[], int sz)
{
//趟数:sz-1
int i = 0;
for (i = 0; i < sz - 1; 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;
}
}
}
}
void print_arr(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("
");
}
int main()
{
int arr[] = { 9,8,7,6,5,4,3,2,1 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz);
print_arr(arr, sz);
return 0;
}
#include
#include
void print_arr(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("
");
}
//?较e1和e2指向的元素
//void*-无具体类型指针
int cmp_int(const void* e1, const void* e2)
{
//默认升序,想要降序的话,把e1和e2交换位置
return *(int*)e1 - *(int*)e2;//e1直接解引用不行,要先把e1强转成int*类型,再解引用
}
//测试qsort排序整型数组
void test1()
{
int arr[] = { 1,4,2,6,5,3,7,9,0,8 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_int);
print_arr(arr, sz);
}
int main()
{
test1();
return 0;
}
#include
#include
#include
struct Stu
{
char name[20];
int age;
float score;
};
//排序成绩
int cmp_stu_by_score(const void* e1, const void* e2)
{
//把e1 e2强转成结构体类型
if (((struct Stu*)e1)->score > ((struct Stu*)e2)->score)
{
return 1;
}
else if (((struct Stu*)e1)->score < ((struct Stu*)e2)->score)
{
return -1;
}
else
{
return 0;
}
}
//排序年龄
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)
{
return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
//引头文件#include
}
//打印
void print_stu(struct Stu arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%s %d %f
", arr[i].name, arr[i].age, arr[i].score);
}
printf("
");
}
void test2()
{
struct Stu arr[] = { {"zhangsan",20,87.5f},{"lisi",22,99.0f},{"wangwu", 10, 68.5f} };
//按照成绩来排序
int sz = sizeof(arr) / sizeof(arr[0]);
//qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_score);
//qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
print_stu(arr, sz);
}
int main()
{
test2();
return 0;
}
//自制qsort排序整型数组
#include
#include
#include
//排序数字
int cmp_int(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
void print_arr1(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("
");
}
void Swap(char* buf1, char* buf2, int width)
{
int i = 0;
//一对一对字符交换
for (i = 0; i < width; i++)//交换width对字节
{
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
//?制qsort:并未通过宽度推断类型,用宽度计算偏移量
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++)
{
//if (arr[j] > arr[j + 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*并不知道会操作几个字节,所以我们要把偏移量width传进去
}
}
}
}
void test3()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
print_arr1(arr, sz);
}
int main()
{
test3();
return 0;
}
//测试自制qsort排序结构体数据
#include
#include
#include
struct Stu
{
char name[20];
int age;
float score;
};
//排序成绩
int cmp_stu_by_score(const void* e1, const void* e2)
{
if (((struct Stu*)e1)->score > ((struct Stu*)e2)->score)
{
return 1;
}
else if (((struct Stu*)e1)->score < ((struct Stu*)e2)->score)
{
return -1;
}
else
{
return 0;
}
}
//排序年龄
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)
{
return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
void print_stu2(struct Stu arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%s %d %f
", arr[i].name, arr[i].age, arr[i].score);
}
printf("
");
}
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++;
}
}
//?制qsort
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++)
{
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()
{
struct Stu arr[] = { {"zhangsan",20,87.5f},{"lisi",22,99.0f},{"wangwu", 10, 68.5f} };
//按照成绩来排序
int sz = sizeof(arr) / sizeof(arr[0]);
//?制qsort的调?
//bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_socre);
//bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
print_stu2(arr, sz);
}
int main()
{
test4();
return 0;
}
那么接下来我们从不同角度思考问题:
那么接下来再重新看这块代码
//void qsort(void* base, 不知道排序什么类型 , 就用void*
// size_t num, 元素个数
// size_t width, void*不知道类型,就不知道??,?法知道?次 操作?个字节,所以我们要给出
// int(* compare)(const void* e1, const void* e2)把不同的操作抽象成函数
// );
数组名:
练习1:
#include
int main()
{
int a[] = { 1,2,3,4 };
printf("%d
", sizeof(a));//数组名a单独放在sizeof内部,计算的整个数组的??,单位是字节,4*4 = 16
printf("%d
", sizeof(a + 0));//a表示的?元素的地址,a+0还是数组?元素的地址,是地址??4/8
printf("%d
", sizeof(*a));//a表示的?元素的地址,*a就是对?元素的地址的解引?,就是?元素,??是4个字节
printf("%d
", sizeof(a + 1));//a表示的?元素的地址,a+1是第?个元素的地址,是地址,??就4/8个字节
printf("%d
", sizeof(a[1]));//a[1]是数组的第?个元素,??是4个字节
printf("%d
", sizeof(&a)); //&a 表示是数组的地址,数组的地址也是地址,地址??就是4/8字节
printf("%d
", sizeof(*&a));//可以理解为*和&抵消效果,*&a相当于a,sizeof(a)是16
//&a -> int(*)[4] 数组的地址放在数组指针中
//&a是数组的地址,它的类型是int(*)[4]数组指针,如果解引?,访问的就是4个int的数组,??是16个字节
printf("%d
", sizeof(&a + 1));//&a是数组的地址,&a+1 跳过整个数组后的地址,是地址就是4/8
printf("%d
", sizeof(&a[0]));//&a[0]取出数组第?个元素的地址,是地址就是4/8
printf("%d
", sizeof(&a[0] + 1));//&a[0]+1就是第?个元素的地址,是地址??就是4/8个字节
//&a[0] - 地址的类型:int*
return 0;
}
sizeof只关注占空间的,单位是字节 ->南北通吃
sizeof不关注类型
sizeof是操作符,不是函数
strlen关注的字符串中的位置,计算的是之前出现了多少个字符
strlen指针对字符串
strlen是库函数
练习2:
#include
#include
int main()
{
//字符数组
char arr[] = { 'a','b','c','d','e','f' };
printf("%d
", sizeof(arr));//arr作为数组名单独放在sizeof内部,计算的整个数组的??,单位是字节,6
printf("%d
", sizeof(arr + 0));//arr就是?元素的地址,arr+0还是?元素的地址,地址??就是4/8
printf("%d
", sizeof(*arr));//arr就是?元素的地址,*arr就是?元素,是?个字符,??是?个字节,1
printf("%d
", sizeof(arr[1]));//arr[1]就是数组的第?个元素,是?个字符,??是1个字节
printf("%d
", sizeof(&arr));//&arr取出的是数组的地址,数组的地址也是地址,地址就是4/8个字节
printf("%d
", sizeof(&arr + 1));//&arr取出的是数组的地址,&arr+1,跳过了整个数组,&arr+1还是地址,地址就是4/8个字节
printf("%d
", sizeof(&arr[0] + 1));//&arr[0]是第?个元素的地址,&arr[0]+1就是第?个元素的地址,地址就是4/8个字节
printf("%d
", strlen(arr));//arr是?元素的地址,但是arr数组中没有,计算的时候就不知道什么时候停?,结果是:随机值
printf("%d
", strlen(arr + 0));//arr是?元素的地址,arr+0还是?元素的地址,结果是:随机值
printf("%d
", strlen(*arr)); //err,strlen需要的是?个地址,从这个地址开始向后找字符,直到,统计字符的个数。
//但是*arr是数组的?元素,也就是'a',这是传给strlen的就是'a'的ascii码值97,strlen函数会把97作为起始地址,统计字符串,会形成内存访问冲突
printf("%d
", strlen(arr[1]));//err 和上?个?样,内存访问冲突
printf("%d
", strlen(&arr));//&arr是arr数组的地址,虽然类型和strlen的参数类型有所差异,但是传参过去后,还是从第?个字符的位置向后数字符,结果还是随机值。
printf("%d
", strlen(&arr + 1));//随机值 数组地址+1->跳过整个数组
printf("%d
", strlen(&arr[0] + 1));//随机值 数组元素+1->跳过这个元素
return 0;
}
练习3:
int main()
{
char arr[] = "abcdef";
printf("%d
", strlen(arr));//arr是?元素的地址,向后寻找直到遇?,结果为6
printf("%d
", strlen(arr + 0));//arr就是?元素的地址,arr+0还是?元素的地址,结果同上
printf("%d
", strlen(*arr));//err,strlen需要的是?个地址,从这个地址开始向后找字符,直到,统计字符的个数。但是* arr是数组的?元素,也就是'a', 这是传给strlen的就是'a'的ascii码值97,strlen函数会把97作为起始地址,统计字符串,会形成内存访问冲突
printf("%d
", strlen(arr[1]));//err 和上?个?样,内存访问冲突
printf("%d
", strlen(&arr));//&arr是arr数组的地址,从第?个字符的位置向后数字符,结果是6
printf("%d
", strlen(&arr + 1));//数组地址+1->跳过整个数组 随机值
printf("%d
", strlen(&arr[0] + 1));//数组元素地址+1->跳过这个元素->跳到了b,向后数在之前有5个元素
printf("%d
", sizeof(arr));//数组名a单独放在sizeof内部,计算的整个数组的??,单位是字节,有7个元素,1*7=7
printf("%d
", sizeof(arr + 0));//?单独的sizeof,?&->?元素地址,+0还是?元素地址,4/8个字节
printf("%d
", sizeof(*arr));//arr就是?元素的地址,*arr就是?元素,是?个字符,??是?个字节,1
//*arr->*(arr + 0)->arr[0]
printf("%d
", sizeof(arr[1]));//arr[1]就是数组的第?个元素,是?个字符,??是1个字节
printf("%d
", sizeof(&arr));//&arr取出的是数组的地址,数组的地址也是地址,地址就是4/8个字节
printf("%d
", sizeof(&arr + 1));//&arr取出的是数组的地址,&arr+1,跳过了整个数组,&arr+1还是地址,地址就是4/8个字节
printf("%d
", sizeof(&arr[0] + 1));//&arr[0]是第?个元素的地址,&arr[0]+1就是第?个元素的地址,地址就是4/8个字节
return 0;
}
练习4:
#include
#include
int main()
{
char* p = "abcdef";
printf("%d
", sizeof(p)); //p是?个指针变量,sizeof(p)计算的就是指针变量的??,4 / 8个字节
printf("%d
", sizeof(p + 1));//p是指针变量,是存放地址的,p+1也是地址,地址??就是4/8字节
printf("%d
", sizeof(*p));//p是char*的指针,解引?访问?个字节,即*p访问1个字节
printf("%d
", sizeof(p[0]));//p[0]--> *(p+0) -> *p ?元素:1个字节
printf("%d
", sizeof(&p));//&p也是地址,是地址就是4/8字节,&p是?级指针
printf("%d
", sizeof(&p + 1)); //&p是地址, + 1后还是地址,是地址就是4 / 8字节
&p + 1,是p的地址+1,在内存中跳过p变量后的地址
printf("%d
", sizeof(&p[0] + 1));//p[0]就是a,&p[0]就是a的地址,&p[0]+1就是b的地址,是地址就是4/8字节
//p[0]-- > * (p + 0)->*p
printf("%d
", strlen(p));//p中存放的是'a'的地址,strlen(p)就是从'a'的位置向后求字符串的?度,?度是6
printf("%d
", strlen(p + 1));//p+1是'b'的地址,从b的位置开始求字符串?度是5
printf("%d
", strlen(*p));//err,strlen需要的是?个地址,从这个地址开始向后找字符,直到,统计字符的个数。但是* arr是数组的?元素,也就是'a', 这是传给strlen的就是'a'的ascii码值97,strlen函数会把97作为起始地址,统计字符串,会形成内存访问冲突
printf("%d
", strlen(p[0]));//err 和上?个?样,内存访问冲突
printf("%d
", strlen(&p));//随机值 向后找不到
printf("%d
", strlen(&p + 1));//随机值 同上
printf("%d
", strlen(&p[0] + 1));//p[0] -> *(p+0) -> *p ->'a' ,&p[0]就是?字符的地址,&p[0]+1就是第?个字符的地址
//从第2个字符的位置向后数字符串,?度是5
return 0;
}
练习5:
int main()
{
//?维数组
int a[3][4] = { 0 };
printf("%d
", sizeof(a));//数组名单独放在sizeof内部,计算的是整个数组的?? 3*4*4=48
printf("%d
", sizeof(a[0][0]));//?个元素 -> 4个字节
printf("%d
", sizeof(a[0]));//a[0]表示第??的数组名,a[0]作为数组名单独放在sizeof内部,计算的是第??的??(整个数组的??)16
printf("%d
", sizeof(a[0] + 1));//a[0]作为第??的数组名,没有&,没有单独放在sizeof内部,所以a[0]表示的就是?元素的地址,即a[0][0]的地址,a[0] + 1就是第??第?个元素的地址,是地址就是4 / 8
printf("%p
", &a[0][0]);//02D8FB94
printf("%p
", a[0] + 1);//02D8FB98
printf("%d
", sizeof(*(a[0] + 1)));//a[0] + 1是第??第?个元素的地址,对他进?解引?,就访问到了该元素,??是4个字节
printf("%d
", sizeof(a + 1));//a是?维数组数组名,没有&,没有单独放在sizeof内部,a表示数组?元素(第??的地址),a+1跳到了第??的地址,是类型为int(*)[4]的数组指针,是地址就是4 / 8个字节。
printf("%p
", &a[0][0]);//008FF764
printf("%p
", &a + 1);//008FF794 跳过整个?维数组 还是地址4/8
printf("%p
", &a[0][0]);//0094FA4C 第??地址
printf("%p
", a + 1);//0094FA5C 第??地址
printf("%d
", sizeof(*(a + 1)));//a+1是第??的地址,*(a+1)就是第??,相当于第??的数组名,*(a + 1)->a[1], sizeof(*(a + 1))计算的是第??的??,16字节
printf("%d
", sizeof(&a[0] + 1));//a[0]是第??的地址,&a[0]是第??的地址,&a[0]+1是第??的地址,是地址就是4/8字节
printf("%d
", sizeof(*(&a[0] + 1)));//&a[0] + 1是第??,*(&a[0] + 1))是对第??解引?,相当于拿到了第??的数组名,相当于第??,也就是a[1], sizeof(a[1])??是16字节
printf("%d
", sizeof(*a));//a是?维数组数组名,没有&,没有单独放在sizeof内部,a表示?元素地址,*a就是?维数组的?元素,也就是第??,sizeof(*a)就是16个字节。* a->*(a + 0)->a[0]
printf("%d
", sizeof(a[3]));//感觉a[3]越界了,但是没关系,压根不会去计算括号内的东?,回去退到括号内的类型,括号?的类型是int[4], ??是16个字节
return 0;
}
练习6:
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a + 1);
printf("%d,%d", *(a + 1), *(ptr - 1));
return 0;
}
//程序的结果是什么?5 6
练习7:
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量??是20个字节
int main()
{
p = (struct Test*)0x100000;
printf("%p
", p + 0x1);//00100014
printf("%p
", (unsigned long)p + 0x1);//00100001
printf("%p
", (unsigned int*)p + 0x1);//00100004
注意:0x1是1
printf("%x
", p + 0x1);//0x1->1;p+0x1->p+1 结构体指针+!跳过整个结构体,20个字节,20转换成16进制是14,0x100000 + 14->0x100014,打印的结果是10014
printf("%x
", (unsigned long)p + 0x1);//把p转化成unsigned long形式,p就是?个数字,0x100000不再是地址,0x100000 + 1->0x100001,打印的结果是100001
printf("%x
", (unsigned int*)p + 0x1);//整形指针+1->跳过四个字节,0x100000 + 4->0x100004, 打印的结果是100004
//%p 以地址的形式打印,?位0不会省略
//%x 就是打印16进制,?位0会省略
return 0;
}
练习8:坑!
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;
}
练习9:
#include
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int* p;
p = a[0];
printf("%d", p[0]);
return 0;
}
练习10:
int main()
{
int a[5][5];
int(*p)[4];
p = a;
printf("%p,%d
", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
return 0;
}
练习11:
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));
return 0;
}
练习12:
#include
int main()
{
char* a[] = { "work","at","alibaba" };
char** pa = a;
pa++;
printf("%s
", *pa);
return 0;
}
练习13:难度略大
int main()
{
char* c[] = { "ENTER","NEW","POINT","FIRST" };
char** cp[] = { c + 3,c + 2,c + 1,c };
char*** cpp = cp;
printf("%s
", **++cpp);
printf("%s
", *-- * ++cpp + 3);
printf("%s
", *cpp[-2] + 3);
printf("%s
", cpp[-1][-1] + 1);
return 0;
}