C语言,你真的弄懂了么?

程序(来源 ):

 

#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.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/545984.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Oracle自增序列

SQL Server中数据库可以自增字段&#xff0c;但是Oracle中没有这个选项&#xff0c;实际应用中我们可以使用序列(Sequence)实现想要的功能。创建Sequence语法如下&#xff1a;CREATE SEQUENCE SEQUENCE_NAME? START WITH 1 --从 1 开始INCREMENT BY 1 --每次增加 1NOMA…

阿里《Java开发手册》最新嵩山版发布!

《Java 开发手册》是阿里巴巴集团技术团队的集体智慧结晶和经验总结&#xff0c;经历了多次大规模一线实战的检验及不断完善&#xff0c;公开到业界后&#xff0c;众多社区开发者踊跃参与&#xff0c;共同打磨完善&#xff0c;系统化地整理成册&#xff0c;当前的版本是嵩山版。…

递归转化成非递归过程_8086微处理器中的递归和重入过程

递归转化成非递归过程As we all know that a procedure is a set of instruction written separately which can be used any time in the code when required. A normal procedure execution includes calling of the procedure, shifting the control of the processor to th…

the development of c language(转)

c语言之父Dennis Ritchie 写的关于c语言开发历史的文章&#xff0c;来自这里 lisp专家Richard P.Gabriel 的《the Rise of Worse is Better 》&#xff08;wikipedia入口 &#xff0c;c2入口 &#xff0c;《Worse is Better 》&#xff0c;《软件开发宗旨 》&#xff09;中也…

漫谈软件研发特种部队之中的一个

特种部队&#xff0c;是指进行特殊任务的部队&#xff0c;具有编制灵活、人员精干、装备精良、机动高速、训练有素、战斗力强等特点。 特种部队最早出如今二战期间。德国于1939年9月1日的波兰战役中首次投入了一种被称为“勃兰登堡”部队的特种部队作为德国突击波兰的先锋&…

Oracle创建用户和授权

在OracleXE中创建scott用户1、打开SQL*Plus&#xff0c;以 sys用户登录数据库connect / as sysdba2、依次执行下面命令--DROP USER scott CASCADE;CREATE USER scott IDENTIFIED BY tiger;GRANT connect,resource TO scott;GRANT CREATE DATABASE LINK, CREATE MATERIALIZED VI…

不要一把梭了,这才是SQL优化的正确姿势!|原创干货

这是我的第 83 篇原创文章作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;年少不知优化苦&#xff0c;遇坑方知优化难。——村口王大爷全文内容预览&#xff1a;我之前有很多…

PLSQL_性能优化系列10_Oracle Array数据组优化

2014-09-25 Created By BaoXinjian 一、摘要 集合是Oracle开发中经常遇到的情况&#xff0c;Oracle集合分为三种情况&#xff1a;索引表集合(index by table)、嵌套表集合(nested table)、可变集合(varry table)。 PL/SQL中没有数组的概念&#xff0c;他的集合数据类型和数组是…

最小c编译器

最小c编译器&#xff08;来源 &#xff08;最好在linux下操作&#xff09;&#xff09;代码有好几个版本&#xff0c;我选择otccelfn.c 。 /*Obfuscated Tiny C Compiler with ELF outputCopyright (C) 2001-2003 Fabrice BellardThis software is provided as-is, without any…

在Java中使用Collat​​or和String类进行字符串比较

Given two strings and we have to compare them using Collator and String classed in Java. 给定两个字符串&#xff0c;我们必须使用Java中分类的Collat​​or和String进行比较。 Using Collator class – to compare two strings, we use compare() method – it returns…

Oracle数据库中表格的级联删除问题

数据库表中没有设置级联删除.怎样用SQL语句实现:如:EMP表中有字段DEPT_NO是外键POS表中有字段DEPT_NO是外键DEPT表中有字段DEPT_NO,如何实现删除DEPT表中数据时将EMP表,POS表中的相关数据也删除;这里有两种方法&#xff1a;方法一&#xff1a;触发器解决create or replace trig…

IDEA 2020.2 重磅发布,动画级新功能预览!

Guide 关注了 IDEA 的官推&#xff0c;平时没事就会去看看有没有啥比较好的更新。今天下午看到IntelliJ IDEA 2020.2 都已经发布并且还支持了 Java15。然后&#xff0c;我就去官网简单看了一下新特性。单看新特性&#xff0c;这个新版本还是有一点香的。虽然我还木有升级到这个…

访问控制模型ACL和RBAC

2019独角兽企业重金招聘Python工程师标准>>> 1.ACL ACL是最早也是最基本的一种访问控制机制&#xff0c;它的原理非常简单&#xff1a;每一项资源&#xff0c;都配有一个列表&#xff0c;这个列表记录的就是哪些用户可以对这项资源执行CRUD中的那些操作。当系统试图…

最常见并发面试题整理!(速度收藏)

前言并发编程是面试中必问的知识点之一&#xff0c;所以本文整理了一些最为常见的并发面试题&#xff0c;一起来看吧~1. synchronized的实现原理以及锁优化&#xff1f;synchronized的实现原理synchronized作用于「方法」或者「代码块」&#xff0c;保证被修饰的代码在同一时间…

JavaScript中的嵌套事件处理(在鼠标移动事件上)

Multiple event handling is the secret ingredient of dynamic WebPages seen now-a-days. 多重事件处理是当今动态网页的秘密组成部分。 Now, let’s get started... 现在&#xff0c;让我们开始吧... Example Code 范例程式码 <html lang"en"><head&…

在Oracle中CHAR,NCHAR,VARCHAR,VARCHAR2,NVARCHAR2这五种类型的区别

【在Oracle中CHAR,NCHAR,VARCHAR,VARCHAR2,NVARCHAR2这五种类型的区别 】1.CHAR(size)和VARCHAR(size)的区别 CHAR为定长的字段&#xff0c;最大长度为2K字节&#xff1b; VARCHAR为可变长的字段&#xff0c;最大长度为4K字节&#xff1b; 2.CHAR(size)和NCHAR(size)的区别 CHA…

Android 照相功能

使用内置的Camera 应用程序捕获图像 探索Android 所提供的内置功能&#xff0c;内置的图像捕获与存储功能为Android 上全部媒体功能提供了一个非常好的切入点&#xff0c;为我们在以后的章节中处理音频和视频奠定了基础。如今介绍怎样利用内置的Camera&#xff08;摄像头&#…

皮尔逊相关性_皮尔逊的相关性及其在机器学习中的意义

皮尔逊相关性Today we would be using a statistical concept i.e. Pearsons correlation to help us understand the relationships between the feature values (independent values) and the target value (dependent value or the value to be predicted ) which will furt…

磊哥最近面试了好多人,聊聊我的感受!(附面试知识点)

这是我的第 84 篇原创文章作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;一些读者可能知道&#xff0c;磊哥前段时间又回来上班了&#xff0c;详见《磊哥又滚回职场了...》…

M4 宏处理器

2019独角兽企业重金招聘Python工程师标准>>> M4 宏处理器 Brian W. KernighanDennis M. Ritchie Bell LaboratoriesMurray Hill, New Jersey 07974 翻译&#xff1a;寒蝉退士 译者声明&#xff1a;译者对译文不做任何担保&#xff0c;译者对译文不拥有任何权利并且不…