首页 > 其他分享 >C相关的一些题目(易混淆)

C相关的一些题目(易混淆)

时间:2023-11-21 21:32:43浏览次数:41  
标签:混淆 题目 int 地址 数组 printf cpp 相关 sizeof

一维数组

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

标签:混淆,题目,int,地址,数组,printf,cpp,相关,sizeof
From: https://blog.51cto.com/u_16160587/8506560

相关文章

  • 斯特林数相关式子的证明
    具体数学221页给了很多斯特林恒等式,但是没有给出证明,现在我们来证明一下。前置知识斯特林数的递推公式\[{n\bracek}={n-1\bracek-1}+k{n-1\bracek}\]\[{n\brackk}={n-1\brackk-1}+(n-1){n-1\brackk}\]斯特林数的生成函数:\[\sum_{i\ge0}{n\bracei}x^i=(\sum_{k\ge......
  • 密码传输保存相关
    MD5(MessageDigestAlgorithm5)是一种常见的哈希函数,它将输入数据转换成固定长度的哈希值(通常是128位的16进制数),并且不可逆。MD5以不可预测的方式将任意长度的数据映射到固定长度的输出。尽管MD5在过去被广泛用于数据完整性验证和加密存储密码等领域,但由于其安全性漏洞已被发现,不......
  • 关于代码混淆,看这篇就够了
    ​代码混淆一.基本概念java的bytecode很容易通过JAD等反编译工具还原出源代码。这样势必不满足安全的定义。如何一定程度上保护需要防止被反编译的源代码呢?混淆(obfuscate)技术。注意:用obfuscate防盗版是根本不可能,连汇编这种东西都能被**掉,而java代码基本上等同于开源的同义词。用......
  • mysql 安全相关
    密码复杂度安装validate_password插件,开启密码校验相关配置1.检查是否已安装该插件SELECTPLUGIN_NAME,PLUGIN_STATUSFROMINFORMATION_SCHEMA.PLUGINSWHEREPLUGIN_NAME='validate_password';2.安装插件installpluginvalidate_passwordSONAME'validate_password.d......
  • Chain-Of-Note:解决噪声数据、不相关文档和域外场景来改进RAG的表现
    CoN要点CoN框架由三种不同的类型组成,研究称之为阅读笔记。上面的图像,类型(A)显示了检索到的数据或文档回答查询的位置。LLM仅使用NLG从提供的数据中格式化答案。 https://avoid.overfit.cn/post/1a108bbaf6c84b5fbc51554fefa222cd......
  • 11.9 实现磁盘相关操作
    11.9.1遍历磁盘容量如下代码实现了在Windows系统中获取所有磁盘驱动器的信息。具体包括两个函数,一个用于获取驱动器类型,另一个用于获取驱动器空间信息。主函数则调用这两个函数来遍历所有逻辑驱动器并输出相应的信息。在输出驱动器空间信息时,会输出该驱动器的总大小、已用空间以......
  • 题目集4-6及期中考试
    题目集4-6及期中考试21207218-SZY前言:    显而易见,这三次的题目集呈现出了与以往不同的难度,题目4有四道题,而题目5和6都只有一道题,而且完成时间变成了两个星期。题目4主要难度在于是菜单计价程序3,其是在菜单2的基础上增加了更多的细节,而后面题目5,6都是在菜单3上增加了更......
  • PTA题目集4、5、6以及期中考试的总结
    一、前言在过去做完的PTA题目集4、5、6以及期中考试,相比前几次的题目集来说难度都相对提高了许多,对于基础相对比较薄弱的我做起来也比较吃力,但是题量比之前都少了很多,后两次题目集都只有菜单计价程序一题,最主要的也还是菜单计价程序这一类题目,代码量很大。这类题目对于类的考察......
  • 南昌航空大学JAVA Blog-2题目4-6期中考试
    一.前言 在进行题目集4-6的练习时,老师课堂上讲的内容一般都会围绕在这次题目集需要用到的新内容上。对于题目集代码的完成有很大的帮助,如课堂讲的封装、继承、多态等内容,简化了代码修改的难度,正则表达式则在一定程度上减少了代码量。但是就我个人认为,这几次的题目集除了期中考试......
  • 题目集4-6以及期中的总结
    前言:目前完成了一些关于Java的编程练习题,并且在逐渐掌握Java语言的基本知识和编程技巧。每个题目都涉及不同的知识点和概念,通过挑战自己解决问题,本人有效地学习和应用这些知识。对于复杂的题目集,确实需要更多的时间和精力来理解和解决。尽管如此,坚持练习,并解决各种难题会帮助提高......