首页 > 代码库 > 6个变态的C语言Hello World程序——更好的理解C(2、3)
6个变态的C语言Hello World程序——更好的理解C(2、3)
接上一篇文章,6个变态的C语言Hello World程序——更好的理解C ,这篇文章给大家带来变态的Hello World程序2、3
hello2.c
#include<stdio.h> main(){ int x=0,y[14],*z=&y;*(z++)=0x48;*(z++)=y[x++]+0x1D; *(z++)=y[x++]+0x07;*(z++)=y[x++]+0x00;*(z++)=y[x++]+0x03; *(z++)=y[x++]-0x43;*(z++)=y[x++]-0x0C;*(z++)=y[x++]+0x57; *(z++)=y[x++]-0x08;*(z++)=y[x++]+0x03;*(z++)=y[x++]-0x06; *(z++)=y[x++]-0x08;*(z++)=y[x++]-0x43;*(z++)=y[x]-0x21; x=*(--z);while(y[x]!=NULL)putchar(y[x++]); }
这个一看就比较平淡啦。主要涉及的就是指针以及ASCII码的问题。将整型数组的首地址赋值给int *类型的指针z,通过*(z++)=y[x++]+ASCII码,依次为数组中的元素赋值为‘H’, ‘e’, ‘l‘, ‘l‘, ‘o‘, ‘,‘, ‘ ‘, ‘w‘, ‘o‘, ‘r‘, ‘l‘, ‘d‘, ‘!‘,‘\0‘。将与y[13]赋值为‘\0’,作为while循环的退出条件。
我们主要来辨析下指针与数组的关系吧:
我们必须明确数组与指针其实是不同的。
1)指针保存的是地址,首先取得指针的内容,作为地址从该地址中取得数据,是一种间接当问方式;数组是直接访问数据,a[i]就是取a+i为地址中的内容。对编译器而言,一个数据就是地址,一个指针就是指向地址的地址。
2)定义指针时编译器只是分配指针本身的空间,除非赋值为字符串常量。在ASCI C中初始化指针时所创建的字符串被定义为只读。试图通过指针修改字符串的值程序会出现未定义的行为。
#include <stdio.h> int main(void) { char *p="hello"; // printf("%c\n",p[1]); // p[1]='F'; printf(",p[1] changed to %c",p[1]); char a[]="world"; printf("%c",a[1]); a[1]='U'; printf(",a[1] changed to %c",a[1]); } 若注释取消,程序就会挂掉。</span>3)数组名表示的是内存中一块位置固定的地址,不可以作为赋值表达式的左值;指针定义时分配的是本身的地址,可作为赋值表达式的左值,指向内存中的不同区域。
4)当你以a[i]这样的形式对数组进行访问时总是被编译器解释为像*(a+i)这样的指针访问。
5)在作为函数参数的情况下,数组与指针时等价的,作为参数的数组始终会被编译器修改为指向数组第一个元素的指针。
6)在其他情况下,定义和声明必须匹配。如果定义为数组,在其他文件中对他进行声明时也必须声明为数组,指针也是如此。声明只是简单的告诉编译器在其他地方创建的对象的名字,定义时才会为对象分配内存。
hello3.c
int n[]={0x48, 0x65,0x6C,0x6C, 0x6F,0x2C,0x20, 0x77,0x6F,0x72, 0x6C,0x64,0x21, 0x0A,0x00},*m=n; main(n){putchar (*m)!='\0'?main (m++):exit(n++);}
这个同上面那个其实是比较类似的,甚至更简单就是一个字符与ASCII码的转换。
先声明一个全局的整型数组,里面存放ASCII码值。并将其首地址赋值给整型指针m。
main(n){putchar (*m)!=‘\0‘?main (m++):exit(n++);}这地方用了一个条件表达式:若putchar(*m) != ‘\0‘为真,则继续main(m++),递归调用自己;否则exit(n++)退出函数。exit是结束一个进程,它将删除进程使用的内存空间,同时把错误信息返回父进程,而return是返回函数值并退出函数,return是语言级别的,它表示了调用堆栈的返回。exit本身不是系统调用,而是一个C标准库的函数而已,在stdlib里面,系统调用是exit内部实现去完成的。