广东建立网站,前端开发网页设计,科技无国界,php家具网站模版文章目录 深入了解指针#xff08;一#xff09;1.了解内存和地址2.指针变量和地址2.1取地址操作符#xff08;#xff09;2.2指针变量和解引⽤操作符#xff08;*#xff09; 3. 指针的大小4.指针变量类型的意义5. const修饰指针5.1使用const修饰变量5.2当const修饰… 文章目录 深入了解指针一1.了解内存和地址2.指针变量和地址2.1取地址操作符2.2指针变量和解引⽤操作符* 3. 指针的大小4.指针变量类型的意义5. const修饰指针5.1使用const修饰变量5.2当const修饰指针 6. 指针运算6.1指针-整数6.2指针-指针6.3指针的关系运算 7. 野指针7.1. 指针未初始化7.2指针的越界7.3 指针指向的空间释放 深入了解指针二1-数组名的了解2. 使⽤指针访问数组3. ⼀维数组传参的本质4. 冒泡排序5.二级指针6.指针数组7. 指针数组模拟⼆维数组 深入了解指针三1.字符指针变量2.数组指针变量2.1了解数组指针变量的概念2.2数组指针变量初始化 3.二维数组传参的本质4.函数指针变量4.1 函数指针变量的创建4.2 函数指针变量的使⽤ 5.函数指针数组6.转移表 深入了解指针四1.回调函数2.qrost的使用3.qrost函数的模拟实现 深入了解指针五1. sizeof和strlen的对⽐1.1 sizeof1.2 strlen1.3 sizeof和strlen的对⽐ 2. 数组和指针笔试题分析2.1一维数组2.2 字符数组2.3 ⼆维数组 3. 指针运算笔试题分析 深入了解指针一
1.了解内存和地址
内存划分为⼀个个的内存单元每个内存单元的⼤⼩取1个字节。 ⼀个字节空间⾥⾯能放8个⽐特位一个比特位可以储存2进制的位1或者0。 1Byte 8bit 1KB 1024Byte 1MB 1024KB 1GB 1024MB 1TB 1024GB 1PB 1024TB 每个内存单元也都有⼀个编号在计算机中我们把内存单元的编号也称为地址。C语⾔中给地址起了新的名字叫指针。 内存单元的编号地址指针
2.指针变量和地址
2.1取地址操作符
#define _CRT_SECURE_NO_WARNINGS
#includestdio.h
int main()
{ //利用获取a的地址int a 20;printf(%p, a);return 0;
}在不同的环境下地址也会不同。
2.2指针变量和解引⽤操作符*
通过取地址操作符()拿到的地址是⼀个数值⽐如00FAFC00这个数值有时候也是需要存储起来⽅便后期再使⽤的那我们把这样的地址值存放在哪⾥呢答案是指针变量中。
#include stdio.h
int main()
{int a 20;int * pa a;//取出a的地址并存储到指针变量pa中return 0;解引⽤操作符(*)。
#includestdio.h
int main()
{ //利用获取a的地址int a 20;int* p a;printf(%d\n, *p);*p 10;printf(%d, *p);return 0;
}p是p中存放的地址改变p相当于改变a的变量。
3. 指针的大小
32位平台下地址是32个bit位即4个字节 64位平台下地址是64个bit位即8个字节
int main()
{printf(%zd\n, sizeof(int*));printf(%zd\n, sizeof(char*));printf(%zd\n, sizeof(float*));printf(%zd\n, sizeof(double*));printf(%zd\n, sizeof(short*));return 0;
}X86环境输出结果 X64环境输出结果
4.指针变量类型的意义
其实指针类型是有特殊意义的指针的类型决定了对指针解引⽤的时候有多⼤的权限⼀次能操作⼏个字节。 ⽐如 char* 的指针解引⽤就只能访问⼀个字节⽽ int* 的指针的解引⽤就能访问四个字节。
int main()
{int a 20;int* pi a;char* pc (char *)a;printf(%p\n, a);printf(%p\n, pc);printf(%p\n, pc1);printf(%p\n, pi);printf(%p\n, pi1);
}5. const修饰指针
5.1使用const修饰变量
x的本质还是变量因为有const修饰编译器在语法上不允许修改这个变量。
5.2当const修饰指针
const 放在*的左边const int * p / int const * p; / 意思表示指针指向的内容不能通过指针来改变了。但是指针变量本身的值是可以改的。
#define _CRT_SECURE_NO_WARNINGS
#includestdio.h
int main()
{int n 0;printf(n %d\n, n);//const int * p n; int const *pn;/*p 20;n20;printf(n %d\n, n);return 0;
} const 放在*的右边int * const p; 意思表示指针变量p本身不可以修改了但是指针指向的内容是可以通过指针变量来改变的。 #includestdio.h
int main()
{int n 0;printf(n %d\n, n);int * const p n;// int num 20;// p num;//printf(n %d\n, num);*p 20;printf(n %d\n, n);return 0;
}当*左右都有const const int * const p; 意思指针变量p不能被修改指针变量p指向的内容也不能被修改。
6. 指针运算
指针的基本运算有三种分别是 • 指针±整数 • 指针-指针 • 指针的关系运算
6.1指针±整数
指针整数
int main()
{int arr[10] { 1,2,3,4,5,6,7,8,9,10 };int* p arr[0];//*p获取arr数组的首地址int s sizeof(arr) / sizeof(arr[0]);for (int i 0; i s; i){printf(%d , *pi);}return 0;
} 指针-整数
int main()
{int arr[10] { 1,2,3,4,5,6,7,8,9,10 };int s sizeof(arr) / sizeof(arr[0]);int* p arr[s - 1];for (int i 0; i s; i){printf(%d , *p-i );}return 0;
}6.2指针-指针
指针之间的运算的前提是两个指针指向同一块空间 不能相加只能相减
指针-指针本质是地址-地址获取的绝对值是指针和指针之间的元素个数。
6.3指针的关系运算
int main()
{int arr[10] { 1,2,3,4,5,6,7,8,9,10 };int* p arr;//*p获取arr数组的首地址int s sizeof(arr) / sizeof(arr[0]);while (p arr[s]){printf(%d , *p);p;}return 0;
}7. 野指针
7.1. 指针未初始化 7.2指针的越界 7.3 指针指向的空间释放 深入了解指针二
1-数组名的了解
首先单独获取arr的地址会发现与数组的首元素地址一样 数组名就是数组⾸元素(第⼀个元素)的地址。
int main()
{int arr[5] { 1,2,3,4,5 };printf(%p\n,arr);printf(%p, arr[0]);return 0;
}其实数组名就是数组⾸元素(第⼀个元素)的地址是对的但是有两个例外 1• sizeof(数组名)sizeof中单独放数组名这⾥的数组名表⽰整个数组计算的是整个数组的⼤⼩ 单位是字节 而在sizeof 中arr代表的是整个数组
int main()
{int arr[5] { 1,2,3,4,5 };int n sizeof(arr);printf(%d, n);return 0;
}2• 数组名这⾥的数组名表⽰整个数组取出的是整个数组的地址整个数组的地址和数组⾸元素 的地址是有区别的
int main()
{int arr[5] { 1,2,3,4,5 };printf(%p\n, arr);printf(%p\n, arr);printf(%p\n, arr1);printf(%p\n, arr1);return 0;
}2. 使⽤指针访问数组
了解清楚地址就可以轻松访问数组内容。 可以使⽤arr[i]可以访问数组的元素那p[i]是否也可以访问数组呢
#include stdio.h
int main()
{int i 0;int arr[10] { 0 };int* p arr;int sz sizeof(arr) / sizeof(arr[0]);for ( i 0; i sz; i){scanf(%d,pi);//pi可以换成arr[i]}for ( i 0; i sz; i){printf(%d , *(pi));//*pi)可以换成arr[i]}return 0;
}3. ⼀维数组传参的本质
void szcc(int arr[])
{int sz sizeof(arr) / sizeof(arr[0]);for (int a 0; a sz; a){printf(\n%d , arr[a]);}
}
int main()
{int arr[5] { 1,2,3,4,5 };int sz sizeof(arr) / sizeof(arr[0]);for (int a 0; a sz; a){printf(%d , arr[a]);}szcc(arr);return 0;
}
在32位环境下
函数形参的部分理论上应该使⽤指针变量来接收⾸元素的地址。那么在函数内部我们写sizeof(arr) 计算的是⼀个地址的⼤⼩单位字节⽽不是数组的⼤⼩单位字节。正是因为函数的参数部分是本质是指针所以在函数内部是没办法求的数组元素个数的
所以⼀维数组传参形参的部分可以写成数组的形式也可以写成指针的形式。 void test1(int arr[])//参数写成数组形式本质上还是指针
{
printf(%d\n, sizeof(arr));
}
void test2(int* arr)//参数写成指针形式
{printf(%d\n, sizeof(arr));//计算⼀个指针变量的⼤⼩
}4. 冒泡排序 void mppx(int arr[], int n)
{for (int i 0; i n - 1; i){for (int j 0;jn-i-1;j){if (arr[j] arr[j 1]){int temparr[j];arr[j] arr[j 1];arr[j 1] temp;}}}
}
void print(int arr[], int n)
{for (int i 0; i n; i){printf(%d , arr[i]);}printf(\n);
}
int main() //冒泡排序
{int arr[] { 10,4,5,6,8,7,9,1,2,3 };int n sizeof(arr) / sizeof(arr[0]);mppx(arr, n);print(arr, n);return 0;
}再优化一些 减少重复排序
void mppx(int arr[], int n)
{for (int i 0; i n - 1; i){int flag 0;for (int j 0;jn-i-1;j){if (arr[j] arr[j 1]){flag 1;int temparr[j];arr[j] arr[j 1];arr[j 1] temp;}}if (flag 0)//这⼀趟没交换就说明已经有序后续不需要排序了直接break跳出循环。break;}}
void print(int arr[], int n)
{for (int i 0; i n; i){printf(%d , arr[i]);}printf(\n);
}
int main() //冒泡排序
{int arr[] { 10,4,5,6,8,7,9,1,2,3 };int n sizeof(arr) / sizeof(arr[0]);mppx(arr, n);print(arr, n);return 0;
}5.二级指针
二级指针通俗来讲就是指针变量的地址
int main()
{int a 10;int* p a;//p是指针变量是一级指针变量int** pa p;//*p 是二级指针变量printf(%d, **pa);//输出为10return 0;
}三级指针就是二级指针便的地址int ***paapa 以此类推四、五……指针。
6.指针数组
int main()
{int a 1;int b 2;int c 3;int* arr[] { a,b,c };//指针数组 或者{a,b,c}for (int i 0; i 3; i){printf(%d , *(arr[i]));}return 0;
}7. 指针数组模拟⼆维数组 int main()
{int arr1[5] { 0,1,2,3,4 };int arr2[5] { 6,7,8,9,10 };int arr3[5] { 11,12,13,14,15 };int* arr[3] { arr1,arr2,arr3 };//||{arr1,arr2,arr3}for (int i 0; i 3; i){for (int j 0; j 5; j){printf(%d , arr[i][j]);}}return 0;
}深入了解指针三
1.字符指针变量
int main()
{char arr[] abcdef;char* p arr;printf(%c\n, *p);const char* ps abcdef;//这⾥是把⼀个字符串放到ps指针变量里了吗printf(%s\n, ps);printf(%c\n, arr[2]);return 0;
}代码 const char* ps “abcdef”; 特别容易让同学以为是把字符串 abcdef放到字符指针 ps ⾥了但是本质是把字符串 abcdef. ⾸字符的地址放到了ps中。 这⾥arr3和arr4指向的是⼀个同⼀个常量字符串。C/C会把常量字符串存储到单独的⼀个内存区域当⼏个指针指向同⼀个字符串的时候他们实际会指向同⼀块内存。但是⽤相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以arr1和arr2不同arr3和arr4相同。
int main()
{char arr1[] abcdef;char arr2[] abcdef;const char *arr3 abcdef;const char *arr4 abcdef;if (arr1 arr2)printf(arr1和arr2是内存相同的\n);elseprintf(arr1和arr2是内存不同的\n);if (arr3 arr4)printf(arr3和arr4是内存相同的\n);elseprintf(arr3和arr4是内存不同的\n);return 0;
}2.数组指针变量
2.1了解数组指针变量的概念
整形指针变量 ——变量——存放的是整形的地址 字符指针变量——变量——存放的是字符的地址 数组指针——变量——存放的是数组的地址
int (p)[5] 诠释 * 先与p结合 说明 p是一个指针变量 [5]代表的是存放的是5个整形的数组所以p是一个指针指向数组这就是一个数组指针。 注意[]的优先级比高所以要用确保p先于*结合。
2.2数组指针变量初始化
#define _CRT_SECURE_NO_WARNINGS
#includestdio.h
int main()
{int arr[3] { 0 };int(*p)[3] arr;//得到的是数组的地址return 0;
}p与arr的类型一样
3.二维数组传参的本质
二维数组传参形参是数组时
void xs(int a[3][4], int b, int c)
{for (int i 0; i b; i){for (int j 0; j c; j){printf(%d , a[i][j]);}}
}
int main()
{int arr[3][4] { 1,2,3,4,5,6,7,8,9101112 };xs(arr, 3, 4);return 0;
}根据数组名是数组⾸元素的地址这个规则⼆维数组的数组名表⽰的就是第⼀⾏的地址是⼀维数组的地址。根据上⾯的例⼦第⼀⾏的⼀维数组的类型就是 int [4] 所以第⼀⾏的地址的类型就是数组指针类型 int(*)[4] 。那就意味着⼆维数组传参本质上也是传递了地址传递的是第⼀⾏这个⼀维数组的地址那么形参也是可以写成指针形式的。如下
void xs(int(*p)[4], int b, int c)
{for (int i 0; i b; i){for (int j 0; j c; j){printf(%d , *(*(pi)j));}}
}
int main()
{int arr[3][4] { 1,2,3,4,5,6,7,8,9 ,10,11,12};xs(arr, 3, 4);return 0;
}4.函数指针变量
变量可以取地址、数组可以取地址、函数也可以取地址。 写一个简单函数来看看
int mul(int x, int y)
{return x * y;
}
int main()
{printf(%p\n, mul);printf(%p\n, mul);return 0;
} 函数是有地址的函数名就是函数的地址当然也可以通过 函数名的⽅式获得函数的地址。 如果我们要将函数的地址存放起来就得创建函数指针变量咯函数指针变量的写法其实和数组指针⾮常类似。如下
4.1 函数指针变量的创建 int (*p)(x, y) mul;//x和y写上或者省略都是可以的
int —— p指向函数的返回类型 (*p)—— 函数指针变量名 (int x, int y)—— p指向函数的参数类型和个数的交代
4.2 函数指针变量的使⽤
int mul(int x, int y)
{return x * y;
}
int main()
{int (*p)(x, y) mul;//x和y写上或者省略都是可以的int ret (*p)(2, 4);printf(%d\n, ret);printf(%d\n, (*p)(2, 3));printf(%d\n, p(3, 4));return 0;
}5.函数指针数组
存放函数指针的数组
#define _CRT_SECURE_NO_WARNINGS
#includestdio.h
int add(int x, int y)
{return x y;
}
int sub(int x, int y)
{return x - y;
}int main()
{int (*p1)(int x, int y) add;int (*p2)(int x, int y) sub;//在这里p1与p2的类型相同我们可以把其放在数组int (*p[4])(int, int) { add,sub };//这里[4]代表函数指针数组大小可以存放4个函数指针。printf(%d\n,p[0](20, 30));printf(%d\n, p[1](20, 30));return 0;
}6.转移表
函数指针数组的应用
首先使用数组的方式实现一个简易的计算功能 void meau()
{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;
}
void calc(int(*p)(int a, int b))
{int a 0; int b 0; int c 0;printf(请输入两个整数进行运算\n);scanf(%d %d, a, b);c p(a, b);printf(运算结果%d\n, c);
}
int main()
{int input 1;
do{int a 0; int b 0; int c 0;meau();scanf(%d, input);switch(input){case 1:printf(请输入两个整数进行运算\n);scanf(%d %d, a, b);c add(a, b);printf(运算结果%d\n, c);break;case 2:printf(请输入两个整数进行运算\n);scanf(%d %d, a, b);c sub(a, b);printf(运算结果%d\n, c);break;case 3:printf(请输入两个整数进行运算\n);scanf(%d %d, a, b);c mul(a, b);printf(运算结果%d\n, c);break;case 4:printf(请输入两个整数进行运算\n);scanf(%d %d, a, b);c div(a, b);printf(运算结果%d\n, c);break;case 0:printf(退出计算\n); break;default :printf(选择错误请重新选择/n); break;}
} while (input);return 0;
}用函数指针数组的方式实现一个转移的效果
void meau()
{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;
}
void calc(int(*p)(int a, int b))
{int a 0; int b 0; int c 0;printf(请输入两个整数进行运算\n);scanf(%d %d, a, b);c p(a, b);printf(运算结果%d\n, c);
}
int main()
{int input 1;do{meau();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;
}深入了解指针四
1.回调函数
回调函数就是⼀个通过函数指针调⽤的函数。 如果你把函数的指针地址作为参数传递给另⼀个函数当这个指针被⽤来调⽤其所指向的函数时被调⽤的函数就是回调函数。回调函数不是由该函数的实现⽅直接调⽤⽽是在特定的事件或条件发⽣时由另外的⼀⽅调⽤的⽤于对该事件或条件进⾏响应。
在上面转移表的实现中已经使用的回调函数接下来给诠释一下 在这里列如 int add(int x,int y)——到void calc(int(*p)(int a, int b))——cp(a,b); 这就是一次回调函数的实现。
2.qrost的使用
qrost—库函数—可以实现任意数据类型的快速排序。
void qsort(void* base, //base中存放的是待排序数组的第一个元素的地址 size_t num, //num存放的是base指向的数组中的元素个数 size_t size, //size是base指向的数组中一个元素的长度单位是字节 int (compar)(const voidp1, const voidp2) //函数指针-指向了一个比较函数这个比较函数是用来比较数组中的两个元素的 //如果p1指向的元素大于p2指向的元素那么函数返回0的数字 如果p1指向的元素等于p2指向的元素那么函数返回0 如果p1指向的元素小于p2指向的元素那么函数返回0的数字 ); void 类型的指针不能解引用操作符也不能/-整数的操作 这种指针变量一般是用来存放地址的 使用之前要强制类型转换成想要的类型 char类型
#define _CRT_SECURE_NO_WARNINGS
#includestdio.h
#includestring.h
void com(const void* p1, const void* p2)
{return (*(char*)p1 - *(char*)p2);
}
int main()
{ //qsort函数的实现char arr[] fd gas;size_t a strlen(arr);qsort(arr, a, sizeof(arr[0]), com);for (int i 0; i a; i){printf(%c , arr[i]);}return 0;
}int 类型
void com(const void* p1, const void* p2)
{return (*(int*)p1 - *(int*)p2);
}
int main()
{ //qsort函数的实现int arr[] {2,6,8,4,5,3,1,7,9,10};size_t a sizeof(arr) / sizeof(arr[0]);qsort(arr, a, sizeof(arr[0]), com);for (int i 0; i a; i){printf(%d , arr[i]);}return 0;
}结构体类型
#includestdio.h
struct stu
{char name[15];//名字int age;//年龄
};
int com_age(const void* p1, const void* p2)
{return (*(struct stu*)p1).age - (*(struct stu*)p2).age;
}
int main()
{ //qsort函数的实现struct stu arr[3] { {liuxin,18},{zhangsan,20},{wangwu,16} };size_t a sizeof(arr) / sizeof(arr[0]);qsort(arr, a, sizeof(arr[0]), com_age);for (int i 0; i a; i){printf(%d ,arr[i].age);}return 0;
}结构体中名字的比较
#includestring.h
struct stu
{char name[15];//名字int age;//年龄
};
//名字是字符串不能直接相减来比较
//使用strcmp函数来比较
//strcmp(字符串1字符串2)
//如果字符串1字符串2 返回0 字符串1字符串2返回0 字符串1字符串2返回0
//strcmp从字符串的第一个字符开始比较 若第一个字符相等则向后推一个
char com_name(const void* p1, const void* p2)
{return strcmp( (*(struct stu*)p1).name,(*(struct stu*)p2).name);
}
int main()
{ //qsort函数的实现struct stu arr[3] { {liuxin,18},{zhangsan,20},{wangwu,16} };size_t a sizeof(arr) / sizeof(arr[0]);qsort(arr, a, sizeof(arr[0]), com_name);for (int i 0; i a; i){printf(%s ,arr[i].name);}return 0;
}3.qrost函数的模拟实现
这个是int类型的想实现其他类型需要改cmp和打印的方式
int cmp(const void* p1, const void* p2)
{return (*(int*)p1 - *(int*)p2);
}
void jh(char* e1, char* e2, size_t with)
{for (int i 0; i with; i){char a *e1;*e1 *e2;*e2 a;*e1;*e2;}
}
void myself_qsort(void *base, size_t num,size_t with,int (*cmp)(const void *p1,const void *p2)) //这里可以看文章开头有解释
{for (int i 0; i num-1; i){for (int j 0; j num-1-i; j){if (cmp((char*)basej*with,(char*)base(j1)*with)0){jh((char*)base j * with, (char*)base (j 1)*with,with);}}}
}
int main()
{int arr[] { 2,6,5,7,9,8,4,3,1,10 };size_t num sizeof(arr) / sizeof(arr[0]);myself_qsort(arr, num, sizeof(arr[0]), cmp);for (int a 0; a num ; a){printf(%d , arr[a]);}return 0;
}深入了解指针五
1. sizeof和strlen的对⽐
1.1 sizeof
在学习操作符的时候我们学习了 sizeof sizeof 计算变量所占内存内存空间⼤⼩的单位是字节如果操作数是类型的话计算的是使⽤类型创建的变量所占内存空间的⼤⼩。 sizeof 只关注占⽤内存空间的⼤⼩不在乎内存中存放什么数据。 int main()
{int a 0;printf(%d\n, sizeof a);printf(%d\n, sizeof (a));printf(%d\n, sizeof (int));return 0;}1.2 strlen
strlen 是C语⾔库函数功能是求字符串⻓度。函数原型如下 size_t strlen ( const char * str ); int main() { char arr1[] { ‘a’,‘gh’,‘s’,‘f’}; char arr2[] “abcefgh”; printf(“%d\n”, strlen(arr1)); printf(“%d\n”, strlen (arr2));//strlen 需要包含#include
printf(%d\n, sizeof(arr1));
printf(%d\n, sizeof(arr2));return 0; }
1.3 sizeof和strlen的对⽐
sizeof:
sizeof是操作符sizeof计算操作数所占内存的⼤⼩单位是字节不关注内存中存放什么数据
strlen: 4. strlen是库函数使⽤需要包含头⽂件 string.h 5. srtlen是求字符串⻓度的统计的是 \0 之前字符的个数 6. 关注内存中是否有 \0 如果没有 \0 就会持续往后找可能会越界
2. 数组和指针笔试题分析
接下来的代码环境都是在32位下
2.1一维数组
#define _CRT_SECURE_NO_WARNINGS
#includestdio.h
int main()
{//数组名是数组首元素的地址//但是有两个例外//1. sizeof(数组名)数组名表示整个数组计算的是整个数组的大小单位是字节//2. 数组名数组名表示整个数组取出的是整个数组的地址int a[] { 1,2,3,4 };printf(%zd\n, sizeof(a));// 16 a代表数组名数组中4个整数大小为4*416 printf(%zd\n, sizeof(a 0));// 4/8 首元素地址 地址在32位下为864位下为4printf(%zd\n, sizeof(*a)); // 4 首元素大小printf(%zd\n, sizeof(a 1));// 4/8 首元素地址1printf(%zd\n, sizeof(a[1])); // 4 代表a[1]元素大小 printf(%zd\n, sizeof(a));// 4/8 整个数组的地址还是地址printf(%zd\n, sizeof(*a));// 16 整个数组大小printf(%zd\n, sizeof(a 1));// 4/8 a1)代表跳过数组的下一个地址还是地址所以还是4/8printf(%zd\n, sizeof(a[0]));// 4/8 首元素地址printf(%zd\n, sizeof(a[0] 1));// 4/8 首元素地址1下一个地址return 0;
}
%zd修饰size_t类型比%d更准确
2.2 字符数组
代码1 //字符数组 char arr[] { a,b,c,d,e,f };printf(%zd\n, sizeof(arr));// 6 数组的大小6个字符大小为6printf(%zd\n, sizeof(arr 0)); // 4/8 首地址大小printf(%zd\n, sizeof(*arr));// 1 arr是首元素的地址,*arr 就是首元素printf(%zd\n, sizeof(arr[1]));// 1 arr[1]这个元素大小printf(%zd\n, sizeof(arr));// 4/8 数组地址printf(%zd\n, sizeof(arr 1));// 4/8 a1)代表跳过数组的下一个地址还是地址所以还是4/8printf(%zd\n, sizeof(arr[0] 1));// 4/8 arr[0]1是第二个元素的地址是地址大小4/8个字节return 0;
}代码2
char arr[] { a,b,c,d,e,f };
printf(%zd\n, strlen(arr));// 随机值 没有标注\0
printf(%zd\n\n\n, strlen(arr 0));// 随机值 首元素地址向后找到、0
//printf(%zd\n, strlen(*arr));// 报错 arr[1] 指的是a 在指的是97 streln会把97当作地址 向后统计字符串长度 97作为地址的空间不一定属于当前程序
//printf(%zd\n, strlen(arr[1]));// 报错 b-98 也是非法访问
printf(%zd\n, strlen(arr));// 随机值 arr数组地址传给strlen向找\0
printf(%zd\n, strlen(arr 1));// 随机值 没有\0
printf(%zd\n, strlen(arr[0] 1)); // 随机值 return 0;代码 3
char arr[] abcdef;
printf(%zd\n, sizeof(arr));// 7 字符串大小\0也算
printf(%zd\n, sizeof(arr 0));// 4/8 首元素地址
printf(%zd\n, sizeof(*arr));// 1 首元素大小
printf(%zd\n, sizeof(arr[1]));// 1 a字符大小
printf(%zd\n, sizeof(arr));// 4/8
printf(%zd\n, sizeof(arr 1));// 4/8
printf(%zd\n, sizeof(arr[0] 1));// 4/8代码4
char arr[] abcdef;
printf(%zd\n, strlen(arr));// 6 \0之前的字符个数
printf(%zd\n, strlen(arr 0));// 6 首地址地址向后找\0
printf(%zd\n, strlen(*arr));// 报错 a-97
printf(%zd\n, strlen(arr[1]));//报错 ’b-98
printf(%zd\n, strlen(arr));//6 首地址向后找到\0
printf(%zd\n, strlen(arr 1));// 随机值
printf(%zd\n, strlen(arr[0] 1));//5 代码5
char* p abcdef;
printf(%zd\n, sizeof(p));// 4/8 p是指针变量 计算的是指针变量的大小
printf(%zd\n, sizeof(p 1));// 4/8 p1是b的地址
printf(%zd\n, sizeof(*p)); // 1 *p-a
printf(%zd\n, sizeof(p[0]));// 1 把p[]当数组p[0]-a
printf(%zd\n, sizeof(p));// 4/8 整个字符串地址
printf(%zd\n, sizeof(p 1));// 4/8
printf(%zd\n, sizeof(p[0] 1));// 4 / 8代码6
char* p abcdef;
printf(%zd\n, strlen(p));//6
printf(%zd\n, strlen(p 1));//5
printf(%zd\n, strlen(*p));//报错 *p-a
printf(%zd\n, strlen(p[0]));//报错 a
printf(%zd\n, strlen(p));//随机值
printf(%zd\n, strlen(p 1));//随机值
printf(%zd\n, strlen(p[0] 1));//52.3 ⼆维数组
int a[3][4] { 0 };
printf(%zd\n, sizeof(a));//48 整个数组
printf(%zd\n, sizeof(a[0][0]));//4
printf(%zd\n, sizeof(a[0]));//16 a[0][]一维数组a[0]是第一行的数组名现在单独放在sizeof内部计算的是第一行的大小
printf(%zd\n, sizeof(a[0] 1));// 4/8 地址
printf(%zd\n, sizeof(*(a[0] 1)));//4 *(a[0] 1) 是第一行第二个元素4个字节
printf(%zd\n, sizeof(a 1));// 4/8 首地址1
printf(%zd\n, sizeof(*(a 1)));// 16 a[1][]
printf(%zd\n, sizeof(a[0] 1));//4/8 a[0]1就是第二行的地址,是地址就是4/8个字节
printf(%zd\n, sizeof(*(a[0] 1)));//16
printf(%zd\n, sizeof(*a));//16
printf(%zd\n, sizeof(a[3]));//16 不存在越界因为不会真实的访问内存仅仅是通过类型推导就可以知道长度的3. 指针运算笔试题分析
代码1 int a[5] { 1, 2, 3, 4, 5 };int* ptr (int*)(a 1);printf(%zd,%zd, *(a 1), *(ptr - 1));// *a1)--指向 数组第一2个元素 2
//ptr-1)--强制类型转换int* -1 到了数组最后一个元素地址 ,所以*ptr-1)5代码2
//在X86环境下
//假设结构体的⼤⼩是20个字节
//程序输出的结果是啥
struct Test
{int Num;char* pcName;short sDate;char cha[2];short sBa[4];
}*p (struct Test*)0x100000;
int main()
{ //0x16进制printf(%p\n, p 0x1);//结构体20个字节 0x100014printf(%p\n, (unsigned long)p 0x1);//unsigned long一个字节 0x100001printf(%p\n, (unsigned int*)p 0x1);//0x100004return 0;
}代码3
int main()
{int a[3][2] { (0, 1), (2, 3), (4, 5) };int* p;p a[0];printf(%d, p[0]);//1 注意表达式的操作使用 01就是1return 0;
}代码4 //假设环境是x86环境程序输出的结果是啥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内存中的补码以16进制的形式打印 -4的 源码 10000000 00000000 00000000 00000100// 补码 11111111 11111111 11111111 11111100
return 0; // F F F F F F F C代码5
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 (aa1)第三行首地址 int*类型*ptr1-1- 5,// 5 第二行首地址
return 0; 代码6
char* a[] { work,at,alibaba };
char** pa a;
pa;
printf(%s\n, *pa);// at pa -*pa到了at
return 0;代码7
char* c[] { ENTER,NEW,POINT,FIRST };
char** cp[] { c 3,c 2,c 1,c };
char*** cpp cp; //注意cpp会运算效果保留
printf(%s\n, **cpp);//POINT **cpp -c2-POINT
printf(%s\n, *-- * cpp 3);//ER 再cpp 到了c1 - NEW 再--就到了 ENTER 再首元素3 到了ER
printf(%s\n, *cpp[-2] 3);//ST 接上面 使用*cpp[-2] 就到了c3 - FIRST 再首元素3 到了ST
printf(%s\n, cpp[-1][-1] 1);//EW 第三个的运算没有产生后续影响 所以cpp[-1][-1]到了NEW 再首元素1就是EW
return 0;这就是指针系列2万多字的总篇章喜欢的话可以点点关注、收藏、赞。 (⑅•͈ᴗ•͈).:*♡