下面代码的输出是什么?(非常考基础水平的一道题)

  char *c[] = {"ENTER","NEW","POINT","FIRST"};
  char **cp[] = { c + 3 , c + 2 , c + 1 , c};
  char ***cpp = cp;
  int main(void)
  {
  printf("%s",**++cpp);
  printf("%s",*--*++cpp+3);
  printf("%s",*cpp[-2]+3);
  printf("%s ",cpp[-1][-1]+1);
  return 0;
  }

  解答:
  c是一个指针数组,每个数组元素都是char*类型的指针,值分别是那些字符串(的首地址):
  c[0] = "ENTER"
  c[1] = "NEW"
  c[2] = "POINT"
  c[3] = "FIRST"
  而[]和*是本质一样的运算,即c[i]=*(c+i)。
  c和c+i都是char *[]类型,它可以退化成char **类型,再看cp,它正好是一个char **的数组,来看它的值:
  cp[0] = c + 3
  cp[1] = c + 2
  cp[2] = c + 1
  cp[3] = c
  引用后有:cp[0][0]=*(c + 3)=c[3]="FIRST",以此类推。
  cp是char **[]类型,它可以退化成char ***类型,看后的cpp,它正是char ***类型,它是一个指针变量,和上面两个不同,上面两个是数组。
  这样分析过后,下面的解析一目了然了:
  printf("%s",**++cpp);
  ++cpp的值是cp+1,引用一次后是cp[1]再引用是*cp[1]=c[2]="POINT",第一句的输出
  printf("%s",*--*++cpp+3);
  再++cpp的值是cp+2,引用一次是cp[2]=c+1,再对这进行--,减后是c再引用是c[0]="ENTER"再+3,字符串指针指到"ER",输出是"ER"
  printf("%s",*cpp[-2]+3);
  这时cpp的值是cp+2,cpp[-2]=*(cpp-2)=*(cp+2-2)=cp[0]=c+3,再引用是c[3]="FIRST",+3 字符串指针指到"ST",输出是"ST"
  printf("%s ",cpp[-1][-1]+1);
  cpp还是cp+2,cpp[-1]=*(cpp-1)=*(cp+2-1)=cp[1]=c+2,再[-1]得*(c+2-1)=c[1]="NEW",+1字符串指针指到"EW",输出是"EW"。
  结构体

  #include <stdio.h>
  struct data
  {
  int a;
  unsigned short b;
  };
  int main(void)
  {
  data mData;
  mData.b = 0x0102;
  char *pData = (char *)&mData;
  printf("%d %d", sizeof(pData), (int)(*(pData + 4)));
  return 0;
  }
  输出:4 2
  说明:一般变量都是从高到低分配内存地址,但对于结构体来说,结构体的成员在内存中顺序存放,所占内存地址依次增高,第一个成员处于低地址处,后一个成员处于高地址处,但结构体成员的内存分配不一定是连续的,编译器会对其成员变量依据前面介绍的 “对齐”原则进行处理。

  补充知识点:
  除了栈以外,堆、只读数据区、全局变量地址增长方向都是从低到高的。
  改变string变量的值?
  #include <iostream>
  #include <string>
  using namespace std;
  void chg_str(string str) {
  str = "ichgit";
  }
  int main() {
  string s = "sarrr";
  chg_str(s);
  printf("%s ", s.c_str());
  cout << s << endl;
  return 0;
  }
  输出:仍为“sarrr”。
  解释:string是传值参数,不能修改其值。要想改变string变量的值,可以改为传地址方式:
  #include <iostream>
  #include <string>
  using namespace std;
  void chg_str(string *str) {
  *str = "ichgit";
  }
  int main() {
  string s = "sarrr";
  chg_str(&s);
  printf("%s ", s.c_str());
  cout << s << endl;
  return 0;
  }
  静态变量的输出
  #include <stdio.h>
  int sum(int a) {
  int c = 0;
  static int b = 3; // 只执行一次
  c++;
  b += 2;
  return (a + b + c);
  }
  int main() {
  int i;
  int a = 2;
  for(i = 0; i < 5; ++i) {
  printf("%d ", sum(a));
  }
  return 0;
  }
  输出:8 10 12 14 16
  解释:存储在静态数据区的变量会在程序刚开始运行时完成初始化,也是的一次初始化,此后该初始化不再执行,相当于一次执行后作废,静态局部变量保存了前次被调用后留下的值。
  返回值加const修饰的必要性
  你觉得下面两种写法有区别吗?
  int GetInt(void)
  const int GetInt(void)
  如果是下面的呢?其中A 为用户自定义的数据类型。
  A GetA(void)
  const A GetA(void)
  答案:没有任何区别。
  解释:如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const 修饰没有任何价值。所以,对于值传递来说,加const没有太多意义。
  所以:
  不要把函数int GetInt(void) 写成const int GetInt(void)。
  不要把函数A GetA(void) 写成const A GetA(void)。
  在编程中要尽可能多的使用const(比如函数参数采用const&修饰),这样可以获得编译器的帮助,以便写出健壮性的代码。