程序(来源 ):

#include <stdio.h>int main(void) {int x[4];printf("%p\n", (void*) (x));printf("%p\n", (void*) (x + 1));printf("%p\n", (void*) (&x));printf("%p\n", (void*) (&x + 1));
}

假设x的地址为n,那么输出为:

n
n+4
n
n+16

window下使用gcc编译输出结果:

0x22cd44
0x22cd48
0x22cd44
0x22cd54

前三个还比较好理解,最后一行中&x实际表示是一个类型为int (*)[4]类型的指针,所以&x+1后地址增加16。

有一个和上面类似的程序(源自《C语言深度剖析》,可以在网上搜索下载到):

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

此程序输出结果为2,5

类似的还有个程序:

#include <stdio.h>int main(void)
{int a[] = {1,2,3,4,5,6,7,8,9,10};int (*p1)[3] = &a;int (*p2)[4] = a;printf("%d %d\n",*(*(p1+1)+1),*(*(p2+2)+1));return 0;
}

该程序编译时会提示警告(p1和p2初始化采用不兼容的指针类型)。

这里,p1和p2都是数组指针。p1指向有三个元素的整型数组,p2指向有四个元素的整形数组。

数组指针p1类似于一个二维数组b[][3],而根据二维数组b[i][j]可以表示成*(*(b+i)+j)的形式。所以*(*(p1+1)+1)相当于二维数组b[][3]中的b[1][1],因而对应于a[4],所以输出结果为5,类似的可以得出另一个指针的输出结果。

所以整个输出结果为5,10

同样,还有个程序:

#include <stdio.h>
int main(void)
{int a[4] = {1,2,3,4};int *ptr1 = (int*)(&a+1);int *ptr2 = (int *)((int)a+1);printf("%x,%x\n",ptr1[-1],*ptr2);return 0;
}

ptr1[-1]的值(跟上面的例子的情况类似)为4

而*ptr2的值则根据处理器的不同而可能有不同的结果(参见大端模式和小端模式 endianness )

如果为小端模式(例如intel x86兼容处理器,8051,avr等),那么*ptr2(注意使用题目中使用了%x输出格式)输出结果为2000000

如果为大端模式(例如motorola 68k,powerpc,IBM sys/360等),那么*ptr2输出结果为100

判断处理器使用什么模式,可以使用下面函数进行检测(相关链接 ):

/*little endian when return 1,else  big endian*/
int CheckEndian()
{union{int i;char c;}e;e.i = 1;return (e.c == 1);
}

如果使用大端模式,那么数组a在内存中表示(十六进制)为 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 04

ptr2指向第二个00,所以为00 00 01 00(即0x100)

如果是小端模式,那么数组a在内存中表示为01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00,ptr2指向第一个00,所以其指向内容为00 00 00 02,由于使用了小端模式,所以需要颠倒过来表示即02000000(也即0x2000000).

接着是一些二维数组和二级指针的一些例子(同样源自《c语言深度剖析》):

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

编译后运行,结果为2,因为圆括号内的使用了逗号表达式,二维数组a的初始化相当于int a[3][2]={2,4,6};

#include <stdio.h>
int main(void)
{int a[5][5];int (*p)[4];p = a;printf("a_ptr=%#p,p_ptr=%#p\n",&a[4][2],&p[4,2]);printf("%p,%d\n",&p[4][2]-&a[4][2],&p[4][2]-&a[4][2]);return 0;
}

编译后运行,结果为

a_ptr=0X0022FF70,p_ptr=0X0022FF38
FFFFFFFC,-4

这是因为二维数组实际上仍然用一维数组来表示。而int (*p)[4]相当于把a的一维数组表示又转化成二维数组[][4],这样&p[4][2]相当于p+4*4+2,&a[4][2]相当于p+4*5+2,所以二者相减结果为-4.

接着是几个内存分配的程序(源自《高质量程序设计指南--c++/c语言》)

(1)

#include <stdio.h>void getmemory(char *p)
{p = (char*)malloc(100*sizeof(*p));}
int main(void)
{char *str = NULL;getmemory(str);strcpy(str,"hello,world");printf("%s\n",str);return 0;
}

编译运行后程序会发生崩溃,因为getmemory只是将NULL值传给参数p,然后又给p分配了100个字节空间,对str没有任何改变。由于str仍未NULL,所以对空串进行串拷贝会发生崩溃。

(2)

#include <stdio.h>char *getmemory(void)
{char p[] = "hello,world";return p;
}
int main(void)
{char *str = NULL;str = getmemory();printf("%s\n",str);return 0;
}

编译运行该程序,其输出结果为乱码。

这是因为C语言中栈帧布局可知,getmemory被调用后,会在栈上分配数组p来存放"hello,world"字符串并返回该串地址,但是在getmemory返回后,在栈上分配的数组部分已经变成无效状态,此时调用printf函数,就会覆盖掉原来数组p中的内容。但是输出串地址仍是以前的地址,所以可能输出乱码。

(3)

#include <stdio.h>void getmemory(char **p, int num)
{*p = (char*)malloc(num);
}
int main(void)
{char *str = NULL;getmemory(&str,100);strcpy(str,"hello,world");printf("%s\n",str);return 0;
}

编译运行该代码会输出”hello,world",但是该程序没有将分配空间释放,所以可能会产生内存泄漏

(4)

#include <stdio.h>void test(void)
{char *str = (char*)malloc(100);strcpy(str,"hello");free(str);if(str != NULL){strcpy(str,"world");printf("%s\n",str);}
}
int main(void)
{test();return 0;
}

编译运行该程序后,可能产生非常危险的后果。因为前面给str分配空间并释放,但是并没有将str设置为NULL,因而str成为“野指针”,下面还要继续对str原来位置复制一个串"world"并输出,这就成了篡改堆中内容,可能带来非常严重的后果。

接下来是一个要求不用循环和条件语句输出1到1000的所有整数(来源 )。

(方法1):该方法会产生一个错误(除0故障),但能输出正确结果

#include <stdio.h>
#define MAX 1000
int boom;
int foo(n) {boom = 1 / (MAX-n+1);printf("%d\n", n);foo(n+1);
}
int main() {foo(1);
}

(方法二):

#include <stdio.h>
#include <stdlib.h>void f(int j)
{static void (*const ft[2])(int) = { f, exit };printf("%d\n", j);ft[j/1000](j + 1);
}int main(int argc, char *argv[])
{f(1);
}

这段代码可以简化为:

#include <stdio.h>
#include <stdlib.h>void main(int j) {printf("%d\n", j);(&main + (&exit - &main)*(j/1000))(j+1);
}

运行此程序时,由于不带任何参数,所以j的初始值为1(相当于argc参数,只是这里变量名换一下而已,不影响程序的执行),然后输出1,下一句中j/1000值为0(j为1-999之间任意整数时其值均为0),所以相当于执行(&main)(2),这是一个函数指针调用,然后输出2,继续执行下去直至j为999时,会调用(&main)(1000),此时输出1000,j/1000值变为1,所以下一步调用(&main+(&exit-&main))(1001),即exit(1001),此时使用exit跳出函数的执行。

不使用中间变量交换两个整型变量的值,代码如下:

int x,y;
x=x^y;
y=x^y;
x=x^y;

Duff's device :

send(to, from, count)register short *to, *from;register count;{register n=(count+7)/8;switch(count%8){case 0:    do{ *to = *from++;case 7:        *to = *from++;case 6:        *to = *from++;case 5:        *to = *from++;case 4:        *to = *from++;case 3:        *to = *from++;case 2:        *to = *from++;case 1:        *to = *from++;}while(--n>0);}}

这个代码格式是老的代码格式。具体讲解见上面的链接。

检查一个字符串(名称为s1)是否是另外一个字符串(名称为s2)的旋转版本(来源 )。例如"stackoverflow"的旋转版本字符串有: "tackoverflows",“overflowstack"等。

算法实现方法如下:

(1)确定两个串长度相等

(2)将s1和s1连接起来,检查s2是否是连接后的串的字串

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int IsRotation(char s1[], char s2[])
{int len1 = strlen(s1),len2=strlen(s2);char *str = malloc((len1+len1+1)*sizeof(char));if(len1 != len2)return 0;if(str == NULL){fprintf(stderr,"error while allocating space\n");return -1;}if(strcpy(str,s1) == NULL || strcat(str,s1) == NULL){fprintf(stderr,"error while copying or concatenate string s1 to str\n");return -1;}if(strstr(str,s2) != NULL)return 1;return 0;
}

这段代码是我自己写的,可能有不完善的地方。当执行中出错时返回-1,如果是旋转串则返回1,否则返回0.

C语言,你真的弄懂了么?相关推荐

  1. 【图解Java】这下可以真的弄懂Java IO了~

    文章目录 写在前面 Java IO(Stream) 一.IO流的分类和概念 1.1 流的分类 流的原理和解析 二.IO流的常见用法 2.1 这里是输入流的一些读取方法 2.2 提供的一些移动指针的方法 ...

  2. paddle 图标注_化工工艺流程图,你真的弄懂了吗?

    [重磅福利:领取化工活动家全套精品书籍200+]领取丨<化工设备技术问答丛书>共14本领取丨<炼油工业技术知识丛书>共13本领取丨<乙烯相关书籍>共7本领取丨< ...

  3. 阿里面试官常问的TCP和UDP,你真的弄懂了吗?

    作为软件测试,大家都知道一些常用的网络协议是我们必须要了解和掌握的,面试的时候面试官也非常喜欢问一些协议相关的问题,其中有两个协议因为非常基础,出现的频率非常之高,分别是 "TCP 协议&q ...

  4. [集合竞价-AI量化]天天做超短,集合竞价的盘口语言你真的读懂了麽?

    有很多大神会跟我探讨研发策略的一些方法,还有因子的一些构成和写法之类的. 因子的内容在本篇暂时不做赘述,咱们先来讲讲 集合竞价的故事吧. (ps:小妹水平有限,本篇仅作为抛砖引玉去给大家在研发策略途中 ...

  5. 阿里面试官常问问题,TCP和UDP的区别,你真的弄懂了吗?

    作为一个软件测试,我们都知道一些常用的网络协议是必须被我们理解和掌握的.面试的时候,面试官也喜欢问一些协议相关的问题,包括TCP和UDP这两个协议,出现的频率很高,因为都很基础.因为两种协议都在传输层 ...

  6. 【转载】哈希表的原理,真的很难弄懂么?

    [转载]哈希表的原理,真的很难弄懂么? 刘小爱v 发布时间:05-0909:06科技达人 转载路径: https://baijiahao.baidu.com/s?id=1666172942887109 ...

  7. c语言sizeof char,sizeof 你真的弄明白了吗?来看看这个例子

    原标题:sizeof 你真的弄明白了吗?来看看这个例子 sizeof基础 在C语言中,sizeof是一个操作符(operator),而不是函数!其用于判断数据类型或者表达式长度(所占的内存字节数).其 ...

  8. 计算机二级C语言知识点大全,弄懂这些题集所含知识点稳过(三)

    综合题集知识点三 以下是备考计算机二级C语言冲刺阶段总结的题集中所含的知识点!踏踏实实弄懂每一个知识点,就稳了. 2020年11月6日星期五 补充:不出所料,一次性顺利通过,虽然还是有点不太满意.可能 ...

  9. 我花了30天的时间去弄懂了一个网站的基础建站流程,其中真的是受益良多

    在开始正式的购买域名.购买空间建设网站之前,我整整花了30天的时间去弄懂了网站的建设流程,可以说那段时间给我的感受是受益良多,感慨万千啊. 为什么会有如此的感慨呢,首先是我的大半时间其实不是好好利用的 ...

最新文章

  1. 关于更换液晶屏(LCD)后“输入不支援”的一种解决方案
  2. c++模板类静态成员变量_一文讲透父子类中静态变量,成员变量初始化顺序原理...
  3. C++_IO与文件5-文件的输入与输出
  4. Linux中Docker部署Redis
  5. Expression Blend学习5控件
  6. NSGA2算法中文版详细介绍
  7. 怎么学习大数据,入门大数据要掌握哪些知识?
  8. 计算机汉字录入及信息表示,2010年青岛市初中8年级信息技术会考考试知识点说明...
  9. 迅捷会员管理系统v1.7免费版正式发布了-为创业而生
  10. 安卓手機 adb shell常用命令
  11. unity 安卓接入科大讯飞 语音合成
  12. 计算机专业可以从事什么工作?
  13. Xcode6的新特性、
  14. C++设计模式8--装饰模式 Decorator --动态的增减功能
  15. 支付宝支付-当面付和App支付
  16. 虚幻4游戏开发_4_导入fbx和编辑
  17. win10monkey安装教程_详解win10下pytorch-gpu安装以及CUDA详细安装过程
  18. 基于MATLAB的变长信源编码算法的性能比较
  19. 女朋友发的微信消息撤回了,python一串代码,让你大呼过瘾
  20. QT界面完成录音与播放功能以及环境配置

热门文章

  1. MIPCache 域名升级
  2. 【二分+二维前缀和】Largest Allowed Area
  3. UVA - 12412 ​​​​​​​A Typical Homework (a.k.a Shi Xiong Bang Bang Mang)
  4. 015. 深入JVM学习—Java引用类型
  5. iptables 状态策略 允许内网连接外网 拒绝外网主动连入内网 _ 笔记
  6. 比较两大虚拟桌面厂商的系统镜像管理
  7. 选择软路由的七大理由
  8. 深入理解Linux内存映射机制
  9. android 广告栏效果,实现android广告栏效果
  10. Windows配置tomcat环境