)
系列文章编程随闪--指针一编程随闪--指针二1.sizeof单目操作符和strlen()函数1.1sizeof单目操作符在学习操作符的时候我们学习了 sizeof 操作符sizeof 操作符是计算变量所占内存空间大小的单位是字节如果操作数是类型则计算的是使用该类型创建的变量所占内存空间的大小。即sizeof操作符只能用来计算占用内存空间的大小单位字节不关心内存空间中的值。//sizeof是单目操作符并不是函数因为函数的调用必须要带括号 #includestdio.h int main() { int a 10; printf(%d\n, sizeof(a)); printf(%d\n, sizeof a);// sizeof 操作符是计算变量所占内存空间大小的单位是字节 printf(%d\n, sizeof(int));//操作数是类型则计算的是使用该类型创建的变量所占内存空间的大小。 return 0; }1.2strlen()函数strlen()是C语言库函数该函数是求字符串长度的。//函数原型 size_t strlen ( const char * str );strlen()库函数所需头文件为string.h统计的是从strlen()函数的参数str这个地址开始向后一直到 \0 之前字符串中字符的个数。strlen()库函数会一直向后找 \0 字符直到找到为止。所以可能存在越界查找。#include stdio.h int main() { char arr1[3] {a, b, c}; char arr2[] abc; printf(%d\n, strlen(arr1));//随机值没有\0 printf(%d\n, strlen(arr2));//3 printf(%d\n, sizeof(arr1));//3 printf(%d\n, sizeof(arr2));//4,\0也占一字节 return 0; }#includestdio.h int main() { int a 8; short s 4; printf(%d\n, sizeof(s a 2));//2 short类型大小 /* 为什么sizeof中表达式不参与计算 C语言是编译型语言要经过编译链接运行 表达式执行是在运行时实现的而sizeof操作符则是在编译时执行的编译时其表达式取决于s的类型short则直接换成short所占字节大小2字节 */ printf(%d\n, s);//4 return 0; }sizeof单目操作符不关注内存中的内容strlen()函数会关注内存中的内容1sizeof是操作符2sizeof计算操作数所占内存的大小单位是字节3不关注内存中存放什么数据4sizeof括号中有表达式表达式不参与计算1strlen()是库函数需要包含头文件string.h才能使用2strlen()函数是求字符串长度的函数统计的是 ‘\0’ 之前字符的个数3该函数会关注字符串中是否有 \0 如果没有 \0 ,会持续往后找。可能会造成越界2.数组和指针相关题解析2.1一维数组#includestdio.h /* sizeof操作符 */ int main() { int a[] { 1,2,3,4 };//4个元素 printf(%d\n, sizeof(a));//16字节 //sizeof(a)就是sizeof(数组名)的场景 printf(%d\n, sizeof(a 0));//4字节或8字节 //sizeof(a 0)不是sizeof(数组名)则表示首元素地址类型int*a0还是首元素的地址 printf(%d\n, sizeof(*a));//4字节 //a是首元素地址*a为首元素大小就是4字节。*a a[0] *(a 0) printf(%d\n, sizeof(a 1)); //不符合特例则a表示首元素地址类型为int* ,a1跳过一个整型表示第二个元素的地址 printf(%d\n, sizeof(a[1]));//第二个元素大小4字节 printf(%d\n, sizeof(a));//4/8字节 //a是整个数组的地址数组地址也是一个地址是地址就是4/8字节 printf(%d\n, sizeof(*a));//16字节 //1*、抵消sizeof(a),整个数组大小 //2a取整个数组的地址类型是int (*)[4]*解引用后访问的是整个数组 printf(%d\n, sizeof(a 1));//4/8字节 //a整个数组地址a 1是跳过整个数组后那个位置的地址 printf(%d\n, sizeof(a[0])); //取首元素地址地址大小4/8字节 printf(%d\n, sizeof(a[0] 1));//数组第二个元素的地址大小4/8字节 return 0; }2.2字符数组#includestdio.h /* sizeof操作符 */ int main() { char arr[] { a,b,c,d,e,f }; printf(%d\n, sizeof(arr));//6字节 //数组名单独放在sizeof内部计算的是整个数组的大小 printf(%d\n, sizeof(arr 0));//4/8字节 //arr是数组名表示首元素的地址arr0还是首元素的地址 printf(%d\n, sizeof(*arr));//1字节 //arr首元素地址*arr表示首元素大小为1字节 printf(%d\n, sizeof(arr[1]));//第二个元素的值大小1字节 printf(%d\n, sizeof(arr));//4/8字节 //arr整个数组的地址 printf(%d\n, sizeof(arr 1));//4/8字节 //arr整个数组的地址1跳过整个数组指向数组后面的空间 printf(%d\n, sizeof(arr[0] 1));//4/8字节 //arr[0]取数组第一个元素的地址1取第二个元素的地址 return 0; }#includestdio.h #includestring.h /* strlen()函数 */ int main() { char arr[] { a,b,c,d,e,f }; printf(%d\n, strlen(arr));//随机值 //arr首元素地址strlen()从首地址开始数但数组中没有\0会越界访问结果是随机的什么时候遇到\0未知 printf(%d\n, strlen(arr 0));//随机值 //arr是首元素地址0还是首元素地址但没有\0会越界访问结果随机 printf(%d\n, strlen(*arr));//err错误代码越界访问 //arr是首元素地址*解引用后为首元素a,a的ASCII码值为97将97视为地址传递给strlen().strlen()得到的就是野指针 printf(%d\n, strlen(arr[1]));//err,错误代码 //数组第二个元素bb的ASCII码为98strlen()得到野指针 printf(%d\n, strlen(arr));//随机值 //arr整个数组的地址起始和首元素地址相同 printf(%d\n, strlen(arr 1));//随机值 //arr整个数组的地址1跳过整个数组后开始向后访问和arr差6长度 printf(%d\n, strlen(arr[0] 1));//随机值 //arr[0]取首元素地址1为第二个元素的地址。从第二个元素地址开始向后数比arr差1 return 0; }#includestdio.h int main() { //字符串末尾自动放置\0 char arr[] abcdef; printf(%d\n, sizeof(arr));//7字节 // printf(%d\n, sizeof(arr 0));//4/8字节 //arr首元素地址0还是首元素地址 printf(%d\n, sizeof(*arr));//1字节 //arr首元素地址*arr是首元素 printf(%d\n, sizeof(arr[1]));//1字节第二个元素 printf(%d\n, sizeof(arr));//4/8字节 //arr是整个数组的地址是地址就是4/8字节 printf(%d\n, sizeof(arr 1));//4/8字节 //arr是数组地址1跳过整个数组指向数组后的地址 printf(%d\n, sizeof(arr[0] 1));//4/8字节 //arr[0]首元素地址1为第二个元素的地址 return 0; }#includestdio.h #includestring.h int main() { //字符串末尾自动放置\0 char arr[] abcdef; printf(%d\n, strlen(arr));//6字节 //arr首元素地址则从首元素开始数遇到\0停止 printf(%d\n, strlen(arr 0));//6字节 //arr首元素地址0还是首元素地址 //printf(%d\n, strlen(*arr));//err错误代码 //arr首元素地址*解引用为首元素aASCII为97越界访问 //printf(%d\n, strlen(arr[1]));//err错误代码 //第二个元素本身bASCII码为98越界访问 printf(%d\n, strlen(arr));//6字节 //arr是数组地址和首元素地址起始相同遇到\0停止 //arr数组地址类型为char (*)[7] //size_t strlen(const char* c1);类型不匹配 printf(%d\n, strlen(arr 1));//随机值 //arr数组地址1跳过整个数组向后访问为随机值 printf(%d\n, strlen(arr[0] 1));//5字节 //arr[0]首元素地址1第二个元素地址 return 0; }#includestdio.h int main() { //字符串末尾自动放置\0 //指针变量p指向常量字符串常量字符串不可修改加const更严谨 const char* p abcdef; printf(%d\n, sizeof(p));//4/8字节 //p指针变量存储首元素地址 printf(%d\n, sizeof(p 1));//4/8字节 //p1第二个元素地址 printf(%d\n, sizeof(*p));//1字节 //p首元素地址*解引用后为a本身 printf(%d\n, sizeof(p[0]));//1字节首元素本身 printf(%d\n, sizeof(p));//4/8字节 //p二级指针存储p指针变量地址 printf(%d\n, sizeof(p 1));//4/8字节 printf(%d\n, sizeof(p[0] 1));//4/8字节 //p[0] return 0; }#includestdio.h #includestring.h int main() { //字符串末尾自动放置\0 //指针变量p指向常量字符串常量字符串不可修改加const更严谨 const char* p abcdef; printf(%d\n, strlen(p));//6字节 //p存首字符地址遇到\0停止 printf(%d\n, strlen(p 1));//5字节 //p1第二个元素地址 //printf(%d\n, strlen(*p));//error错误代码 //*p第一个元素本身aASCII 97,会报错越界访问 //printf(%d\n, strlen(p[0]));//error //同上 printf(%d\n, strlen(p));//所求值和字符串没关系为随机值 //p指针变量为一级指针p为二级指针是p的地址strlen()从p指针变量起始位置向后数p变量存放地址不知道 printf(%d\n, strlen(p 1));//随机值和字符串没关系和p也没关系 // printf(%d\n, strlen(p[0] 1));//5字节 //p[0]是首字符a地址1第二个字符b地址 return 0; }2.3二维数组#includestdio.h //数组名的理解除了俩个例外其余情况下数组名表示数组首元素的地址那么是哪俩种例外呢 //1sizeof(数组名)数组名表示整个数组计算的是整个数组元素所占内存字节大小 //2,数组名数组名表示整个数组取的是整个数组的地址 int main() { int a[3][4] { 0 }; printf(%d\n, sizeof(a));//12*4 48字节 //a是二维数组数组名sizeof(数组名)表示整个二维数组的大小 printf(%d\n, sizeof(a[0][0]));//4字节 //a[0][0]第一行第一列元素本身 printf(%d\n, sizeof(a[0]));//4*4 16字节 //a[0]第一行的数组名数组名单独放在sizeof()内部计算的是第一行一维数组的总大小 printf(%d\n, sizeof(a[0] 1));//4/8字节 //a[0]是第一行一维数组的数组名表示一维数组首元素地址1表一维数组第二个元素地址 printf(%d\n, sizeof(*(a[0] 1)));//4字节 //a[0]第一行一维数组的数组名表一维数组首元素地址1为第二个元素地址*解引用为一维数组第二个元素大小为其类型int printf(%d\n, sizeof(a 1));//4/8字节 //a为二维数组数组名表示二维数组首元素地址1表二维数组第二个元素地址第二行一维数组int(*)[4] printf(%d\n, sizeof(*(a 1)));//4*4 16字节 //访问第二行一维数组*(a1) 等价于a[1],为第二行数组名 printf(%d\n, sizeof(a[0] 1));//4/8字节 //a[0]是二维数组首元素的地址第一行一维数组的数组地址 1是第二个元素的地址第二行一维数组的数组地址 printf(%d\n, sizeof(*(a[0] 1)));//4*4 16字节 //二维数组第二个元素的地址解引用后访问第二行一维数组 //a[0]等价于a,(a[0] 1)等价于(a 1) printf(%d\n, sizeof(*a));//4*4 16字节 //a数组名表示二维数组首元素地址解引用后访问首元素第一行一维数组整行 //*a等价于*(a0)等价于a[0] printf(%d\n, sizeof(a[3]));//4 * 4 16字节 //sizeof()不看内容只计算大小,虽然a[3]不存在但是通过类型的推断是可以算出字节大小 //a[3]是第四行数组名单独放在sizeof内部计算的是第四行的大小 return 0; }2.4指针#includestdio.h int main() { //结果是? int a[5] { 1, 2, 3, 4, 5 }; int* ptr (int*)(a 1); //a表数组地址类型为int(*)[5],1还是数组地址与int*类型不同故需要强制类型转换 //ptr指针变量存储的是跳过整个数组后的地址 printf(%d,%d, *(a 1), *(ptr - 1)); //a数组名表示数组首元素地址1为第二个元素的地址解引用后为第二个元素2 //ptr指针变量存储的是数组地址之后的地址类型为int*,-1回退4字节指向5的地址解引用后为5 return 0; }#includedstdio.h //在X86环境下 //假设结构体的⼤⼩是20个字节 //程序输出的结果是啥 struct Test { int Num; char* pcName; short sDate; char cha[2]; short sBa[4]; }*p (struct Test*)0x100000;//结构体指针变量存放地址 int main() { //结果是? printf(%p\n, p 0x1);// 00100014 //p1跳过一个结构体指针20字节 printf(%p\n, (unsigned long)p 0x1);//0x100000 0x1 00100001 //unsigned long无符号长整型p不是指针了则其内容就是整型了1就是1 printf(%p\n, (unsigned int*)p 0x1);//0x10000400100004 //unsigned int*无符号整型指针1跳过一个无符号整型类型4字节 return 0; }#includestdio.h int main() { //结果是? int a[3][2] { (0, 1), (2, 3), (4, 5) }; //(0, 1)为逗号表达式从左向右 依次计算最后一个表达式结果为整个表达式结果 //a[3][2] {1,3,5}为{1,3,5,0,0,0}; int* p; p a[0];//p存储二维数组首元素地址一维数组数组地址int(*)[2] printf(%d, p[0]);//1 //p[0]等价于*(p 0)等价于*(a[0] 0)等价于*(*(a 0) 0),即访问第一行第一列为1 return 0; }#includestdio.h int main() { ////假设环境是x86环境程序输出的结果是啥 int a[5][5]; int(*p)[4];//p指针变量为数组指针,p类型是int(*)[4] p a;//a数组名表示首元素地址第一行一维数组的数组地址,a类型为int (*)[5] //上行代码会有警告因为有类型差异 printf(%p,%d\n, p[4][2] - a[4][2], p[4][2] - a[4][2]);//-4地址形式FFFFFFFC-4整型形式(-4) //指针-指针的绝对值得到指针之间的元素个数小地址 - 大地址为负数 //-4补码为FFFFFFFC return 0; }图2.1p[4][2]与a[4][2]#includestdio.h int main() { ////假设环境是x86环境程序输出的结果是啥 int aa[2][5] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int* ptr1 (int*)(aa 1);//数组指针类型强制类型转换为整型指针 //arr取整个二维数组的数组地址1跳过整个二维数组指向二维数组后的地址 int* ptr2 (int*)(*(aa 1));//(int*)可以不带,类型都相同没必要强制类型转换 //aa二维数组名表示二维数组首元素地址第一行一维数组数组地址,int(*)[5],1表示二维数组第二个元素地址第二行一维数组的数组地址 //解引用之后为第二行一维数组的数组名表示一维数组的首元素地址即6的地址 printf(%d,%d, *(ptr1 - 1), *(ptr2 - 1));//105 return 0; }#includestdio.h int main() { ////假设环境是x86环境程序输出的结果是啥 char* a[] { work,at,alibaba };//字符指针数组 char** pa a;//a数组名表示数组首元素地址 pa;//到第二个数组元素at的地址 printf(%s\n, *pa);//at return 0; }图2.2字符指针数组#includestdio.h int main() { ////假设环境是x86环境程序输出的结果是啥 char* c[] { ENTER,NEW,POINT,FIRST }; char** cp[] { c 3,c 2,c 1,c }; char*** cpp cp;//数组名表首元素地址 printf(%s\n, **cpp);//POINT //cpp原指向cp数组首元素c3的地址,自增后指向c2的地址,对其解引用得到c2,再解引用得到c2中的值首字符地址 printf(%s\n, *-- * cpp 3);//ER //cpp指向c2的地址,自增指向c1的地址,解引用得到c1,自减指向c对c解引用得到c中的值首字符地址3得到之后的er printf(%s\n, *cpp[-2] 3);//ST //cpp[-2]等价于*(cpp-2),cpp指向c1的地址-2得到c3的地址解引用得到c3,再对c3解引用得到c3中的值首字符地址3得到之后的ST printf(%s\n, cpp[-1][-1] 1);//EW //cpp指向c1的地址cpp[-1]等价于*(cpp - 1).-1指向c2的地址解引用得到c2 // 这里要注意的是*(cpp - 1)中cpp-1是一个表达式cpp的值不会因此改变这很重要 //(c2)[-1]等价于*((c2) - 1),(c2) -1得到c1对c1解引用得到c1的值首元素地址1得到之后的EW return 0; }写到这里指针系列就结束了。如果你有不懂的地方可以留言我会第一时间解答。