- 浏览: 70600 次
- 性别:
- 来自: 杭州
最新评论
程序(来源 ):
#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;
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编译器
2011-11-08 14:09 1425最小c编译器(来源 (最好在linux下操作))代码有好几个 ... -
the development of c language(转)
2011-11-08 09:25 1106c语言之父Dennis Ritchie 写的关于c语言开发历 ... -
pe文件格式实例解析
2011-11-07 10:05 0环境:windows xp 速龙3000+(即x86兼容32位 ... -
小型elf "Hello,World"程序
2011-11-06 23:59 1325参考链接:http://timelessname.com/el ... -
elf文件格式实例解析
2011-11-05 23:00 6285试验环境:archlinux 速龙3000+(即x86兼 ... -
高质量的c源代码
2011-11-03 10:18 1097现在自由软件及开源软件越来越流行,有大量的附带源程序 ... -
fltk 库
2011-09-26 19:47 1773fltk是一个小型、开源、支持OpenGL 、跨平台(win ... -
《Introduction to Computing Systems: From bits and gates to C and beyond》
2011-09-25 23:33 2118很好的一本计算机的入门书,被很多学校采纳作为教材,作者Yale ... -
csapp bufbomb实验
2011-09-16 14:21 4550csapp (《深入理解计算机系统》)一书中有一个关于缓冲区 ... -
the blocks problem(uva 101 or poj 1208)
2011-09-11 20:57 1805题目描述见:uva 101 or poj 1208 ... -
the blocks problem(uva 101 or poj 1208)
2011-09-11 20:56 0题目描述见:uva 101 or poj 1208 ... -
部分排序算法c语言实现
2011-09-02 14:51 989代码比较粗糙,主要是用于对排序算法的理解,因而忽略了边界和容错 ... -
编译器开发相关资源
2011-08-31 08:40 1173开发编译器相关的一些网络资源: how difficu ... -
zoj 1025 Wooden Sticks
2011-07-23 20:25 944题目见:zoj 1025 先对木棒按照长度进行排序,然后再计 ... -
zoj 1088 System Overload
2011-07-23 17:30 1139约瑟夫环 (josephus problem )问题, ... -
zoj 1091 Knight Moves
2011-07-23 09:05 819题目见zoj 1091 使用宽度搜索优先来求解, ... -
zoj 1078 palindrom numbers
2011-07-22 19:31 1120题目见zoj 1078 主要是判断一个整数在基数为2 ... -
zoj 1006 do the untwist
2011-07-22 13:24 902题目见zoj 1006 或poj 1317 简单 ... -
zoj 3488 conic section
2011-07-22 12:23 970题目见zoj 3488 很简单的题目,却没能一次搞定,因 ... -
zoj 1005 jugs
2011-07-22 11:43 811题目内容见zoj1005 由于A,B互素且A的容 ...
相关推荐
必须弄懂的495个C语言问题.pdf 是个好东西 给自己打好基础
必须弄懂的495个C语言问题.pdf )
及其经典的一本C语言学习教程,当你完全明白并且弄懂本书中的所有内容后,你已经可以称得上一名合格的C语言程序员了,推荐大家学习本书的内容
c语言是公认的经典编程语言,而c语言中的指针又是c语言中的精华所在,所以,要想真正了解并掌握C语言,就需要弄懂指针的奥妙,本书详细的介绍了指针的各种用法与适用场合,让您一目了然,从此不再惧怕指针
预编译处理虽然不难,但是学好C语言最好能弄懂预编译
你必须知道的495个C语言问题 高清PDF中文版
认真的读完这一笔记,每个序号都弄懂,相信你的c语言的基础就可以了,接下来就是怎么建设成一栋大楼了
很适合应对c语言的考试,很基础,弄懂这些考试没问题了
基础的 非常重要的 很容易出错的 495个C语言问题,弄懂后其他内容就容易理解。
区分C语言C++的值传递和引用传递,实际程序代码分析,帮助弄懂二者的差异性
C语言基础复习资料 考试把我的这份资料全弄懂了,就能得到满分!
考试真题 考试的要点,只要弄懂考试必过.C语言二级轻松考过.
南京信息工程大学 C语言上课PPT,对于上课的同学可以用于查漏补缺,对于想考南京信息工程大学计软院研究生的同学呢,基本弄懂PPT上,140不是问题。
该资料中包含历年C语言考试的笔试试卷和C语言中上机必考的题型。如果能弄懂里面的东西,二级考试应该不会有问题了。
1. 看得懂:C语言课程设计是如何开发和搭建的 2. 学得会:指针的知识逻辑,链表的链接过程 3. 搞得清:链表,指针,结构体,文件存储,数组的知识 4. 弄得明:程序系统的运行逻辑和交互方式 阅读建议: 这是我在...
C语言基础,常用的面试题,弄懂了就差不多可以面试去 了。