C相关的一些题目(易混淆)
  j12w6g2rqsge 2023年11月22日 28 0

一维数组

int main(){
    char arr[]="abcdef";
    // arr[]存放的是abcdef\0 7个字符 每个字符为1

    printf("%d\n",sizeof (arr));
    //7  计算数组的大小7
    printf("%d\n",sizeof (arr+ 0));
    // 8  计算地址大小 4或8 跟具体是64位机器还是32位机器有关
    printf("%d\n",sizeof (*arr));
    //1   char类型的数组的 步长 解引用只取一个字节的大小 这里如果输出*arr 则应该是a 即首元素地址
    printf("%c\n",*arr);
    printf("%d\n",sizeof (arr[1]));
    // 1
    printf("%d\n",sizeof (&arr));
    //8  数组的地址
    printf("%d\n",sizeof (&arr+1));
    //8  数组地址的下一个地址(跳过整个数组后的地址)
    printf("%d\n",sizeof (&arr[0]+1));
    //8   第二个元素b的地址
    //以上这三个最后算出来的都是一个地址
    printf("%d\n",&arr[0]+1);
    return  0;

}


int main(){
    char arr[]="abcdef";


    printf("%d\n",strlen(arr));
    //6
    printf("%d\n",strlen(arr+0));
    //6 同上 效果一样的

    printf("%d\n",strlen(*arr)); 
    //报错这里只有一个字符a 并不是一个数组 没办法计算

    printf("%d\n",strlen(arr[1]));
    //报错 同上 传入一个b字符

    printf("%d\n",strlen(&arr));
    //6 &arr 数组的地址 从第一个元素开始数 数到6个字符
    printf("%d\n",strlen(&arr+1));
    //0下个数组的地址 随机
    printf("%d\n",strlen(&arr[0]+1));
    //5 元素b的地址从b的地址往后数 能数五个字符 那长度就是5


}
int main(){
    char *p="abcdef";//p存放的事a的地址

    printf("%d\n",sizeof (p));//8
    // 这里是个地址 64位系统 地址是8个字节 其实就是计算的指针变量p的大小

    printf("%d\n",sizeof (p+1));//8
    // p+1就是字符b的地址 本质还是个地址 所以还是8个字节

    printf("%d\n",sizeof (*p));//1
    // *p其实就是字符串的第一个字符a 那就是字符类型所占大小为一个字节所以是1

    printf("%d\n",sizeof (p[0]));//1
    // int arr[10]; arr[0]==*(arr+0)  实际是字符a这个变量所占空间大小 字符类型占1个字节
    //p[0]==*(p+0)

    printf("%d\n",sizeof (&p));//8
    //实际是p的地址 是地址所占字节数还是4/8

    printf("%d\n",sizeof (&p+1));//8
    //跳过p的下一个地址 但还是个地址 所以还是4/8

    printf("%d\n",sizeof (&p[0]+1));//8
    //&p[0]是a的地址 在+1 其实就是字符b的地址所以还是地址还是4/8




}


int main(){
    char * p="abcdef";//p 代表a的地址
    
    printf("%d\n", strlen(p));//6 
    //从a开始数直到数完这个字符串 6个字符所以是6
    
    printf("%d\n", strlen(p+1));//5  
    //从b开始数直到数完这个字符串 5个字符 所以是5
    
    printf("%d\n", strlen(*p));//报错
    //*p就是a 不是个字符穿 所以汇报错
    printf("%d\n", strlen(p[0]));//报错
    //同上

    printf("%d\n", strlen(&p));//未知
    //p代表a的地址 这里拿到的是p的地址 随机值
    
    printf("%d\n", strlen(&p+1));//未知
    // &p+1 拿到的事p的下一个地址  随机值
    
    printf("%d\n", strlen(&p[0]+1));//5
    //p[0]是a 取地址p[0]就是拿到a的地址  再+1 其实就是b的地址
    // 传入b的地址 往后数5个字符所以长度为5 同strlen(p+1)
    
}

二维数组

int main(){
    int a[3][4]={ 0 };
    printf("%d\n",sizeof (a));//3X4X4=48  单独放一个数组名计算数组的大小

    printf("%d\n",sizeof (a[0][0]));//4

    printf("%d\n",sizeof (a[0]));//16
    // a[0]其实是一个第一行4个元素的一维数组的数组名
    //数组名单独放在sizeof()内计算第一行的大小 所以大小是4X4=16

    printf("%d\n",sizeof (a[0]+1));//8
    // 数组名表示首元素地址+1 其实就是第二个元素的地址
    //注意不是第二行的地址 而是第一行的第二个元素的地址

    printf("%d\n",sizeof (*(a[0]+1)));//4
    // 第一行第二个元素 int类型 4个字节

    printf("%d\n",sizeof (a+1));//8
    // a 是二维数组的数组名,没有sizeof(数组名)
    // 也没有&(数组名) 所以a是首元素地址
    // 而二维数组堪称一维数组时 二维数组首元素就是第一行
    // 那么a+1 就是第二行这个一位数组的地址
    printf("%d\n",sizeof (*(a+1)));//16
    // 第二行的地址 本质上是个数组
    // 所以计算这个数组的大小其实就是4X4=16

    printf("%d\n",sizeof (&a[0]+1));//8
    //这里毫无疑问是个地址 大小肯定是4/8 但是具体是谁的地址呢?
    //第二行的地址 &a[0]取出第一行数组的地址  +1就是第二行的地址

    printf("%d\n",sizeof (*(&a[0]+1)));//16
    //第二行数组的地址解引用其实就是算第二行这个一维数组的大小 其实就是4X4=16

    printf("%d\n",sizeof (*a));//16
    //a是首元素地址 其实就是第一行地址
    //*a就是第一行数组 大小为4X4=16

    printf("%d\n",sizeof (a[3]));//16
    //sizeof 并不会真的去访问a[3]
    //所以 a[3]在sizeof中其实是等价于a[0] 、a[1]、a[2]


}

指针


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;
    //0x1 就是数字1  等价于+1
    
    printf("%p\n",p+0x1);//0x100020 就是加20个字节 
    //转成16进制为0x100014但是在某些电脑上是0x100020
    printf("%p\n",(unsigned long )p+0x1);//0x100001
    //强转p为整数 然后加1 就是0x100001
    printf("%p\n",(unsigned int* )p+0x1);//0x100004
    //强转p为无符号整型  +1其实就是跳过一个无符号整型 
    //其实就是四个字节 那也就是+4
    //0x100004
    return 0;

}

指针+ - 整数,取决于指针类型。


int main(){

    int a[4]= {1,2,3,4};
    int *ptr1=(int *)(&a+1);//
    int *ptr2=(int *)((int) a+1);
    //地址转换int类型 再加1其实就是地址只加了1  
    //如果地址是0x00000011 那么转换成int 再加1
    //就是17 +1 =18  再转换为地址类型 16进制0x00000012  其实就是偏移了一个字节
    //把数组表示出来
    //01000000    02000000   03000000   04000000
    //偏移一个字节其实就是00
    printf("%x,%x",ptr1[-1],*ptr2);// 32位操作系统输出结果4,2000000
    //64位操作系统*ptr2会报错


    return 0;

}

int main(){
    int a[3][2]={(0,1),(2,3),(4,5)};
    //1 3 a[0] 代表首元素地址 就是1的地址
    //5 0 a[1]
    //0 0 a[2]
    //这里主要需要注意这种类型的二维数组初始化 圆括号里面放的是个逗号表达式,逗号表达式的结果其实是最后一个数
    //所以实际上只存了1  3  5 并且 1 3 5 是按照顺序依次放入的
    int *p;
    p=a[0];
    printf("%d",p[0]);//1
    //p[0] 等价于*(p+0) p是1的地址 +0还是1的地址所以结果是1
    return 0;
}

稍微难一点的

//困难***
int main()
{
    int a[5][5];
    int (*p)[4];//*p是个int类型指针
    // 后面跟了[4]代表它是个指向了含有4个整型元素的数组的指针 也就是数组指针
    p = a;//a是数组名也就是首元素地址 也就是前第一行数组的地址
    //这里赋值 两个数组的角标是不一样的 会有警告 a的值强行赋值给了p
    printf("%p,%d\n",&p[4][2]-&a[4][2],&p[4][2]-&a[4][2]);//0xfffffffc,4
    //p[4][2]等价于 *(*(p+4)+2)
    return 0;

}

C相关的一些题目(易混淆)_数组名

这是a[5][5]这个二维数组

我们上面分析过了*p是个数组指针,指向了一个数组 数组里有4个整型元素。

强行将a赋值给p后其实是数组a[0]的地址赋值给p其实就是

C相关的一些题目(易混淆)_指针_02

根据上面代码注释中的分析 p[4][2] 等价于*(*(p+4)+2)

但是我们要清楚的是 p是个指向整型数组的指针 p+1 就是跳过四个元素 

所以

p+4的位置如下

C相关的一些题目(易混淆)_指针_03

我们能够拿到如上图所示的红色方框四个方框其实是一个数组

(p+4)其实就是整型了那么这里再让(p+4)+2 再对其解引用 就是往后偏移两个格子

最后我们找到了p[4][2] ,a[4][2]很简单便可以得到

C相关的一些题目(易混淆)_数组名_04

地址-地址 其实就是看两个地址之前有多少个元素 也就是求其中的元素个数很明显有4个元素

p[4][2] 在前 a[4][2]在后   所以应该是-4

%d 就正常打印-4即可

但是%p要注意 地址在存储时候 它不关心有无符号 ,我们按照计算机组成原理中的存储原理

-4的原码10000000 00000000 0000000 00000100

反码(符号位不变其他位置按位取反)

11111111 11111111 11111111 11111011

补码(反码+1)

11111111 11111111 11111111 11111100

%p打印的最后就是补码

其实就是 0xfffffffc

最终的结果为 0xfffffffc,-4

(注:如果是64位的操作系统 结果为0xfffffffffffffffc,-4 多了前面八个f)


int main(){
    int  aa[2][5]={1,2,3,4,5,6,7,8,9,10};
    int *ptr1=(int*)(&aa+1);//ptr1是跳过整个数组的下一个数组首地址
    int *ptr2=(int*)(*(aa+1));//aa+1指向第二行的首元素地址 解引用其实是 元素6 在转为int类型指针 实际上
    //ptr2就是元素6的地址 ptr2-1往前移动一格 也就是元素5的地址 再解引用就是5。
    printf("%d,%d",*(ptr1-1),*(ptr2-1));//10 5
    //ptr1-1 往前移动一格 在解引用就是10

}

把这个二维数组画出来

C相关的一些题目(易混淆)_指针_05

找到ptr1的位置 其实就是aa+1  


C相关的一些题目(易混淆)_指针_06

aa+1指向第二行的首元素地址 解引用其实是 元素6 在转为int类型指针 实际上

 ptr2就是元素6的地址 ptr2-1往前移动一格 也就是元素5的地址 再解引用就是5。

C相关的一些题目(易混淆)_数组_07

最后答案位10,5



int main(){
    char *a[]={"work","at","alibaba"};
    //char *p = "abcdef"; 这里指的是把a的地址放在p中
    // a是个字符指针数组,里面存的都是字符类型的指针
    //a是数组名表示首元素地址  第一个元素类型位char* 首元素地址就应该是 char**
    //pa++ 指的是跳过一个char*类型 则此时pa指向at的a的地址
    char **pa = a;
    pa++;
    printf("%s\n",*pa);
    //打印出来其实应该是 a开头的一个字符串 "at"
    return 0;

}


最难的一道题

int main(){

    char* c[]={"ENTER","NEW","POINT","FIRST"};//c是数组名 是个指针数组 里面存的都是char类型的指针
    char* *cp[]={c+3,c+2,c+1,c}; //C是个char*类型 地址就应该是 char**
    //cp同样是个指针数组 里面存放的都是 指向char*类型地址的指针
    char* **cpp=cp;
    printf("%s\n",**++cpp);//POINT
    printf("%s\n",*--*++cpp+3);// ER
    printf("%s\n",*cpp[-2]+3);// ST
    printf("%s\n",cpp[-1][-1]+1);//EW
    return 0;
}

把上面printf前的三行代码用图画出来如下

C相关的一些题目(易混淆)_数组名_08

C相关的一些题目(易混淆)_数组名_09

    printf("%s\n",**++cpp);//POINT

cpp指向C+3的地址 ++cpp指向 C+2的地址

解引用一次 拿到POINT的地址 再解引用一次得到字符串“POINT”

那么此时cpp指向的是C+2

C相关的一些题目(易混淆)_字符串_10

printf("%s\n",*--*++cpp+3);// ER

上面算过之后 cpp已经指向C+2的地址 ++cpp 就是指向C+1的地址 

解引用一次拿到C+1 也就是NEW的地址

再-- 就是指向NEW的上一个字符串地址也就是ENTER的地址

再解引用一次得到字符串ENTER 然后再加三 注意此时已经是字符串首字母E的地址了 +3指的是 往后数3个char类型的空间 也就是到字母E 的地址 然后输出就是ER 后面\0停止 


C相关的一些题目(易混淆)_数组名_11

    printf("%s\n",*cpp[-2]+3);// ST

cpp[-2]等价于 (cpp-2) 所以原式子cpp[-2]+3等价于 *(*(cpp-2))+3

注意 这个式子cpp并没有发生变化 上面两个cpp都有自增这里并cpp[-2]并没有改变cpp指向的地址 为了方便理解才写成*(*(cpp-2))+3

根据上面cpp指向C+1的地址 可知 cpp-2指向C+3的地址如上图

解引用一次得到C+3 也就是FIRST的地址

然后再解引用一次得到字符串FIRST首字母F的地址 再加3即往后数三个字符就得到ST

C相关的一些题目(易混淆)_数组名_12

printf("%s\n",cpp[-1][-1]+1);//EW

cpp[-1][-1]+1等价于* (*(cpp-1)-1)+1

根据上面cpp指向C+1的地址 可知 cpp-1如上图指向C+2的地址

解引用一次 得到C+2也就是POINT的地址 -1 则往上移动指针指向NEW的地址

再解引用一次得到字符串NEW 然后+1 往后移动一个字符得到EW

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月22日 0

暂无评论

推荐阅读
j12w6g2rqsge