C语言指针重点总结(5)

1. sizeof和strlen的对比

1.1 sizeof

sizeof不是函数,而是一个操作符,sizeof 计算变量所占内存内存空间大小的,单位是字节,如果操作数是类型的话,计算的是使用类型创建的变量所占内存空间的大小。 sizeof 只关注占⽤内存空间的大小,不在乎内存中存放什么数据。

sizeof()的操作数可以是一个变量也可以是一个数据类型,需要注意的是当操作数是变量时()可以省去,当操作数是数据类型时()不可以省去。

#inculde <stdio.h>
int main()
{
     int a = 10;
     printf("%d\n", sizeof(a));
     printf("%d\n", sizeof a);
     printf("%d\n", sizeof(int));
 
     return 0;
}

1.2 strlen

1.2.1 strlen函数简介

strlen 是C语⾔库函数,使用时要包含头文件string.h,功能是求字符串长度。函数原型如下: 

size_t strlen ( const char * str );  返回值size_t类型实质上就是无符号整型,打印时的形式的zd%,参数是一个字符指针,const修饰的是*str,使得不能通过指针改变原字符串的内容。本质是从该字符指针指向的位置/字符开始计数,直到遇到\0(\0的ASCII码是0),停止计数,返回字符不是\0的个数,strlen要遇到\0才停止计算,故strlen可能出现越界查找的问题。

1.2.2 strlen函数的模拟实现

size_t my_strlen(const char* p)
{
    int count = 0;
    while(*p) //*p != '\0'
    {
       count++;
       p++;
    }
    return count;
}

1.2.3 strlen函数使用示例

#include <stdio.h>
int main()
{
     char arr1[3] = {'a', 'b', 'c'};
     char arr2[] = "abc";
     printf("%zd\n", strlen(arr1));//随机数
     printf("%zd\n", strlen(arr2));//3
 
     printf("%zd\n", sizeof(arr1));//3
     printf("%zd\n", sizeof(arr1));//4
     return 0;
}

1.3 sizeof 和 strlen的对比

2. 数组和指针笔试题解析

数组名的意义

1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。

2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。

3. 除此之外所有的数组名都表示首元素的地址。

2.1 ⼀维数组 

    int a[] = {1,2,3,4};
    printf("%d\n",sizeof(a));      //4*4=16 szieof(数组名)特殊用法表示求整个数组的大小
    printf("%d\n",sizeof(a+0));    //4/8 数组名a表示首元素地址,即元素1的地址
    printf("%d\n",sizeof(*a));     //4 *a == *(a+0) == a[0] == 1
    printf("%d\n",sizeof(a+1));    //4/8 数组名a表示首元素地址,a+1是a[1]的地址
    printf("%d\n",sizeof(a[1]));   //4 a[1] == 2
    printf("%d\n",sizeof(&a));     //4/8 &数组名,表示整个数组的地址
    printf("%d\n",sizeof(*&a));    //16 1.*和&可以相互抵消,sizeof(*&a) == sizeof(a)
                                   //2.&数组名,表示整个数组的地址,解引用后就是整个数组
    printf("%d\n",sizeof(&a+1));   //4/8 &数组名,表示整个数组的地址,&a+1跳过整个数组        
    printf("%d\n",sizeof(&a[0]));  //4/8 数组第一个元素的地址
    printf("%d\n",sizeof(&a[0]+1));//4/8 数组第二个元素的地址
    
    

注意点:
地址是4/8个字节的原因,地址长度由当前系统决定,当编译器选择32位机(x86),数据的地址有32位,32/8(一个字节的位数)=4(Byte)。当编译器选择64位机(x64),数据的地址有64位,64/8(一个字节的位数)=8(Byte)

注意不要混淆

32位机的%p打印出来地址是0000 00ff:%p是以16进制打印地址的,32/4(二进制转十六进制1111=f)=8位,64位机的%p打印出来地址是0000 0000 0000 00ff:64/4(二进制转十六进制1111=f)=16位

2.2 字符数组

代码1

char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));           //6 sizeof(数组名),计算整个数组的大小
printf("%d\n", sizeof(arr+0));         //4/8 数组名arr表首元素地址,arr+0还是首元素的地址
printf("%d\n", sizeof(*arr));          //1 数组名arr表首元素地址,*arr == 'a'
printf("%d\n", sizeof(arr[1]));        //1 arr[1] == 'b'
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表示&arr[1]即符号'b'的地址

代码2

    char arr[] = {'a','b','c','d','e','f'};
    printf("%d\n", strlen(arr));       //随机值 数组名arr表示首元素地址,直到遇到\0停止计数
    printf("%d\n", strlen(arr+0));     //随机值 数组名arr表示首元素地址,arr+0还是首元素地址
    printf("%d\n", strlen(*arr));      //err *arr == 'a','a'的ASCII码是97,取的地址是97
    printf("%d\n", strlen(arr[1]));    //err arr[1] == 'b','b'的ASCII码是98,取的地址是98
    printf("%d\n", strlen(&arr));      //随机值 strlen函数的参数类型是char*,而&arr表示整个数 
                                       //组的地址,指针类型是char(*)[6],是一个数组的指针,所以  
                                       //会报警告但不会报err,结果与strlen(arr)的结果保持一致
    printf("%d\n", strlen(&arr+1));    //随机值-6 &arr+1跳过整个数组的大小
    printf("%d\n", strlen(&arr[0]+1)); //随机值-1 &arr[0]+1表示'b'的地址

 代码3

    char arr[] = "abcdef"; //等价于char arr[] = {'a','b','c','d','e','f','\0'};
    printf("%d\n", sizeof(arr));      //7 sizeof(数组名)特殊用法求整个数组的大小
    printf("%d\n", sizeof(arr+0));    //4/8 数组名arr表示首元素地址,arr+0仍表示首元素地址
    printf("%d\n", sizeof(*arr));     //1 *arr == 'a'
    printf("%d\n", sizeof(arr[1]));   //1 arr[1] == 'b'
    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 == &arr[1],字符'b'的地址

 代码4 

    char arr[] = "abcdef";
    printf("%d\n", strlen(arr));     //6 数组名arr表示首元素的地址
    printf("%d\n", strlen(arr+0));   //6 arr+0仍表示首元素的地址
    printf("%d\n", strlen(*arr));    //err *arr == 'a' ASCII码表示97
    printf("%d\n", strlen(arr[1]));  //err arr[1] == 'b' ASCII码表示98  
    printf("%d\n", strlen(&arr));    //6 &arr表示整个数组的地址
    printf("%d\n", strlen(&arr+1));  //随机值 &arr+1跳过整个数组
    printf("%d\n", strlen(&arr[0]+1));//5  &arr[0]+1 ==&arr[1]

 

 代码5

    char *p = "abcdef";         
    printf("%d\n", sizeof(p));     //4/8 p是指向字符串第一个字符a的指针
    printf("%d\n", sizeof(p+1));   //4/8 p+1指向字符串第二个字符b的指针
    printf("%d\n", sizeof(*p));    //1 *p == 'a',表示一个字符
    printf("%d\n", sizeof(p[0]));  //1 p[0] == *(p+0) == 'a'
    printf("%d\n", sizeof(&p));    //4/8 &p表示指针的地址,是二级指针,类型是char* *:第二个*表 
                                   //示是一个指针,第二个*表示该指针指向对象的类型是char*,即+-
                                   //该指针时跳过的是一个char*类型(地址,4/8个字节) 
    printf("%d\n", sizeof(&p+1));  //4/8
    printf("%d\n", sizeof(&p[0]+1));//4/8 &p[0]+1 == &p[1]即字符'b'的地址

跳过一个char*变量,char*是一个指向char类型的指针/地址,大小是4/8个字节,则跳过一个char*变量就是跳过4/8个字节。

 代码6

    char *p = "abcdef";
    printf("%d\n", strlen(p));       //6 指针p指向字符'a'
    printf("%d\n", strlen(p+1));     //5 指针p+1指向字符'b'
    printf("%d\n", strlen(*p));      //err *p == 'a',ASCII码值是97
    printf("%d\n", strlen(p[0]));    //err p[0] == *p == 'a',ASCII码值是97
    printf("%d\n", strlen(&p));      //随机值 指针p的地址,二级指针
    printf("%d\n", strlen(&p+1));    //随机值 &p+1跳过一个char*类型
    printf("%d\n", strlen(&p[0]+1)); //5 &p[0]+1 == &p[1]即字符'b'的地址

最后一个strlen(&p[0]+1) ,指针p优先与[]结合,p[0] == *p == 'a' ,再与&结合,表示第一个元素的地址,+1表示第二个元素的地址,下面详见操作符的优先级。


 

2.3 ⼆维数组

    int a[3][4] = {0};
    printf("%d\n",sizeof(a));        //3*4*4=48 sizeof(数组名),计算整个二维数组的有多少个字节
    printf("%d\n",sizeof(a[0][0]));  //4 a[0][0]表示整个数组的第一个元素
    printf("%d\n",sizeof(a[0]));     //4*4=16 a[0]表示二维数组第1行的数组名,sizeof(数组名)特 
                                     //殊用法,表达整个数组的大小
    printf("%d\n",sizeof(a[0]+1));   //4/8 数组名a[0]表示数组首元素的地址,即&a[0][0],+1是
                                     //a[0][1]的地址即&a[0][1]
    printf("%d\n",sizeof(*(a[0]+1)));//4 等价于*&a[0][1] == a[0][1] == 0
    printf("%d\n",sizeof(a+1));      //4/8 二维数组数组名a表示首元素地址,二维数组的首元素是第 
                                     //一行,故a+1 == &a[0]+1,表示第二行的地址即&a[1]
    printf("%d\n",sizeof(*(a+1)));   //4*4=16 等价于*&a[1] == a[1],求第二行的总字节数 
    printf("%d\n",sizeof(&a[0]+1));  //4/8 表示第二行的地址即&a[1]
    printf("%d\n",sizeof(*(&a[0]+1)));//4*4=16 等价于*&a[1] == a[1],求第二行的总字节数
    printf("%d\n",sizeof(*a));        //4*4=16 二维数组数组名a表示首元素地址,二维数组的首元素
                                      //是第一行,故*a == *&a[0] == a[0],求第一行的总字节数
    printf("%d\n",sizeof(a[3]));      //4*4=16 不会报错,sizeof()只是求()里的变量类型有多少个
                                      //字节,本质相当于求sizeof(a[0]),即求一行的字节个数

3. 指针运算笔试题解析

3.1 题目1

#include <stdio.h>
int main()
{
 int a[5] = { 1, 2, 3, 4, 5 };
 int *ptr = (int *)(&a + 1);
 printf( "%d,%d", *(a + 1), *(ptr - 1));
 return 0;
}
//程序的结果是什么?2 5

3.2 题目2

//在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);
    printf("%p\n", (unsigned long)p + 0x1);//注意这里指针p强转为了为符号长整型,相当于转换成了 
                                           //地址(相当于0000 00ff),+1就是普通的整数+1
    printf("%p\n", (unsigned int*)p + 0x1);
    return 0;
}

假设的原因是x64环境下结构体的大小就不是20个字节了 

3.3 题目3

#include <stdio.h>
int main()
{
    int a[3][2] = { (0, 1), (2, 3), (4, 5) };
    int *p;
    p = a[0];
    printf( "%d", p[0]);  //1
    return 0;
}

注意点:逗号表达式,从左向右依次执⾏。整个表达式的结果是最后⼀个表达式的结果。 

3.4 题目4

//假设环境是x86环境,程序输出的结果是啥?
#include <stdio.h>
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]);
    return 0;
} //答案:FFFF FFFC   4

 限制环境是因为想要一个唯一的答案,32位和64位的地址长度不同。

3.5 题目5

#include <stdio.h>
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 5
    return 0;
}
详解:int *ptr2 = (int *)(*(aa + 1));
二维数组数组名aa表首元素的地址,二维数组的首元素是第一行,则aa是第一行的地址,aa+1是第二行的地址及&aa[1],*(aa+1) == *&aa[1] == aa[1](第二行的数组名),数组名aa[1]表示首元素aa[1][0]的地址.*(aa + 1)本身就是int*类型,这里的强转如同虚设。

3.6 题目6

#include <stdio.h>
int main()
{
    char *a[] = {"work","at","alibaba"};//[]的优先级比*高,故a先与[]结合,a是一个指针数组,指 
                                        //针类型是char*
    char**pa = a;                       //两级指针,数组名a表示首元素的地址,pa指向a[0]
    pa++;                               //pa指向a[1]
    printf("%s\n", *pa);                //*pa == a[1] == 指向字符'a'的char*指针
    return 0;
}
//答案:at

3.7 题目7

 

#include <stdio.h>
int main()
{
    char *c[] = {"ENTER","NEW","POINT","FIRST"};
    char**cp[] = {c+3,c+2,c+1,c};
    char***cpp = cp;
    printf("%s\n", **++cpp);     
    printf("%s\n", *--*++cpp + 3);
    printf("%s\n", *cpp[-2] + 3);
    printf("%s\n", cpp[-1][-1] + 1);
    return 0;
}
//POINT
//ER
//ST
//EW

优先级:++ > * >+

 

  • 34
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值