C语言代码实例

#include
#include
#include /* 包含了使用字符串函数的所需的原型和声明,便于程序执行错误检查 */
#include
typedef struct
{  int len;
   int len1;
   char data[0];//不占用内存空间,会在后面连续的分配一块内存空间使用,和指针区别开来
}SS1;

typedef struct
{  int len;
   int len1;
   int data[1];//占用内存空间,会在后面连续的分配一块内存空间使用,和指针区别开来
}SS2;

typedef struct
{  int len;
   int len1;
   char *data;//不占用内存空间,会在后面连续的分配一块内存空间使用,和指针区别开来
}SS3;

typedef struct
{  char len;
   int len1;
   char data;
}W0;

typedef struct
{  char len;
   char len1;
   int data;
}W1;

typedef struct
{  int len;
   char len1;
   short data;
   double data1;
   int len2;
}W2;

//#pragma pack(2)

/*结构体数据类型的对齐规则如下(常见陷阱)

1.数据类型自身的对齐值:

对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4,单位字节。

2.结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。

3.指定对齐值:#pragma pack (value)时的指定对齐值value。

4.数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值。

*/

#pragma pack(2)/* 设置2字节对齐 */
typedef struct

   char len1;
   long int data1;
   int len2;
}W3,*b;
#pragma pack() /* 取消指定对齐,恢复缺省对齐(4字节对齐) */

typedef enum
{
 Sunday,
 Monday,
 Tuesday,
 Wednesday,
 Thursday,
 Friday,
 Saturday //尾部不能有逗号
}weekday;


void testarr(int *m);
void teststatic();
void Show(short n);

void testarr(int *m)
{
 printf("arr name m1 = %d\n", sizeof(m));
}

void teststatic()
{
 static int n=10;
 printf("n = %d\n", n);
 n++;
 
}


void Show(short n) {
short i;
printf("%d(",n);
for(i = 0; i < 16; ++i)
printf("%d",(n & (1 << (15 - i))) >> (15 - i));
printf(")B\n");
}


int main(int argc, char **argv)
{
 
 #if 0
 /*1.C语言的输入和输出函数的用法*/
 printf("--------------------------1------------------------------\n");
 int a[10] = {0};
 //a++;
 printf("a=%d\n",a[0]);
 //printf("a=%d\n",a);
 //printf("a=%d\n",&a);
 
 int b = 0;
 scanf("%d",&b);
 printf("%d\n",b);
 

 /*2.利用结构体定义变长数组和指针的对比*/
 printf("--------------------------2------------------------------\n");
 typedef struct
 {  int len;
    int data[0];
 }SS;
 
 SS sa;//结构体相关的局部变量初始化,不初始化后面的数据是一个随机值
 //SS sa={0};
 memset(&sa,0,sizeof(SS));//清空以上结构体中的随机值
 printf("sizeof(SS)=%d\n",sizeof(SS));
 printf("len=%d\n",sa.len);
 printf("data=%d\n",sa.data[0]);
 
 sa.len = 10;
 sa.data[0] = 80;
 sa.data[10] = 80;
 sa.data[20] = 80;
 sa.data[50] = 80;
 sa.data[60] = 80;
 sa.data[65] = 80;
 sa.data[66] = 80;//到了67就不行了
 
 printf("len = %d\n",sa.len);
 printf("data0 = %d\n",sa.data[0]);
 printf("data10 = %d\n",sa.data[10]);
 printf("data20 = %d\n",sa.data[20]);
 printf("data50 = %d\n",sa.data[50]);
 printf("data60 = %d\n",sa.data[60]);
 printf("data60 = %d\n",sa.data[65]);
 printf("data60 = %d\n",sa.data[66]);
 printf("sizeof(SS) = %d\n",sizeof(SS));
 printf("sizeof(sa) = %d\n",sizeof(sa));
 
 //SS1 sa1={0};
 printf("sizeof(SS1)=%d\n",sizeof(SS1));
 printf("sizeof(SS2)=%d\n",sizeof(SS2));
 printf("sizeof(SS3)=%d\n",sizeof(SS3));
 
    /*3.数组名和指针的关系(重难点)*/
 printf("--------------------------3------------------------------\n");
 
    int *m = 0;
 m=a;
 printf("m = %d\n", *m);
 a=m;//a的实质是一个常量,因此不能放在右边
    #endif
 
 /*4.数组名作为函数形参(退化成指针)*/
 printf("--------------------------4------------------------------\n");
 int m1[15] = {0};
 printf("arr name m1 = %d\n", sizeof(m1));
 /* 退化成指针 */
 testarr(m1);
 testarr(&m1[0]);
 
 /*5.sizeof运算符(类型占内存的大小) 和 strlen()函数(计算字符串的长度) 区别*/
 printf("--------------------------5------------------------------\n");
 char str[20]="0123456789abcd";
 int a = strlen(str);//a=10;int b=sizeof(str) str退化为指针
 int b = sizeof(str);//而b=20
 int i = 0;
 printf("a = %d, b = %d\n", a, b);

 
 /* 6. string:是一串以NUL字节结尾的字符集合*/
 
 //while(NUL != str[i]) NUL需要定义和NULL(指针初值0在stdio.h定义指针初始化防止野指针很重要,局部变量在使用之前也一定要初始化)区别
 printf("--------------------------6------------------------------\n");
 while('\0' != str[i])
 {
  printf("STR the number of %d is %c\n",i,str[i]);
  i++;
 }
 
 /*7.&(位运算结构是逐个位相与的值) &&(逻辑运算,结果是1 or 0只有两个结果 具有短路功能,只要第一个为false后面的就不在执行)*/
 printf("--------------------------7------------------------------\n");
 int x1 = 7 & 8;
 printf("BIT calc number of %d\n",x1);
    int x2 = 7 && 8;
 printf("Logic calc number of %d\n",x2);
 
 /*8.a指针常量(指针指向的地址不变,内容可变) b常量指针(指针指向的内存中的内容不变,地址可变)的区别*/
 printf("--------------------------8------------------------------\n");
 char* const pa = "abc";
 //pa = "bcf";//非法
 //*pa = 'p';非法,字符串常量不可以改变,数组定义的字符串可以修改,容易忽视!!!!!!
 printf("pa content is %c\n",*pa);
 
    char const * pb = "abc";
 pb = "bcf";
 //*pb = "efc";//非法,内容不可以变
 printf("pb content is %c\n",*pb);
 
 //特例
 char const * const pc = "123abc"; //这个pc指向的地址和内容都不变,极端情况
 printf("pc content is %c\n",*pc);//用于输出 指向的特定字符
 printf("pc string content is %s\n" , pc);//用于输出整个字符串(原因????)
 
 
 /*9.a指针函数(实质是一个函数,返回值是一个指针) b函数指针(函数名是一个指针)的区别*/
 /*
 //类型标识符    *函数名(参数表)
 a.int*   f(x,y);  指针函数

 b.int (*f) (int x); //声明一个函数指针
   f=func; //将func函数的首地址赋给指针f(回调函数的变相使用)
   */
  
   /* 函数指针的应用实例------回调函数(写一个通用的数比较的回调函数,应用于不同的数据类型)  转换表(简化袖珍式计算器不用switch) */
    printf("--------------------------分割开来------------------------------\n");
 
 
 
    /* 数组指针(定义 int (*p)[n])
  *()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度
  *
  * 指针数组(定义 int *p[n])
  *[]优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素
  *
  * */
  printf("--------------------------9------------------------------\n");
  
   /*10. static修饰局部变量的运用*/
   printf("--------------------------10------------------------------\n");
   /* 1.该静态局部变量n在全局数据区分配内存,而不是栈里面;
    * 2.静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化;
 * 3.静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0;
 * 4.它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束;*/
   teststatic();
   teststatic();
   teststatic();
  
   /*11.  逻辑移位 算数移位(算数左移和逻辑左移的结果一致,只有负数的两种右移的值不一样,
    * 具体是逻辑右移还是算数右移 与编译器有关(通过这个可以判断编译器的类型,补1支持算数,补0逻辑,这个可以区别在C语言进行移植的时候作用重大),
    * 从下面的结果来看 这个是逻辑数补充1的)*/
    printf("--------------------------11------------------------------\n");
    int h = -2;
 int h1 = h >> 1;
 int h2 = h >> 5;
 int h3 = h >> 10;
    printf("h1 = %d\n",h1);
 printf("h2 = %d\n",h2);
    printf("h3 = %d\n",h3);
 
 /*char 类型的变量右移操作情况考虑越界和计算机内存的原反补码情况 可以用下面的几组值来确定本编译器是否默认为char有符号型*/
 char sq = 153; //在C语言中默认的基础数据结构是signed ---char截取低八位1001101-----由于首位是1,因此是一个负数的补码(计算机中都是以补码存在,整数的原反补相同,负数的不同),移位之后1110011(负数补码-源码,符号位不变,后面的数据位取反加一)
 printf("sq = %d\n",sq >> 4);//-7的原因在上面 解释(重要)
 
 char sq1 = 127; //没有越界,可以
 printf("sq1 = %d\n",sq1 >> 4);//7
 
 char sq2 = 128; //同153
 printf("sq2 = %d\n",sq2 >> 4);//-8
 
 int h0 = -2;//-2的 有符号形右移(0x1111111111111110),右移超过1位之后时钟是-1,务必注意这一点(负数的逻辑右移是重点)
 printf("左移时,末位补0,右移时,首位补1。\n");
 Show(h0);
 printf("左移4位后:");
 Show(h0 << 4);
 printf("右移4位后:");
 Show(h0 >> 4);

 short n1 = -1280;
 printf("左移时,末位补0,右移时,首位补1。\n");
 Show(n1);
 printf("左移4位后:");
 Show(n1 << 4);
 printf("右移4位后:");
 Show(n1 >> 4);
 
 /* 12. 位运算经典实例常用 三种位操作*/
 printf("--------------------------12------------------------------\n");
 /*利用复合赋值符优势(value的值只计算一次,打字错误的可能性更小)把特定的位 设置为1 把特定的位设置为 0*/
 int value = 0;
 value |= 1 << 1; //特定位置1
 printf("value = %d \n",value);
 
 value &= ~(1 << 1);//特定位置0
 printf("value = %d \n",value);
 
 
 value = 3;
 value &= 1 << 1; //提取特定位置1的值,其他位置清零
 printf("value = %d \n",value);
 
 /* 13. 左值 右值(左值意味着计算机内存中一个位置 右值意味着一个值 数组的名不能作为左值,左值可以作为右值使用,反过来是不可以的)*/
 printf("--------------------------13------------------------------\n");
 int a13 = 12;
 int b13 = 13;
 //b13 + 1 = a13;非法的右值作为左值
 b13 = a13;
 printf("b13 = %d \n",b13);
 
 /* 14. 指针的地址和内容*/
 printf("--------------------------14------------------------------\n");
 
 /*非法使用,NULL解引用(*)和编译器有关,因此比较好的编程习惯是在使用*之前,首先判断指针 != NULL在使用,不然程序有潜在的危险
 int *p = NULL;
 printf("*p = %d \n",*p);
 */
 
 //int g = 0;
 //int *p = &g;
 int *p = 0;
 if (p != NULL)//指针解引用的规范用法
 {
     printf("*p = %d \n",*p); 
 }
 //printf("p = %x \n",p);
 
 /*15.  指针的指针 */
 printf("--------------------------15------------------------------\n");
 int p1 = 12;// p1作为左值的时候表示内存地址,存储12的位置,当做为右值是表示存储的内容12
 int *p2 = &p1;
 int **p3 = &p2;//指针的指针
 //int **p4 = p2;//非法, 
 if (&p2 != p2)//&p2和&p1一样(指针变量p2和普通的变量p1都是变量,&表示得到p2在内存中的地址) p2 == &p1(该内存中存储的值),以上两种表达形式,左值右值的代表的含义不一样
 {
    printf("&p2 not same p2 \n");     
 }
 printf("**p3 = %d \n",**p3);
 //printf("**p4 = %d \n",**p4);
 
 /*16.  指针的运算
  * 以下三种运算都是基于同一个数组而言的,指针运算的前提,如果不是同一个数组,指针必然会指向我们不确定的位置

  * a.指针的加法  指针+整数
  * b.指针的减法  指针-指针
  * c.指针的关系运算
  *
  * */
 printf("--------------------------16------------------------------\n");
 
 /*17. 指针越界  指向未知值的指针(指针错误的根源)*/
 printf("--------------------------17------------------------------\n");
 
 
 /*18. 计算结构体的大小
  * 计算的两个规则:
  * 一、起始位置(从0开始)为成员数据类型所占内存的整数倍(0也算),若不足则不足部分用数据将内存填充为该数据类型的整数倍。
  * 二、结构体所占总内存为其成员变量中所占空间最大数据类型的整数倍
  * 三、注意采用什么样的字节对齐方式来计算,重点关注w3结构体大小计算的了例子。
  * */
 printf("--------------------------18------------------------------\n");
 printf("sizeof(W0)=%d\n",sizeof(W0));  //12
 printf("sizeof(W1)=%d\n",sizeof(W1));  //8
 printf("sizeof(W2)=%d\n",sizeof(W2));  //24
 printf("sizeof(W3)=%d\n",sizeof(W3));  //10(2字节对齐) 12(4字节对齐情况)
 printf("sizeof(b)=%d\n",sizeof(b));  //4,指针就是占4个字节
 printf("sizeof(long)=%d\n",sizeof(long));  //4
 printf("sizeof(long long)=%d\n",sizeof(long long));  //8
 
 /*19. int的整型负数强制类型转化为无符号整型*/
    printf("--------------------------19------------------------------\n");
 char f0 = -2;
 printf("f0 = %x\n",(unsigned int)((unsigned int) f0));
 
 
 /*20. 指针 和 字符串 重要 字符串常量的不可变性*/
    printf("--------------------------20------------------------------\n");
 char *s0 = "mj";
 /* 下面注释掉的表达式犯了两个错误1.把字符串存储到一个字符'm'的位置,内存溢出 2.以上即使是 *s = 'l',也不对,以上指针的
  * 名命方式是字符串常量不可修改,如果想修改必须通过数组来名命*/
    //*s0 = "like";
 
 /* 区别下面的两种不同类型的输出 */
 printf("s=%s\n" , s0);
 printf("s=%c\n" , *s0);
 
 
 
 /*21. C语言中常量的定义规则
  *
  * 1.整型
  * 2.实型(注意后面的 E、e 类型作为常量的写法规则)
  * 3.字符型
  * 4.字符串型
  *
  * */
    printf("--------------------------21.C语言中常量规则------------------------------\n");
 //int ll = 1.23e1.5;后面的值典型的不可以作为常量,也不可以作为赋值,对于E、e类型有两个必须的条件:a.E/e的前面必须有数 b.E.e后面的数必须为整数
 
 
 /*22. malloc动态内存分配和数组的区别
  * 数组形式的优点是简单,但是缺点是:编译时申请内存,过大实际存储少,浪费,容易溢出。
  * malloc执行的时候动态分配内存,注意使用之前判断是否申请成功。
  * */
    printf("--------------------------22------------------------------\n");
 int kk[1000] = {0};
 int *mk = (int*)malloc(sizeof(int)*1000);//注意强制类型转换
 if (mk == NULL)
 {
     printf("动态内存申请失败\n");
 }
 
    /*23. 结构体相关知识总结
  *
  *
  * 用于定义一个寄存器的值,比较长用的现象
  * 结构体变量用  .   指针变量用 ->进行引用
  *
  * */
 
     printf("--------------------------23------------------------------\n");
 
     /*24. 联合体相关知识总结
  *
  * 联合的所有成员引用的内存中的相同位置
  * 在一个成员长度不同的联合里,分配给联合的内存数量取决于它的最长成员长度。
  * 利用联合法 和指针的强制类型转换的两种方法 判断大端小端(常规使用方法)
  *
  *
  * 大端小端字节序的区别应用:
  * 在网络编程里,网络字节序是big-endian的,而大部分的PC的系统都是X86处理器系列,
  * X86采用的是little-endian,所以需要将网络数据流转换成本地数据流的话,需要进行字节序的转换。
  *
  *
  *
  * */
     printf("--------------------------24------------------------------\n");
 
  /* 指针法 */
     int a24 = 1;
  int *mm = &a24;
  char *b24 = (char*) mm;
 
  if (*b24 == 1)
  {
      printf("little end!\n"); 
  }
  else
     {
      printf("big end!\n"); 
  }
 
  /* 联合体法 :union型数据所占的空间等于其最大的成员所占的空间,对union型成员的存取都是相对于该联合体基地址的偏移量为0处开始,即,联合体的访问不论对哪个变量的存取都是从union的首地址位置开始。*/
  union {
  char m;
        int n;
 } f1 = {1};
 
  if (f1.n == 1)
  {
      printf("little end!\n"); 
  }else
     {
      printf("big end!\n"); 
  }
 
 
    /*25. 利用memset()初始化结构体
  *
  * 如果对一个结构体变量进行赋初值零时,采用枚举的方法对其所有元素赋零值是不正确的,
  * 应该那些因数据对齐而产生的冗余空间并没有被清空,
  * 所以应该采用memset()函数对该结构体变量所占用的内存空间一字节一字节的进行清空。
  *
  * 原型:extern void *memset(void *buffer, int c, int count);

  * 功能:把buffer(是一个地址)所指内存区域的前count个字节设置成字符c。
  *
  * */
     printf("--------------------------25------------------------------\n");
  SS1 ss01;
  memset(&ss01,0,sizeof(ss01));//注意是第一个参数是-------&ss01,不要错误的用ss01,易错点
  printf("ss01.len = %d,ss01.len1 = %d,ss01.data[0] = %c,\n",ss01.len,ss01.len1,ss01.data[0]);
 
 
 
 
    /*26. 数据的强制类型转换(常用)
  *
  *
  *
  *
  * -1
  * */
     printf("--------------------------26------------------------------\n");
 
  /* 1.float to int */
  float ff = -1.5;
  printf("ff = %d\n",(long int)(ff)); //强制类型转换 浮点到整形  损失精度
  printf("ff = %d\n",(int)(round(ff)));//固定函数(同时采用的四舍五入)
 
  /*将浮点型取整(4舍5入),和固定函数的执行效果一致*/
  if (ff < 0)
  {
   printf("ff = %d\n",(int)(ff - 0.5));
   }
   else
   {
   printf("ff = %d\n",(int)(ff + 0.5));
   }
  
  
   /* 2.signed int to unsigned int 重点关注*/
   /* 逻辑数就是编译器(代码表述)所使用的数据,物理数(补码)是逻辑数在具体CPU体系下的物理存储方式。区分以上两个概念
    * C语言中的逻辑移位 物理移位
    *
    *
    * 转换规则:
一、短数据类型扩展为长数据类型
1、有符号数向其它类型数据转化(如char到int,char到unsigned int)时,进行符号位扩展;无符号数向其它类型转换(如unsigned char到int, long)时,进行零扩展。
a)char x=10001001b; short y=x; 则y的值应为11111111 10001001b;
B)unsigned char x=10001001b; short y=x; 则y的值应为00000000 10001001b;
二、长数据类型缩减为短数据类型
由大数据类型向小数据类型转换时,保留低位字节153那个变换
三、同一长度的数据类型中有符号数与无符号数的相互转化
直接将内存中的数据赋给要转化的类型,数值大小则会发生变化。
另短类型扩展为长类型时,但短类型与长类型分属有符号数与无符号数时,
则先按规则一进行类型的扩展,再按本规则直接将内存中的数值原封不动的赋给对方。
    * */
   int q = -1;
   unsigned int q1 = 0xffffffff;
   printf("q = %x\n",(unsigned int)q);/* 有符号的数转化为同等长度的无符号的数---物理数不变,逻辑数可能变化 */
   printf("q = %ld\n",(long int)q);/* 将int型数据送给long型变量时,其逻辑数保持不变,而物理数有所改变 */
   printf("q1 = %d\n",(signed int)q1);/* 无符号的数转化为同等长度的有符号的数---物理数不变,逻辑数可能变化 */
  
  
  
    /*27. 枚举使用
  *
  *
  *
  *
  *
  * */
     printf("--------------------------27------------------------------\n");
 
  weekday days = Sunday;
  printf("days = %d\n",Sunday);
  printf("days = %d\n",days);
 
 
 
 
 
  
 return 0;
}


 

你可能感兴趣的:(C语言)